Do we need to use the HttpURLConnection error stream when an IOException is thrown

According to the technical guide from Oracle Java, we should consume the HttpURLConnection error HttpURLConnection when an IOException throw

http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html

What can you do to help with Keep-Alive? Do not leave the connection ignoring the response body. This can lead to TCP downtime. It should be garbage collection when they are not longer.

If getInputStream () returns successfully, read the whole body response.

When calling getInputStream () from an HttpURLConnection, if an exception is an IOException, catch the exception and call getErrorStream () to get the response body (if any).

Reading the body of the response clears the connection, even if you are not interested in the answer itself. But if the body of the response is long, and you are not interested in the rest, when you see starting, you can close the InputStream. But you need to know that more data may be in its way. Thus, the compound cannot be cleaned for reuse.

Here is an example of code that matches the above recommendation:

Here is a sample code

 try { URL a = new URL(args[0]); URLConnection urlc = a.openConnection(); is = conn.getInputStream(); int ret = 0; while ((ret = is.read(buf)) > 0) { processBuf(buf); } // close the inputstream is.close(); } catch (IOException e) { try { respCode = ((HttpURLConnection)conn).getResponseCode(); es = ((HttpURLConnection)conn).getErrorStream(); int ret = 0; // read the response body while ((ret = es.read(buf)) > 0) { processBuf(buf); } // close the errorstream es.close(); } catch(IOException ex) { // deal with the exception } } 

Does this apply to the Android platform? Since I don't see such a technique in most Android code examples.

+7
android
source share
2 answers

If you are not interested in displaying an error message to the user, close the InputStream or invoke disconnect on HttpURLConnection in finally without reading the error message. This is what you see in most examples.

I stumbled upon the following comment in one of the source code while looking at the implementation of HttpURLConnection. This may cause connections to close without reading all data.

This needs to be called when the connection unexpectedly closes to cancel the cache entry and prevent the HTTP connection from being reused. HTTP messages are sent sequentially, so whenever a message cannot be read before completion, subsequent messages cannot be read and the connection should be dropped.

According to the Android HttpURLConnection implementation, in case of an exception:

  • If the error is not read and the InputStream is closed, the connection will be considered inapplicable and closed.
  • If you read the error and then close the InputStream , the connection is considered reusable and added to the connection pool.

You can see in the image below, the connection and connectionReleased variables are set to null and true respectively, once all the data has been read. Note that getErrorStream returns an InputStream , so it is also valid in an exception script.

enter image description here

Code analysis: consider FixedLengthInputStream, one of the specialized implementations of InputStream . Here is the implementation of the close method:

  @Override public void close() throws IOException { if (closed) { return; } closed = true; if (bytesRemaining != 0) { unexpectedEndOfInput(); } } 

The bytesRemaining instance bytesRemaining contains the number of bytes that is still readable by the InputStream . The following is the uniqueEndOfInput method:

 protected final void unexpectedEndOfInput() { if (cacheRequest != null) { cacheRequest.abort(); } httpEngine.release(false); } 

Here is the implementation of release . Calling disconnect on an HttpURLConnection instance results in a call to this release method with false . The final if check ensures that the connection must be closed or added to the connection pool for reuse.

 public final void release(boolean reusable) { // If the response body comes from the cache, close it. if (responseBodyIn == cachedResponseBody) { IoUtils.closeQuietly(responseBodyIn); } if (!connectionReleased && connection != null) { connectionReleased = true; // We cannot reuse sockets that have incomplete output. if (requestBodyOut != null && !requestBodyOut.closed) { reusable = false; } // If the headers specify that the connection shouldn't be reused, don't reuse it. if (hasConnectionCloseHeader()) { reusable = false; } if (responseBodyIn instanceof UnknownLengthHttpInputStream) { reusable = false; } if (reusable && responseBodyIn != null) { // We must discard the response body before the connection can be reused. try { Streams.skipAll(responseBodyIn); } catch (IOException e) { reusable = false; } } if (!reusable) { connection.closeSocketAndStreams(); connection = null; } else if (automaticallyReleaseConnectionToPool) { HttpConnectionPool.INSTANCE.recycle(connection); connection = null; } } } 

The code you use to handle the IOException, the error stream is read and then closed, re-connects and is added to the connection pool. At the moment when all data is read from the InputStream , connection is added to the connection pool. The following is the read method of the FixedLengthInputStream method:

 @Override public int read(byte[] buffer, int offset, int count) throws IOException { Arrays.checkOffsetAndCount(buffer.length, offset, count); checkNotClosed(); if (bytesRemaining == 0) { return -1; } int read = in.read(buffer, offset, Math.min(count, bytesRemaining)); if (read == -1) { unexpectedEndOfInput(); // the server didn't supply the promised content length throw new IOException("unexpected end of stream"); } bytesRemaining -= read; cacheWrite(buffer, offset, read); if (bytesRemaining == 0) { endOfInput(true); } return read; } 

When the bytesRemaining variable becomes 0, endOfInput is called , which will call the release method with the true parameter, which will ensure pooling.

 protected final void endOfInput(boolean reuseSocket) throws IOException { if (cacheRequest != null) { cacheBody.close(); } httpEngine.release(reuseSocket); } 
+6
source

If it is documented for Java, it is tied to the Android platform.

0
source

All Articles