102

I have a webservice which is accepting a POST method with XML. It is working fine then at some random occasion, it fails to communicate to the server throwing IOException with message The target server failed to respond. The subsequent calls work fine.

It happens mostly, when i make some calls and then leave my application idle for like 10-15 min. the first call which I make after that returns this error.

I tried couple of things ...

I setup the retry handler like

HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {

            public boolean retryRequest(IOException e, int retryCount, HttpContext httpCtx) {
                if (retryCount >= 3){
                    Logger.warn(CALLER, "Maximum tries reached, exception would be thrown to outer block");
                    return false;
                }
                if (e instanceof org.apache.http.NoHttpResponseException){
                    Logger.warn(CALLER, "No response from server on "+retryCount+" call");
                    return true;
                }
                return false;
            }
        };

        httpPost.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, retryHandler);

but this retry never got called. (yes I am using right instanceof clause). While debugging this class never being called.

I even tried setting up HttpProtocolParams.setUseExpectContinue(httpClient.getParams(), false); but no use. Can someone suggest what I can do now?

IMPORTANT Besides figuring out why I am getting the exception, one of the important concerns I have is why isn't the retryhandler working here?

Foolish
  • 3
  • 4
Em Ae
  • 8,167
  • 27
  • 95
  • 162
  • I don't think it is something with client code. May be destination server is too busy in handling responses? – kosa May 11 '12 at 21:22
  • 1
    I tried fiddler to bombard the destination server but it worked fine. I even tried to execute the same steps to reproduce the error using fiddler, but no luck! – Em Ae May 11 '12 at 21:29
  • What kind of web server is running the service, and during the 10-15 minute wait is the service getting other requests, or is the service idle? – Shawn May 11 '12 at 21:40
  • Tomcat. No its a mock service atm and it's not receiving anything else but my calls. – Em Ae May 11 '12 at 21:45
  • I am getting this error as well but in a wrapper of ResourceAccessException. – Sagar Kharab Oct 27 '19 at 07:05

10 Answers10

147

Most likely persistent connections that are kept alive by the connection manager become stale. That is, the target server shuts down the connection on its end without HttpClient being able to react to that event, while the connection is being idle, thus rendering the connection half-closed or 'stale'. Usually this is not a problem. HttpClient employs several techniques to verify connection validity upon its lease from the pool. Even if the stale connection check is disabled and a stale connection is used to transmit a request message the request execution usually fails in the write operation with SocketException and gets automatically retried. However under some circumstances the write operation can terminate without an exception and the subsequent read operation returns -1 (end of stream). In this case HttpClient has no other choice but to assume the request succeeded but the server failed to respond most likely due to an unexpected error on the server side.

The simplest way to remedy the situation is to evict expired connections and connections that have been idle longer than, say, 1 minute from the pool after a period of inactivity. For details please see the 2.5. Connection eviction policy of the HttpClient 4.5 tutorial.

ok2c
  • 26,450
  • 5
  • 63
  • 71
  • I'm using HTTTPClient 4.5.1 , here for two continuous retry server failed respond but third time succeed, so why does it not connect in first attempt even i kept time 1 minute as metioned. – Kundan Atre May 16 '16 at 13:37
  • @oleg ,What is the difference between idle and expired connections? – Ales May 23 '17 at 12:23
  • 1
    Some connections may have expiration time beyond which they should be considered no longer valid / re-usable. Idle connections are perfectly valid and re-usable but momentarily being unused. – ok2c May 24 '17 at 07:05
  • Is it safe to catch this NoHttpResponseException and perform a retry? – avmohan Aug 03 '17 at 11:42
  • 2
    It depends on many factors. It _should_ be safe for safe and idempotent methods as long as the server conforms to the HTTP spec with regards to method safety and idempotency. – ok2c Aug 04 '17 at 09:56
  • I have a monitor thread which sweeps all idle and expired connections every 5 seconds. I'm getting an info type `NoHttpResponseException` from time to time, although the request worked fine after 1 or 2 retries. Is there a way to ignore the info type `NoHttpResponseException` from the log since the code works fine? – Fan Jin Nov 16 '18 at 02:49
  • 3
    Using one minute timeout as recommended here was exactly the problem cause in my case: apache client talking to local jetty server. AFAIK by default, jetty closes connections after 30 seconds of inactivity. Setting `connectionManager.setValidateAfterInactivity(500)`, i.e., half a second solved my problem. I guess, a few seconds would do equally well, but I don't care. – maaartinus Dec 16 '18 at 17:55
  • 1
    We're getting this error, but: we can see the request gets to the server, but the client apparently doesn't get the response, since we get the `NoHttpResponseException`. If the connection was indeed stale, as you state, then how could the server get the request to begin with? Can anybody explain this scenario? Would cleaning up stale connections fix this? – Frans Dec 17 '18 at 13:37
  • solution link does not work anymore, please update – nuclear_party Oct 26 '22 at 16:59
25

Accepted answer is right but lacks solution. To avoid this error, you can add setHttpRequestRetryHandler (or setRetryHandler for apache components 4.4) for your HTTP client like in this answer.

Community
  • 1
  • 1
Jehy
  • 4,729
  • 1
  • 38
  • 55
  • 4
    Given that the original question specifies a POST, is a retry handler the right approach here? – Brian Agnew Nov 28 '17 at 15:46
  • 2
    Are you asking if a POST is safe to retry? As a design principle, a PUT is idempotent. Retrying a POST may have unintended consequences. Then again, not all PUTs are written idempotently. – activedecay Apr 06 '18 at 21:46
  • It is almost perfectly safe to retry POST request with the method I provided - because it handles and retries only NoHttpResponseException which is much more possisible due to client side rather then server side. – Jehy Apr 08 '18 at 05:12
  • 14
    @Jehy It's not perfectly safe at all... NoHttpResponseException means that the client didn't get a response, not that the server didn't get and/or processed the request – Lyrkan Dec 06 '18 at 09:27
  • the safety could be ensured by implementation, if content parts are re-used during key/identity creation, server could spit out 400 recognizing that the entry was already created. – Aubergine Feb 11 '20 at 15:18
9

Solution: change the ReuseStrategy to never

Since this problem is very complex and there are so many different factors which can fail I was happy to find this solution in another post: How to solve org.apache.http.NoHttpResponseException

Never reuse connections: configure in org.apache.http.impl.client.AbstractHttpClient:

httpClient.setReuseStrategy(new NoConnectionReuseStrategy());

The same can be configured on a org.apache.http.impl.client.HttpClientBuilder builder:

builder.setConnectionReuseStrategy(new NoConnectionReuseStrategy());
Anti-g
  • 121
  • 1
  • 4
8

HttpClient 4.4 suffered from a bug in this area relating to validating possibly stale connections before returning to the requestor. It didn't validate whether a connection was stale, and this then results in an immediate NoHttpResponseException.

This issue was resolved in HttpClient 4.4.1. See this JIRA and the release notes

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • 21
    I am using 4.5.3 version, but still getting **NoHttpResponseException**. In my case, I am getting this Exception in every 4th or 5th continuous request not in the 1st request. Any idea what might be the reason in my case? – Sahil Chhabra Feb 26 '18 at 17:46
  • 1
    @SahilChhabra Same scenario at my side. Were you able find any solution to your problem? – Manish Bansal Dec 09 '19 at 15:48
6

Although accepted answer is right, but IMHO is just a workaround.

To be clear: it's a perfectly normal situation that a persistent connection may become stale. But unfortunately it's very bad when the HTTP client library cannot handle it properly.

Since this faulty behavior in Apache HttpClient was not fixed for many years, I definitely would prefer to switch to a library that can easily recover from a stale connection problem, e.g. OkHttp.

Why?

  1. OkHttp pools http connections by default.
  2. It gracefully recovers from situations when http connection becomes stale and request cannot be retried due to being not idempotent (e.g. POST). I cannot say it about Apache HttpClient (mentioned NoHttpResponseException).
  3. Supports HTTP/2.0 from early drafts and beta versions.

When I switched to OkHttp, my problems with NoHttpResponseException disappeared forever.

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

Nowadays, most HTTP connections are considered persistent unless declared otherwise. However, to save server ressources the connection is rarely kept open forever, the default connection timeout for many servers is rather short, for example 5 seconds for the Apache httpd 2.2 and above.

The org.apache.http.NoHttpResponseException error comes most likely from one persistent connection that was closed by the server.

It's possible to set the maximum time to keep unused connections open in the Apache Http client pool, in milliseconds.

With Spring Boot, one way to achieve this:

public class RestTemplateCustomizers {
    static public class MaxConnectionTimeCustomizer implements RestTemplateCustomizer {

        @Override
        public void customize(RestTemplate restTemplate) {
            HttpClient httpClient = HttpClientBuilder
                .create()
                .setConnectionTimeToLive(1000, TimeUnit.MILLISECONDS)
                .build();

            restTemplate.setRequestFactory(
                new HttpComponentsClientHttpRequestFactory(httpClient));
        }
    }
}

// In your service that uses a RestTemplate
public MyRestService(RestTemplateBuilder builder ) {
    restTemplate = builder
         .customizers(new RestTemplateCustomizers.MaxConnectionTimeCustomizer())
         .build();
}
Nathan
  • 8,093
  • 8
  • 50
  • 76
Berthier Lemieux
  • 3,785
  • 1
  • 25
  • 25
  • In my case, I am getting this Exception in every 4th or 5th continuous request not in the 1st request. Any idea what might be the reason in my case? – Sahil Chhabra Feb 26 '18 at 17:47
  • +1 Good, detailed answer, but... IMHO it's rather a workaround for the Apache HttpClient faulty behavior. The proper solution would be to switch to a library that is able to gracefully recover from a stale connection (e.g. OkHttp). – G. Demecki Jun 03 '19 at 09:30
2

This can happen if disableContentCompression() is set on a pooling manager assigned to your HttpClient, and the target server is trying to use gzip compression.

Alkanshel
  • 4,198
  • 1
  • 35
  • 54
  • you helped me a lot with your comment. I had the same issue as this but with Spring Cloud Zuul 1.3.2.RELEASE and under the high load My solution was to copy-paste `org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter` and remove `disableContentCompression` call I didn't change the dependency version because there is quite a mess with jars – Artem Ptushkin Sep 05 '19 at 15:30
1

Use PoolingHttpClientConnectionManager instead of BasicHttpClientConnectionManager

BasicHttpClientConnectionManager will make an effort to reuse the connection for subsequent requests with the same route. It will, however, close the existing connection and re-open it for the given route.

Elikill58
  • 4,050
  • 24
  • 23
  • 45
Alok Sahoo
  • 11
  • 4
0

So I had this problem when my http server was running in a Docker container. I tried a few different things based on some research, but the solution went away when I deleted my container and rebuilt the image from the Dockerfile. Something must have been holding on to that port somehow even thought I tried deleting my container and rebuilding it already... the problem has not occurred again since completely rebuilding the image itself. I wanted to post this in the event that someone else runs into this issue running their http server in a container and I hope it prevents them from losing the hours I lost...

  • This does not really answer the question. If you have a different question, you can ask it by clicking [Ask Question](https://stackoverflow.com/questions/ask). To get notified when this question gets new answers, you can [follow this question](https://meta.stackexchange.com/q/345661). Once you have enough [reputation](https://stackoverflow.com/help/whats-reputation), you can also [add a bounty](https://stackoverflow.com/help/privileges/set-bounties) to draw more attention to this question. - [From Review](/review/late-answers/34502448) – dpapadopoulos Jun 07 '23 at 15:12
-2

I have faced same issue, I resolved by adding "connection: close" as extention,

Step 1: create a new class ConnectionCloseExtension

import com.github.tomakehurst.wiremock.common.FileSource;
import com.github.tomakehurst.wiremock.extension.Parameters;
import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
import com.github.tomakehurst.wiremock.http.HttpHeader;
import com.github.tomakehurst.wiremock.http.HttpHeaders;
import com.github.tomakehurst.wiremock.http.Request;
import com.github.tomakehurst.wiremock.http.Response;

public class ConnectionCloseExtension extends ResponseTransformer {
  @Override
  public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
    return Response.Builder
        .like(response)
        .headers(HttpHeaders.copyOf(response.getHeaders())
            .plus(new HttpHeader("Connection", "Close")))
        .build();
  }

  @Override
  public String getName() {
    return "ConnectionCloseExtension";
  }
}

Step 2: set extension class in wireMockServer like below,

final WireMockServer wireMockServer = new WireMockServer(options()
                .extensions(ConnectionCloseExtension.class)
                .port(httpPort));
Chandrahasan
  • 1,991
  • 24
  • 26