JDK-4717638 : Socket's getInputStream().close() doesn't specify that it closes socket
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 1.1,1.4.0,6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic,linux
  • CPU: generic,x86
  • Submitted: 2002-07-19
  • Updated: 2017-05-16
  • Resolved: 2006-04-29
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
6 b83Fixed
Related Reports
Duplicate :  
Description
Name: nt126004			Date: 07/19/2002


FULL PRODUCT VERSION :
java version "1.4.0_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0_01-b03)
Java HotSpot(TM) Client VM (build 1.4.0_01-b03, mixed mode)

FULL OPERATING SYSTEM VERSION :
Linux piglet 2.4.18-5ipsec #1 Mon Jun 24 03:53:03 PDT 2002
i686 unknown
  
Red Hat Linux release 7.3 (Valhalla)
  
  
ADDITIONAL OPERATING SYSTEMS :
This is applies to all platforms.

A DESCRIPTION OF THE PROBLEM :
Preface - The real problem is that the java.net.Socket specification doesn't 
specify that closing the input stream closes the socket.  So this example
performs to spec, but the spec is weak and doesn't cover this scenario.

SocketInputStream and SocketOutputStream close the
underlying socket when their close() methods are called.
This is unintuitive since socket.getInputStream().close()
shoud have the same semantics as socket.shutdownInput() and
similarly for socket.getOutputStream()close().

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the submitted program.

EXPECTED VERSUS ACTUAL BEHAVIOR :
Closing the OutputStream of a socket should not affect the
InputStream or vice versa.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.net.SocketException: null fd object
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.read(SocketInputStream.java:116)
	at sun.nio.cs.StreamDecoder$CharsetSD.readBytes(StreamDecoder.java:404)
	at sun.nio.cs.StreamDecoder$CharsetSD.implRead(StreamDecoder.java:442)
	at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:179)
	at java.io.InputStreamReader.read(InputStreamReader.java:167)
	at java.io.BufferedReader.fill(BufferedReader.java:136)
	at java.io.BufferedReader.readLine(BufferedReader.java:299)
	at java.io.BufferedReader.readLine(BufferedReader.java:362)
	at Client.run(SocketTest.java:41)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.*;
import java.net.*;
 
class Server extends Thread
{ 
    public void run() {
	try {
	    System.err.println("listen");
	    ServerSocket socket = new ServerSocket(SocketTest.PORT);
 
	    System.err.println("Accept");
	    Socket client = socket.accept();
 
	    PrintStream out = new PrintStream(client.getOutputStream());
	     
	    for (int i = 0; ; ++i) {
		out.println("*************** msg "+ i +"******************");
	    }
	}
	catch (Exception e) {
	    e.printStackTrace(System.err);
	}
    }
} 
 
class Client extends Thread
{ 
    public void run() {
	try {
	    sleep(500);
 
	    System.err.println("Connect");
	    Socket socket =
		new Socket("localhost", SocketTest.PORT);
 
 
	    BufferedReader reader = new BufferedReader
		(new InputStreamReader(socket.getInputStream()));
 
	    for (int i = 0; ; ++i) {
		System.out.println(reader.readLine());
 
		if (i == 10)
		    socket.getOutputStream().close();
	    }
	}
	catch (Exception e) {
	    e.printStackTrace(System.err);
	}
    }
} 
 
public class SocketTest {
    public static final int PORT = 55555;
 
    public static void main(String[] args)
	throws Exception {
 
	Thread server = new Server();
	Thread client = new Client();
 
	server.start();
	client.start();
 
	System.err.println("Threads started");
 
	server.join();
	client.join();
    }
 
} 

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

CUSTOMER WORKAROUND :
Use socket.shutdownInput() or socket.shutdownOutput() to
unidirectionally close a socket.
(Review ID: 159423) 
======================================================================
Suggested fix from java.net member leouser:

A DESCRIPTION OF THE FIX :
BUG ID 4717638 Socket's getInputStream().close doesn't specify that it closes socket.
FILES AFFECTED: java.net.Socket
JDK: jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin


DISCUSION( embedded in test case):
/**
 * BUG ID: 4717638 Sockets's getInputStream().close() doesn't specify that it closes socket
 * This JavaDoc bug pertains not to just the input stream, but the output stream
 * and the channel.  There are 2 avenues of attack here:
 * 1. Alter the behavior of the close calls on the streams
 * 2. Alter the javadoc so the close call's behavior is clear.
 *
 * I have discarded 1 because it is too radical to have existing clients
 * that been built around the behavior of close() to suddenly not have this behavior.
 * There are times when altering the behavior may be ok, this is not one of them
 * in my opinion.
 *
 * Hence we choose 2, which essentially is just stating that calling 'close' on
 * any of the streams/channels closes the Socket.  Not that hard to add and it
 * will result in removing any surprises associated with calling close.  Ideally
 * when methods return an interface and that interfaces methods do more than what
 * they contract to do, it would be good for the returning method to document
 * the enhancements and deviations from the contract.  Otherwise it is up to
 * the programmer to discover the behavior by observation, not good.
 *
 * ANTI-RATIONALE:
 * 1. The Socket will be locked into this behavior since the contract now
 * states that this is how it operates.  But I refer you to to the rejection
 * of avenue 1 to understand why we are at liberty here to alter the doc.
 *
 * TESTING PLAN:
 * Prove that calling close on the streams/channels close the Socket, hence
 * proving that the JavaDoc is now accurate.
 *
 * FILES AFFECTED: java.net.Socket
 * JAVA VERSION: jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
 * Ran and tested on a Suse 7.3 distribution.
 *
 * Brian Harry
 * ###@###.###
 * Jan 9, 2006
 */


unified diff:
--- /home/nstuff/java6/jdk1.6.0/java/net/Socket.java	Thu Dec 15 02:16:45 2005
+++ /home/javarefs/java/net/Socket.java	Mon Jan  9 12:10:18 2006
@@ -704,6 +704,11 @@
      * java.nio.channels.ServerSocketChannel#accept ServerSocketChannel.accept}
      * methods.
      *
+     * <p> A SocketChannel that is acquired from the Socket instance and
+     * is subsequently closed will in turn close the Socket.  Hence, to
+     * keep the Socket instance open, protect against the SocketChannel
+     * from being closed.
+     *
      * @return  the socket channel associated with this socket,
      *          or <tt>null</tt> if this socket was not created
      *          for a channel
@@ -748,6 +753,11 @@
      *
      * </ul>
      *
+     * <p> An InputStream that is acquired from the Socket instance and
+     * is subsequently closed will in turn close the Socket.  Hence, to
+     * keep the Socket instance open, protect against the InputStream
+     * from being closed.
+     *
      * @return     an input stream for reading bytes from this socket.
      * @exception  IOException  if an I/O error occurs when creating the
      *             input stream, the socket is closed, the socket is
@@ -787,6 +797,11 @@
      * is in non-blocking mode then the output stream's <tt>write</tt>
      * operations will throw an {@link
      * java.nio.channels.IllegalBlockingModeException}.
+     *
+     * <p> An OutputStream that is acquired from the Socket instance and
+     * is subsequently closed will in turn close the Socket.  Hence, to
+     * keep the Socket instance open, protect against the OutputStream from
+     * being closed.
      *
      * @return     an output stream for writing bytes to this socket.
      * @exception  IOException  if an I/O error occurs when creating the


JUnit TESTCASE :
import junit.framework.TestCase;
import junit.textui.TestRunner;
import static java.lang.System.out;
import java.io.*;
import java.net.*;
import java.nio.channels.*;


/**
 * BUG ID: 4717638 Sockets's getInputStream().close() doesn't specify that it closes socket
 * This JavaDoc bug pertains not to just the input stream, but the output stream
 * and the channel.  There are 2 avenues of attack here:
 * 1. Alter the behavior of the close calls on the streams
 * 2. Alter the javadoc so the close call's behavior is clear.
 *
 * I have discarded 1 because it is too radical to have existing clients
 * that been built around the behavior of close() to suddenly not have this behavior.
 * There are times when altering the behavior may be ok, this is not one of them
 * in my opinion.
 *
 * Hence we choose 2, which essentially is just stating that calling 'close' on
 * any of the streams/channels closes the Socket.  Not that hard to add and it
 * will result in removing any surprises associated with calling close.  Ideally
 * when methods return an interface and that interfaces methods do more than what
 * they contract to do, it would be good for the returning method to document
 * the enhancements and deviations from the contract.  Otherwise it is up to
 * the programmer to discover the behavior by observation, not good.
 *
 * ANTI-RATIONALE:
 * 1. The Socket will be locked into this behavior since the contract now
 * states that this is how it operates.  But I refer you to to the rejection
 * of avenue 1 to understand why we are at liberty here to alter the doc.
 *
 * TESTING PLAN:
 * Prove that calling close on the streams/channels close the Socket, hence
 * proving that the JavaDoc is now accurate.
 *
 * FILES AFFECTED: java.net.Socket
 * JAVA VERSION: jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
 * Ran and tested on a Suse 7.3 distribution.
 *
 * Brian Harry
 * ###@###.###
 * Jan 9, 2006
 */
public class TestSocket extends TestCase{


    public TestSocket(String method){
	super(method);
    }


    public void testSocketDocIsAccurate(){

	out.println();
	try{
	    Socket s1 = new Socket( "127.0.0.1", 10000);
	    out.println( s1 + " is closed? " + s1.isClosed());
	    InputStream ins = s1.getInputStream();
	    ins.close();
	    assertEquals( true, s1.isClosed());
	    out.println("InputStream close, closed the socket? " + s1.isClosed());
	}
	catch(IOException io){
	    io.printStackTrace();
	}

	try{
	    Socket s2 = new Socket( "127.0.0.1", 10000);
	    out.println( s2 + " is closed? "+ s2.isClosed());
	    OutputStream os = s2.getOutputStream();
	    os.close();
	    assertEquals( true, s2.isClosed());
	    out.println("OutputStram close, closed the socket? " + s2.isClosed());
	}
	catch(IOException io){
	    io.printStackTrace();
	}

	try{
	    InetSocketAddress isa = new InetSocketAddress(10000);
	    SocketChannel sc = SocketChannel.open(isa);
	    Socket s3 = sc.socket();
	    out.println( s3 + " is closed? " + s3.isClosed());
	    SocketChannel sc2 = s3.getChannel();
	    sc2.close();
	    assertEquals( true, s3.isClosed());
	    out.println("SocketChannel close, closed the socket? " + s3.isClosed());
	}
	catch(IOException io){
	    io.printStackTrace();
	}

    }



    public static void main(String ... args){
	
	Thread t = new Thread(){

		public void run(){
		    
		    try{
			ServerSocket ss = new ServerSocket(10000);
			int i = 3;
			while(i != 0){
			    ss.accept();
			    i--;
			}
			sleep(1000);//enough time for the other thread to complete.

		    }
		    catch(IOException io){ io.printStackTrace(); }
		    catch(InterruptedException ie){}
		}


	    };
	t.start();
	try{
	    Thread.currentThread().sleep(3000);
	}
	catch(InterruptedException ie){}
	TestCase tc = new TestSocket("testSocketDocIsAccurate");
	TestRunner.run(tc);


    }




}


FIX FOR BUG NUMBER:
4717638

Comments
EVALUATION Contribution-forum:https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?forumID=1463&messageID=10817
10-01-2006

EVALUATION The behaviour is correct and getInputStream().close() has always closed the socket. However this isn't specified so this but is tracking the specification clarification (not an implementation change). Accordining I've changed the synopsis. ###@###.### 2002-07-22
22-07-2002