5

Configure Proxy Route Planner on Feign

I need to know how to make requests being behind a proxy server using the spring boot REST client. I can do this configuration using apache commons for REST requests like this:

Method that performs POST:

Map <String, Object> map = new TreeMap <String, Object> ();

DynamicProxyRoutePlanner routePlanner = getProxy (param);

CloseableHttpClient closeableHttpClient = HttpClients.custom (). SetRoutePlanner (routePlanner) .build ();

GetProxy method:

private DynamicProxyRoutePlanner getProxy (Map <Integer, Object> param) {
        HttpHost proxy = new HttpHost ((String) param.get (PROXY), (Integer) param.get (PROXY_PORT));

        DynamicProxyRoutePlanner routePlanner = new DynamicProxyRoutePlanner (proxy);
        return routePlanner;
}

DynamicProxyRoutePlanner class:

public class DynamicProxyRoutePlanner implements HttpRoutePlanner {

    private DefaultProxyRoutePlanner defaultProxyRoutePlanner = null;

    public DynamicProxyRoutePlanner (HttpHost host) {
        defaultProxyRoutePlanner = new DefaultProxyRoutePlanner (host);
    }

    public void setProxy (HttpHost host) {
        defaultProxyRoutePlanner = new DefaultProxyRoutePlanner (host);
    }

    public HttpRoute determineRoute (HttpHost target, HttpRequest request, HttpContext context) throws HttpException {
        return defaultProxyRoutePlanner.determineRoute (target, request, context);
    }
}

Summing up ...

  • I need to create this same ProxyRoutePlanner configuration on Feign.

EDITED --

I created a class

@Configuration
public class FeignConfiguration {

with the methods

@Bean
    @ConditionalOnMissingBean (CloseableHttpClient.class)
    public CloseableHttpClient httpClient () {
        DynamicProxyRoutePlanner routePlanner = getProxy ();
        CredentialsProvider credentialsProvider = getCredentials ();

        return HttpClients.custom ()
                .setDefaultCredentialsProvider (credentialsProvider)
                .setRoutePlanner (routePlanner) .build ();
    }

    private DynamicProxyRoutePlanner getProxy () {
        HttpHost proxy = new HttpHost (HOST_PROXY, PORT_PROXY);
        return new DynamicProxyRoutePlanner (proxy);
    }


    public CredentialsProvider getCredentials () {
        AuthCache authCache = new BasicAuthCache ();
        CredentialsProvider credsProvider = new BasicCredentialsProvider ();
        credsProvider.setCredentials (new AuthScope (HOST_PROXY, PORT_PROXY, AuthScope.ANY_HOST, "ntlm"),
                new NTCredentials (USER, PASS, "", DOMAIN));
        HttpClientContext context = HttpClientContext.create ();
        context.setCredentialsProvider (credsProvider);
        context.setAuthCache (authCache);
        return credsProvider;
    }

I have refitted the connection tests but even though Feign does not seem to be trying to use the proxy settings, see the error below:

2018-04-25 09: 59: 10.494 ERROR 25309 --- [nio-9063-exec-2] oaccC [. [. [.] [DispatcherServlet]: Servlet.service () for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.RetryableException: Connection refused (Connection refused) executing POST http://sinaflor-api.des.basis.com.br/api/empreendimentos] with root cause

java.net.ConnectException: Connection refused (Connection refused)
at java.net.PlainSocketImpl.socketConnect (Native Method) ~ [na: 1.8.0_171]
at java.net.AbstractPlainSocketImpl.doConnect (AbstractPlainSocketImpl.java:350) ~ [na: 1.8.0_171]
at java.net.AbstractPlainSocketImpl.connectToAddress (AbstractPlainSocketImpl.java:206) ~ [na: 1.8.0_171]
at java.net.AbstractPlainSocketImpl.connect (AbstractPlainSocketImpl.java:188) ~ [na: 1.8.0_171]
at java.net.SocksSocketImpl.connect (SocksSocketImpl.java:392) ~ [na: 1.8.0_171]
at java.net.Socket.connect (Socket.java:589) ~ [na: 1.8.0_171]
at sun.net.NetworkClient.doConnect (NetworkClient.java:175) ~ [na: 1.8.0_171]
at sun.net.www.http.HttpClient.openServer (HttpClient.java:463) ~ [na: 1.8.0_171]
at sun.net.www.http.HttpClient.openServer (HttpClient.java:558) ~ [na: 1.8.0_171]
at sun.net.www.http.HttpClient. <init> (HttpClient.java:242) ~ [na: 1.8.0_171]
at sun.net.www.http.HttpClient.New (HttpClient.java:339) ~ [na: 1.8.0_171]
at sun.net.www.http.HttpClient.New (HttpClient.java:357) ~ [na: 1.8.0_171]
at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient (HttpURLConnection.java:1220) ~ [na: 1.8.0_171]
at sun.net.www.protocol.http.HttpURLConnection.plainConnect0 (HttpURLConnection.java:1156) ~ [na: 1.8.0_171]
at sun.net.www.protocol.http.HttpURLConnection.plainConnect (HttpURLConnection.java:1050) ~ [na: 1.8.0_171]
at sun.net.www.protocol.http.HttpURLConnection.connect (HttpURLConnection.java:984) ~ [na: 1.8.0_171]
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0 (HttpURLConnection.java:1334) ~ [na: 1.8.0_171]
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream (HttpURLConnection.java:1309) ~ [na: 1.8.0_171]
at feign.Client $ Default.convertAndSend (Client.java:133) ~ [feign-core-9.3.1.jar: na]
at feign.Client $ Default.execute (Client.java:73) ~ [feign-core-9.3.1.jar: na]
at feign.SynchronousMethodHandler.executeAndDecode (SynchronousMethodHandler.java:97) ~ [feign-core-9.3.1.jar: na]
at feign.SynchronousMethodHandler.invoke (SynchronousMethodHandler.java:76) ~ [feign-core-9.3.1.jar: na]
at feign.ReflectiveFeign $ FeignInvocationHandler.invoke (ReflectiveFeign.java:103) ~ [feign-core-9.3.1.jar: na]
at com.sun.proxy $ Proxy112.send (Unknown Source) ~ [na: na]

3 Answers3

4

I was able to resolve the proxy configuration in feign using okHttpClient like this:

@Configuration
public class FeignConfigurationProxy {

    @Value("${url.proxy_host}")
    private String HOST_PROXY;
    @Value("${url.proxy_port}")
    private Integer PORT_PROXY;

    private OkHttpClient okHttpClient;
    private Proxy proxy;

    @PostConstruct
    public void init() {
        buildProxy();
        buildOkHttpClient();
    }

    @Bean(name = "feign")
    public Feign buildClient() {
        return Feign.builder().client(new feign.okhttp.OkHttpClient(okHttpClient)).build();
    }


    @Bean(name = "okhttpclient")
    public OkHttpClient okHttpClient() {
        return okHttpClient;
    }

    public void buildOkHttpClient() {
        buildProxy();
        okHttpClient = new OkHttpClient.Builder().proxy(proxy).build();
    }

    public void buildProxy() {
        proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(HOST_PROXY, PORT_PROXY));
    }

}

in pom.xml I added the dependencies:

<dependency>
<groupId> io.github.openfeign </ groupId>
<artifactId> feign-okhttp </ artifactId>
<version> 9.5.0 </ version>
</ dependency>
<dependency>
<groupId> com.squareup.okhttp3 </ groupId>
<artifactId> okhttp </ artifactId>
<version> 3.10.0 </ version>
</ dependency>
  • Does the feign-okhttp perform better than the apache one? What are the performance configuration best practices? – Tal Avissar Jul 02 '18 at 04:54
  • maybe the same performance, using spring-cloud-feign, by default it uses the okhttp client instead of the apache client, which makes me think that okhttp actually has an advantage over the apache client! When it comes to setting up the http client, I believe that the best way to configure it is the default, by customizing the minimum possible, since it has already been designed in a way that will behave quickly and steadily for most cases, leaving the customizations for cases like this one where I had to actually handle requests through corporate proxy or not. – Diego Cândido da Silva Jul 02 '18 at 19:53
  • @DiegoCândidodaSilva - The spring cloud openfeign default is not OkHttpClient. It's instead feign.Client.Default. The `OkHttpClient` and `ApacheHttpClient` feign clients can be used by setting `feign.okhttp.enabled` or `feign.httpclient.enabled` to true, respectively, and having them on the classpath. Reference: https://cloud.spring.io/spring-cloud-openfeign/reference/html/#spring-cloud-feign-overriding-defaults – Amith Kumar Mar 12 '21 at 05:56
3

With Spring cloud openfeign supporting three underlying implementations:

  1. Default
  2. ApacheHttpClient
  3. OkHttpClient

Depending on what implementation is used in your project, here is how you configure the feign client to use HTTP proxy: TRICK: You configure the feign client and then override the default client bean in the Spring application context.

With Default:

import feign.Client;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.InetSocketAddress;
import java.net.Proxy;

@Configuration
public class FeignConfiguration {
  ...
  @Bean
  public Client feignClient() {
      return new Client.Proxied(null, null, 
                 new Proxy(Proxy.Type.HTTP,
                     new InetSocketAddress(proxyHost, proxyPort)));
  }
}

With ApacheHttpClient:

when you have feign.httpclient.enabled: true in application.yml and below in your pom.xml or build.gradle:

pom.xml
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

build.gradle
implementation 'io.github.openfeign:feign-httpclient'


import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.http.HttpHost;

@Configuration
public class FeignConfiguration {
  ...
  @Bean
  public CloseableHttpClient feignClient() {
    return HttpClientBuilder.create().setProxy(
              new HttpHost(proxyHost, proxyPort)).build();
  }
}

With OkHttpClient:

when you have feign.okhttp.enabled: true in application.yml and below in your pom.xml or build.gradle:

pom.xml
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

build.gradle
implementation 'io.github.openfeign:feign-okhttp'


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.InetSocketAddress;
import java.net.Proxy;
import okhttp3.OkHttpClient;

@Configuration
public class FeignConfiguration {
  ...
  @Bean
  public OkHttpClient okHttpClient() {
    return new OkHttpClient.Builder()
       .proxy(new Proxy(Proxy.Type.HTTP, 
             new InetSocketAddress(proxyHost, proxyPort)))
       .build();
  }
}
Amith Kumar
  • 4,400
  • 1
  • 21
  • 28
0

Just define your CloseableHttpClient as Spring Bean.

From Spring Cloud Edgware release, if you define CloseableHttpClient as Spring Bean, it will be used instead of default one.

yongsung.yoon
  • 5,489
  • 28
  • 32
  • Do you have any examples of this configuration, how would you use feign? – Diego Cândido da Silva Apr 24 '18 at 14:20
  • No special configuration is required. Currrent FeignAutoConfiguration class only load its own CloseableHttpClient bean if not exist like following. @ConditionalOnMissingBean(CloseableHttpClient.class). So if you define your CloseableHttpClient as spean, it will be used inside feign. – yongsung.yoon Apr 25 '18 at 04:17
  • I edited the topic with the new errors after the suggested implementation, if you can help me with this solution will help me a lot. – Diego Cândido da Silva Apr 25 '18 at 14:13
  • which version of spring cloud are you using? And you don't need to use @ConditionalOnMissingBean for you. – yongsung.yoon Apr 25 '18 at 14:58