Stefan Falk
Stefan Falk

Reputation: 25387

Getting java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode

I've created a test for the creation of a new user:

private static String USERS_ENDPOINT = "http://localhost:8080/users/";
private static String GROUPS_ENDPOINT = "http://localhost:8080/groups/";

@Test
@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
public void whenCreateAppUser() {

    AppUser appUser = new AppUser();
    appUser.setUsername("[email protected]");
    appUser.setPassword("password");

    // Throws java.net.HttpRetryException
    template.postForEntity(USERS_ENDPOINT, appUser, AppUser.class);

    ResponseEntity<AppUser> appUserResponse = template.getForEntity(USERS_ENDPOINT + "1/", AppUser.class);

    assertEquals("Username is incorrect. AppUser not created?",
            appUser.getUsername(), appUserResponse.getBody().getUsername());
}

However, for some reason I am getting:

Caused by: java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1692)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
    at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:55)
    at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:49)
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:735)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:700)
    ... 34 more

For the call

template.postForEntity(USERS_ENDPOINT, appUser, AppUser.class);

I actually don't know what I changed because this used to work for me. Any idea what causes this issue?


My WebSecurity settings are:

@Override
public void configure(WebSecurity web) throws Exception {

    final String[] SWAGGER_UI = {
            "/swagger-resources/**",
            "/swagger-ui.html",
            "/v2/api-docs",
            "/webjars/**"
    };

    web.ignoring().antMatchers("/pub/**", "/users")
            .antMatchers(SWAGGER_UI);
}

Upvotes: 17

Views: 21268

Answers (3)

Alex Shesterov
Alex Shesterov

Reputation: 27525

Spring's RestTemplate and Spring Boot's TestRestTemplate will on JDK's internal HttpURLConnection implementation by default, which fails to access the body of an HTTP-response with status 401 "Unauthorized".

A magical solution is to include Apache's HTTP Client into the classpath — e.g. with Maven,

  • For Spring Boot 3.x.x:

    <dependency>
      <groupId>org.apache.httpcomponents.client5</groupId>
      <artifactId>httpclient5</artifactId>
      <version>5.2.1</version>
      <scope>test</scope>
    </dependency>
    
  • For Spring Boot 2.x.x:

    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.6</version>
      <scope>test</scope>
    </dependency>
    

In this case, Spring (Boot) will use Apache's HTTP Client which doesn't face the problem.


See also: https://github.com/spring-projects/spring-security-oauth/issues/441#issuecomment-92033542

Upvotes: 37

blacelle
blacelle

Reputation: 2217

As answered in java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode

an alternative is:

SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setOutputStreaming(false);
return requestFactory;

Upvotes: 1

mwatzer
mwatzer

Reputation: 864

Using the HttpClient from the Apache HttpComponents Client library solved the issue for me.

To use the Apache HttpClient you have to configure your RestTemplate the following way:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestClientConfig {

    @Bean
    public RestTemplate restTemplate() {
        var template = new RestTemplate();

        template.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

        return template;
    }

}

Upvotes: 5

Related Questions