BAUMERT Yoann
BAUMERT Yoann

Reputation: 79

Spring Boot 3.4.0 Broken HTTP interface

I was using the latest version Spring Boot 3.3.6 with HTTP interface like following:

    @Bean
    public RestRepositoryUser restRepositoryUser(
            @NonNull @Value("${api.url}") String apiUrl) throws URISyntaxException {

        RestClient restClient = RestClient.builder().baseUrl(new URI(apiUrl)).build();

        HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder()
                .exchangeAdapter(RestClientAdapter.create(restClient)).build();

        return factory.createClient(RestRepositoryGsUser.class);
    }

with the following content on RestRepositoryUser.class

public interface RestRepositoryUser {

    @GetExchange("/api/clientPf-data")
    public ResponseEntity<List<ClientPf>> findAllPortfolios(
            @RequestHeader("api-version") String version,
            @RequestHeader("api-requesterId") String requesterId,
            @RequestHeader("api-invocationContext") String invocationContext);
}

and in my service code I use it like this

@Log4j2
@Service
@RequiredArgsConstructor
public class ServicePortfolio {

    private final RestRepositoryUser restRepositoryPortfolio;

    public List<ClientPf> findAllPortfolios() {

        ResponseEntity<List<ClientPf>> response =
                restRepositoryPortfolio.findAllPortfolios("1.0", "maydaylocal", "maydaylocal");
        log.info("FindAllPortfolios: response from api " + response.getStatusCode());
        return response.getBody();
    }
}

When I migrate on Spring boot 3.4.0, I updated accordingly the dependency for my http stuff

        <dependency>
            <groupId>org.apache.httpcomponents.client5</groupId>
            <artifactId>httpclient5</artifactId>
        </dependency>

By testing, it looks all my GET requests are not working anymore, I face a strange behavior where:

I tested to play with the new parameters "spring.http.client.factory". Per default, as I understood it is using the "http-components" one.

Adding to this new parameter, I also tried to modify my RestClient in order to use RestTemplate instead and here is what I found:

Tests with RestTemplate

Tests with RestClient:

Upvotes: 1

Views: 2673

Answers (2)

rpmerf
rpmerf

Reputation: 171

In case someone else find this and wants to use the CloseableHttpClient, here is what I did.

private static CloseableHttpClient buildClient() {
    RequestConfig requestConfig = RequestConfig.custom().setProtocolUpgradeEnabled(false).build();
    return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build();
}

To use the client:

HttpGet get = new HttpGet(url);
try (CloseableHttpClient client = buildClient()) {
   HttpClientResponseHandler<Integer> responseHandler = response -> {
      return response.getCode();
   }
   Integer status = client.execute(get, responseHandler);
   ...
} catch (Exception e) {
   ...
}

I was getting a 400 Bad Request back from the server, but with this code, the call works as it did before.

Upvotes: 1

BAUMERT Yoann
BAUMERT Yoann

Reputation: 79

Ok, found the solution. Here is what I found:

I was calling an http endpoint on my HTTP interface. So I was thinking that this code (provided in the Spring patchnote) was useless in my case:

public HttpComponentsClientHttpRequestFactoryBuilder httpComponentsClientHttpRequestFactoryBuilder() {
    return ClientHttpRequestFactoryBuilder.httpComponents().withDefaultRequestConfigCustomizer(
            (builder) -> builder.setProtocolUpgradeEnabled(false));
}

So, it was not the case: I have to declare it whatever if it is HTTP or HTTPS endpoint. (not sure if it is normal). I was not seeing the 401 on server side because the request was blocked by the gateway before. That being said, I still don't get why I had some timeouts, but it looks to be solved too.

And also, I've to force the new function in my bean config:

        RestClient restClient = RestClient.builder().baseUrl(apiUrl)
            .requestFactory(httpComponentsClientHttpRequestFactoryBuilder().build()) // this line
            .defaultHeaders(header -> {
                header.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
                header.set("api-requesterId", "test");
                header.set("api-invocationContext", "test");
            }).build();

More info: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.4-Release-Notes#apache-http-components-and-envoy

Those headers were causing issues:

  • Upgrade: TLS/1.2
  • Connection: Upgrade

I hope it could help some people in my case!

Upvotes: 5

Related Questions