Co-authored-by: Ryan Baxter <524254+ryanjbaxter@users.noreply.github.com>
This commit is contained in:
@@ -233,7 +233,7 @@ public class FeignAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public okhttp3.OkHttpClient client(okhttp3.OkHttpClient.Builder builder, ConnectionPool connectionPool,
|
||||
public okhttp3.OkHttpClient okHttpClient(okhttp3.OkHttpClient.Builder builder, ConnectionPool connectionPool,
|
||||
FeignHttpClientProperties httpClientProperties) {
|
||||
boolean followRedirects = httpClientProperties.isFollowRedirects();
|
||||
int connectTimeout = httpClientProperties.getConnectionTimeout();
|
||||
|
||||
@@ -1,77 +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.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import okhttp3.ConnectionPool;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
|
||||
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
|
||||
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Defualt configuration for {@link OkHttpClient}.
|
||||
*
|
||||
* @author Ryan Baxter
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Spencer Gibb
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
|
||||
public class OkHttpFeignConfiguration {
|
||||
|
||||
private okhttp3.OkHttpClient okHttpClient;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ConnectionPool.class)
|
||||
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
|
||||
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
||||
int maxTotalConnections = httpClientProperties.getMaxConnections();
|
||||
long timeToLive = httpClientProperties.getTimeToLive();
|
||||
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
|
||||
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool,
|
||||
FeignHttpClientProperties httpClientProperties) {
|
||||
boolean followRedirects = httpClientProperties.isFollowRedirects();
|
||||
int connectTimeout = httpClientProperties.getConnectionTimeout();
|
||||
Duration reaTimeout = httpClientProperties.getOkHttp().getReadTimeout();
|
||||
this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation())
|
||||
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects)
|
||||
.readTimeout(reaTimeout).connectionPool(connectionPool).build();
|
||||
return this.okHttpClient;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
if (this.okHttpClient != null) {
|
||||
this.okHttpClient.dispatcher().executorService().shutdown();
|
||||
this.okHttpClient.connectionPool().evictAll();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,11 +30,9 @@ 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.clientconfig.OkHttpFeignConfiguration;
|
||||
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
|
||||
@@ -48,7 +46,6 @@ import org.springframework.context.annotation.Import;
|
||||
@ConditionalOnClass(OkHttpClient.class)
|
||||
@ConditionalOnProperty("spring.cloud.openfeign.okhttp.enabled")
|
||||
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
|
||||
@Import(OkHttpFeignConfiguration.class)
|
||||
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
|
||||
class OkHttpFeignLoadBalancerConfiguration {
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ 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.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -50,7 +49,7 @@ class FeignHttpClient5ConfigurationTests {
|
||||
@Test
|
||||
void shouldInstantiateHttpClient5ByDefaultWhenDependenciesPresent() {
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
|
||||
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run();
|
||||
.sources(FeignAutoConfiguration.class).run();
|
||||
|
||||
verifyHc5BeansAvailable(context);
|
||||
|
||||
@@ -63,7 +62,7 @@ class FeignHttpClient5ConfigurationTests {
|
||||
void shouldNotInstantiateHttpClient5ByWhenDependenciesPresentButPropertyDisabled() {
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder()
|
||||
.properties("spring.cloud.openfeign.httpclient.hc5.enabled=false").web(WebApplicationType.NONE)
|
||||
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run();
|
||||
.sources(FeignAutoConfiguration.class).run();
|
||||
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> context.getBean(CloseableHttpClient.class));
|
||||
|
||||
@@ -1,103 +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 java.lang.reflect.Field;
|
||||
|
||||
import javax.net.ssl.SSLContextSpi;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import org.apache.http.config.Lookup;
|
||||
import org.apache.http.conn.HttpClientConnectionManager;
|
||||
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;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
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;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.context = new SpringApplicationBuilder()
|
||||
.properties("debug=true", "spring.cloud.openfeign.httpclient.disableSslValidation=true")
|
||||
.web(WebApplicationType.NONE).sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
|
||||
.run();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void disableSslTest() {
|
||||
try {
|
||||
HttpClientConnectionManager connectionManager = this.context.getBean(HttpClientConnectionManager.class);
|
||||
Lookup<ConnectionSocketFactory> socketFactoryRegistry = getConnectionSocketFactoryLookup(connectionManager);
|
||||
assertThat(socketFactoryRegistry.lookup("https")).isNotNull();
|
||||
assertThat(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers()).isNull();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
// FIXME: java 16 need junit 5 compatible modified classpath extension
|
||||
if (e.getMessage() == null || !e.getMessage().startsWith("Unable to make field private final")) {
|
||||
ReflectionUtils.rethrowRuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Lookup<ConnectionSocketFactory> getConnectionSocketFactoryLookup(
|
||||
HttpClientConnectionManager connectionManager) {
|
||||
DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator) this
|
||||
.getField(connectionManager, "connectionOperator");
|
||||
return (Lookup) this.getField(connectionOperator, "socketFactoryRegistry");
|
||||
}
|
||||
|
||||
private X509TrustManager getX509TrustManager(Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
|
||||
ConnectionSocketFactory connectionSocketFactory = socketFactoryRegistry.lookup("https");
|
||||
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) this.getField(connectionSocketFactory, "socketfactory");
|
||||
SSLContextSpi sslContext = (SSLContextSpi) this.getField(sslSocketFactory, "context");
|
||||
return (X509TrustManager) this.getField(sslContext, "trustManager");
|
||||
}
|
||||
|
||||
protected <T> Object getField(Object target, String name) {
|
||||
Field field = ReflectionUtils.findField(target.getClass(), name);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
return ReflectionUtils.getField(field, target);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
@@ -47,8 +46,7 @@ class FeignOkHttpConfigurationTests {
|
||||
"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();
|
||||
.web(WebApplicationType.NONE).sources(FeignAutoConfiguration.class).run();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.cloud.openfeign.invalid;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
@@ -64,7 +63,7 @@ class FeignClientValidationTests {
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class })
|
||||
@Import({ FeignAutoConfiguration.class })
|
||||
@EnableFeignClients(clients = NameAndServiceIdConfiguration.Client.class)
|
||||
protected static class NameAndServiceIdConfiguration {
|
||||
|
||||
@@ -79,7 +78,7 @@ class FeignClientValidationTests {
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class })
|
||||
@Import({ FeignAutoConfiguration.class })
|
||||
@EnableFeignClients(clients = { DuplicatedFeignClientNamesConfiguration.FooClient.class,
|
||||
DuplicatedFeignClientNamesConfiguration.BarClient.class })
|
||||
protected static class DuplicatedFeignClientNamesConfiguration {
|
||||
|
||||
@@ -25,10 +25,10 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||
import org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration;
|
||||
import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -97,8 +97,8 @@ class FeignLoadBalancerAutoConfigurationTests {
|
||||
|
||||
private ConfigurableApplicationContext initContext(String... properties) {
|
||||
return new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
|
||||
.sources(HttpClientConfiguration.class, LoadBalancerAutoConfiguration.class,
|
||||
BlockingLoadBalancerClientAutoConfiguration.class, FeignLoadBalancerAutoConfiguration.class)
|
||||
.sources(LoadBalancerAutoConfiguration.class, BlockingLoadBalancerClientAutoConfiguration.class,
|
||||
FeignLoadBalancerAutoConfiguration.class, FeignAutoConfiguration.class)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.test;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import feign.Client;
|
||||
import okhttp3.ConnectionPool;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockingDetails;
|
||||
@@ -28,10 +25,6 @@ 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.DefaultOkHttpClientConnectionPoolFactory;
|
||||
import org.springframework.cloud.commons.httpclient.DefaultOkHttpClientFactory;
|
||||
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
|
||||
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -52,23 +45,9 @@ import static org.mockito.Mockito.mockingDetails;
|
||||
@DirtiesContext
|
||||
class OkHttpClientConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
OkHttpClientFactory okHttpClientFactory;
|
||||
|
||||
@Autowired
|
||||
OkHttpClientConnectionPoolFactory connectionPoolFactory;
|
||||
|
||||
@Autowired
|
||||
FeignBlockingLoadBalancerClient feignClient;
|
||||
|
||||
@Test
|
||||
void testFactories() {
|
||||
assertThat(connectionPoolFactory).isInstanceOf(OkHttpClientConnectionPoolFactory.class);
|
||||
assertThat(connectionPoolFactory).isInstanceOf(TestConfig.MyOkHttpClientConnectionPoolFactory.class);
|
||||
assertThat(okHttpClientFactory).isInstanceOf(OkHttpClientFactory.class);
|
||||
assertThat(okHttpClientFactory).isInstanceOf(TestConfig.MyOkHttpClientFactory.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHttpClientWithFeign() {
|
||||
Client delegate = feignClient.getDelegate();
|
||||
@@ -94,38 +73,11 @@ class OkHttpClientConfigurationTests {
|
||||
@EnableAutoConfiguration
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
public OkHttpClientConnectionPoolFactory connectionPoolFactory() {
|
||||
return new MyOkHttpClientConnectionPoolFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OkHttpClientFactory clientFactory(OkHttpClient.Builder builder) {
|
||||
return new MyOkHttpClientFactory(builder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OkHttpClient client() {
|
||||
return mock(OkHttpClient.class);
|
||||
}
|
||||
|
||||
static class MyOkHttpClientConnectionPoolFactory extends DefaultOkHttpClientConnectionPoolFactory {
|
||||
|
||||
@Override
|
||||
public ConnectionPool create(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
|
||||
return new ConnectionPool();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MyOkHttpClientFactory extends DefaultOkHttpClientFactory {
|
||||
|
||||
MyOkHttpClientFactory(OkHttpClient.Builder builder) {
|
||||
super(builder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.cloud.openfeign.valid;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
@@ -62,7 +61,7 @@ class FeignClientValidationTests {
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class })
|
||||
@Import({ FeignAutoConfiguration.class })
|
||||
@EnableFeignClients(clients = GoodUrlConfiguration.Client.class)
|
||||
protected static class GoodUrlConfiguration {
|
||||
|
||||
@@ -78,7 +77,7 @@ class FeignClientValidationTests {
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class })
|
||||
@Import({ FeignAutoConfiguration.class })
|
||||
@EnableFeignClients(clients = PlaceholderUrlConfiguration.Client.class)
|
||||
protected static class PlaceholderUrlConfiguration {
|
||||
|
||||
@@ -94,7 +93,7 @@ class FeignClientValidationTests {
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class })
|
||||
@Import({ FeignAutoConfiguration.class })
|
||||
@EnableFeignClients(clients = GoodServiceIdConfiguration.Client.class)
|
||||
protected static class GoodServiceIdConfiguration {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user