Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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();             }         }      }  } 
like image 935
John Avatar asked Jan 30 '11 18:01

John


People also ask

What is the purpose of URLConnection class?

The Java URLConnection class represents a communication link between the URL and the application. It can be used to read and write data to the specified resource referred by the URL.

What is the difference between URLConnection and HttpURLConnection?

URLConnection is the base class. HttpURLConnection is a derived class which you can use when you need the extra API and you are dealing with HTTP or HTTPS only. HttpsURLConnection is a 'more derived' class which you can use when you need the 'more extra' API and you are dealing with HTTPS only.

What is URLConnection in Android?

The abstract class URLConnection is the superclass of all classes that represent a communications link between the application and a URL. Instances of this class can be used both to read from and to write to the resource referenced by the URL.

What does connection getInputStream do?

Calling getInputStream() signals that the client is finished sending it's request, and is ready to receive the response (per HTTP spec).


2 Answers

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."

like image 178
robert_x44 Avatar answered Oct 14 '22 04:10

robert_x44


Although the getInputStream() method can certainly cause a URLConnection object to initiate an HTTP request, it is not a requirement to do so.

Consider the actual workflow:

  1. Build a request
  2. Submit
  3. Process the response

Step 1 includes the possibility of including data in the request, by way of an HTTP entity. It just so happens that the URLConnection class provides an OutputStream object as the mechanism for providing this data (and rightfully so for many reasons that aren't particularly relevant here). Suffice to say that the streaming nature of this mechanism provides the programmer an amount of flexibility when supplying the data, including the ability to close the output stream (and any input streams feeding it), before finishing the request.

In other words, step 1 allows for supplying a data entity for the request, then continuing to build it (such as by adding headers).

Step 2 is really a virtual step, and can be automated (like it is in the URLConnection class), since submitting a request is meaningless without a response (at least within the confines of the HTTP protocol).

Which brings us to Step 3. When processing an HTTP response, the response entity -- retrieved by calling getInputSteam() -- is just one of the things we might be interested in. A response consists of a status, headers, and optionally an entity. The first time any one of these is requested, the URLConnection will perform virtual step 2 and submit the request.

No matter if an entity is being sent via the connection's output stream or not, and no matter whether a response entity is expected back, a program will ALWAYS want to know the result (as provided by the HTTP status code). Calling getResponseCode() on the URLConnection provides this status, and switching on the result may end the HTTP conversation without ever calling getInputStream().

So, if data is being submitted, and a response entity is not expected, don't do this:

// request is now built, so... InputStream ignored = urlConnection.getInputStream(); 

... do this:

// request is now built, so... int result = urlConnection.getResponseCode(); // act based on this result 
like image 28
slash_rick_dot Avatar answered Oct 14 '22 06:10

slash_rick_dot