After executing a request I would like to check the request headers, but it doesn't work.
I call getRequestProperties()
on an instance of sun.net.www.protocol.http.HttpURLConnection
and I always get an IllegalStateException with the message "Already connected". As if I wanted to set request properties. But I only want to read them.
The responsible code for this behaviour is in the HttpUrlConnection:
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/sun/net/www/protocol/http/HttpURLConnection.java#HttpURLConnection.getRequestProperties%28%29
public synchronized Map<String, List<String>> getRequestProperties() {
if (connected)
throw new IllegalStateException("Already connected");
// ...
}
Ok so maybe I should only read the request properties after disconnecting. But it turns out, disconnect()
doesn't set connected
to false
. Although it should: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/sun/net/www/protocol/http/HttpURLConnection.java#HttpURLConnection.disconnect%28%29
It also doesn't seem to make a difference if I read the stream to the end or not. Closing the InputStream before or after calling disconnect doesn't make a difference either.
I'm confused. Can you help me?
disconnect()
set connected to false?The code to reproduce this is a Unit test for Android (I use Robolectric), but I think you can use it in a Java project as well and call it from main() after removing the test annotation:
/**
* Test if HttpUrlConnection works as expected, because in some cases it seems it doesn't
*
* @throws Exception
*/
@Test
public void testHttpUrlConnection() throws Exception
{
final URL url = new URL("http://www.stackoverflow.com");
final HttpURLConnection urlConnection = ( HttpURLConnection ) url.openConnection( );
urlConnection.setRequestMethod("GET");
InputStream is = null;
try
{
is = urlConnection.getInputStream();
assertEquals(200, urlConnection.getResponseCode());
}
catch (IOException ex)
{
is = urlConnection.getErrorStream( );
}
final String result = copyStreamToString(is); // some html response
// Streams must be closed before disconnecting (according to http://stackoverflow.com/a/11056207/3596676)
is.close();
assertTrue((Boolean) getFieldViaRecursiveReflection(urlConnection, "connected"));
// urlConnection should always be disconnected (according to http://developer.android.com/reference/java/net/HttpURLConnection.html)
urlConnection.disconnect();
assertFalse((Boolean) getFieldViaRecursiveReflection(urlConnection, "connected")); // AssertionError
// getRequestProperties throws IllegalStateException ("already connected")
Map<String, List<String>> requestProperties = urlConnection.getRequestProperties();
// do stuff with the properties
// return the result
}
private static String copyStreamToString( final InputStream is ) throws IOException
{
if ( is == null )
{
return "";
}
BufferedReader reader = new BufferedReader( new InputStreamReader( is ) );
String result = copyBufferedReaderToString( reader );
reader.close( );
return result;
}
private static String copyBufferedReaderToString( final BufferedReader bufferedReader ) throws IOException
{
StringBuffer sb = new StringBuffer( );
String line;
while ( ( line = bufferedReader.readLine( ) ) != null )
{
sb.append( line );
}
return sb.toString( );
}
private static Object getFieldViaRecursiveReflection(final Object object, final String attribute) throws Exception
{
return getFieldViaRecursiveReflection(object, object.getClass(), attribute);
}
private static Object getFieldViaRecursiveReflection(final Object object, final Class<?> c, final String attribute) throws Exception
{
try
{
final Field field = c.getDeclaredField(attribute);
field.setAccessible(true);
return field.get(object);
}
catch (NoSuchFieldException ex)
{
/* end of type hierarchy? */
Class<?> superClass = c.getSuperclass();
if (superClass == null)
{
throw ex;
}
else
{
return getFieldViaRecursiveReflection(object, superClass, attribute);
}
}
}
As no one posted an answer in the 2 months since I asked the question, but today I had to deal with the problem again and found a solution, I will answer my own question.
I can't answer all the questions in the answer (e.g. "why doesn't urlConnection.disconnect()
set the connected
attribute of urlConnection
to false
?"), but I found the solution for the main problem, which was that reading the headers of a request didn't work when the urlConnection was connected.
For some reason, which I can't remember, I wanted/needed to check the request headers after the request was done and the response was there. But I looked at the implementation of getRequestProperties()
in sun.net.www.protocol.http.HttpURLConnection
again (see the code here) and noticed that a method called filterAndAddHeaders
gets called. So it seems that headers not only get read in that method, but set. I'm not sure why this is done in a getter method (the getRequestProperties()
one), but it makes sense that when the request is already done, you should warn the user when he tries to add request headers - in this case with the IllegalStateException
that bothered me so much.
To come to the solution:
I just moved the call to getRequestProperties()
to before the request gets sent. And now everything works fine.
P.S.:
Please note that this is not all there is to it. One of my unit tests ran successfully even though I called getRequestProperties()
after the request. In that case the urlConnection
internal attribute connected
was set to false. I haven't figured it all out, but it may have been related to the response status code 304 (not modified). Maybe this helps as a hint if you have to deal with this problem and for some reason can't move the getRequestProperties()
call to before sending the request.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With