Skip to content

Commit ce2342d

Browse files
kistlersryanjbaxterCopilot
authored
Allow Customizer for HttpClientConnectionManager in HttpClient5FeignConfiguration (#1305)
* allow customization of 'hc5ConnectionManager' bean in 'HttpClient5FeignConfiguration' via new customizer supplied as beans Signed-off-by: Severin Kistler <kistlerseverin@gmail.com> * add tests and documentation Signed-off-by: Severin Kistler <kistlerseverin@gmail.com> * add example in the docs on how to use the HttpClientConnectionManagerBuilderCustomizer, as it already exists for httpClient5FeignConfiguration.HttpClientBuilderCustomizer a few lines above Signed-off-by: Severin Kistler <kistlerseverin@gmail.com> * Update docs/modules/ROOT/pages/spring-cloud-openfeign.adoc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Ryan Baxter <ryan.baxter@broadcom.com> * Update spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/HttpClient5FeignConfiguration.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Ryan Baxter <ryan.baxter@broadcom.com> --------- Signed-off-by: Severin Kistler <kistlerseverin@gmail.com> Signed-off-by: Ryan Baxter <ryan.baxter@broadcom.com> Co-authored-by: Ryan Baxter <ryan.baxter@broadcom.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 4267eac commit ce2342d

File tree

3 files changed

+69
-8
lines changed

3 files changed

+69
-8
lines changed

docs/modules/ROOT/pages/spring-cloud-openfeign.adoc

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,13 @@ When it comes to the Apache HttpClient 5-backed Feign clients, it's enough to en
144144
You can customize the HTTP client used by providing a bean of either `org.apache.hc.client5.http.impl.classic.CloseableHttpClient` when using Apache HC5.
145145

146146
You can further customise http clients by setting values in the `spring.cloud.openfeign.httpclient.xxx` properties. The ones prefixed just with `httpclient` will work for all the clients, the ones prefixed with `httpclient.hc5` to Apache HttpClient 5, and the ones prefixed with `httpclient.http2` to Http2Client. You can find a full list of properties you can customise in the appendix.
147-
If you can not configure Apache HttpClient 5 by using properties, there is an `HttpClient5FeignConfiguration.HttpClientBuilderCustomizer` interface for programmatic configuration.
147+
If you can not configure Apache HttpClient 5 by using properties, there is an `HttpClient5FeignConfiguration.HttpClientBuilderCustomizer` interface for programmatic configuration. Similarly, to configure the `HttpClientConnectionManager`, you can use `HttpClient5FeignConfiguration.HttpClientConnectionManagerBuilderCustomizer`. Both usages are shown in the example below.
148148

149149
TIP: Apache HTTP Components `5.4` have changed defaults in the HttpClient relating to HTTP/1.1 TLS upgrades. Most proxy servers handle upgrades without issue, however, you may encounter issues with Envoy or Istio. If you need to restore previous behaviour, you can use `HttpClient5FeignConfiguration.HttpClientBuilderCustomizer` to do it, as shown in the example below.
150150

151151
[source,java,indent=0]
152152
----
153-
@Configuration
154-
public class FooConfiguration {
153+
public class HttpClientConfiguration {
155154
156155
@Bean
157156
public HttpClient5FeignConfiguration.HttpClientBuilderCustomizer httpClientBuilder() {
@@ -164,6 +163,24 @@ public class FooConfiguration {
164163
}
165164
----
166165

166+
TIP: Apache HTTP Components `5.5.1` have changed defaults in the HttpClient relating to TLS handshake timeouts. An unset TLS handshake timeout is now implicitly bound by the `connectTimeout` while it was unbounded previously. To restore previous defaults, you can use `HttpClient5FeignConfiguration.HttpClientBuilderCustomizer` to do it, as shown in the example below.
167+
168+
[source,java,indent=0]
169+
----
170+
public class HttpClientConnectionManagerConfiguration {
171+
172+
@Bean
173+
public HttpClient5FeignConfiguration.HttpClientConnectionManagerBuilderCustomizer httpClientConnectionManagerBuilder() {
174+
return (httpClientConnectionManagerBuilder) -> {
175+
TlsConfig.Builder tlsConfigBuilder = TlsConfig.custom();
176+
// 0 seconds timeout means infinite
177+
tlsConfigBuilder.setHandshakeTimeout(Timeout.of(0, TimeUnit.SECONDS));
178+
httpClientConnectionManagerBuilder.setDefaultTlsConfig(tlsConfigBuilder.build());
179+
};
180+
}
181+
}
182+
----
183+
167184
TIP: Starting with Spring Cloud OpenFeign 4, the Feign Apache HttpClient 4 is no longer supported. We suggest using Apache HttpClient 5 instead.
168185

169186
Spring Cloud OpenFeign _does not_ provide the following beans by default for feign, but still looks up beans of these types from the application context to create the Feign client:

spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/HttpClient5FeignConfiguration.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ public class HttpClient5FeignConfiguration {
7171

7272
@Bean
7373
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
74-
public HttpClientConnectionManager hc5ConnectionManager(FeignHttpClientProperties httpClientProperties) {
75-
return PoolingHttpClientConnectionManagerBuilder.create()
74+
public HttpClientConnectionManager hc5ConnectionManager(FeignHttpClientProperties httpClientProperties,
75+
ObjectProvider<List<HttpClientConnectionManagerBuilderCustomizer>> customizerProvider) {
76+
PoolingHttpClientConnectionManagerBuilder httpClientConnectionManager = PoolingHttpClientConnectionManagerBuilder.create()
7677
.setSSLSocketFactory(httpsSSLConnectionSocketFactory(httpClientProperties.isDisableSslValidation()))
7778
.setMaxConnTotal(httpClientProperties.getMaxConnections())
7879
.setMaxConnPerRoute(httpClientProperties.getMaxConnectionsPerRoute())
@@ -84,8 +85,11 @@ public HttpClientConnectionManager hc5ConnectionManager(FeignHttpClientPropertie
8485
.setDefaultSocketConfig(SocketConfig.custom()
8586
.setSoTimeout(Timeout.of(httpClientProperties.getHc5().getSocketTimeout(),
8687
httpClientProperties.getHc5().getSocketTimeoutUnit()))
87-
.build())
88-
.build();
88+
.build());
89+
90+
customizerProvider.getIfAvailable(List::of).forEach(c -> c.customize(httpClientConnectionManager));
91+
92+
return httpClientConnectionManager.build();
8993
}
9094

9195
@Bean
@@ -174,4 +178,21 @@ public interface HttpClientBuilderCustomizer {
174178

175179
}
176180

181+
/**
182+
* Callback interface that customizes {@link PoolingHttpClientConnectionManagerBuilder}
183+
* objects before HttpClientConnectionManager is created.
184+
*
185+
* @author Severin Kistler
186+
* @since 5.1.0
187+
*/
188+
public interface HttpClientConnectionManagerBuilderCustomizer {
189+
190+
/**
191+
* Customize PoolingHttpClientConnectionManagerBuilder.
192+
* @param builder the {@code PoolingHttpClientConnectionManagerBuilder} to customize
193+
*/
194+
void customize(PoolingHttpClientConnectionManagerBuilder builder);
195+
196+
}
197+
177198
}

spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClient5ConfigurationTests.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
2222
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
2323
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
24+
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
2425
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
2526
import org.junit.jupiter.api.Test;
2627
import org.mockito.Mockito;
@@ -29,6 +30,7 @@
2930
import org.springframework.boot.WebApplicationType;
3031
import org.springframework.boot.builder.SpringApplicationBuilder;
3132
import org.springframework.cloud.openfeign.clientconfig.HttpClient5FeignConfiguration.HttpClientBuilderCustomizer;
33+
import org.springframework.cloud.openfeign.clientconfig.HttpClient5FeignConfiguration.HttpClientConnectionManagerBuilderCustomizer;
3234
import org.springframework.context.ConfigurableApplicationContext;
3335
import org.springframework.context.annotation.Bean;
3436
import org.springframework.context.annotation.Configuration;
@@ -99,14 +101,35 @@ void shouldInstantiateHttpClient5ByUsingHttpClientBuilderCustomizer() {
99101
}
100102
}
101103

104+
@Test
105+
void shouldInstantiateHttpClientConnectionManager5ByUsingHttpClientConnectionManagerBuilderCustomizer() {
106+
ConfigurableApplicationContext context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
107+
.sources(FeignAutoConfiguration.class, Config.class)
108+
.run();
109+
110+
HttpClientConnectionManager httpClientConnectionManager = context.getBean(HttpClientConnectionManager.class);
111+
assertThat(httpClientConnectionManager).isNotNull();
112+
HttpClientConnectionManagerBuilderCustomizer customizer = context.getBean(HttpClientConnectionManagerBuilderCustomizer.class);
113+
verify(customizer).customize(any(PoolingHttpClientConnectionManagerBuilder.class));
114+
115+
if (context != null) {
116+
context.close();
117+
}
118+
}
119+
102120
@Configuration
103121
static class Config {
104122

105123
@Bean
106-
HttpClientBuilderCustomizer customizer() {
124+
HttpClientBuilderCustomizer httpClientCustomizer() {
107125
return Mockito.mock(HttpClientBuilderCustomizer.class);
108126
}
109127

128+
@Bean
129+
HttpClientConnectionManagerBuilderCustomizer httpClientConnectionManagerCustomizer() {
130+
return Mockito.mock(HttpClientConnectionManagerBuilderCustomizer.class);
131+
}
132+
110133
}
111134

112135
}

0 commit comments

Comments
 (0)