From 1f100f2c87047812e9db448fdd60da6cb6bc5aed Mon Sep 17 00:00:00 2001 From: Severin Kistler Date: Wed, 17 Dec 2025 14:20:54 +0100 Subject: [PATCH 1/5] allow customization of 'hc5ConnectionManager' bean in 'HttpClient5FeignConfiguration' via new customizer supplied as beans Signed-off-by: Severin Kistler --- .../HttpClient5FeignConfiguration.java | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/HttpClient5FeignConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/HttpClient5FeignConfiguration.java index 34f03faf9..d4855d734 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/HttpClient5FeignConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/HttpClient5FeignConfiguration.java @@ -71,8 +71,9 @@ public class HttpClient5FeignConfiguration { @Bean @ConditionalOnMissingBean(HttpClientConnectionManager.class) - public HttpClientConnectionManager hc5ConnectionManager(FeignHttpClientProperties httpClientProperties) { - return PoolingHttpClientConnectionManagerBuilder.create() + public HttpClientConnectionManager hc5ConnectionManager(FeignHttpClientProperties httpClientProperties, + ObjectProvider> customizerProvider) { + PoolingHttpClientConnectionManagerBuilder httpClientConnectionManager = PoolingHttpClientConnectionManagerBuilder.create() .setSSLSocketFactory(httpsSSLConnectionSocketFactory(httpClientProperties.isDisableSslValidation())) .setMaxConnTotal(httpClientProperties.getMaxConnections()) .setMaxConnPerRoute(httpClientProperties.getMaxConnectionsPerRoute()) @@ -84,8 +85,11 @@ public HttpClientConnectionManager hc5ConnectionManager(FeignHttpClientPropertie .setDefaultSocketConfig(SocketConfig.custom() .setSoTimeout(Timeout.of(httpClientProperties.getHc5().getSocketTimeout(), httpClientProperties.getHc5().getSocketTimeoutUnit())) - .build()) - .build(); + .build()); + + customizerProvider.getIfAvailable(List::of).forEach(c -> c.customize(httpClientConnectionManager)); + + return httpClientConnectionManager.build(); } @Bean @@ -174,4 +178,21 @@ public interface HttpClientBuilderCustomizer { } + /** + * Callback interface that customize {@link HttpClientBuilder} objects before + * HttpClientConnectionManager created. + * + * @author Severin Kistler + * @since 5.1.0 + */ + public interface HttpClientConnectionManagerBuilderCustomizer { + + /** + * Customize PoolingHttpClientConnectionManagerBuilder. + * @param builder the {@code PoolingHttpClientConnectionManagerBuilder} to customize + */ + void customize(PoolingHttpClientConnectionManagerBuilder builder); + + } + } From 48be6e1dea4c4cdd8e1227a1f17ef6256ae91db0 Mon Sep 17 00:00:00 2001 From: Severin Kistler Date: Wed, 17 Dec 2025 16:30:01 +0100 Subject: [PATCH 2/5] add tests and documentation Signed-off-by: Severin Kistler --- .../ROOT/pages/spring-cloud-openfeign.adoc | 2 +- .../FeignHttpClient5ConfigurationTests.java | 25 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/spring-cloud-openfeign.adoc b/docs/modules/ROOT/pages/spring-cloud-openfeign.adoc index bd7932fb8..d43b8c1cb 100644 --- a/docs/modules/ROOT/pages/spring-cloud-openfeign.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-openfeign.adoc @@ -144,7 +144,7 @@ When it comes to the Apache HttpClient 5-backed Feign clients, it's enough to en 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. 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. -If you can not configure Apache HttpClient 5 by using properties, there is an `HttpClient5FeignConfiguration.HttpClientBuilderCustomizer` interface for programmatic configuration. +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`. 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. diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClient5ConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClient5ConfigurationTests.java index 829dc6ae2..acde39055 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClient5ConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClient5ConfigurationTests.java @@ -21,6 +21,7 @@ import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -29,6 +30,7 @@ import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.openfeign.clientconfig.HttpClient5FeignConfiguration.HttpClientBuilderCustomizer; +import org.springframework.cloud.openfeign.clientconfig.HttpClient5FeignConfiguration.HttpClientConnectionManagerBuilderCustomizer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -99,14 +101,35 @@ void shouldInstantiateHttpClient5ByUsingHttpClientBuilderCustomizer() { } } + @Test + void shouldInstantiateHttpClientConnectionManager5ByUsingHttpClientConnectionManagerBuilderCustomizer() { + ConfigurableApplicationContext context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + .sources(FeignAutoConfiguration.class, Config.class) + .run(); + + HttpClientConnectionManager httpClientConnectionManager = context.getBean(HttpClientConnectionManager.class); + assertThat(httpClientConnectionManager).isNotNull(); + HttpClientConnectionManagerBuilderCustomizer customizer = context.getBean(HttpClientConnectionManagerBuilderCustomizer.class); + verify(customizer).customize(any(PoolingHttpClientConnectionManagerBuilder.class)); + + if (context != null) { + context.close(); + } + } + @Configuration static class Config { @Bean - HttpClientBuilderCustomizer customizer() { + HttpClientBuilderCustomizer httpClientCustomizer() { return Mockito.mock(HttpClientBuilderCustomizer.class); } + @Bean + HttpClientConnectionManagerBuilderCustomizer httpClientConnectionManagerCustomizer() { + return Mockito.mock(HttpClientConnectionManagerBuilderCustomizer.class); + } + } } From c23f06eb8e610caa234dd3573c55eaa071b4a15a Mon Sep 17 00:00:00 2001 From: Severin Kistler Date: Thu, 18 Dec 2025 10:45:08 +0100 Subject: [PATCH 3/5] 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 --- .../ROOT/pages/spring-cloud-openfeign.adoc | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/spring-cloud-openfeign.adoc b/docs/modules/ROOT/pages/spring-cloud-openfeign.adoc index d43b8c1cb..5ff9472eb 100644 --- a/docs/modules/ROOT/pages/spring-cloud-openfeign.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-openfeign.adoc @@ -144,14 +144,13 @@ When it comes to the Apache HttpClient 5-backed Feign clients, it's enough to en 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. 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. -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`. +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. 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. [source,java,indent=0] ---- -@Configuration -public class FooConfiguration { +public class HttpClientConfiguration { @Bean public HttpClient5FeignConfiguration.HttpClientBuilderCustomizer httpClientBuilder() { @@ -164,6 +163,24 @@ public class FooConfiguration { } ---- +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. + +[source,java,indent=0] +---- +public class HttpClientConnectionManagerConfiguration { + + @Bean + public HttpClient5FeignConfiguration.HttpClientConnectionManagerBuilderCustomizer httpClientConnectionManagerBuilder() { + return (httpClientConnectionManagerBuilder) -> { + TlsConfig.Builder tlsConfigBuilder = TlsConfig.custom(); + // 0 seconds timeout means infinite + tlsConfigBuilder.setHandshakeTimeout(Timeout.of(0, TimeUnit.SECONDS)); + httpClientConnectionManagerBuilder.setDefaultTlsConfig(tlsConfigBuilder.build()); + }; + } +} +---- + TIP: Starting with Spring Cloud OpenFeign 4, the Feign Apache HttpClient 4 is no longer supported. We suggest using Apache HttpClient 5 instead. 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: From 3b4cc33646876eca6794a7878c81f41d2af5b935 Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Thu, 18 Dec 2025 09:34:00 -0500 Subject: [PATCH 4/5] Update docs/modules/ROOT/pages/spring-cloud-openfeign.adoc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Ryan Baxter --- docs/modules/ROOT/pages/spring-cloud-openfeign.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/spring-cloud-openfeign.adoc b/docs/modules/ROOT/pages/spring-cloud-openfeign.adoc index 5ff9472eb..4ca68c192 100644 --- a/docs/modules/ROOT/pages/spring-cloud-openfeign.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-openfeign.adoc @@ -174,8 +174,8 @@ public class HttpClientConnectionManagerConfiguration { return (httpClientConnectionManagerBuilder) -> { TlsConfig.Builder tlsConfigBuilder = TlsConfig.custom(); // 0 seconds timeout means infinite - tlsConfigBuilder.setHandshakeTimeout(Timeout.of(0, TimeUnit.SECONDS)); - httpClientConnectionManagerBuilder.setDefaultTlsConfig(tlsConfigBuilder.build()); + tlsConfigBuilder.setHandshakeTimeout(Timeout.of(0, TimeUnit.SECONDS)); + httpClientConnectionManagerBuilder.setDefaultTlsConfig(tlsConfigBuilder.build()); }; } } From 9dd3164a30edac0d2cbed4ea82e203e4132ccc5d Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Thu, 18 Dec 2025 09:34:24 -0500 Subject: [PATCH 5/5] 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 --- .../openfeign/clientconfig/HttpClient5FeignConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/HttpClient5FeignConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/HttpClient5FeignConfiguration.java index d4855d734..c09153808 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/HttpClient5FeignConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/HttpClient5FeignConfiguration.java @@ -179,8 +179,8 @@ public interface HttpClientBuilderCustomizer { } /** - * Callback interface that customize {@link HttpClientBuilder} objects before - * HttpClientConnectionManager created. + * Callback interface that customizes {@link PoolingHttpClientConnectionManagerBuilder} + * objects before HttpClientConnectionManager is created. * * @author Severin Kistler * @since 5.1.0