Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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);
}
like image 603
Stefan Falk Avatar asked Mar 05 '18 20:03

Stefan Falk


3 Answers

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:

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

In this case, Spring (or 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


P.S. I've just fought the same problem :)

like image 146
Alex Shesterov Avatar answered Nov 01 '22 12:11

Alex Shesterov


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;
    }

}
like image 4
mwatzer Avatar answered Nov 01 '22 12:11

mwatzer


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;
like image 1
blacelle Avatar answered Nov 01 '22 13:11

blacelle