Imagine the following code which creates a custom RestClient instance, specifically using a custom requestFactory, no beans:

val restClient = RestClient
    .builder()
    .requestFactory(ClientHttpRequestFactoryBuilder.httpComponents().build())
    .baseUrl(config.baseUrl)
    .defaultHeader(USER_AGENT_HEADER_NAME, USER_AGENT_HEADER_VALUE)
    .defaultUriVariables(mapOf(CLIENT_ID_PARAMETER_NAME to config.clientId))
    .requestInterceptor(BasicAuthenticationInterceptor(config.clientId, config.clientSecret))
    .apply(restClientSsl.fromBundle(REST_CLIENT_SSL_BUNDLE_NAME))
    .build()

When this RestClient is created - I would expect it to use a custom request factory based on standard ClientHttpRequestFactoryBuilder.httpComponents().build(), no Spring Beans involved.

However, instead, this RestClient instance still uses globally defined Spring Bean, for example:

@Bean
fun httpComponentsClientHttpRequestFactoryBuilder(): HttpComponentsClientHttpRequestFactoryBuilder {
    return ClientHttpRequestFactoryBuilder.httpComponents().withDefaultRequestConfigCustomizer() {
        it.setProtocolUpgradeEnabled(false)
    }
}

It means that I can't create a custom instance of RestClient that ignores global configuration and instead I have to globally override the Spring Bean instance of HttpComponentsClientHttpRequestFactoryBuilder

Am I doing something wrong? :) I'm not that good with Spring however, so thanks for pointing out to the right Spring configuration, etc. There is some info here but I'm not sure.

Using Spring Boot org.springframework.boot version 3.4.2, spring-web 6.2.2.

Thanks! πŸ˜„

Comment From: bclozel

Hello @user1bh The build call you have shared should work as you have described. Now you are using Spring Boot, and it's not clear how your custom client is contributed to the application context with other beans and how the auto-configuration is working in your case.

Can you share a minimal sample Spring Boot application that reproduces the behavior you are seeing please?

Comment From: user1bh

@bclozel thanks for your response! While I was creating a minimal repro-project, I have found the reason for the problem: it was the auto-configured RestClientSsl that I used.. You've had a right guess about auto-configuration.

So now, instead of using RestClientSsl I'm manually applying an sslBundle however via RestTemplate.

Instead of the following:

val restClient = RestClient
    .builder()
    .requestFactory(ClientHttpRequestFactoryBuilder.httpComponents().build())
    .baseUrl(config.baseUrl)
    .defaultHeader(USER_AGENT_HEADER_NAME, USER_AGENT_HEADER_VALUE)
    .defaultUriVariables(mapOf(CLIENT_ID_PARAMETER_NAME to config.clientId))
    .requestInterceptor(BasicAuthenticationInterceptor(config.clientId, config.clientSecret))
    .apply(restClientSsl.fromBundle(REST_CLIENT_SSL_BUNDLE_NAME)) // The problem was here <---
    .build()

Now I'm doing custom RestClient creation via RestTemplate:

val restClient = RestClient.create(
    restTemplateBuilder
        .requestFactoryBuilder(ClientHttpRequestFactoryBuilder.httpComponents())
        .rootUri(config.baseUrl)
        .defaultHeader(USER_AGENT_HEADER_NAME, USER_AGENT_HEADER_VALUE)
        .interceptors(BasicAuthenticationInterceptor(config.clientId, config.clientSecret))
        .sslBundle(sslBundles.getBundle(REST_CLIENT_SSL_BUNDLE_NAME)) // This is the fix <---
        .build()
)

Maybe last question: @bclozel do you know if it's possible to supply an sslBundle to RestClient builder directly, without using an intermediate RestTemplate? Thanks! πŸ˜„

Comment From: bclozel

Thanks for getting back to us. I believe this is covered in the Spring Boot reference documentation about RestClientSsl. ClientHttpRequestFactorySettings seems like a good approach here.

Comment From: user1bh

@bclozel thanks again! Indeed the provided example with ClientHttpRequestFactorySettings works well πŸ‘