0

I have many filters that I use to manipulate different requests.

I'm overriding the default netty httpClient provided by Spring Gateway so I can set programatically some sslContext - mTLS in my case. This is fine, but I will also need to have a second netty httpClient so I can connect to other server with no ssl at all, or just regular tls.

I was wondering if it's possible either to set sslContext on specific filters or to decide what httpClient to use based on the filter or some other extension point that spring gateway provides.

Do you think it is possible to have multiple httpClients and decide which one to use?

I tried to create a new bean which configures a httpClient and set some sslContext like this:

@Bean
public reactor.netty.http.client.HttpClient httpClient() {

  final PrivateKey privateKey = getPrivateKey();
  final X509Certificate x509Certificate = getX509Certificate();

  final SslContext sslContext =
      SslContextBuilder.forClient()
          .trustManager(InsecureTrustManagerFactory.INSTANCE)
          .keyManager(privateKey, x509Certificate)
          .build();

  return reactor.netty.http.client.HttpClient.create().secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
}

While I use this httpClient to connect via mTLS it works fine, but I'd like to decide what httpClient to use based on specific filters.

Javato
  • 1
  • 1
  • I also needed to find out it recently, see the link https://stackoverflow.com/questions/75029850/multiple-different-sslcontext-in-webclient-from-webflux – kerbermeister Jan 18 '23 at 15:33
  • @kerbermeister wow, couldn't find it while searching about this issue... seems we were trying the same, but still, how can I use one httpClient or the other based on my filters? is there any extension point which allows me to decide what bean to use? AFAIK, only request is mutable through filters chain, but not the httpClient itself. Am I wrong? – Javato Jan 19 '23 at 11:01

1 Answers1

0

I had a similar requirement where I needed to use different HttpClients for different routes. The way I went about it was to create a class that extends the org.springframework.cloud.gateway.filter.NettyRoutingFilter class and override the HttpClient getHttpClient(Route route, ServerWebExchange exchange) method. In that method you can specify what HttpClient should be used for what route. And example class is below.

import java.util.List;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.config.HttpClientProperties;
import org.springframework.cloud.gateway.filter.NettyRoutingFilter;
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import reactor.netty.http.client.HttpClient;

@Component
public class CustomNettyRoutingFilter extends NettyRoutingFilter {

    private final HttpClient customClientOne;
    private final HttpClient customClientTwo;
    
    
    public CustomNettyRoutingFilter(HttpClient httpClient,
            ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider, HttpClientProperties properties) {
        super(httpClient, headersFiltersProvider, properties);
        customClientOne=createCustomClientOne();
        customClientTwo=createCustomClientTwo();
    }

    @Override
    protected HttpClient getHttpClient(Route route, ServerWebExchange exchange) {
        if(shouldUseClientOne(route)) {
            return customClientOne;
        }
        if(shouldUseClientTwo(route)) {
            return customClientTwo;
        }
        //If handeling another route use the default client.
        return super.getHttpClient(route, exchange);
    }
    
    @Override
    public int getOrder() {
        return return super.getOrder() - 1;
    }   
    
    //Other methods here
}

If an instance of this CustomNettyRoutingFilter class is loaded as a bean in the ApplicationContext it will be added to the list of GlobalFilters used on each request. (I used @Component for that in this case). The original NettyRoutingFilter created by the Spring Cloud Gateway is still in the global filters list and to make sure the custom one fires before the original one I override the getOrder() method and have it return a value one less that what the NettyRoutingFilter returns (lower order values correspond to higher priority). I don’t know if its required to override the getOrder() method because it was working for me with it but I think it’s a safer bet to do the override. (You can see the list of global filters here using an actuator endpoint see: here)

I’m not at all sure if this is the best practice way of doing something like this but did work for me.

David Harris
  • 705
  • 1
  • 7
  • 16