spring-cloud-openfeign icon indicating copy to clipboard operation
spring-cloud-openfeign copied to clipboard

SpringBoot OpenFeign Ignores Custom CloseablehttpClient

Open sidhartha11 opened this issue 2 years ago • 3 comments

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.

sidhartha11 avatar Apr 08 '22 19:04 sidhartha11

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

SSchrodingerCat avatar Apr 14 '22 12:04 SSchrodingerCat

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.

jmsjr avatar Jun 03 '22 00:06 jmsjr

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

sidhartha11 avatar Jun 06 '22 18:06 sidhartha11

Closing as confirmed as resolved by the reporter.

OlgaMaciaszek avatar Sep 05 '22 10:09 OlgaMaciaszek