What's the difference between CloseableHttpResponse.close() and httpPost.releaseConnection()?

47.7k Views Asked by At
CloseableHttpResponse response = null;
try {
    // do some thing ....
    HttpPost request = new HttpPost("some url");
    response = getHttpClient().execute(request);
    // do some other thing ....
} catch(Exception e) {
    // deal with exception
} finally {
    if(response != null) {
        try {
            response.close(); // (1)
        } catch(Exception e) {}
        request.releaseConnection(); // (2)
    }
}

I've made a http request like above.

In order to release the underlying connection, is it correct to call (1) and (2)? and what's the difference between the two invocation?

2

There are 2 best solutions below

0
On
  1. CloseableHttpResponse.close() closes the tcp socket
  2. HttpPost.releaseConnection() closes the tcp socket
  3. EntityUtils.consume(response.getEntity()) allows you to re-use the tcp socket

Details

CloseableHttpResponse.close() closes the tcp socket, preventing the connection from being re-used. You need to establish a new tcp connection in order to initiate another request.

This is the call chain that lead me to the above conclusion:

  1. HttpResponseProxy.close()
  2. -> ConnectionHolder.close()
  3. -> ConnectionHolder.releaseConnection(reusable=false)
  4. -> managedConn.close()
  5. -> BHttpConnectionBase.close()
  6. -> Socket.close()

HttpPost.releaseConnection() also closes the Socket. This is the call chain that lead me to the above conclusion:

  1. HttpPost.releaseConnection()
  2. HttpRequestBase.releaseConnect()
  3. AbstractExecutionAwareRequest.reset()
  4. ConnectionHolder.cancel() (
  5. ConnectionHolder.abortConnection()
  6. HttpConnection.shutdown()

Here is experimental code that also demonstrates the above three facts:

import java.lang.reflect.Constructor;
import java.net.Socket;
import java.net.SocketImpl;
import java.net.SocketImplFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

public class Main {

    private static SocketImpl newSocketImpl() {
        try {
            Class<?> defaultSocketImpl = Class.forName("java.net.SocksSocketImpl");
            Constructor<?> constructor = defaultSocketImpl.getDeclaredConstructor();
            constructor.setAccessible(true);
            return (SocketImpl) constructor.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception {
        // this is a hack that lets me listen to Tcp socket creation
        final List<SocketImpl> allSockets = Collections.synchronizedList(new ArrayList<>());
        Socket.setSocketImplFactory(new SocketImplFactory() {
            public SocketImpl createSocketImpl() {
                SocketImpl socket = newSocketImpl();
                allSockets.add(socket);
                return socket;
            }
        });

        System.out.println("num of sockets after start: " + allSockets.size());
        CloseableHttpClient client = HttpClientBuilder.create().build();
        System.out.println("num of sockets after client created: " + allSockets.size());
        HttpGet request = new HttpGet("http://www.google.com");
        System.out.println("num of sockets after get created: " + allSockets.size());
        CloseableHttpResponse response = client.execute(request);
        System.out.println("num of sockets after get executed: " + allSockets.size());
        response.close();
        System.out.println("num of sockets after response closed: " + allSockets.size());
        response = client.execute(request);
        System.out.println("num of sockets after request executed again: " + allSockets.size());
        request.releaseConnection();
        System.out.println("num of sockets after release connection: " + allSockets.size());
        response = client.execute(request);
        System.out.println("num of sockets after request executed again for 3rd time: " + allSockets.size());
        EntityUtils.consume(response.getEntity());
        System.out.println("num of sockets after entityConsumed: " + allSockets.size());
        response = client.execute(request);
        System.out.println("num of sockets after request executed again for 4th time: " + allSockets.size());
    }

}

pom.xml

<project>
    <modelVersion>4.0.0</modelVersion>
    

    <groupId>org.joseph</groupId>
    <artifactId>close.vs.release.conn</artifactId>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>

    <build>
        <plugins>
         </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.13</version>
        </dependency>
    </dependencies>
</project>

Output:

num of sockets after start: 0
num of sockets after client created: 0
num of sockets after get created: 0
num of sockets after get executed: 1
num of sockets after response closed: 1
num of sockets after request executed again: 2
num of sockets after release connection: 2
num of sockets after request executed again for 3rd time: 3
num of sockets after entityConsumed: 3
num of sockets after request executed again for 4th time: 3

Notice that both .close() and .releaseConnection() both result in a new tcp connection. Only consuming the entity allows you to re-use the tcp connection.

If you want the connect to be re-usable after each request, then you need to do what @Matt recommended and consume the entity.

8
On

Short answer:

request.releaseConnection() is releasing the underlying HTTP connection to allow it to be reused. response.close() is closing a stream (not a connection), this stream is the response content we are streaming from the network socket.

Long Answer:

The correct pattern to follow in any recent version > 4.2 and probably even before that, is not to use releaseConnection.

request.releaseConnection() releases the underlying httpConnection so the request can be reused, however the Java doc says:

A convenience method to simplify migration from HttpClient 3.1 API...

Instead of releasing the connection, we ensure the response content is fully consumed which in turn ensures the connection is released and ready for reuse. A short example is shown below:

CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://targethost/homepage");
CloseableHttpResponse response1 = httpclient.execute(httpGet);
try {
    System.out.println(response1.getStatusLine());
    HttpEntity entity1 = response1.getEntity();
    // do something useful with the response body
    String bodyAsString = EntityUtils.toString(exportResponse.getEntity());
    System.out.println(bodyAsString);
    // and ensure it is fully consumed (this is how stream is released.
    EntityUtils.consume(entity1);
} finally {
    response1.close();
}