JDK-7167293 : FtpURLConnection connection leak on FileNotFoundException
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 7
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86
  • Submitted: 2012-05-08
  • Updated: 2018-02-08
  • Resolved: 2016-04-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7 JDK 8 JDK 9 Other
6u151Fixed 7u141Fixed 8u131Fixed 9 b114Fixed openjdk7uFixed
Description
FULL PRODUCT VERSION :
java version "1.7.0_04"
Java(TM) SE Runtime Environment (build 1.7.0_04-b22)
Java HotSpot(TM) 64-Bit Server VM (build 23.0-b21, mixed mode)

java version "1.6.0_25"
Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)


java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04-415-11M3646)
Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01-415, mixed mode)

java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.13) (6b20-1.9.13-0ubuntu1~10.04.1)
OpenJDK Client VM (build 19.0-b09, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7600]

Darwin Dimitris-Dimitropouloss-MacBook-Pro.local 11.3.0 Darwin Kernel Version 11.3.0: Thu Jan 12 18:47:41 PST 2012; root:xnu-1699.24.23~1/RELEASE_X86_64 x86_64

Linux ubuntu 2.6.32-41-generic-pae #88-Ubuntu SMP Thu Mar 29 14:24:36 UTC 2012 i686 GNU/Linux





EXTRA RELEVANT SYSTEM CONFIGURATION :
This problem seems to be reproducible in both Oracle JDK 6 and 7 and OpenJDK 6 and 7 in any environment tested (mac, windows, linux)

A DESCRIPTION OF THE PROBLEM :
Using java.net.URL.openStream for an ftp resource that is not available on an ftp server throws an FileNotFoundException but leaves an underlying connection (an FTPClient) open.

A common side-effect (apart from the socket leak) is that the firewall rules of a typical ftp server will eventually (after a few such exceptions) deny serving even valid resources, with a "too many connections open" error message.
 


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I am attaching a sample program.

Just try to open a stream using a URL object for a resource like
ftp://ftp.oracle.com/doesNotExist.txt

using something as simple as
new URL("ftp://ftp.oracle.com/doesNotExist.txt").openStream()

This call will correctly return a FileNotFoundException since the specified path does not exist on that server.

But the natural outcome of the exception is that the user does not have access to an InputStream or any other API method that he could use to close any internal connection in case of a FileNotFound (or any Runtime) exception.

Repeat that for about 10-15 times and then try to open
 
new URL("ftp://ftp.oracle.com/FAQ_README.htm")

This will fail too (even though the resource exists) because most FTP servers have a limit on the number of open connections that allow from an individual client.

So waiting for a "clean-up" during garbage collection is not a practical option.

The implementation of (FtpURLConnection), seems to keep a connection open (an FTPClient), with no available API method to terminate it.

The same is also true if the user uses directly the URLConnection  API (URL.openConnection).

Both URL and URLConnection are effectively immutable after the connection has been established and they do not provide an API method for changing the invalid URL path to a valid one or any way of cleanup in case of a FileNotFound (or any Runtime) exception.



EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
When methods FtpURLConnection.getInputStream or FtpURLConnection.getOutputStream throw any type of exception, they should clean up any allocated resources (FtpClient)
ACTUAL -
There are system socket connections to the FTP server that stay open after the exception is thrown without any means of closing them.

In a typical situation, after a few exceptions, the FTP server will stop serving any resources to the client with a "too many open connections" error message

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;


public class FtpURLConnectionLeak {
    
    public static void main(String []args) {

        URL url=null;
        
        try {
            url = new URL("ftp://ftp.oracle.com/doesNotExist.txt");
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return;
        }
        
        for(int i=0;i<100;i++) {
            
            InputStream stream=null;
            try {
                stream=url.openStream();
            } catch (FileNotFoundException e) {
                // It should always reach this point since the path does not exist
                System.err.println("Correct behaviour for attempt "+i+":"+e);
            } catch (IOException e) {
                System.err.println("Due to the ftp client connection leak, we have propably reached the firewall limit of the FTP server");
                e.printStackTrace();
                break;
            }
            
            assert(stream==null) : "It will never reach here since the path is invalid";

            if(stream!=null) {
                // There is no other way to close the underlying connection
                // but this code is unreachable
                try {
                    stream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    return;
                }
            }
        }
        
        // Due to the connection leak, its not possible now to download valid URLs from that ftp server
    }
}


---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
There is no easy workaround from the API side.

Resolving this would be easy by adding a try  block that would include the full body of getInputStream() and getOutputStream() of FtpURLConnection.java with a catch statement (or a finally block) that would include a call to ftp.closeServer(); in case of a FileNotFoundException or any run time exception.

(for example for the OpenJDK 6 version at)

http://www.java2s.com/Open-Source/Java/6.0-JDK-Modules-sun/net/sun/net/www/protocol/ftp/FtpURLConnection.java.htm