Why do you have to call URLConnection#getInputStream to be able to write out to URLConnection#getOutputStream?

John picture John · Jan 30, 2011 · Viewed 31.4k times · Source

I'm trying to write out to URLConnection#getOutputStream, however, no data is actually sent until I call URLConnection#getInputStream. Even if I set URLConnnection#doInput to false, it still will not send. Does anyone know why this is? There's nothing in the API documentation that describes this.

Java API Documentation on URLConnection: http://download.oracle.com/javase/6/docs/api/java/net/URLConnection.html

Java's Tutorial on Reading from and Writing to a URLConnection: http://download.oracle.com/javase/tutorial/networking/urls/readingWriting.html

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;

public class UrlConnectionTest {

    private static final String TEST_URL = "http://localhost:3000/test/hitme";

    public static void main(String[] args) throws IOException  {

        URLConnection urlCon = null;
        URL url = null;
        OutputStreamWriter osw = null;

        try {
            url = new URL(TEST_URL);
            urlCon = url.openConnection();
            urlCon.setDoOutput(true);
            urlCon.setRequestProperty("Content-Type", "text/plain");            

            ////////////////////////////////////////
            // SETTING THIS TO FALSE DOES NOTHING //
            ////////////////////////////////////////
            // urlCon.setDoInput(false);

            osw = new OutputStreamWriter(urlCon.getOutputStream());
            osw.write("HELLO WORLD");
            osw.flush();

            /////////////////////////////////////////////////
            // MUST CALL THIS OTHERWISE WILL NOT WRITE OUT //
            /////////////////////////////////////////////////
            urlCon.getInputStream();

            /////////////////////////////////////////////////////////////////////////////////////////////////////////
            // If getInputStream is called while doInput=false, the following exception is thrown:                 //
            // java.net.ProtocolException: Cannot read from URLConnection if doInput=false (call setDoInput(true)) //
            /////////////////////////////////////////////////////////////////////////////////////////////////////////

        } catch (Exception e) {
            e.printStackTrace();                
        } finally {
            if (osw != null) {
                osw.close();
            }
        }

    }

}

Answer

robert_x44 picture robert_x44 · Jan 30, 2011

The API for URLConnection and HttpURLConnection are (for better or worse) designed for the user to follow a very specific sequence of events:

  1. Set Request Properties
  2. (Optional) getOutputStream(), write to the stream, close the stream
  3. getInputStream(), read from the stream, close the stream

If your request is a POST or PUT, you need the optional step #2.

To the best of my knowledge, the OutputStream is not like a socket, it is not directly connected to an InputStream on the server. Instead, after you close or flush the stream, AND call getInputStream(), your output is built into a Request and sent. The semantics are based on the assumption that you will want to read the response. Every example that I've seen shows this order of events. I would certainly agree with you and others that this API is counterintuitive when compared to the normal stream I/O API.

The tutorial you link to states that "URLConnection is an HTTP-centric class". I interpret that to mean that the methods are designed around a Request-Response model, and make the assumption that is how they will be used.

For what it's worth, I found this bug report that explains the intended operation of the class better than the javadoc documentation. The evaluation of the report states "The only way to send out the request is by calling getInputStream."