Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions docs/modules/ROOT/pages/spring-cloud-openfeign.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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() {
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<List<HttpClientConnectionManagerBuilderCustomizer>> customizerProvider) {
PoolingHttpClientConnectionManagerBuilder httpClientConnectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(httpsSSLConnectionSocketFactory(httpClientProperties.isDisableSslValidation()))
.setMaxConnTotal(httpClientProperties.getMaxConnections())
.setMaxConnPerRoute(httpClientProperties.getMaxConnectionsPerRoute())
Expand All @@ -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
Expand Down Expand Up @@ -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);

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}

}

}
Loading