Remove http client 4 support (#785)

This commit is contained in:
Olga Maciaszek-Sharma
2022-11-10 18:50:12 +01:00
committed by GitHub
parent 6f1fab1ab3
commit d30540d144
29 changed files with 267 additions and 716 deletions

View File

@@ -16,13 +16,10 @@
package org.springframework.cloud.openfeign;
import java.io.IOException;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.databind.Module;
@@ -31,17 +28,9 @@ import feign.Client;
import feign.Feign;
import feign.Target;
import feign.hc5.ApacheHttp5Client;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import jakarta.annotation.PreDestroy;
import okhttp3.ConnectionPool;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.beans.factory.annotation.Value;
@@ -54,8 +43,6 @@ import org.springframework.cache.interceptor.CacheInterceptor;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.openfeign.security.OAuth2AccessTokenInterceptor;
@@ -94,8 +81,6 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
FeignEncoderProperties.class })
public class FeignAutoConfiguration {
private static final Log LOG = LogFactory.getLog(FeignAutoConfiguration.class);
@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
@@ -212,75 +197,6 @@ public class FeignAutoConfiguration {
// SC loadbalancer is not on the class path.
// see corresponding configurations in FeignLoadBalancerAutoConfiguration
// for load-balanced clients.
@SuppressWarnings("rawtypes")
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnMissingBean(CloseableHttpClient.class)
@ConditionalOnProperty(value = "spring.cloud.openfeign.httpclient.enabled", matchIfMissing = true)
@Conditional(HttpClient5DisabledConditions.class)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
@Autowired(required = false)
private RegistryBuilder registryBuilder;
private CloseableHttpClient httpClient;
@Bean
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
public HttpClientConnectionManager connectionManager(
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(
httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(), this.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.createBuilder().setConnectionManager(httpClientConnectionManager)
.setDefaultRequestConfig(defaultRequestConfig).build();
return this.httpClient;
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(HttpClient httpClient) {
return new ApacheHttpClient(httpClient);
}
@PreDestroy
public void destroy() {
this.connectionManagerTimer.cancel();
if (this.httpClient != null) {
try {
this.httpClient.close();
}
catch (IOException e) {
if (LOG.isErrorEnabled()) {
LOG.error("Could not correctly close httpClient.");
}
}
}
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@@ -328,10 +244,15 @@ public class FeignAutoConfiguration {
}
// the following configuration is for alternate feign clients if
// SC loadbalancer is not on the class path.
// see corresponding configurations in FeignLoadBalancerAutoConfiguration
// for load-balanced clients.
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttp5Client.class)
@ConditionalOnMissingBean(org.apache.hc.client5.http.impl.classic.CloseableHttpClient.class)
@ConditionalOnProperty(value = "spring.cloud.openfeign.httpclient.hc5.enabled", havingValue = "true")
@ConditionalOnProperty(value = "spring.cloud.openfeign.httpclient.hc5.enabled", havingValue = "true",
matchIfMissing = true)
@Import(org.springframework.cloud.openfeign.clientconfig.HttpClient5FeignConfiguration.class)
protected static class HttpClient5FeignConfiguration {

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2013-2022 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
*
* https://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.openfeign;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
/**
* @author Nguyen Ky Thanh
*/
public class HttpClient5DisabledConditions extends AnyNestedCondition {
public HttpClient5DisabledConditions() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnMissingClass("feign.hc5.ApacheHttp5Client")
static class ApacheHttp5ClientClassMissing {
}
@ConditionalOnProperty(value = "spring.cloud.openfeign.httpclient.hc5.enabled", havingValue = "false",
matchIfMissing = true)
static class HttpClient5Disabled {
}
}

View File

@@ -1,125 +0,0 @@
/*
* Copyright 2013-2022 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
*
* https://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.openfeign.clientconfig;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import jakarta.annotation.PreDestroy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Default configuration for {@link CloseableHttpClient}.
*
* @author Ryan Baxter
* @author Marcin Grzejszczak
* @author Spencer Gibb
* @author Olga Maciaszek-Sharma
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(CloseableHttpClient.class)
public class HttpClientFeignConfiguration {
private static final Log LOG = LogFactory.getLog(HttpClientFeignConfiguration.class);
private final Timer connectionManagerTimer = new Timer("FeignApacheHttpClientConfiguration.connectionManagerTimer",
true);
private CloseableHttpClient httpClient;
@Autowired(required = false)
private RegistryBuilder registryBuilder;
@Bean
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
public HttpClientConnectionManager connectionManager(
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(
httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
@Bean
@ConditionalOnProperty(value = "spring.cloud.openfeign.compression.response.enabled", havingValue = "true")
public CloseableHttpClient customHttpClient(HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement().useSystemProperties();
this.httpClient = createClient(builder, httpClientConnectionManager, httpClientProperties);
return this.httpClient;
}
@Bean
@ConditionalOnProperty(value = "spring.cloud.openfeign.compression.response.enabled", havingValue = "false",
matchIfMissing = true)
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) {
this.httpClient = createClient(httpClientFactory.createBuilder(), httpClientConnectionManager,
httpClientProperties);
return this.httpClient;
}
private CloseableHttpClient createClient(HttpClientBuilder builder,
HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
.setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
CloseableHttpClient httpClient = builder.setDefaultRequestConfig(defaultRequestConfig)
.setConnectionManager(httpClientConnectionManager).build();
return httpClient;
}
@PreDestroy
public void destroy() {
this.connectionManagerTimer.cancel();
if (this.httpClient != null) {
try {
this.httpClient.close();
}
catch (IOException e) {
if (LOG.isErrorEnabled()) {
LOG.error("Could not correctly close httpClient.");
}
}
}
}
}

View File

@@ -53,8 +53,8 @@ import org.springframework.context.annotation.Import;
// Order is important here, last should be the default, first should be optional
// see
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({ HttpClientFeignLoadBalancerConfiguration.class, OkHttpFeignLoadBalancerConfiguration.class,
HttpClient5FeignLoadBalancerConfiguration.class, DefaultFeignLoadBalancerConfiguration.class })
@Import({ OkHttpFeignLoadBalancerConfiguration.class, HttpClient5FeignLoadBalancerConfiguration.class,
DefaultFeignLoadBalancerConfiguration.class })
public class FeignLoadBalancerAutoConfiguration {
@Bean

View File

@@ -43,11 +43,13 @@ import org.springframework.context.annotation.Import;
*
* @author Nguyen Ky Thanh
* @author changjin wei(魏昌进)
* @author Olga Maciaszek-Sharma
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttp5Client.class)
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
@ConditionalOnProperty(value = "spring.cloud.openfeign.httpclient.hc5.enabled", havingValue = "true")
@ConditionalOnProperty(value = "spring.cloud.openfeign.httpclient.hc5.enabled", havingValue = "true",
matchIfMissing = true)
@Import(HttpClient5FeignConfiguration.class)
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
class HttpClient5FeignLoadBalancerConfiguration {

View File

@@ -1,84 +0,0 @@
/*
* Copyright 2013-2022 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
*
* https://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.openfeign.loadbalancer;
import java.util.List;
import feign.Client;
import feign.httpclient.ApacheHttpClient;
import org.apache.http.client.HttpClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.openfeign.HttpClient5DisabledConditions;
import org.springframework.cloud.openfeign.clientconfig.HttpClientFeignConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Configuration instantiating a {@link LoadBalancerClient}-based {@link Client} object
* that uses {@link ApacheHttpClient} under the hood.
*
* @author Olga Maciaszek-Sharma
* @author Nguyen Ky Thanh
* @author changjin wei(魏昌进)
* @since 2.2.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
@ConditionalOnProperty(value = "spring.cloud.openfeign.httpclient.enabled", matchIfMissing = true)
@Conditional(HttpClient5DisabledConditions.class)
@Import(HttpClientFeignConfiguration.class)
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
class HttpClientFeignLoadBalancerConfiguration {
@Bean
@ConditionalOnMissingBean
@Conditional(OnRetryNotEnabledCondition.class)
public Client feignClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient,
LoadBalancerClientFactory loadBalancerClientFactory,
List<LoadBalancerFeignRequestTransformer> transformers) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory,
transformers);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
@ConditionalOnBean(LoadBalancedRetryFactory.class)
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true",
matchIfMissing = true)
public Client feignRetryClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient,
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory,
List<LoadBalancerFeignRequestTransformer> transformers) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancedRetryFactory,
loadBalancerClientFactory, transformers);
}
}

View File

@@ -47,7 +47,7 @@ class EnableFeignClientsTests {
@BeforeEach
void setUp() {
context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("debug=true", "spring.cloud.openfeign.httpclient.enabled=false")
.properties("debug=true", "spring.cloud.openfeign.httpclient.hc5.enabled=false")
.sources(EnableFeignClientsTests.PlainConfiguration.class).run();
}

View File

@@ -47,7 +47,7 @@ class FeignAutoConfigurationTests {
private final ApplicationContextRunner runner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(FeignAutoConfiguration.class))
.withPropertyValues("spring.cloud.openfeign.httpclient.enabled=false");
.withPropertyValues("spring.cloud.openfeign.httpclient.hc5.enabled=false");
@Test
void shouldInstantiateDefaultTargeterWhenFeignCircuitBreakerIsDisabled() {

View File

@@ -18,27 +18,22 @@ package org.springframework.cloud.openfeign;
import java.util.Map;
import feign.Client;
import feign.RequestInterceptor;
import feign.httpclient.ApacheHttpClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration;
import org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingInterceptor;
import org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration;
import org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
* @author Biju Kunjummen
* @author Olga Maciaszek-Sharma
*/
class FeignCompressionTests {
@@ -49,8 +44,8 @@ class FeignCompressionTests {
"spring.cloud.openfeign.compression.request.enabled=true",
"spring.cloud.openfeign.okhttp.enabled=false")
.withConfiguration(AutoConfigurations.of(FeignAutoConfiguration.class,
FeignContentGzipEncodingAutoConfiguration.class, FeignAcceptGzipEncodingAutoConfiguration.class,
HttpClientConfiguration.class, PlainConfig.class))
FeignContentGzipEncodingAutoConfiguration.class,
FeignAcceptGzipEncodingAutoConfiguration.class))
.run(context -> {
FeignContext feignContext = context.getBean(FeignContext.class);
Map<String, RequestInterceptor> interceptors = feignContext.getInstances("foo",
@@ -63,27 +58,4 @@ class FeignCompressionTests {
});
}
@Configuration(proxyBeanMethods = false)
protected static class PlainConfig {
@Autowired
private Client client;
@Bean
public ApacheHttpClient client() {
/*
* We know our client is an AppacheHttpClient because we disabled the OK HTTP
* client. FeignAcceptGzipEncodingAutoConfiguration won't load unless there is
* a bean of type ApacheHttpClient (not Client) in this test because the bean
* is not yet created and so the application context doesnt know that the
* Client bean is actually an instance of ApacheHttpClient, therefore
* FeignAcceptGzipEncodingAutoConfiguration will not be loaded. We just create
* a bean here of type ApacheHttpClient so that the configuration will be
* loaded correctly.
*/
return (ApacheHttpClient) this.client;
}
}
}

View File

@@ -18,36 +18,26 @@ package org.springframework.cloud.openfeign;
import feign.Client;
import feign.hc5.ApacheHttp5Client;
import feign.httpclient.ApacheHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.test.ClassPathExclusions;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Nguyen Ky Thanh
* @author Olga Maciaszek-Sharma
*/
class FeignHttpClient5ConfigurationTests {
private static void verifyHc4BeansAvailable(ConfigurableApplicationContext context) {
org.apache.http.impl.client.CloseableHttpClient httpClient4 = context
.getBean(org.apache.http.impl.client.CloseableHttpClient.class);
assertThat(httpClient4).isNotNull();
org.apache.http.conn.HttpClientConnectionManager connectionManager4 = context
.getBean(org.apache.http.conn.HttpClientConnectionManager.class);
assertThat(connectionManager4).isInstanceOf(org.apache.http.impl.conn.PoolingHttpClientConnectionManager.class);
Client client = context.getBean(Client.class);
assertThat(client).isInstanceOf(ApacheHttpClient.class);
}
private static void verifyHc5BeansAvailable(ConfigurableApplicationContext context) {
CloseableHttpClient httpClient = context.getBean(CloseableHttpClient.class);
assertThat(httpClient).isNotNull();
@@ -58,12 +48,9 @@ class FeignHttpClient5ConfigurationTests {
}
@Test
void verifyHttpClient5AutoConfig() {
ConfigurableApplicationContext context = new SpringApplicationBuilder()
.properties("spring.cloud.openfeign.httpclient.hc5.enabled=true",
"spring.cloud.openfeign.httpclient.enabled=false")
.web(WebApplicationType.NONE).sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
.run();
void shouldInstantiateHttpClient5ByDefaultWhenDependenciesPresent() {
ConfigurableApplicationContext context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run();
verifyHc5BeansAvailable(context);
@@ -73,65 +60,17 @@ class FeignHttpClient5ConfigurationTests {
}
@Test
void hc5ShouldWinIfTheBothVersionsAvailable() {
void shouldNotInstantiateHttpClient5ByWhenDependenciesPresentButPropertyDisabled() {
ConfigurableApplicationContext context = new SpringApplicationBuilder()
.properties("spring.cloud.openfeign.httpclient.hc5.enabled=true",
"spring.cloud.openfeign.httpclient.enabled=true")
.web(WebApplicationType.NONE).sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
.run();
.properties("spring.cloud.openfeign.httpclient.hc5.enabled=false").web(WebApplicationType.NONE)
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run();
Client client = context.getBean(Client.class);
assertThat(client).isInstanceOf(ApacheHttp5Client.class);
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> context.getBean(CloseableHttpClient.class));
if (context != null) {
context.close();
}
}
@Test
void hc4ShouldBeTheDefaultIfHc5NotEnabled() {
ConfigurableApplicationContext context = new SpringApplicationBuilder()
.properties("spring.cloud.openfeign.httpclient.hc5.enabled=false",
"spring.cloud.openfeign.httpclient.enabled=true")
.web(WebApplicationType.NONE).sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
.run();
verifyHc4BeansAvailable(context);
if (context != null) {
context.close();
}
}
@ClassPathExclusions({ "feign-hc5-{version:\\d.*}.jar", "httpclient5-{version:\\d.*}.jar",
"httpcore5-{version:\\d.*}.jar", "httpcore5-h2-{version:\\d.*}.jar" })
static class WithoutLoadBalancerAndHc5InClasspath {
@Test
void hc4ShouldWinEvenHc5ConfigEnabled() {
ConfigurableApplicationContext context = new SpringApplicationBuilder()
.properties("spring.cloud.openfeign.httpclient.hc5.enabled=true").web(WebApplicationType.NONE)
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run();
verifyHc4BeansAvailable(context);
if (context != null) {
context.close();
}
}
@Test
void hc4ShouldBeTheDefault() {
ConfigurableApplicationContext context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run();
verifyHc4BeansAvailable(context);
if (context != null) {
context.close();
}
}
}
}

View File

@@ -28,6 +28,7 @@ import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.impl.conn.DefaultHttpClientConnectionOperator;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.WebApplicationType;
@@ -41,6 +42,8 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
// TODO: Bring back when there's corresponding HttpClient 5 support in Commons
@Disabled
class FeignHttpClientConfigurationTests {
private ConfigurableApplicationContext context;

View File

@@ -22,7 +22,7 @@ import java.util.Objects;
import feign.Client;
import feign.Feign;
import feign.Target;
import feign.httpclient.ApacheHttpClient;
import feign.hc5.ApacheHttp5Client;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -159,7 +159,7 @@ class FeignHttpClientUrlTests {
ReflectionUtils.makeAccessible(field);
Client client = (Client) ReflectionUtils.getField(field, feign);
if (target.name().equals("localappurl")) {
assertThat(client).isInstanceOf(ApacheHttpClient.class).as("client was wrong type");
assertThat(client).isInstanceOf(ApacheHttp5Client.class).as("client was wrong type");
}
return feign.target(target);
}

View File

@@ -22,7 +22,7 @@ import java.util.Objects;
import feign.Client;
import feign.Feign;
import feign.Target;
import feign.httpclient.ApacheHttpClient;
import feign.hc5.ApacheHttp5Client;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -160,7 +160,7 @@ class FeignHttpClientUrlWithRetryableLoadBalancerTests {
ReflectionUtils.makeAccessible(field);
Client client = (Client) ReflectionUtils.getField(field, feign);
if (target.name().equals("localappurl")) {
assertThat(client).isInstanceOf(ApacheHttpClient.class).as("client was wrong type");
assertThat(client).isInstanceOf(ApacheHttp5Client.class).as("client was wrong type");
}
return feign.target(target);
}

View File

@@ -45,7 +45,8 @@ class FeignOkHttpConfigurationTests {
void setUp() {
this.context = new SpringApplicationBuilder()
.properties("debug=true", "spring.cloud.openfeign.httpclient.disableSslValidation=true",
"spring.cloud.openfeign.okhttp.enabled=true", "spring.cloud.openfeign.httpclient.enabled=false",
"spring.cloud.openfeign.okhttp.enabled=true",
"spring.cloud.openfeign.httpclient.hc5.enabled=false",
"spring.cloud.openfeign.httpclient.okhttp.read-timeout=9s")
.web(WebApplicationType.NONE).sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
.run();

View File

@@ -51,9 +51,8 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Aaron Whiteside
*/
@SpringBootTest(classes = BeansFeignClientTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
value = { "spring.application.name=feignclienttest",
"logging.level.org.springframework.cloud.openfeign.valid=DEBUG",
"spring.cloud.openfeign.httpclient.enabled=false", "spring.cloud.openfeign.okhttp.enabled=false" })
value = { "spring.application.name=feignclienttest", "spring.cloud.openfeign.httpclient.hc5.enabled=false",
"spring.cloud.openfeign.okhttp.enabled=false" })
@DirtiesContext
public class BeansFeignClientTests {

View File

@@ -25,15 +25,15 @@ import java.util.List;
import com.google.protobuf.InvalidProtocolBufferException;
import feign.RequestTemplate;
import feign.httpclient.ApacheHttpClient;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;
import feign.hc5.ApacheHttp5Client;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.ProtocolVersion;
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatchers;
@@ -58,7 +58,7 @@ import static org.assertj.core.api.Assertions.fail;
* @author Olga Maciaszek-Sharma
*/
@ExtendWith(MockitoExtension.class)
class ProtobufSpringEncoderTest {
class ProtobufSpringEncoderTests {
@Mock
private HttpClient httpClient;
@@ -72,28 +72,30 @@ class ProtobufSpringEncoderTest {
void testProtobuf() throws IOException {
// protobuf convert to request by feign and ProtobufHttpMessageConverter
RequestTemplate requestTemplate = newRequestTemplate();
newEncoder().encode(this.request, Request.class, requestTemplate);
requestTemplate.target("http://example.com");
newEncoder().encode(request, Request.class, requestTemplate);
HttpEntity entity = toApacheHttpEntity(requestTemplate);
byte[] bytes = read(entity.getContent(), (int) entity.getContentLength());
assertThat(this.request.toByteArray()).isEqualTo(bytes);
assertThat(request.toByteArray()).isEqualTo(bytes);
org.springframework.cloud.openfeign.encoding.proto.Request copy = org.springframework.cloud.openfeign.encoding.proto.Request
.parseFrom(bytes);
assertThat(copy).isEqualTo(this.request);
assertThat(copy).isEqualTo(request);
}
@Test
void testProtobufWithCharsetWillFail() throws IOException {
// protobuf convert to request by feign and ProtobufHttpMessageConverter
RequestTemplate requestTemplate = newRequestTemplate();
newEncoder().encode(this.request, Request.class, requestTemplate);
requestTemplate.target("http://example.com");
newEncoder().encode(request, Request.class, requestTemplate);
// set a charset
requestTemplate.body(requestTemplate.body(), StandardCharsets.UTF_8);
HttpEntity entity = toApacheHttpEntity(requestTemplate);
byte[] bytes = read(entity.getContent(), (int) entity.getContentLength());
// http request-body is different with original protobuf body
assertThat(this.request.toByteArray().length).isNotEqualTo(bytes.length);
assertThat(request.toByteArray().length).isNotEqualTo(bytes.length);
try {
org.springframework.cloud.openfeign.encoding.proto.Request copy = org.springframework.cloud.openfeign.encoding.proto.Request
.parseFrom(bytes);
@@ -117,16 +119,19 @@ class ProtobufSpringEncoderTest {
}
private HttpEntity toApacheHttpEntity(RequestTemplate requestTemplate) throws IOException {
final List<HttpUriRequest> request = new ArrayList<>(1);
BDDMockito.given(this.httpClient.execute(ArgumentMatchers.any()))
.will((Answer<HttpResponse>) invocationOnMock -> {
request.add((HttpUriRequest) invocationOnMock.getArguments()[0]);
return new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("http", 1, 1), 200, null));
final List<ClassicHttpRequest> request = new ArrayList<>(1);
BDDMockito.given(httpClient.execute(ArgumentMatchers.any(), ArgumentMatchers.any(),
ArgumentMatchers.any(HttpContext.class))).will((Answer<HttpResponse>) invocationOnMock -> {
request.add((ClassicHttpRequest) invocationOnMock.getArguments()[1]);
try (ClassicHttpResponse response = new BasicClassicHttpResponse(200)) {
response.setVersion(new ProtocolVersion("http", 1, 1));
return response;
}
});
new ApacheHttpClient(this.httpClient).execute(requestTemplate.resolve(new HashMap<>()).request(),
new ApacheHttp5Client(httpClient).execute(requestTemplate.resolve(new HashMap<>()).request(),
new feign.Request.Options());
HttpUriRequest httpUriRequest = request.get(0);
return ((HttpEntityEnclosingRequestBase) httpUriRequest).getEntity();
ClassicHttpRequest httpUriRequest = request.get(0);
return httpUriRequest.getEntity();
}
private byte[] read(InputStream in, int length) throws IOException {

View File

@@ -20,7 +20,6 @@ import java.util.Map;
import feign.Client;
import feign.hc5.ApacheHttp5Client;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import org.junit.jupiter.api.Test;
@@ -43,22 +42,15 @@ class FeignLoadBalancerAutoConfigurationTests {
@Test
void shouldInstantiateDefaultFeignBlockingLoadBalancerClientWhenHttpClientDisabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.enabled=false",
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.hc5.enabled=false",
"spring.cloud.loadbalancer.retry.enabled=false");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalanced(context, Client.Default.class);
}
@Test
void shouldInstantiateHttpFeignClientWhenEnabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.loadbalancer.retry.enabled=false");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalanced(context, ApacheHttpClient.class);
}
@Test
void shouldInstantiateOkHttpFeignClientWhenEnabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.enabled=false",
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.hc5.enabled=false",
"spring.cloud.openfeign.okhttp.enabled=true", "spring.cloud.loadbalancer.retry.enabled=false",
"spring.cloud.openfeign.httpclient.okhttp.read-timeout=9s");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
@@ -74,18 +66,8 @@ class FeignLoadBalancerAutoConfigurationTests {
}
@Test
void shouldInstantiateHttpFeignClient5WhenEnabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.enabled=false",
"spring.cloud.openfeign.okhttp.enabled=false", "spring.cloud.openfeign.httpclient.hc5.enabled=true",
"spring.cloud.loadbalancer.retry.enabled=false");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalanced(context, ApacheHttp5Client.class);
}
@Test
void shouldInstantiateHttpFeignClient5WhenBothHttpClientAndHttpClient5Enabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.enabled=true",
"spring.cloud.openfeign.okhttp.enabled=false", "spring.cloud.openfeign.httpclient.hc5.enabled=true",
void shouldInstantiateHttpFeignClient5WhenAvailableAndOkHttpDisabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.okhttp.enabled=false",
"spring.cloud.loadbalancer.retry.enabled=false");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalanced(context, ApacheHttp5Client.class);
@@ -93,21 +75,14 @@ class FeignLoadBalancerAutoConfigurationTests {
@Test
void shouldInstantiateRetryableDefaultFeignBlockingLoadBalancerClientWhenHttpClientDisabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.enabled=false");
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.hc5.enabled=false");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalancedWithRetries(context, Client.Default.class);
}
@Test
void shouldInstantiateRetryableHttpFeignClientWhenEnabled() {
ConfigurableApplicationContext context = initContext();
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalancedWithRetries(context, ApacheHttpClient.class);
}
@Test
void shouldInstantiateRetryableOkHttpFeignClientWhenEnabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.enabled=false",
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.hc5.enabled=false",
"spring.cloud.openfeign.okhttp.enabled=true");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalancedWithRetries(context, OkHttpClient.class);
@@ -115,16 +90,7 @@ class FeignLoadBalancerAutoConfigurationTests {
@Test
void shouldInstantiateRetryableHttpFeignClient5WhenEnabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.enabled=false",
"spring.cloud.openfeign.okhttp.enabled=false", "spring.cloud.openfeign.httpclient.hc5.enabled=true");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalancedWithRetries(context, ApacheHttp5Client.class);
}
@Test
void shouldInstantiateRetryableHttpFeignClient5WhenBothHttpClientAndHttpClient5Enabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.enabled=true",
"spring.cloud.openfeign.okhttp.enabled=false", "spring.cloud.openfeign.httpclient.hc5.enabled=true");
ConfigurableApplicationContext context = initContext();
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalancedWithRetries(context, ApacheHttp5Client.class);
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright 2013-2022 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
*
* https://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.openfeign.test;
// TODO: Bring back when there's corresponding HttpClient 5 support in Commons
//
// import java.io.IOException;
// import java.lang.reflect.Field;
// import java.util.concurrent.TimeUnit;
//
// import feign.Client;
// import feign.hc5.ApacheHttp5Client;
//
// import org.apache.hc.client5.http.classic.HttpClient;
// import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
// import org.apache.hc.client5.http.io.HttpClientConnectionManager;
// import org.apache.http.config.RegistryBuilder;
// import org.junit.jupiter.api.Test;
// import org.mockito.MockingDetails;
// import org.mockito.Mockito;
//
// import org.springframework.beans.factory.annotation.Autowired;
// import org.springframework.boot.SpringBootConfiguration;
// 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.openfeign.EnableFeignClients;
// import org.springframework.cloud.openfeign.FeignClient;
// import
// org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
// import org.springframework.context.annotation.Bean;
// import org.springframework.context.annotation.Configuration;
// import org.springframework.test.annotation.DirtiesContext;
// import org.springframework.util.ReflectionUtils;
//
// import static org.assertj.core.api.Assertions.assertThat;
// import static org.mockito.ArgumentMatchers.any;
// import static org.mockito.Mockito.doReturn;
// import static org.mockito.Mockito.mock;
// import static org.mockito.Mockito.mockingDetails;
//
/// **
// * @author Ryan Baxter
// * @author Olga Maciaszek-Sharma
// */
// @SpringBootTest(properties = { "spring.cloud.openfeign.okhttp.enabled: false",
// "spring.cloud.loadbalancer.retry.enabled=false" })
// @DirtiesContext
// class ApacheHttpClient5ConfigurationTests {
//
// @Autowired
// ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
//
// @Autowired
// ApacheHttpClientFactory httpClientFactory;
//
// @Autowired
// FeignBlockingLoadBalancerClient feignClient;
//
// @Test
// void testFactories() {
// assertThat(connectionManagerFactory).isInstanceOf(ApacheHttpClientConnectionManagerFactory.class);
// assertThat(connectionManagerFactory)
// .isInstanceOf(ApacheHttpClientConfigurationTestApp.MyApacheHttpClientConnectionManagerFactory.class);
// assertThat(httpClientFactory).isInstanceOf(ApacheHttpClientFactory.class);
// assertThat(httpClientFactory)
// .isInstanceOf(ApacheHttpClientConfigurationTestApp.MyApacheHttpClientFactory.class);
// }
//
// @Test
// void testHttpClientWithFeign() {
// Client delegate = feignClient.getDelegate();
// assertThat(delegate instanceof ApacheHttp5Client).isTrue();
// ApacheHttp5Client apacheHttpClient = (ApacheHttp5Client) delegate;
// HttpClient httpClient = getField(apacheHttpClient, "client");
// MockingDetails httpClientDetails = mockingDetails(httpClient);
// assertThat(httpClientDetails.isMock()).isTrue();
// }
//
// @SuppressWarnings("unchecked")
// 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;
// }
//
// @SpringBootConfiguration
// @EnableAutoConfiguration
// @EnableFeignClients(clients = { ApacheHttpClientConfigurationTestApp.FooClient.class })
// static class ApacheHttpClientConfigurationTestApp {
//
// @FeignClient(name = "foo")
// interface FooClient {
//
// }
//
// 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 {
//
// MyApacheHttpClientFactory(HttpClientBuilder builder) {
// super(builder);
// }
//
// @Override
// public HttpClientBuilder createBuilder() {
// CloseableHttpClient client = mock(CloseableHttpClient.class);
// CloseableHttpResponse response = mock(CloseableHttpResponse.class);
// StatusLine statusLine = mock(StatusLine.class);
// doReturn(200).when(statusLine).getStatusCode();
// Mockito.doReturn(statusLine).when(response).getStatusLine();
// Header[] headers = new BasicHeader[0];
// doReturn(headers).when(response).getAllHeaders();
// try {
// Mockito.doReturn(response).when(client).execute(any(HttpUriRequest.class));
// }
// catch (IOException e) {
// e.printStackTrace();
// }
// HttpClientBuilder builder = mock(HttpClientBuilder.class);
// Mockito.doReturn(client).when(builder).build();
// return builder;
// }
//
// }
//
// @Configuration(proxyBeanMethods = false)
// static class MyConfig {
//
// @Bean
// public ApacheHttpClientFactory apacheHttpClientFactory(HttpClientBuilder builder) {
// return new MyApacheHttpClientFactory(builder);
// }
//
// @Bean
// public ApacheHttpClientConnectionManagerFactory connectionManagerFactory() {
// return new MyApacheHttpClientConnectionManagerFactory();
// }
//
// }
//
// }
//
// }

View File

@@ -1,175 +0,0 @@
/*
* Copyright 2013-2022 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
*
* https://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.openfeign.test;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;
import feign.Client;
import feign.httpclient.ApacheHttpClient;
import org.apache.http.Header;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
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.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.junit.jupiter.api.Test;
import org.mockito.MockingDetails;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
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.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockingDetails;
/**
* @author Ryan Baxter
* @author Olga Maciaszek-Sharma
*/
@SpringBootTest(properties = { "spring.cloud.openfeign.okhttp.enabled: false",
"spring.cloud.loadbalancer.retry.enabled=false" })
@DirtiesContext
class ApacheHttpClientConfigurationTests {
@Autowired
ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
@Autowired
ApacheHttpClientFactory httpClientFactory;
@Autowired
FeignBlockingLoadBalancerClient feignClient;
@Test
void testFactories() {
assertThat(connectionManagerFactory).isInstanceOf(ApacheHttpClientConnectionManagerFactory.class);
assertThat(connectionManagerFactory)
.isInstanceOf(ApacheHttpClientConfigurationTestApp.MyApacheHttpClientConnectionManagerFactory.class);
assertThat(httpClientFactory).isInstanceOf(ApacheHttpClientFactory.class);
assertThat(httpClientFactory)
.isInstanceOf(ApacheHttpClientConfigurationTestApp.MyApacheHttpClientFactory.class);
}
@Test
void testHttpClientWithFeign() {
Client delegate = feignClient.getDelegate();
assertThat(delegate instanceof ApacheHttpClient).isTrue();
ApacheHttpClient apacheHttpClient = (ApacheHttpClient) delegate;
HttpClient httpClient = getField(apacheHttpClient, "client");
MockingDetails httpClientDetails = mockingDetails(httpClient);
assertThat(httpClientDetails.isMock()).isTrue();
}
@SuppressWarnings("unchecked")
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;
}
@SpringBootConfiguration
@EnableAutoConfiguration
@EnableFeignClients(clients = { ApacheHttpClientConfigurationTestApp.FooClient.class })
static class ApacheHttpClientConfigurationTestApp {
@FeignClient(name = "foo")
interface FooClient {
}
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 {
MyApacheHttpClientFactory(HttpClientBuilder builder) {
super(builder);
}
@Override
public HttpClientBuilder createBuilder() {
CloseableHttpClient client = mock(CloseableHttpClient.class);
CloseableHttpResponse response = mock(CloseableHttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
doReturn(200).when(statusLine).getStatusCode();
Mockito.doReturn(statusLine).when(response).getStatusLine();
Header[] headers = new BasicHeader[0];
doReturn(headers).when(response).getAllHeaders();
try {
Mockito.doReturn(response).when(client).execute(any(HttpUriRequest.class));
}
catch (IOException e) {
e.printStackTrace();
}
HttpClientBuilder builder = mock(HttpClientBuilder.class);
Mockito.doReturn(client).when(builder).build();
return builder;
}
}
@Configuration(proxyBeanMethods = false)
static class MyConfig {
@Bean
public ApacheHttpClientFactory apacheHttpClientFactory(HttpClientBuilder builder) {
return new MyApacheHttpClientFactory(builder);
}
@Bean
public ApacheHttpClientConnectionManagerFactory connectionManagerFactory() {
return new MyApacheHttpClientConnectionManagerFactory();
}
}
}
}

View File

@@ -48,7 +48,7 @@ import static org.mockito.Mockito.mockingDetails;
*/
@SpringBootTest(properties = { "spring.cloud.openfeign.okhttp.enabled: true",
"spring.cloud.httpclientfactories.ok.enabled: true", "spring.cloud.openfeign.okhttp.enabled: true",
"spring.cloud.openfeign.httpclient.enabled: false", "spring.cloud.loadbalancer.retry.enabled=false" })
"spring.cloud.openfeign.httpclient.hc5.enabled: false", "spring.cloud.loadbalancer.retry.enabled=false" })
@DirtiesContext
class OkHttpClientConfigurationTests {

View File

@@ -47,8 +47,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
*/
@SpringBootTest(classes = FeignClientNotPrimaryTests.Application.class, webEnvironment = RANDOM_PORT,
value = { "spring.application.name=feignclientnotprimarytest",
"logging.level.org.springframework.cloud.openfeign.valid=DEBUG",
"spring.cloud.openfeign.httpclient.enabled=false", "spring.cloud.openfeign.okhttp.enabled=false" })
"spring.cloud.openfeign.httpclient.hc5.enabled=false", "spring.cloud.openfeign.okhttp.enabled=false" })
@DirtiesContext
class FeignClientNotPrimaryTests {

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.openfeign.valid;
import java.util.Objects;
import feign.Client;
import feign.hc5.ApacheHttp5Client;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -89,7 +90,7 @@ class FeignHttpClientTests {
assertThat(feignClient).isInstanceOf(FeignBlockingLoadBalancerClient.class);
FeignBlockingLoadBalancerClient client = (FeignBlockingLoadBalancerClient) feignClient;
Client delegate = client.getDelegate();
assertThat(delegate).isInstanceOf(feign.httpclient.ApacheHttpClient.class);
assertThat(delegate).isInstanceOf(ApacheHttp5Client.class);
}
@Test

View File

@@ -55,7 +55,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
@SpringBootTest(classes = FeignOkHttpTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
value = { "spring.application.name=feignclienttest", "spring.cloud.openfeign.circuitbreaker.enabled=false",
"spring.cloud.openfeign.httpclient.enabled=false", "spring.cloud.openfeign.okhttp.enabled=true",
"spring.cloud.openfeign.httpclient.hc5.enabled=false", "spring.cloud.openfeign.okhttp.enabled=true",
"spring.cloud.httpclientfactories.ok.enabled=true", "spring.cloud.loadbalancer.retry.enabled=false" })
@DirtiesContext
class FeignOkHttpTests {

View File

@@ -47,8 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
@SpringBootTest(classes = IterableParameterTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
value = { "spring.application.name=iterableparametertest",
"logging.level.org.springframework.cloud.openfeign.valid=DEBUG",
"spring.cloud.openfeign.httpclient.enabled=false", "spring.cloud.openfeign.okhttp.enabled=false",
"spring.cloud.openfeign.httpclient.hc5.enabled=false", "spring.cloud.openfeign.okhttp.enabled=false",
"spring.cloud.openfeign.circuitbreaker.enabled=false" })
@DirtiesContext
class IterableParameterTests {

View File

@@ -96,10 +96,9 @@ import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
* @author Olga Maciaszek-Sharma
*/
@SpringBootTest(classes = ValidFeignClientTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
value = { "spring.application.name=feignclienttest",
"logging.level.org.springframework.cloud.openfeign.valid=DEBUG",
"spring.cloud.openfeign.httpclient.enabled=false", "spring.cloud.openfeign.okhttp.enabled=false",
"spring.cloud.openfeign.circuitbreaker.enabled=true", "spring.cloud.loadbalancer.retry.enabled=false" })
value = { "spring.application.name=feignclienttest", "spring.cloud.openfeign.httpclient.hc5.enabled=false",
"spring.cloud.openfeign.okhttp.enabled=false", "spring.cloud.openfeign.circuitbreaker.enabled=true",
"spring.cloud.loadbalancer.retry.enabled=false" })
@DirtiesContext
class ValidFeignClientTests {

View File

@@ -43,7 +43,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
* @author Ryan Baxter
*/
@SpringBootTest(classes = FeignClientEnvVarTests.Application.class, webEnvironment = RANDOM_PORT,
value = { "spring.application.name=feignclienttest", "spring.cloud.openfeign.httpclient.enabled=false",
value = { "spring.application.name=feignclienttest", "spring.cloud.openfeign.httpclient.hc5.enabled=false",
"basepackage=org.springframework.cloud.openfeign.testclients" })
@DirtiesContext
class FeignClientEnvVarTests {

View File

@@ -20,7 +20,6 @@ import feign.Client;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
@@ -43,15 +42,13 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
/**
* @author Spencer Gibb
* @author Olga Maciaszek-Sharma
*/
@SpringBootTest(classes = FeignClientScanningTests.Application.class, webEnvironment = RANDOM_PORT,
value = { "spring.application.name=feignclienttest", "spring.cloud.openfeign.httpclient.enabled=false" })
value = { "spring.application.name=feignclienttest", "spring.cloud.openfeign.httpclient.hc5.enabled=false" })
@DirtiesContext
class FeignClientScanningTests {
@Value("${local.server.port}")
private int port = 0;
@Autowired
private TestClient testClient;