13

I start a wiremock server in a integration test.

The IT pass in my local BUT some case failed in jenkins server, the error is

localhost:8089 failed to respond; nested exception is org.apache.http.NoHttpResponseException: localhost:8089 failed to respond

I try to add sleep(3000) in my test, that can fix the issue, But I don’t know the root cause of the issue, so the work around not a good idea

I also try to use @AutoConfigureWireMock(port=8089) to replace WireMockServer to start wiremock server, that could fix the problem, BUT I don't know how to do some configuration to the wiremock server using the annotation @AutoConfigureWireMock(port=8089).

Here my code to start a wiremock server, any suggestion to fix "NoHttpResponseException"?

@ContextConfiguration(
initializers = ConfigFileApplicationContextInitializer.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class BaseSpec extends Specification {
@Shared
WireMockServer wireMockServer

def setupSpec() {

wireMockServer = new WireMockServer(options().port(PORT).jettyHeaderBufferSize(12345)
.notifier(new ConsoleNotifier(new Boolean(System.getenv(“IT_WIREMOCK_LOG”) ?: ‘false’))) 
.extensions(new ResponseTemplateTransformer(true)))

wireMockServer.start()

}
Alvin
  • 133
  • 1
  • 1
  • 7

4 Answers4

22

Apache HttpClient suffers from NoHttpResponseException from time to time. This is a very old problem.

Anyway, I guess in your case the problem might be caused by restarting the WireMock server between tests, and at the same time, Apache HttpClient pools HTTP connections and tries to reuse them between tests. If this is the case, there are two solutions:

  1. Disable pooling HTTP connections in your tests. This makes sense because it's considered normal that the WireMock server can be restarted during tests execution. Alternatively, craft your WireMock stubs to always send "Connection": "close" among the headers. The outcome will be the same.

  2. Switch from Apache HttpClient to Square OkHttp. OkHttp, although it pools http connections by default, is always able to gracefully recover from situations like a stale connection. Unfortunately the library from Apache is not so smart.

G. Demecki
  • 10,145
  • 3
  • 58
  • 58
12

Coorect, as already written by G. Demecki it's not related to Wiremock. It’s related to your application server, which is calling wiremock. Today it’s common, to reuse a connection to improve the performance in a micro service infrastructure. So connection-close-header, RequestScoped client, etc is not useful.

Check the apache http client:

httpclient-4.5.2 - PoolingHttpClientConnectionManager

documentation

The handling of stale connections was changed in version 4.4. Previously, the code would check every connection by default before re-using it. The code now only checks the connection if the elapsed time since the last use of the connection exceeds the timeout that has been set. The default timeout is set to 2000ms

Each time a wiremock-endpoint was destroyed and a new one was created for a new test class, it takes 2 seconds, until your application detects, that the previous connection is broken and a new one has to be opened. If you don’t wait 2 seconds, such a NoHttpResponseException could be thrown, depends on the last check.

So a Thread.sleep(2000); looks ugly. But it's not so bad, as long we know why this is required.

  • 3
    Thanks for this useful piece of documentation - I didn't know it was set exactly to 2 seconds! BTW: sleeping for 2 seconds in the tests is incredibly inefficient idea, so sorry, but I cannot upvote ;) – G. Demecki Oct 05 '19 at 14:44
  • Is there any solution for this problem except using 2s timeout between test classes? I am using MockMvc to make requests to WireMock and I am having the same problem. – user1610362 Jul 09 '20 at 12:05
  • 2
    having an apache http client customization for it's test instance solved this issue for me. Fix was to make new PoolingHttpClientConnectionManager and change timeout from 2s to 10ms: `setValidateAfterInactivity(10)` as suggested in https://github.com/tomakehurst/wiremock/issues/97#issuecomment-605041024 – Pafnucy Feb 17 '21 at 20:54
6

Each time a wiremock endpoint is destroyed (because the wiremock server is restarted between tests) and a new one is created for a new test, it takes 2 seconds (as stated in documentation), until the application detects that the previous http connection is broken and a new one has to be opened.

The solution is to simply override the default keep-alive connection behaviour for every stub using .withHeader("Connection", "close"). Something like:

  givenThat(get("/endpoint_path")
            .withHeader("Authorization", equalTo(authHeader))
            .willReturn(
               ok()
                 .withBody(body)
                 .withHeader(HttpHeaders.CONNECTION, "close")
            )
  )

Also possible to do it globally using a transformer:

public class NoKeepAliveTransformer extends ResponseDefinitionTransformer {

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

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

Then this transformer have to be registered when you create the wiremock server:

new WireMockServer(
    options()
        .port(port)
        .extensions(NoKeepAliveTransformer.class)
)
Roland
  • 758
  • 1
  • 7
  • 13
-1

Solution that worked for us in this situation was just adding retry to apache client:

@Configuration
public class FeignTestConfig {

    @Bean
    @Primary
    public HttpClient testClient() {
        return HttpClientBuilder.create().setRetryHandler((exception, executionCount, context) -> {
            if (executionCount > 3) {
                return false;
            }
            return exception instanceof org.apache.http.NoHttpResponseException || exception instanceof SocketException;
        }).build();
    }
}

Socket exception is there as well, because sometimes this exception is thrown instead of NoHttpResponse

Flame239
  • 1,274
  • 1
  • 8
  • 20