Java – HttpURLConnection.getRequestProperties() throws IllegalStateException : “Already connected”

HttpURLConnection.getRequestProperties() throws IllegalStateException : “Already connected”… here is a solution to the problem.

HttpURLConnection.getRequestProperties() throws IllegalStateException : “Already connected”

After executing the request, I want to check the request header, but it doesn’t work.

I

call getRequestProperties() on an instance of sun.net.www.protocol.http.HttpURLConnection, and I keep getting the IllegalStateException message “Already connected”. But I just want to read them.

The code responsible for this behavior is in 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");
    // ...
}

Well, maybe I should only read the request properties after disconnecting. But it turns out that 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 doesn’t seem to make any difference whether I finish reading the stream or not. Closing InputStream before or after calling disconnect is no different.

I’m confused. Can you help me?

  1. Why doesn’t disconnect() set connected to false?
  2. Why can’t I read the request properties when connecting to urlConnection?
  3. How do I properly read the request header after a request?

Reproducing this code is a unit test for Android (I use Robolectric), but I think you can also use it in a Java project and call it from main() after removing the test comments:

/**
 * 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);
        }
    }
}

Solution

Since no one posted the answer in 2 months after I asked the question, but today I had to deal with the problem again and find a solution, I will answer my own question.

I

couldn’t answer all the questions in the answer (e.g. “Why doesn’t urlConnection.disconnect() set the connected property of urlConnection< to false?”), but I found a solution to the main problem, which is when connecting urlConnection does not work when reading the requested header.

For some reason I don’t remember, I think/need to check the request header after the request completes and the response appears. But I looked again at the implementation of getRequestProperties() in sun.net.www.protocol.http.HttpURLConnection (see code.) here) and notice that a method called filterAndAddHeaders is called. So it seems that header is not only read in the method, but also set. I’m not sure why this is done in the getter method (getRequestProperties() method), but when the request has already been completed, you should warn the user when he tries to add a header – in this case, IllegalStateException bothers me a lot.

Arrive at a solution:

I just moved the call to getRequestProperties() before sending the request. Now everything is normal.

P.S

Note that that’s not all. Even though I called getRequestProperties() after the request, one of my unit tests ran successfully. In this case, the urlConnection internal property connected is set to false. I haven’t quite figured it out yet, but it might be related to response status code 304 (not modified). This may help hint if you have to deal with this and for some reason can’t move the getRequestProperties() call before sending the request.

Related Problems and Solutions