Issue
I'm trying to provide CloseableHttpClient to Spring Cloud OpenFeign. Spring Cloud Open Feign Documentationsays it supports CloeableHttpClient. Spring documentation doesn't give any example of actually replacing the HTTP client.
Basically, I'm providing SSLContext to the HTTP client and I want Feign to use this SSLContext loaded client. How to inject this CloseableHttpClient into the Feign?
Following is my relevant configuration:
- I'm using SpringBootApp
@SpringBootApplication
@EnableFeignClients
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
- Feign Client Interface is as follow:
import org.springframework.cloud.openfeign.FeignClient;
//skipping rest of the imports for brevity
@FeignClient(name ="remote-service", url = "${remote.service-url}", configuration = FeignConfig.class)
public interface RemoteServiceApi {
@GetMapping(value = "/api/v1/resources/{Id}")
public String getResource(@PathVariable("Id") String Id);
}
- FeignConfig class
import org.apache.http.impl.client.CloseableHttpClient;
//skipping rest of the imports for brevity
public class FeignConfig {
@Bean
public CloseableHttpClient client() {
CloseableHttpClient httpClient=null;
try {
//... Skipping code for brevity.
//here creating "sslSocketFactory" used in the HttpClient builder below
httpClient = HttpClients.custom().setSSLSocketFactory(sslSocketFactory)
.setMaxConnTotal(10)
.setMaxConnPerRoute(10)
.build();
}catch(IOException | KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | CertificateException e) {
System.err.println("Exception during creation of HttpClient. : "+e.getMessage());
}
return httpClient;
}
}
- In the application.properties feign.httpclient.enabled is set to true
- Springboot version is 2.4.4. Feign version is feign-core-10.10.1
Other part which I didn't understand, how the Spring was going to hook this custom CloseableHttpClient to Feign as it claims. Because when I debug, at runtime I see the Feign annotated interface is implemented by feign.SynchronousMethodHandler class and the 'client' field in this class is of type feign.Client and at runtime it gets com.sun.security.ntlm.Client (may be the default implementation). How CloseableHttpClient is supposed to get injected in feign.Client? There are very few examples of such thing available on the web and they don't explain it.
I found this post on SOF but
- here as well a @Bean of type CloseableHttpClient is injected
- there is no useful answer to this.
Solution
In the bean that we are injecting we have to provide feign.Client's implementation. Simplest is new Client.Default(SSLSocketFactory, HostnameVerifier)
. I changed the httpClient @Bean injection in code in my question from:
@Bean
public CloseableHttpClient client() {
CloseableHttpClient httpClient=null;
try {
//... Skipping code for brevity.
//here creating "sslSocketFactory" used in the HttpClient builder below
httpClient = HttpClients.custom().setSSLSocketFactory(sslSocketFactory)
.setMaxConnTotal(10)
.setMaxConnPerRoute(10)
.build();
}catch(IOException | KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | CertificateException e) {
System.err.println("Exception during creation of HttpClient. : "+e.getMessage());
}
return httpClient;
}
to :
@Bean
public feign.Client client() {
feign.Client client=null;
try {
//... Skipping code for brevity.
//here creating "sslSocketFactory" used in the HttpClient builder below
client = new Client.Default(sslSocketFactory, new DefaultHostnameVerifier());
}catch(IOException | KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | CertificateException e) {
System.err.println("Exception during creation of HttpClient. : "+e.getMessage());
}
}
Learnings from the experience:
When the documentation says, "You can customize the HTTP client used by providing a bean of either ClosableHttpClient when using Apache or OkHttpClient when using OK HTTP."
And then people say they provided CloseableHttpClient as it is in this question which is not answered properly. The bean injection in this will never work.
On the top of that, the OpenFeign's Github documentation talks about using ApacheHttpClient.
This may leave a person confused. The second part in my question
"Other part which I didn't understand, how the Spring was going to hook this custom CloseableHttpClient to Feign as it claims.....
the 'client' field in this class is of type feign.Client "
Answer:
There is no magic, what the ApacheHttpClient OpenFeign Github documentation talks about is OpenFeign's wrapper on the Apache's HttpClient library ApacheHttpClient it implements feign.Client interface.
And this ApacheHttpClient implementation is not available in feign core 10.1.1 dependency that comes with Spring Boot starter for openfeign.
Answered By - Akshay Hiremath
Answer Checked By - David Marino (JavaFixing Volunteer)