Centralizing HTTP client creation and configuration

This commit is contained in:
Ryan Baxter
2017-06-28 11:43:52 -04:00
parent e2954d0117
commit 2aa93abc0f
19 changed files with 705 additions and 162 deletions

View File

@@ -18,14 +18,24 @@ package org.springframework.cloud.netflix.feign;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.netflix.feign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -34,12 +44,17 @@ import feign.Feign;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import javax.annotation.PreDestroy;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
/**
* @author Spencer Gibb
* @author Julien Roy
*/
@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({FeignHttpClientProperties.class})
public class FeignAutoConfiguration {
@Autowired(required = false)
@@ -86,17 +101,51 @@ public class FeignAutoConfiguration {
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
@Autowired(required = false)
private HttpClient httpClient;
private RegistryBuilder registryBuilder;
private CloseableHttpClient httpClient;
@Bean
public HttpClientConnectionManager connectionManager(ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(false, httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(),
registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
@Bean
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom().
setConnectTimeout(httpClientProperties.getConnectionTimeout()).
setRedirectsEnabled(httpClientProperties.isFollowRedirects()).
build();
this.httpClient = httpClientFactory.createClient(defaultRequestConfig, httpClientConnectionManager);
return this.httpClient;
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient() {
if (this.httpClient != null) {
return new ApacheHttpClient(this.httpClient);
}
return new ApacheHttpClient();
public Client feignClient(HttpClient httpClient) {
return new ApacheHttpClient(httpClient);
}
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
httpClient.close();
}
}

View File

@@ -17,14 +17,22 @@
package org.springframework.cloud.netflix.feign.ribbon;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
import org.springframework.cloud.netflix.feign.support.FeignHttpClientProperties;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -38,6 +46,10 @@ import feign.Request;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import java.util.Timer;
import java.util.TimerTask;
import javax.annotation.PreDestroy;
/**
* Autoconfiguration to be activated if Feign is in use and needs to be use Ribbon as a
* load balancer.
@@ -47,6 +59,7 @@ import feign.okhttp.OkHttpClient;
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@Configuration
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({FeignHttpClientProperties.class})
public class FeignRibbonClientAutoConfiguration {
@Bean
@@ -84,22 +97,54 @@ public class FeignRibbonClientAutoConfiguration {
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientFeignLoadBalancedConfiguration {
private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
private CloseableHttpClient httpClient;
@Autowired(required = false)
private HttpClient httpClient;
private RegistryBuilder registryBuilder;
@Bean
public HttpClientConnectionManager connectionManager(ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(false, httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(),
registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
@Bean
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom().
setConnectTimeout(httpClientProperties.getConnectionTimeout()).
setRedirectsEnabled(httpClientProperties.isFollowRedirects()).
build();
this.httpClient = httpClientFactory.createClient(defaultRequestConfig, httpClientConnectionManager);
return this.httpClient;
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
ApacheHttpClient delegate;
if (this.httpClient != null) {
delegate = new ApacheHttpClient(this.httpClient);
}
else {
delegate = new ApacheHttpClient();
}
SpringClientFactory clientFactory, HttpClient httpClient) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
httpClient.close();
}
}
@Configuration

View File

@@ -0,0 +1,110 @@
/*
*
* * 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.netflix.feign.support;
import java.util.concurrent.TimeUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Ryan Baxter
*/
@ConfigurationProperties(prefix = "feign.httpclient")
public class FeignHttpClientProperties {
public static final boolean DEFAULT_DISABLE_SSL_VALIDATION = false;
public static final int DEFAULT_MAX_CONNECTIONS = 200;
public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 50;
public static final long DEFAULT_TIME_TO_LIVE = 900L;
public static final TimeUnit DEFAULT_TIME_TO_LIVE_UNIT = TimeUnit.SECONDS;
public static final boolean DEFAULT_FOLLOW_REDIRECTS = true;
public static final int DEFAULT_CONNECTION_TIMEOUT = 2000;
public static final int DEFAULT_CONNECTION_TIMER_REPEAT = 3000;
private boolean disableSslValidation = DEFAULT_DISABLE_SSL_VALIDATION;
private int maxConnections = DEFAULT_MAX_CONNECTIONS;
private int maxConnectionsPerRoute = DEFAULT_MAX_CONNECTIONS_PER_ROUTE;
private long timeToLive = DEFAULT_TIME_TO_LIVE;
private TimeUnit timeToLiveUnit = DEFAULT_TIME_TO_LIVE_UNIT;
private boolean followRedirects = DEFAULT_FOLLOW_REDIRECTS;
private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
private int connectionTimerRepeat = DEFAULT_CONNECTION_TIMER_REPEAT;
public int getConnectionTimerRepeat() {
return connectionTimerRepeat;
}
public void setConnectionTimerRepeat(int connectionTimerRepeat) {
this.connectionTimerRepeat = connectionTimerRepeat;
}
public boolean isDisableSslValidation() {
return disableSslValidation;
}
public void setDisableSslValidation(boolean disableSslValidation) {
this.disableSslValidation = disableSslValidation;
}
public int getMaxConnections() {
return maxConnections;
}
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
public int getMaxConnectionsPerRoute() {
return maxConnectionsPerRoute;
}
public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute) {
this.maxConnectionsPerRoute = maxConnectionsPerRoute;
}
public long getTimeToLive() {
return timeToLive;
}
public void setTimeToLive(long timeToLive) {
this.timeToLive = timeToLive;
}
public TimeUnit getTimeToLiveUnit() {
return timeToLiveUnit;
}
public void setTimeToLiveUnit(TimeUnit timeToLiveUnit) {
this.timeToLiveUnit = timeToLiveUnit;
}
public boolean isFollowRedirects() {
return followRedirects;
}
public void setFollowRedirects(boolean followRedirects) {
this.followRedirects = followRedirects;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
}

View File

@@ -17,11 +17,19 @@
package org.springframework.cloud.netflix.ribbon;
import java.net.URI;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -30,17 +38,22 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClas
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.apache.RetryableRibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient;
import org.springframework.cloud.netflix.ribbon.okhttp.RetryableOkHttpLoadBalancingClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.DefaultLoadBalancerRetryHandler;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ConfigurationBasedServerList;
@@ -70,6 +83,7 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttps
@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
@Import(HttpClientConfiguration.class)
public class RibbonClientConfiguration {
@Value("${ribbon.client.name}")
@@ -121,6 +135,71 @@ public class RibbonClientConfiguration {
return serverList;
}
@Configuration
@ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true)
protected static class ApacheHttpClientConfiguration {
private final Timer connectionManagerTimer = new Timer(
"RibbonApacheHttpClientConfiguration.connectionManagerTimer", true);
private CloseableHttpClient httpClient;
@Autowired(required = false)
private RegistryBuilder registryBuilder;
@Bean
public HttpClientConnectionManager httpClientConnectionManager(IClientConfig config,
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
ApacheHttpClientFactory httpClientFactory) {
Integer maxTotalConnections = config.getPropertyAsInteger(CommonClientConfigKey.MaxTotalConnections,
DefaultClientConfigImpl.DEFAULT_MAX_TOTAL_CONNECTIONS);
Integer maxConnectionsPerHost = config.getPropertyAsInteger(CommonClientConfigKey.MaxConnectionsPerHost,
DefaultClientConfigImpl.DEFAULT_MAX_CONNECTIONS_PER_HOST);
Integer timerRepeat = config.getPropertyAsInteger(CommonClientConfigKey.ConnectionCleanerRepeatInterval,
DefaultClientConfigImpl.DEFAULT_CONNECTION_IDLE_TIMERTASK_REPEAT_IN_MSECS);
Object timeToLiveObj = config.getProperty(CommonClientConfigKey.PoolKeepAliveTime);
Long timeToLive = DefaultClientConfigImpl.DEFAULT_POOL_KEEP_ALIVE_TIME;
Object ttlUnitObj = config.getProperty(CommonClientConfigKey.PoolKeepAliveTimeUnits);
TimeUnit ttlUnit = DefaultClientConfigImpl.DEFAULT_POOL_KEEP_ALIVE_TIME_UNITS;
if(timeToLiveObj instanceof Long) {
timeToLive = (Long)timeToLiveObj;
}
if(ttlUnitObj instanceof TimeUnit) {
ttlUnit = (TimeUnit)ttlUnitObj;
}
final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(false, maxTotalConnections,
maxConnectionsPerHost, timeToLive, ttlUnit, registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, timerRepeat);
return connectionManager;
}
@Bean
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
HttpClientConnectionManager connectionManager, IClientConfig config) {
Boolean followRedirects = config.getPropertyAsBoolean(
CommonClientConfigKey.FollowRedirects,
DefaultClientConfigImpl.DEFAULT_FOLLOW_REDIRECTS);
Integer connectTimeout = config.getPropertyAsInteger(
CommonClientConfigKey.ConnectTimeout,
DefaultClientConfigImpl.DEFAULT_CONNECT_TIMEOUT);
RequestConfig defaultRequestConfig = RequestConfig.custom().
setConnectTimeout(connectTimeout).
setRedirectsEnabled(followRedirects).
build();
this.httpClient = httpClientFactory.createClient(defaultRequestConfig, connectionManager);
return httpClient;
}
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
httpClient.close();
}
}
@Configuration
@ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientRibbonConfiguration {
@@ -132,8 +211,8 @@ public class RibbonClientConfiguration {
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient(
IClientConfig config, ServerIntrospector serverIntrospector,
ILoadBalancer loadBalancer, RetryHandler retryHandler) {
RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(
ILoadBalancer loadBalancer, RetryHandler retryHandler, CloseableHttpClient httpClient) {
RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(httpClient,
config, serverIntrospector);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
@@ -147,8 +226,9 @@ public class RibbonClientConfiguration {
public RetryableRibbonLoadBalancingHttpClient retryableRibbonLoadBalancingHttpClient(
IClientConfig config, ServerIntrospector serverIntrospector,
ILoadBalancer loadBalancer, RetryHandler retryHandler,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
RetryableRibbonLoadBalancingHttpClient client = new RetryableRibbonLoadBalancingHttpClient(
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory,
CloseableHttpClient httpClient) {
RetryableRibbonLoadBalancingHttpClient client = new RetryableRibbonLoadBalancingHttpClient(httpClient,
config, serverIntrospector, loadBalancedRetryPolicyFactory);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);

View File

@@ -22,6 +22,7 @@ import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
@@ -50,10 +51,17 @@ import com.netflix.loadbalancer.Server;
public class RetryableRibbonLoadBalancingHttpClient extends RibbonLoadBalancingHttpClient implements ServiceInstanceChooser {
private LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory =
new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
public RetryableRibbonLoadBalancingHttpClient(IClientConfig config, ServerIntrospector serverIntrospector, LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
public RetryableRibbonLoadBalancingHttpClient(IClientConfig config, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
super(config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
}
public RetryableRibbonLoadBalancingHttpClient(CloseableHttpClient delegate, IClientConfig config, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
super(delegate, config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
}
@Override
public RibbonApacheHttpResponse execute(final RibbonApacheHttpRequest request, final IClientConfig configOverride) throws Exception {

View File

@@ -20,12 +20,11 @@ import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.support.AbstractLoadBalancingClient;
@@ -41,27 +40,17 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttps
*/
//TODO: rename (ie new class that extends this in Dalston) to ApacheHttpLoadBalancingClient
public class RibbonLoadBalancingHttpClient extends
AbstractLoadBalancingClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse, HttpClient> {
@Deprecated
public RibbonLoadBalancingHttpClient() {
super();
}
@Deprecated
public RibbonLoadBalancingHttpClient(final ILoadBalancer lb) {
super(lb);
}
AbstractLoadBalancingClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse, CloseableHttpClient> {
public RibbonLoadBalancingHttpClient(IClientConfig config, ServerIntrospector serverIntrospector) {
super(config, serverIntrospector);
}
public RibbonLoadBalancingHttpClient(HttpClient delegate, IClientConfig config, ServerIntrospector serverIntrospector) {
public RibbonLoadBalancingHttpClient(CloseableHttpClient delegate, IClientConfig config, ServerIntrospector serverIntrospector) {
super(delegate, config, serverIntrospector);
}
protected HttpClient createDelegate(IClientConfig config) {
protected CloseableHttpClient createDelegate(IClientConfig config) {
return HttpClientBuilder.create()
// already defaults to 0 in builder, so resetting to 0 won't hurt
.setMaxConnTotal(config.getPropertyAsInteger(CommonClientConfigKey.MaxTotalConnections, 0))

View File

@@ -32,6 +32,9 @@ import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.client.discovery.event.HeartbeatMonitor;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
import org.springframework.cloud.client.discovery.event.ParentHeartbeatEvent;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
@@ -59,7 +62,8 @@ import org.springframework.context.annotation.Import;
@Configuration
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class })
RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
HttpClientConfiguration.class})
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
@@ -102,8 +106,10 @@ public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
@Bean
@ConditionalOnMissingBean(SimpleHostRoutingFilter.class)
public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties zuulProperties) {
return new SimpleHostRoutingFilter(helper, zuulProperties);
public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties zuulProperties,
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
ApacheHttpClientFactory httpClientFactory) {
return new SimpleHostRoutingFilter(helper, zuulProperties, connectionManagerFactory, httpClientFactory);
}
@Bean

View File

@@ -19,9 +19,6 @@ package org.springframework.cloud.netflix.zuul.filters.route;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -30,9 +27,6 @@ import java.util.TimerTask;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
@@ -41,32 +35,21 @@ import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolException;
import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
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.conn.HttpClientConnectionManager;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.protocol.HttpContext;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
@@ -80,8 +63,6 @@ import org.springframework.util.StringUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.HTTPS_SCHEME;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.HTTP_SCHEME;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.ROUTE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SIMPLE_HOST_ROUTING_FILTER_ORDER;
@@ -105,7 +86,9 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
private ProxyRequestHelper helper;
private Host hostProperties;
private PoolingHttpClientConnectionManager connectionManager;
private ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
private ApacheHttpClientFactory httpClientFactory;
private HttpClientConnectionManager connectionManager;
private CloseableHttpClient httpClient;
@EventListener
@@ -130,16 +113,23 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
}
}
public SimpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties properties) {
public SimpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties properties,
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
ApacheHttpClientFactory httpClientFactory) {
this.helper = helper;
this.hostProperties = properties.getHost();
this.sslHostnameValidationEnabled = properties.isSslHostnameValidationEnabled();
this.forceOriginalQueryStringEncoding = properties
.isForceOriginalQueryStringEncoding();
this.connectionManagerFactory = connectionManagerFactory;
this.httpClientFactory = httpClientFactory;
}
@PostConstruct
private void initialize() {
this.connectionManager = connectionManagerFactory.newConnectionManager(this.sslHostnameValidationEnabled,
this.hostProperties.getMaxTotalConnections(), this.hostProperties.getMaxPerRouteConnections(),
this.hostProperties.getTimeToLive(), this.hostProperties.getTimeUnit(), null);
this.httpClient = newClient();
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
@@ -201,50 +191,8 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
return null;
}
protected PoolingHttpClientConnectionManager newConnectionManager() {
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<ConnectionSocketFactory> registryBuilder = RegistryBuilder
.<ConnectionSocketFactory> create()
.register(HTTP_SCHEME, PlainConnectionSocketFactory.INSTANCE);
if (this.sslHostnameValidationEnabled) {
registryBuilder.register(HTTPS_SCHEME,
new SSLConnectionSocketFactory(sslContext));
}
else {
registryBuilder.register(HTTPS_SCHEME, new SSLConnectionSocketFactory(
sslContext, NoopHostnameVerifier.INSTANCE));
}
final Registry<ConnectionSocketFactory> registry = registryBuilder.build();
this.connectionManager = new PoolingHttpClientConnectionManager(registry, null, null, null,
hostProperties.getTimeToLive(), hostProperties.getTimeUnit());
this.connectionManager
.setMaxTotal(this.hostProperties.getMaxTotalConnections());
this.connectionManager.setDefaultMaxPerRoute(
this.hostProperties.getMaxPerRouteConnections());
return this.connectionManager;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
protected HttpClientConnectionManager getConnectionManager() {
return connectionManager;
}
protected CloseableHttpClient newClient() {
@@ -252,30 +200,7 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
.setSocketTimeout(this.hostProperties.getSocketTimeoutMillis())
.setConnectTimeout(this.hostProperties.getConnectTimeoutMillis())
.setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
HttpClientBuilder httpClientBuilder = HttpClients.custom();
if (!this.sslHostnameValidationEnabled) {
httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
}
return httpClientBuilder.setConnectionManager(newConnectionManager())
.disableContentCompression()
.useSystemProperties().setDefaultRequestConfig(requestConfig)
.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
.setRedirectStrategy(new RedirectStrategy() {
@Override
public boolean isRedirected(HttpRequest request,
HttpResponse response, HttpContext context)
throws ProtocolException {
return false;
}
@Override
public HttpUriRequest getRedirect(HttpRequest request,
HttpResponse response, HttpContext context)
throws ProtocolException {
return null;
}
}).build();
return httpClientFactory.createClient(requestConfig, this.connectionManager);
}
private CloseableHttpResponse forward(CloseableHttpClient httpclient, String verb,

View File

@@ -0,0 +1,203 @@
/*
*
* * 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.netflix;
import feign.Client;
import feign.httpclient.ApacheHttpClient;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import org.apache.http.Header;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockingDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientFactory;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient;
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter;
import org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommand;
import org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockingDetails;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = HttpClientConfigurationTestApp.class, value = {"feign.okhttp.enabled: false",
"ribbon.eureka.enabled = false"})
@DirtiesContext
public class HttpClientConfigurationTests {
@Autowired
ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
@Autowired
ApacheHttpClientFactory httpClientFactory;
@Autowired
SimpleHostRoutingFilter simpleHostRoutingFilter;
@Autowired
LoadBalancerFeignClient feignClient;
@Autowired
HttpClientRibbonCommandFactory httpClientRibbonCommandFactory;
@Test
public void testFactories() {
assertTrue(ApacheHttpClientConnectionManagerFactory.class.isInstance(connectionManagerFactory));
assertTrue(HttpClientConfigurationTestApp.MyApacheHttpClientConnectionManagerFactory.class.isInstance(connectionManagerFactory));
assertTrue(ApacheHttpClientFactory.class.isInstance(httpClientFactory));
assertTrue(HttpClientConfigurationTestApp.MyApacheHttpClientFactory.class.isInstance(httpClientFactory));
}
@Test
public void testHttpClientSimpleHostRoutingFilter() {
PoolingHttpClientConnectionManager connectionManager = getField(simpleHostRoutingFilter, "connectionManager");
MockingDetails connectionManagerDetails = mockingDetails(connectionManager);
assertTrue(connectionManagerDetails.isMock());
CloseableHttpClient httpClient = getField(simpleHostRoutingFilter, "httpClient");
MockingDetails httpClientDetails = mockingDetails(httpClient);
assertTrue(httpClientDetails.isMock());
}
@Test
public void testHttpClientWithFeign() {
Client delegate = feignClient.getDelegate();
assertTrue(ApacheHttpClient.class.isInstance(delegate));
ApacheHttpClient apacheHttpClient = (ApacheHttpClient)delegate;
HttpClient httpClient = getField(apacheHttpClient, "client");
MockingDetails httpClientDetails = mockingDetails(httpClient);
assertTrue(httpClientDetails.isMock());
}
@Test
public void testRibbonLoadBalancingHttpClient() {
RibbonCommandContext context = new RibbonCommandContext("foo"," GET", "http://localhost",
false, new LinkedMultiValueMap<String, String>(), new LinkedMultiValueMap<String, String>(),
null, new ArrayList<RibbonRequestCustomizer>(), 0l);
HttpClientRibbonCommand command = httpClientRibbonCommandFactory.create(context);
RibbonLoadBalancingHttpClient ribbonClient = command.getClient();
CloseableHttpClient httpClient = getField(ribbonClient, "delegate");
MockingDetails httpClientDetails = mockingDetails(httpClient);
assertTrue(httpClientDetails.isMock());
}
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;
}
}
@Configuration
@EnableAutoConfiguration
@RestController
@EnableFeignClients(clients = {HttpClientConfigurationTestApp.FooClient.class})
@EnableZuulProxy
class HttpClientConfigurationTestApp {
@RequestMapping
public String index() {
return "hello";
}
static class MyApacheHttpClientConnectionManagerFactory extends DefaultApacheHttpClientConnectionManagerFactory {
@Override
public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation, int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive, TimeUnit timeUnit, RegistryBuilder registry) {
return mock(PoolingHttpClientConnectionManager.class);
}
}
static class MyApacheHttpClientFactory extends DefaultApacheHttpClientFactory {
@Override
public CloseableHttpClient createClient(RequestConfig requestConfig, HttpClientConnectionManager connectionManager) {
CloseableHttpClient client = mock(CloseableHttpClient.class);
CloseableHttpResponse response = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
doReturn(statusLine).when(response).getStatusLine();
Header[] headers = new BasicHeader[0];
doReturn(headers).when(response).getAllHeaders();
try {
doReturn(response).when(client).execute(any(HttpUriRequest.class));
} catch (IOException e) {
e.printStackTrace();
}
return client;
}
}
@Configuration
static class MyConfig {
@Bean
public ApacheHttpClientFactory apacheHttpClientFactory() {
return new MyApacheHttpClientFactory();
}
@Bean
public ApacheHttpClientConnectionManagerFactory connectionManagerFactory() {
return new MyApacheHttpClientConnectionManagerFactory();
}
}
@FeignClient(name="foo", serviceId = "foo")
static interface FooClient {}
}

View File

@@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfigurati
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.ClassPathExclusions;
import org.springframework.cloud.FilteredClassPathRunner;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.netflix.feign.encoding.FeignAcceptGzipEncodingAutoConfiguration;
import org.springframework.cloud.netflix.feign.encoding.FeignAcceptGzipEncodingInterceptor;
@@ -58,7 +59,7 @@ public class FeignCompressionTests {
context = new SpringApplicationBuilder().properties("feign.compression.response.enabled=true",
"feign.compression.request.enabled=true", "feign.okhttp.enabled=false").sources(PropertyPlaceholderAutoConfiguration.class,
ArchaiusAutoConfiguration.class, FeignAutoConfiguration.class, PlainConfig.class, FeignContentGzipEncodingAutoConfiguration.class,
FeignAcceptGzipEncodingAutoConfiguration.class).web(false).run();
FeignAcceptGzipEncodingAutoConfiguration.class, HttpClientConfiguration.class).web(false).run();
}
@After

View File

@@ -0,0 +1,98 @@
/*
*
* * 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.netflix.feign.support;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment;
/**
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
@DirtiesContext
public class FeignHttpClientPropertiesTests {
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@After
public void clear() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void testDefaults() {
setupContext();
assertEquals(FeignHttpClientProperties.DEFAULT_CONNECTION_TIMEOUT, getProperties().getConnectionTimeout());
assertEquals(FeignHttpClientProperties.DEFAULT_MAX_CONNECTIONS, getProperties().getMaxConnections());
assertEquals(FeignHttpClientProperties.DEFAULT_MAX_CONNECTIONS_PER_ROUTE, getProperties().getMaxConnectionsPerRoute());
assertEquals(FeignHttpClientProperties.DEFAULT_TIME_TO_LIVE, getProperties().getTimeToLive());
assertEquals(FeignHttpClientProperties.DEFAULT_DISABLE_SSL_VALIDATION, getProperties().isDisableSslValidation());
assertEquals(FeignHttpClientProperties.DEFAULT_FOLLOW_REDIRECTS, getProperties().isFollowRedirects());
}
@Test
public void testCustomization() {
addEnvironment(this.context, "feign.httpclient.maxConnections=2",
"feign.httpclient.connectionTimeout=2",
"feign.httpclient.maxConnectionsPerRoute=2",
"feign.httpclient.timeToLive=2",
"feign.httpclient.disableSslValidation=true",
"feign.httpclient.followRedirects=false");
setupContext();
assertEquals(2, getProperties().getMaxConnections());
assertEquals(2, getProperties().getConnectionTimeout());
assertEquals(2, getProperties().getMaxConnectionsPerRoute());
assertEquals(2L, getProperties().getTimeToLive());
assertTrue(getProperties().isDisableSslValidation());
assertFalse(getProperties().isFollowRedirects());
}
private void setupContext() {
this.context.register(PropertyPlaceholderAutoConfiguration.class, TestConfiguration.class);
this.context.refresh();
}
private FeignHttpClientProperties getProperties() {
return this.context.getBean(FeignHttpClientProperties.class);
}
@Configuration
@EnableConfigurationProperties
protected static class TestConfiguration {
@Bean
FeignHttpClientProperties zuulProperties() {
return new FeignHttpClientProperties() ;
}
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.cloud.netflix.feign.valid;
import org.junit.Test;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
import org.springframework.cloud.netflix.feign.FeignClient;
@@ -45,7 +46,7 @@ public class FeignClientValidationTests {
}
@Configuration
@Import(FeignAutoConfiguration.class)
@Import({FeignAutoConfiguration.class, HttpClientConfiguration.class})
@EnableFeignClients(clients = GoodUrlConfiguration.Client.class)
protected static class GoodUrlConfiguration {
@@ -67,7 +68,7 @@ public class FeignClientValidationTests {
}
@Configuration
@Import(FeignAutoConfiguration.class)
@Import({FeignAutoConfiguration.class, HttpClientConfiguration.class})
@EnableFeignClients(clients = PlaceholderUrlConfiguration.Client.class)
protected static class PlaceholderUrlConfiguration {
@@ -92,7 +93,7 @@ public class FeignClientValidationTests {
}
@Configuration
@Import(FeignAutoConfiguration.class)
@Import({FeignAutoConfiguration.class, HttpClientConfiguration.class})
@EnableFeignClients(clients = GoodServiceIdConfiguration.Client.class)
protected static class GoodServiceIdConfiguration {

View File

@@ -23,6 +23,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientsPreprocessorIntegrationTests.TestConfiguration;
@@ -66,7 +67,7 @@ public class RibbonClientsPreprocessorIntegrationTests {
@Configuration
@RibbonClients(@RibbonClient(name = "foo", configuration = FooConfiguration.class))
@Import({ UtilAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class })
ArchaiusAutoConfiguration.class, RibbonAutoConfiguration.class, HttpClientConfiguration.class})
protected static class TestConfiguration {
}

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.netflix.ribbon;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.junit.Test;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -69,7 +70,7 @@ public class SpringClientFactoryTests {
public void testConfigureRetry() {
SpringClientFactory factory = new SpringClientFactory();
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(
RibbonAutoConfiguration.class, ArchaiusAutoConfiguration.class);
RibbonAutoConfiguration.class, ArchaiusAutoConfiguration.class, HttpClientConfiguration.class);
addEnvironment(parent, "foo.ribbon.MaxAutoRetries:2");
factory.setApplicationContext(parent);
DefaultLoadBalancerRetryHandler retryHandler = (DefaultLoadBalancerRetryHandler) factory

View File

@@ -25,6 +25,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.BeansException;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.feign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer;
import org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration;
@@ -45,7 +46,7 @@ import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RibbonAutoConfiguration.class, RibbonClientConfiguration.class, LoadBalancerAutoConfiguration.class,
FeignRibbonClientAutoConfiguration.class})
FeignRibbonClientAutoConfiguration.class, HttpClientConfiguration.class})
public class SpringRetryEnabledTests implements ApplicationContextAware {
private ApplicationContext context;

View File

@@ -24,6 +24,7 @@ import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.junit.After;
import org.junit.Before;
@@ -169,7 +170,7 @@ public class RibbonLoadBalancingHttpClientTests {
@Test
public void testNeverRetry() throws Exception {
ServerIntrospector introspector = mock(ServerIntrospector.class);
HttpClient delegate = mock(HttpClient.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
HttpResponse response = mock(HttpResponse.class);
doThrow(new IOException("boom")).when(delegate).execute(any(HttpUriRequest.class));
DefaultClientConfigImpl clientConfig = new DefaultClientConfigImpl();
@@ -220,8 +221,8 @@ public class RibbonLoadBalancingHttpClientTests {
int port = 80;
HttpMethod method = HttpMethod.GET;
URI uri = new URI("http://" + host + ":" + port);
HttpClient delegate = mock(HttpClient.class);
final HttpResponse response = mock(HttpResponse.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
doReturn(statusLine).when(response).getStatusLine();
@@ -252,8 +253,8 @@ public class RibbonLoadBalancingHttpClientTests {
int port = 80;
HttpMethod method = HttpMethod.GET;
URI uri = new URI("http://" + host + ":" + port);
HttpClient delegate = mock(HttpClient.class);
final HttpResponse response = mock(HttpResponse.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
doReturn(statusLine).when(response).getStatusLine();
@@ -285,7 +286,7 @@ public class RibbonLoadBalancingHttpClientTests {
int port = 80;
HttpMethod method = HttpMethod.POST;
URI uri = new URI("http://" + host + ":" + port);
HttpClient delegate = mock(HttpClient.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
@@ -318,7 +319,7 @@ public class RibbonLoadBalancingHttpClientTests {
int port = 80;
HttpMethod method = HttpMethod.POST;
URI uri = new URI("http://" + host + ":" + port);
HttpClient delegate = mock(HttpClient.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
doThrow(new IOException("boom")).doThrow(new IOException("boom again")).doReturn(response).
when(delegate).execute(any(HttpUriRequest.class));
@@ -353,8 +354,8 @@ public class RibbonLoadBalancingHttpClientTests {
int port = 80;
HttpMethod method = HttpMethod.GET;
URI uri = new URI("http://" + host + ":" + port);
HttpClient delegate = mock(HttpClient.class);
final HttpResponse response = mock(HttpResponse.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
doReturn(statusLine).when(response).getStatusLine();
@@ -442,13 +443,13 @@ public class RibbonLoadBalancingHttpClientTests {
String host = serviceName;
int port = 80;
URI uri = new URI("http://" + host + ":" + port);
HttpClient delegate = mock(HttpClient.class);
CloseableHttpClient delegate = mock(CloseableHttpClient.class);
RibbonLoadBalancingHttpClient client = factory.getClient("service",
RibbonLoadBalancingHttpClient.class);
ReflectionTestUtils.setField(client, "delegate", delegate);
ReflectionTestUtils.setField(client, "lb", loadBalancer);
HttpResponse httpResponse = mock(HttpResponse.class);
CloseableHttpResponse httpResponse = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
doReturn(statusLine).when(httpResponse).getStatusLine();

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.netflix.ribbon.test;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
@@ -40,7 +41,7 @@ import com.netflix.loadbalancer.ServerListSubsetFilter;
*/
@Configuration
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
UtilAutoConfiguration.class, RibbonAutoConfiguration.class })
UtilAutoConfiguration.class, RibbonAutoConfiguration.class, HttpClientConfiguration.class})
@RibbonClients(defaultConfiguration = DefaultRibbonConfig.class)
public class RibbonClientDefaultConfigurationTestsConfig {

View File

@@ -23,6 +23,7 @@ import com.netflix.zuul.context.RequestContext;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
@@ -38,6 +39,9 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientFactory;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.RoutesMvcEndpoint;
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
@@ -210,16 +214,23 @@ class SampleCustomZuulProxyApplication {
@EnableZuulProxy
protected static class CustomZuulProxyConfig {
@Bean
public ApacheHttpClientFactory customHttpClientFactory() {
return new CustomApacheHttpClientFactory();
}
@Bean
public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper,
ZuulProperties zuulProperties) {
return new CustomHostRoutingFilter(helper, zuulProperties);
ZuulProperties zuulProperties, ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
ApacheHttpClientFactory httpClientFactory) {
return new CustomHostRoutingFilter(helper, zuulProperties, connectionManagerFactory, httpClientFactory);
}
private class CustomHostRoutingFilter extends SimpleHostRoutingFilter {
public CustomHostRoutingFilter(ProxyRequestHelper helper,
ZuulProperties zuulProperties) {
super(helper, zuulProperties);
ZuulProperties zuulProperties, ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
ApacheHttpClientFactory httpClientFactory) {
super(helper, zuulProperties, connectionManagerFactory, httpClientFactory);
}
@Override
@@ -227,13 +238,13 @@ class SampleCustomZuulProxyApplication {
super.addIgnoredHeaders("X-Ignored");
return super.run();
}
}
private class CustomApacheHttpClientFactory extends DefaultApacheHttpClientFactory {
@Override
protected CloseableHttpClient newClient() {
// Custom client with cookie support.
// In practice, we would want a custom cookie store using a multimap with
// a user key.
return HttpClients.custom().setConnectionManager(newConnectionManager())
public CloseableHttpClient createClient(RequestConfig requestConfig, HttpClientConnectionManager connectionManager) {
return HttpClients.custom().setConnectionManager(connectionManager)
.setDefaultCookieStore(new BasicCookieStore())
.setDefaultRequestConfig(RequestConfig.custom()
.setCookieSpec(CookieSpecs.DEFAULT).build())

View File

@@ -47,6 +47,10 @@ import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoCon
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientFactory;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
@@ -113,7 +117,7 @@ public class SimpleHostRoutingFilterTests {
"zuul.host.maxPerRouteConnections=10", "zuul.host.timeToLive=5",
"zuul.host.timeUnit=SECONDS");
setupContext();
PoolingHttpClientConnectionManager connMgr = getFilter().newConnectionManager();
PoolingHttpClientConnectionManager connMgr = (PoolingHttpClientConnectionManager)getFilter().getConnectionManager();
assertEquals(100, connMgr.getMaxTotal());
assertEquals(10, connMgr.getDefaultMaxPerRoute());
Object pool = getField(connMgr, "pool");
@@ -148,7 +152,7 @@ public class SimpleHostRoutingFilterTests {
@Test
public void defaultPropertiesAreApplied() {
setupContext();
PoolingHttpClientConnectionManager connMgr = getFilter().newConnectionManager();
PoolingHttpClientConnectionManager connMgr = (PoolingHttpClientConnectionManager)getFilter().getConnectionManager();
assertEquals(200, connMgr.getMaxTotal());
assertEquals(20, connMgr.getDefaultMaxPerRoute());
@@ -222,8 +226,16 @@ public class SimpleHostRoutingFilterTests {
}
@Bean
SimpleHostRoutingFilter simpleHostRoutingFilter(ZuulProperties zuulProperties) {
return new SimpleHostRoutingFilter(new ProxyRequestHelper(), zuulProperties);
ApacheHttpClientFactory clientFactory() {return new DefaultApacheHttpClientFactory(); }
@Bean
ApacheHttpClientConnectionManagerFactory connectionManagerFactory() { return new DefaultApacheHttpClientConnectionManagerFactory(); }
@Bean
SimpleHostRoutingFilter simpleHostRoutingFilter(ZuulProperties zuulProperties,
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
ApacheHttpClientFactory clientFactory) {
return new SimpleHostRoutingFilter(new ProxyRequestHelper(), zuulProperties, connectionManagerFactory, clientFactory);
}
}
}