0

Following is the (greatly simplified) code used for communicating from a client machine, to a REST server, using Jersey. There is a 5 minute timeout for establishing a connection at the start, and a 2 minute timeout for reading from the underlying socket.

public class JerseyClient {

  private final Map<Action, WebResource> resources;

  public JerseyClient(URI uri) {
    this.resources = new EnumMap<Action, WebResource>(Action.class);
    this.resources.put(Action.Put, getClient().resource(Action.Put.getURI()));
    this.resources.put(Action.Query, getClient().resource(Action.Query.getURI()));
  }

  private String submit(Action action, String input) throws Exception {
    WebResource resource = this.resources.get(action);
    ClientResponse response = null;
    synchronized (resource) {
      try {
          response = resource.accept(MediaType.APPLICATION_JSON_TYPE,
                  MediaType.TEXT_PLAIN_TYPE).type(MediaType.APPLICATION_JSON_TYPE).
                  post(ClientResponse.class, input);
          String responseString = null;
          // Handle the response and produce a response string...
        return responseString;
      } finally {
        if (response != null) {
          response.close();
        }
      }
    }
  }

  private static Client getClient() {
    Client client = Client.create();
    client.setReadTimeout(2*60*1000);
    client.setConnectTimeout(5*60*1000);
    return client;
  }

  private enum Action {
    Put, Query;
    public URI getURI(){
      switch (this) {
        case Put:
          return URI.create("PUT_URI");
        case Query:
          return URI.create("QUERY_URI");
        default:
          throw new InvalidStateException("Illegal action");
      }
    }
  }
}

The above code works as expected, unless the read timeout triggers at the client. In that case, a SocketTimeoutException is thrown and thus the response object in the submit() method of the JerseyClient class above remains null, so the underlying socket is never closed fully.

Apparently there is a partial closure of the socket by the client since, on the other side, the server enters the CLOSE_WAIT state (i.e., it has received a FIN packet from the client, as per the TCP specification). However, since it never gets the final ACK from the client (which should be sent if response.close() was called), it keeps the connection at CLOSE_WAIT (as shown by netstat), so each timed out REST call from the client potentially creates a dangling CLOSE_WAIT on the server.

Is there any way of solving the issue, without completely re-engineering the above code?

PNS
  • 19,295
  • 32
  • 96
  • 143

1 Answers1

2

Your description doesn't make sense. The name of the state is CLOSE_WAIT, not CLOSED_WAIT, and it means what its (correct) name expresses: it is waiting for the local application to close the socket, after receiving a remote close from the peer.

If your server is entering CLOSE_WAIT:

  1. The client has closed the socket.
  2. The server has not closed the socket. This is a bug in the server.
  3. The server will never get a final ACK from the client until it issues a FIN by closing the socket. The ACK is an acknowledgement of the FIN. Until the server issues the FIN there is nothing for the client to ACK.
  4. It is the close that gets it out of CLOSE_WAIT, not the client ACK.
  5. Calling response.close() at the client has nothing to do with the client sending the final ACK.
user207421
  • 305,947
  • 44
  • 307
  • 483
  • You are right of course about CLOSE_WAIT. The question was written in a harry, now corrected. The scenario remains as originally described. Thanks and +1. :-) – PNS Jul 13 '16 at 23:14
  • The scenario is that the server has not closed the socket and the client has, which is exactly the opposite of what was originally described, and what is still described, your minor edits notwithstanding. – user207421 Jul 13 '16 at 23:17
  • We are tracing the issue, based on one fact, i.e. that there are many CLOSE_WAIT connections. Irrespective of who has done what, based on the documentation, `response.close()` needs to be invoked at the client. Is there any way of doing this, in the above code? That is the main question. Thanks. – PNS Jul 13 '16 at 23:47
  • No it isn't. I repeat. The client has closed the socket. The CLOSE_WAIT state at the server is all the evidence needed of that. The problem is at the server end. – user207421 Jul 14 '16 at 00:36
  • Fair enough. Let the problem be at the server. Is there any way of explicitly closing the socket at the client, given the code shown in the question? – PNS Jul 14 '16 at 01:18
  • Sigh. For the fourth time: **the client has closed the socket.** If it hadn't, there would be no CLOSE_WAIT state to worry about: the connection would still be ESTABLISHED. Why are you so fixated on this non-issue? – user207421 Jul 14 '16 at 01:28
  • We have established that. The socket is presumably closed somehow. However, we also want a way to explicitly close it in similar situations, because the code excerpt above is used in other cases, too. So, without any other reference to the TCP state machine, I will repeat my question, in case you or anybody else has an answer: Is there any way of explicitly closing the client socket, without completely re-engineering the above code? In any case, thanks for your time. – PNS Jul 14 '16 at 01:30
  • 1
    That's a new question, and not one I understand. There is no accessible socket in the above code. It is created and closed as necessary by the underlying library. it is opened for the POST and closed after the response or an error has been received. There is therefore no apparent place in your code where you could possibly want to close the socket prematurely, or afterwards, or whatever your unstated requirement actually is. You should consider upvoting or accepting answers that have helped you. – user207421 Jul 14 '16 at 01:35
  • I have already upvoted your question and said so in my very first comment. If you check the documentation of the `ClientResponse` class, the `response.close()` method has to be explicitly invoked to close the underlying input stream. There are also numerous posts in which people say that without doing so, there are leaks. We will probably need to refactor the code to account for `null` response values, as per the question. Thanks for an informative discussion and for all the help. :-) – PNS Jul 14 '16 at 01:41
  • You do indeed need to close the response, and you are doing so, but if you get an exception instead of a response return you can't, because there is nothing to close. The fact that you have CLOSE_WAIT at the server after getting a timeout exception shows that the socket gets closed when an exception is thrown. There is no problem here to solve. – user207421 Jul 14 '16 at 01:56
  • 1
    I suggest you start again with a new question, new evidence, new everything. This question is futile. – user207421 Jul 14 '16 at 02:30