7

I encountered a very strange situation when using Spring and Wiremock for integration testing: suddenly, one specific test started failing intermittently. A snippet of the error below:

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:10314/my/endpoint": Software caused connection abort: recv failed; nested exception is java.net.SocketException: Software caused connection abort: recv failed
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:785) ~[spring-web-5.3.7.jar:5.3.7]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711) ~[spring-web-5.3.7.jar:5.3.7]
    at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:468) ~[spring-web-5.3.7.jar:5.3.7]
... more logs here ...

The context is as follows: I have added a new test that uses wiremock to stub responses:

wireMockServer.stubFor(WireMock.post("/my/endpoint")
    .withRequestBody(containing(aJsonRequestBodyHere))
    .willReturn(aResponse()
            .withBody(aJsonResponseHere)
            .withStatus(HttpStatus.OK.value())
            .withHeader(HttpHeader.CONTENT_TYPE.toString(), CONTENT_TYPE_APPLICATION_JSON)));

The call to this stub endpoint is made as follows:

given()
    .when()
    .get("my/endpoint")
    .then()
    .body(containsString(theExpectedJsonResponse)))
    .statusCode(200);

The strange part:

  • the same test runs without any problems on my local machine - if run alone
  • when running all the tests on my machine, sometimes, the same test fails, sometimes does not
  • only this test is failing each time; no other test fails
  • when tests are run on Jenkins, it fails 100%
Victor
  • 1,001
  • 2
  • 14
  • 25

4 Answers4

5

After digging a little on this, I have came across this and this articles that described my situation almost 100%.

The root cause seemed to be the fact that tests were executing to fast - maybe the ones that did not do too many things - and Wiremock did not have time to setup correctly for the next test. I have tested this assumption by adding a Thread.sleep(2000) at the beginning of the test and then run many times all the test - all tests passed without problem.

The solution is presented in the first article: register a Transformer class that will intercept all responses and add a Connection=close header to them.

In more details: I have added a Transformer class that extends ResponseDefinitionTransformer and adds the Connection header on each response. Then I have created @Configuration annotated class and registered this Transformer

Transformer class (taken from the first article):

public class NoKeepAliveTransformer extends ResponseDefinitionTransformer {

    @Override
    public ResponseDefinition transform(Request request, ResponseDefinition responseDefinition, FileSource files, Parameters parameters) {
        return ResponseDefinitionBuilder.like(responseDefinition)
                .withHeader(HttpHeaders.CONNECTION, "close")
                .build();
    }

    @Override
    public String getName() {
        return "keep-alive-disabler";
    }
}

Configuration class:

@Configuration
public class WiremockConfiguration {

    @Bean
    WireMockConfigurationCustomizer optionsCustomizer() {
        return options -> options.extensions(NoKeepAliveTransformer.class);
    }
}
Victor
  • 1,001
  • 2
  • 14
  • 25
  • 2
    The root cause here probably isn't WireMock not being ready, since it blocks until fully started. It's more likely to be a problem with the way your HTTP client (the one underlying RestAssured in this case) is pooling connections, which is why adding Connection:close fixes this. I would suggest attempting to fix this in the client's configuration rather than via a WireMock extension, either by disabling connection pooling or enabling stale connection checking. – Tom Aug 26 '21 at 19:22
3

TL;DR

the workaround with .withHeader(HttpHeaders.CONNECTION, "close") on the response fixes:

org.springframework.web.client.ResourceAccessException:
"I/O error on POST request for "http://127.0.0.1:8080/wiremock/my_stubbed_service": Software caused connection abort: recv failed"

Tested with com.github.tomakehurst:wiremock:2.27.2 and com.github.tomakehurst:wiremock-jre8:2.30.1

electrobabe
  • 1,549
  • 18
  • 17
1

Faced the same problem in integrational tests while sending requests through Feign client using WireMock server.

According to Tom's comment in the previous answer decided to fix the client's configuration so that feign retried to send requests. Found the solution described in this article.

The simplest way for Feign is to add @Configuration class with feign.Retryer @Bean:

@Configuration
public class FeignConfig {
    @Bean
    public Retryer retryer() {
        return new Retryer.Default(200L,1000L,5);
    }
}
  • 1
    Even though it may solve the issue (eg. it retries until it does not receive reset connection) it feels more like a hack because it does not fix the underlying issue. – Victor Sep 25 '21 at 12:18
1

From this thread they recommend the following workaround which worked for me:

stubFor(withAuth(get(urlEqualTo(EXPECTED_ENDPOINT))).willReturn(aResponse()
        .withStatus(HttpStatus.OK.value())
        .withHeader(HttpHeaders.CONNECTION, "close")
        .withBody(objectMapper.writeValueAsString(responseObject))
));

Simply override the default keep-alive connection behavior for every stub using .withHeader(HttpHeaders.CONNECTION, "close").