Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sockets of HttpURLConnection are leaked

I'm using OpenJDK 11 on Linux and I need to make sure all my web requests done with HttpURLConnection are properly closed and do not keep any file descriptors open.

Oracle's manual tells to use close on the InputStream and Android's manual tells to use disconnect on the HttpURLConnection object.

I also set Connection: close and http.keepAlive to false to avoid pooling of connections.

This seems to work with plain http requests but not encrypted https requests whose response is sent with non-chunked encoding. Only a GC seems to clean up the closed connections.

This example code:

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

public class Test {
    private static int printFds() throws IOException {
        int cnt = 0;
        try (Stream<Path> paths = Files.list(new File("/proc/self/fd").toPath())) {
            for (Path path : (Iterable<Path>)paths::iterator) {
                System.out.println(path);
                ++cnt;
            }
        }
        System.out.println();
        return cnt;
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        System.setProperty("http.keepAlive", "false");
        for (int i = 0; i < 10; i++) {
            // Must be a https endpoint returning non-chunked response
            HttpURLConnection conn = (HttpURLConnection) new URL("https://www.google.com/").openConnection();
            conn.setRequestProperty("Connection", "close");
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            while (in.readLine() != null) {
            }
            in.close();
            conn.disconnect();
            conn = null;
            in = null;
        }

        Thread.sleep(1000);
        int numBeforeGc = printFds();

        System.gc();
        Thread.sleep(1000);
        int numAfterGc = printFds();
        System.out.println(numBeforeGc == numAfterGc ? "No socket leaks" : "Sockets were leaked");
    }
}

prints this output:

/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
/proc/self/fd/3
/proc/self/fd/4
/proc/self/fd/5
/proc/self/fd/9
/proc/self/fd/6
/proc/self/fd/7
/proc/self/fd/8
/proc/self/fd/10
/proc/self/fd/11
/proc/self/fd/12
/proc/self/fd/13
/proc/self/fd/14
/proc/self/fd/15
/proc/self/fd/16
/proc/self/fd/17
/proc/self/fd/18
/proc/self/fd/19

/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
/proc/self/fd/3
/proc/self/fd/4
/proc/self/fd/5
/proc/self/fd/9
/proc/self/fd/6
/proc/self/fd/7
/proc/self/fd/8

Sockets were leaked

Changing to a http URL makes the sockets close correctly as expected without GC:

/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
/proc/self/fd/3
/proc/self/fd/4
/proc/self/fd/5
/proc/self/fd/6

/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
/proc/self/fd/3
/proc/self/fd/4
/proc/self/fd/5
/proc/self/fd/6

No socket leak

Tested with both OpenJDK 11 and 12. Did I miss something or is this a bug?

like image 779
Emil Avatar asked Nov 06 '22 14:11

Emil


1 Answers

Turns out to be a bug after all: https://bugs.openjdk.java.net/browse/JDK-8216326

shutdownInput is now replaced by close in the latest builds of JDK 11 and 13 (but not 12).

like image 179
Emil Avatar answered Nov 15 '22 12:11

Emil