5

I would like to make a simple HTTP HEAD request, without keep-alive.

How can I do that in Android?

Daniele B
  • 19,801
  • 29
  • 115
  • 173

3 Answers3

8

using HttpClient:

As njzk2 suggested, with HttpClient() it's very straightforward:

HttpResponse response = new HttpClient().execute(new HttpHead(myUrl));

However there is a problem with not being able to close the connection. Usually on the HttpClient, you would get the entity using:

HttpEntity entity = response.getEntity();

and then you would get the input stream from the entity

InputStream instream = entity.getContent();
...
instream.close();

and by closing the input stream, the connection would close. However, in the case of a HEAD request, the entity appears to be null (possibly because HEAD requests don't return the body in the response), so the input stream cannot be fetched and closed and the connection doesn't close either.

In the last edit to his answer, njzk2 is suggesting to use AndroidHttpClient, which is a more recent implementation (API 8) of HttpClient and it actually has a close() method. I haven't used it but I guess it will work fine. However, as the Android development team suggests, the HttpUrlConnection should be the preferred Android client to use.

using HttpUrlConnection:

Actually it seems quite easy to make HEAD requests using HttpUrlConnection and make sure that the connection closes:

    HttpURLConnection urlConnection = null;
    System.setProperty("http.keepAlive", "false");
    try {
        URL url = new URL(stringUrl);
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setRequestMethod("HEAD");
        urlConnection.getInputStream().close();
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
    }
Daniele B
  • 19,801
  • 29
  • 115
  • 173
  • there is also `entity.consumeContent()` – njzk2 Mar 20 '14 at 21:23
  • the problem is that when making `HEAD` requests with `HttpClient`, the entity is `null` – Daniele B Mar 20 '14 at 21:27
  • I really don't see why the connection would stay opened. I have been using HttpClient for quite some time and I am very happy with it. The server never seemed to be overwhelmed with unclosed connections. – njzk2 Mar 20 '14 at 21:28
  • As long as you can close the connection, `HttpClient` is fine and this happens for GET, POST, PUT. But for HEAD requests connections are not closed by the client, and need to be closed by server. This means that server-side these connections will have to go into the TIME_WAIT status, which by default lasts 60 seconds before allowing the same socket to be reused. However, if the connection is closed by the client, the server doesn't have to lock these sockets in TIME_WAIT and can reuse them straight away. – Daniele B Mar 20 '14 at 21:40
  • I don't see why a server would keep a HEAD connection (or any HTTP connection, for that matter) opened once it has sent its response (i.e. when it has the TCP ack for the last packet) – njzk2 Mar 20 '14 at 21:43
  • The socket being in the TIME_WAIT status doesn't mean it's open. TIME_WAIT is a safety measure the server takes in order to make sure no delayed packets comes on that socket from the client (in case the client had sent other packets before receiving the message that the port was closed by the server). In case it's the client to close the connection, the server (after receiving the message that such port has been closed by the client) doesn't need to implement the TIME_WAIT mechanism, so the port can be reused straight away. – Daniele B Mar 20 '14 at 21:53
1

Trivially :

HttpResponse response = new AndroidHttpClient().execute(new HttpHead(myUrl));

Typically you'll use the same AndroidHttpClient for several connections, then call close on it.

njzk2
  • 38,969
  • 7
  • 69
  • 107
  • Thank you! Will the connection be closed by default after that? Or do I need to read the response? – Daniele B Mar 20 '14 at 18:12
  • the headers are in HttpResponse. you don't need to close the connection. – njzk2 Mar 20 '14 at 18:17
  • Actually I can verify on the server (using "netstat") that the connections remain with status "ESTABLISHED". Is there a way to explicitely close the connection using the `HttpDefaultClient`? On `HttpUrlConnection` you can explicitely close a connection with `disconnect()`. Is it possible to make a HEAD request using `HttpUrlConnection`? – Daniele B Mar 20 '14 at 20:44
  • actually there is an `AndroidHttpClient` that has a `close` method to release all connections. – njzk2 Mar 20 '14 at 20:56
  • I found out that it is actually easy to set a `HEAD` request with `HttpUrlConnection`, you just need to add: `httpUrlConnection.setRequestMethod("HEAD");` – Daniele B Mar 20 '14 at 21:02
1

For ordinary Java and Android

I am using some standard Java code to test the existence of a resource and in the same time to check whether a resource has been changed, provided the parameter if_modified_since is non-zero.

        URL url = new URL(adr);
        try {
            URLConnection con = url.openConnection();
            con.setIfModifiedSince(if_modified_since);
            if (con instanceof HttpURLConnection) {
                /* Workaround for https://code.google.com/p/android/issues/detail?id=61013 */
                con.addRequestProperty("Accept-Encoding", "identity");
                ((HttpURLConnection) con).setRequestMethod("HEAD");
                int response = ((HttpURLConnection) con).getResponseCode();
                if (response == HttpURLConnection.HTTP_UNAVAILABLE)
                    return false;
                if (response == HttpURLConnection.HTTP_NOT_MODIFIED)
                    return false;
            }
            if (if_modified_since != 0) {
                long modified = OpenOpts.getLastModified(con);
                if (modified != 0 && if_modified_since >= modified)
                    return false;
            }
            InputStream in = con.getInputStream();
            in.close();
            return true;
        } catch (FileNotFoundException x) {
            return false;
        } catch (UnknownHostException x) {
            return false;
        } catch (SocketException x) {
            return false;
        }

Interestingly the code needs a con.getInputStream() and I don't get some errors here. But I needed some helper code, to also cater for URIs that point to JARs. The helper code is:

     private static long getLastModified(URLConnection con) 
                    throws IOException {
    if (con instanceof JarURLConnection) {
        return ((JarURLConnection) con).getJarEntry().getTime();
    } else {
        return con.getLastModified();
    }
}

The code can be further optimized by some specialization if the URI is schema file: , one can then directly do File.exists() and File.getLastModified().

We do not throw a ServiceUnvailable exception here, we basically assume that the outer code would catch an IOException and then assume a false result of the getHead().