Java close both Connection and InputStream with try statement - java

Should I close HttpUrlConnection and InputStream in this case? Only closing the connection will close the stream also? I feel that it's a bad practice but don't know exactly why.
Closing both:
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
try (AutoCloseable ac = con::disconnect) {
int responseCode = con.getResponseCode();
try (InputStream ins = responseCode >= 400 ? con.getErrorStream() : con.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(ins))) {
// receive response
}
}
Closing Connection only:
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
try (AutoCloseable ac = con::disconnect) {
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(new InputStreamReader(ins)))
// ins will close automatically when con closes?
// receive response
}

When you disconnect the HttpURLConnection object, it MAY also close any InputStream or OutputStream that it has opened.
HttpURLConnection.disconnect() method description:
Calling the disconnect() method may close the underlying socket if a persistent connection is otherwise idle at that time.
You can read more here.
In turn, Socket.close() method description:
Closing this socket will also close the socket's InputStream and OutputStream.
If this socket has an associated channel then the channel is closed as well.
You can read more here.
But pay attention that "disconnecting" HttpURLConnection doesn’t mandatory close the Socket.
It have been already discussed quite well in that thread:
(When keepAlive == true)
If client called HttpURLConnection.getInputSteam().close(), the later call to HttpURLConnection.disconnect() will NOT close the Socket. i.e. The Socket is reused (cached)
If client does not call close(), call disconnect() will close the InputStream and close the Socket.
So in order to reuse the Socket, just call InputStream.close(). Do not call HttpURLConnection.disconnect().
On the other hand, in the official oracle tutorials they suggest to close InputStream explicitly in order to be sure that resources don’t leak.

Related

when I calling InputStream'close(), it will finish over 10s. I want finish in 1s

I read Google provides 2 different examples of HttpURLConnection usage - Should we call HttpURLConnection's disconnect, or InputStream's close?
and I know use close() on Android is correct. But, when I close connection with InputStream'clos(), it cost 10 second to finish. I want finish to close connection in 1s. How do I do?
private HttpURLConnection getConnection(String uri) throws IOException {
try {
URL url = new URL(ROOT_URL + uri);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
DataInputStream dIS = new BufferedInputStream(connection.getInputStream(), 100000);
dIS.close(); //it will finish over 10s
} catch (IOException e) {
}
}

Why HttpURLConnection does not send data unless I try to receive something

I cannot comprehend why doesn't the following code does not put a packet onto wire (confirmed via wireshark). It is a fairly standard method of sending an HTTP POST request, as I believe. I don't intend to read anything just POST.
private void sendRequest() throws IOException {
String params = "param=value";
URL url = new URL(otherUrl.toString());
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setDoOutput(true);
con.setDoInput(true); //setting this to `false` does not help
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "text/plain");
con.setRequestProperty("Content-Length", "" + Integer.toString(params.getBytes().length));
con.setRequestProperty("Accept", "text/plain");
con.setUseCaches(false);
con.connect();
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(params);
wr.flush();
wr.close();
//Logger.getLogger("log").info("URL: "+url+", response: "+con.getResponseCode());
con.disconnect();
}
What happens is... actually nothing, unless I try to read anything. For example by uncommenting the above log line which reads the response code. Trying to read a response via con.getInputStream(); also works. There is no movement of packets. When I uncomment the getResponseCode, I can see that http POST is sent, and then 200 OK is sent back. The order is proper. I.e. I don't get some wild response before sending POST. Everything else looks exactly the same (I can attach wireshark screenshots if needed.). In the debugger the code executes (i.e. does not block anywhere).
I don't understand under what circumstances this can be happening. I belive it should be possible, to send a POST request with con.setDoInput(false);. Currently it doesn't send anything or fails (when trying to execute con.getResponseCode()) with an exception because I obviously promised I won't read anything.
It might be relevant, that before sendRequest I do request some data from the same site, but I trust I close everything properly. I.e:
public static String getData(String urlAddress) throws MalformedURLException, IOException {
URL url = new URL(urlAddress);
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setDoOutput(false);
InputStream in = con.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder data = new StringBuilder();
String line;
while((line = reader.readLine()) != null) {
data.append(line);
}
reader.close();
in.close();
con.getResponseCode();
con.disconnect();
return data.toString();
}
The server for url in both cases is the same, port also, so I believe it is possible to use the same socket for communication. The above code works and retrieves the data properly.
I am not sure, maybe I don't clean something, and it gets cached, so with out an explicit read the POST gets delayed. There is no other traffic on the socket.
Unless you're using fixed-length or chunked transfer mode, HttpURLConnection will buffer all your output until you call getInputStream() or getResponseCode(), so that it can send a correct Content-length header.
If you call getResponseCode() you should have a look at its value.

Error 500 occurs if I get InputStream before writing to connection

The code below
OutputStream outputWeb = conn.getOutputStream();
File file = new File("myfile");
InputStream inputFile = new FileInputStream(file);
InputStream inputWeb = conn.getInputStream();
while((ch=inputFile.read()) >= 0) {
outputWeb.write(ch);
}
causes IOException with message "Server returned HTTP response code: 500 for URL: myurl" just at the line where InputStream is taken from the connection.
While code below does not cause any exceptions and works fine
OutputStream outputWeb = conn.getOutputStream();
File file = new File("myfile");
InputStream inputFile = new FileInputStream(file);
while((ch=inputFile.read()) >= 0) {
outputWeb.write(ch);
}
InputStream inputWeb = conn.getInputStream();
The only difference is that taking input stream located after some writing to connection.
How one can deduce this from API contract? What is happening and why?
conn configuration
URL url = new URL("https://myurl");
HttpURLConnection conn;
// creating http connection
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestProperty("Content-type", "audio....");
UPDATE
It is clear in principle now, but I still wish to see this in API contract. I.e. it should be written somewhere, that I can't gen input stream until write something. Implementations can allow this in any case, just obliging readers of that stream to wait.

HttpURLConnection.getOutputStream() takes 20 seconds. Why?

I'm trying to perform webservice calls in android (2.3.4) using the following code snippet. My problem occurs on conn.getOutputStream, as this method seems to be taking a full 20 seconds to return. Any thoughts?
URL uri = new URL(serviceURI + "/remoteMethod");
HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
Log.d(TAG, "opening output stream");
OutputStreamWriter writer = new OutputStreamWriter(
conn.getOutputStream());
Log.d(TAG, "output stream opened");
String body = getRequestBody();
writer.write(body);
writer.flush();
writer.close();
int responseCode= conn.getResponseCode();
It would appear that this was probably a network problem, and that jarnbjo was actually correct in his suggestion in the comments that the connection was just taking too long. I moved the server and device to another network (webservice is not public), and the problem cleared up.

how to keep multiple Java HttpConnections open to same destination

We are using HttpURLConnection API to invoke a REST API to the same provider often (kind of an aggregation usecase). We want to keep a pool of 5 connections always open to the provider host (always the same IP).
What is the proper solution? Here is what we tried:
System.setProperty("http.maxConnections", 5); // set globally only once
...
// everytime we need a connection, we use the following
HttpURLConnection conn = (HttpURLConnection) (new URL(url)).openConnection();
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.setDoOutput(false);
conn.setUseCaches(true);
...
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
...
At this point we read the input stream until the BufferedReader returns no more bytes. What do we do after that point if we want to reuse the underlying connection to the provider? We were under the impression that if the input stream is completely read, the connection is then added back to the pool.
It's been working for several weeks this way, but today it stopped working producing this exception: java.net.SocketException: Too many open files
We found numerous sockets in the CLOSE_WAIT state like this (by running lsof):
java 1814 root 97u IPv6 844702 TCP colinux:58517->123.123.254.205:www (CLOSE_WAIT)
Won't either conn.getInputStream().close() or conn.disconnect() completely close the connection and remove it from the pool?
We had this problem also on Java 5 and our solution is to switch to Apache HttpClient with pooled connection manager.
The keepalive implementation of Sun's URL handler for HTTP is very buggy. There is no maintenance thread to close idle connections.
Another bigger problem with keepalive is that you need to delete responses. Otherwise, the connection will be orphaned also. Most people don't handle error stream correctly. Please see my answer to this question for an example on how to read error responses correctly,
HttpURLConnection.getResponseCode() returns -1 on second invocation
From here:
The current implementation doesn't buffer the response body. Which means that the application has to finish reading the response body or call close() to abandon the rest of the response body, in order for that connection to be reused. Furthermore, current implementation will not try block-reading when cleaning up the connection, meaning if the whole response body is not available, the connection will not be reused.
I read this as if your solution should work, but that you are also free to call close and the connection will still be reused.
The reference cited by disown was what really helped.
We know Apache HttpClient is better, but that would require another jar and we might use this code in an applet.
Calling HttpURLConnection.connect() was unnecessary. I'm not sure if it prevents connection reuse, but we took it out. It is safe to close the stream, but calling disconnect() on the connection will prevent reuse. Also, setting sun.net.http.errorstream.enableBuffering=true helps.
Here is what we ended up using:
System.setProperty("http.maxConnections", String.valueOf(CONST.CONNECTION_LIMIT));
System.setProperty("sun.net.http.errorstream.enableBuffering", "true");
...
int responseCode = -1;
HttpURLConnection conn = null;
BufferedReader reader = null;
try {
conn = (HttpURLConnection) (new URL(url)).openConnection();
conn.setRequestProperty("Accept-Encoding", "gzip");
// this blocks until the connection responds
InputStream in = new GZIPInputStream(conn.getInputStream());
reader = new BufferedReader(new InputStreamReader(in));
StringBuffer sb = new StringBuffer();
char[] buff = new char[CONST.HTTP_BUFFER_SIZE];
int cnt;
while((cnt = reader.read(buff)) > 0) sb.append(buff, 0, cnt);
reader.close();
responseCode = conn.getResponseCode();
if(responseCode != HttpURLConnection.HTTP_OK) throw new IOException("abnormal HTTP response code:"+responseCode);
return sb.toString();
} catch(IOException e) {
// consume error stream, otherwise, connection won't be reused
if(conn != null) {
try {
InputStream in = ((HttpURLConnection)conn).getErrorStream();
in.close();
if(reader != null) reader.close();
} catch(IOException ex) {
log.fine(ex);
}
}
// log exception
String rc = (responseCode == -1) ? "unknown" : ""+responseCode;
log.severe("Error for HttpUtil.httpGet("+url+")\nServer returned an HTTP response code of '"+rc+"'");
log.severe(e);
}

Categories

Resources