spring-cloud / spring-cloud-openfeign

Support for using OpenFeign in Spring Cloud apps
Apache License 2.0
1.19k stars 774 forks source link

SpringBoot OpenFeign Ignores Custom CloseablehttpClient #699

Closed sidhartha11 closed 2 years ago

sidhartha11 commented 2 years ago

I am trying to create a custom closeablehttpclient using OpenFeign that is behind an NTLM proxy. After searching thru internet I finally discovered an example of a Closeablehttpclient that supposedly works going thru NTLM proxy. When I create a bean of the client ... Spring Boot Simply ignores it ... I THINK. Now This is what I observed:

  1. if I used the exact same bean in a tiny standalone java application .... it works and connects to an external client thru the NTLM proxy.
  2. if I used the exact same bean in a SpringBoot Openfeign client , spring seems to use some other default client ?

Question: If there a bug or limitation in SpringBoot when it comes to using CloseableHttpClients ? I am looking for a simple, clear and concise answer to this if there is such an answer. I can supply additional information if required, however my source code is proprietary and basically illegal for me to share it exactly. I have updated properties file as follows: feign.httpclient.enabled=true feign.httpclient.disableSslValidation=true

Mave Pom as followsI added these dependencies: feign-httpclient httpclient ( 4.5.13 ) Spring boot version 2.6.1

Now the code I use for the http client works stand alone ... but is not called if I try to use it as a custom feign client .. Thanks for any info you can supply.

SSchrodingerCat commented 2 years ago

I think there is a bug.

in FeignAutoConfiguration class, defined how to inject HttpClient:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnMissingBean(CloseableHttpClient.class)
@ConditionalOnProperty(value = "spring.cloud.openfeign.httpclient.enabled", matchIfMissing = true)
@Conditional(HttpClient5DisabledConditions.class)
protected static class HttpClientFeignConfiguration {

    private final Timer connectionManagerTimer = new Timer(
                "FeignApacheHttpClientConfiguration.connectionManagerTimer", true);

    @Autowired(required = false)
    private RegistryBuilder registryBuilder;

    private CloseableHttpClient httpClient;

    @Bean
    @ConditionalOnMissingBean(HttpClientConnectionManager.class)
    public HttpClientConnectionManager connectionManager(
            ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
            FeignHttpClientProperties httpClientProperties) {
        final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(
                httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
                httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(),
                httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
        this.connectionManagerTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                connectionManager.closeExpiredConnections();
            }
        }, 30000, httpClientProperties.getConnectionTimerRepeat());
        return connectionManager;
    }

    @Bean
    public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
            HttpClientConnectionManager httpClientConnectionManager,
            FeignHttpClientProperties httpClientProperties) {
        RequestConfig defaultRequestConfig = RequestConfig.custom()
                .setConnectTimeout(httpClientProperties.getConnectionTimeout())
                .setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
        this.httpClient = httpClientFactory.createBuilder().setConnectionManager(httpClientConnectionManager)
                .setDefaultRequestConfig(defaultRequestConfig).build();
        return this.httpClient;
    }

    @Bean
    @ConditionalOnMissingBean(Client.class)
    public Client feignClient(HttpClient httpClient) {
        return new ApacheHttpClient(httpClient);
    }

    @PreDestroy
    public void destroy() {
        this.connectionManagerTimer.cancel();
        if (this.httpClient != null) {
            try {
                this.httpClient.close();
            }
            catch (IOException e) {
                if (LOG.isErrorEnabled()) {
                    LOG.error("Could not correctly close httpClient.");
                }
            }
        }
    }

}

this class not inject bean otherwise has some Condition like annotation.

be attention of ConditionalOnMissingBean annotation, this annotation means that only inject httpClient when spring context dose not have CloseableHttpClient, so if u declare other Configuration that include CloseableHttpClient bean, openfeign config feign.httpclient.disableSslValidation will be invalid.

from version 4.0.0-SNAPSHOT

jmsjr commented 2 years ago

Not sure if this helps you but I have openfeign use apache httpclient4 ( and httclient5 ) to connect through an outbound proxy that requires NTLM authentication.

Below is a snapshot on how to use hc5 with NTLM proxy :

  <dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-hc5</artifactId>
  </dependency>
@Bean
public ApacheHttp5Client feignClient() {
  ....
  // Proxy credentials
  BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
  credentialsProvider.setCredentials(new AuthScope(proxyHost, proxyPort),
    new NTCredentials(proxyUser, proxyPwd.toCharArray(), "", proxyDomain));

  // Proxy
  clientBuilder.setProxy(new HttpHost(proxyHost, proxyPort));
  clientBuilder.setDefaultCredentialsProvider(credentialsProvider);
  clientBuilder.setProxyAuthenticationStrategy(new DefaultAuthenticationStrategy());

  ....
  return new ApacheHttp5Client(clientBuilder.build());
}

You can also use HttpClient4 if you want.

sidhartha11 commented 2 years ago

thanks for he info. I did manage to figure out the NTLM issue using openfeign with apatche http client. However, since that time we changed from NtlM to a NON-NTLM proxy and everything works smoothly

OlgaMaciaszek commented 2 years ago

Closing as confirmed as resolved by the reporter.