Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android ignoring DefaultHttpClient timeout parameter

i'm stuck with a timeout problem with DefaultHttpClient on Android. I'm trying to set the timeout with the following piece of code:

HttpClient client = new DefaultHttpClient();
HttpParams httpParameters = client.getParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, 4000);
HttpConnectionParams.setSoTimeout(httpParameters, 4000);

But if the device is connected to a network without internet connection, the timeout is never fired and the execution of http request never throws any timeout exception. I'm executing http request as follow:

HttpResponse httpResponse = client.execute(request);

I've tried also to set the timeout on the HttpRequest, with the following lines:

HttpRequestBase request = ...
request.setParams(httpParameters);

Android seems to ignore the timeout settings and when executing http request on a network with no internet connection, all the requests fails after about 20s, and not after my timeout settings.

I've also tried to close all internet connections and abort http request after a timeout with a parallel thread. I've used the following piece of code:

HttpClient client = new DefaultHttpClient();
HttpParams httpParameters = client.getParams();

HttpRequestBase request = ...
request.setParams(httpParameters);

HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutReal);
HttpConnectionParams.setSoTimeout(httpParameters, timeoutReal);

request.setParams(httpParameters);
((DefaultHttpClient) client).setParams(httpParameters);

Thread t = new Thread(){
    public void run()
    {
        try
        {
            Thread.sleep(4000);
            request.abort();
            client.getConnectionManager().closeExpiredConnections();
            client.getConnectionManager().closeIdleConnections(4000,TimeUnit.MILLISECONDS);
            client.getConnectionManager().shutdown();
            Log.i("TEST SHUTDOWN","SHUT DOWN ALL CONNECTIONS");
                }
            catch (InterruptedException e)
            {
            }
        }
    };

try
{
    t.start();
    HttpResponse httpResponse = client.execute(request);
}
catch (Exception e)
{
    Log.i("TEST SHUTDOWN","EXCEPTION "+e);
}
finally
{
    t.interrupt();
}

But even if I see from the logs that the request is aborted and the connection manager is shut down, the execution of the request is not interrupted/aborted and no exception is raised at the timeout set. The request ends always after 20s.

Any idea why?

like image 581
Jacopo Berta Avatar asked Feb 11 '23 11:02

Jacopo Berta


2 Answers

What you are probably seeing is that the DNS lookup is timing out. When your HTTP client attempts to make a connection, the first thing it does is try to resolve the hostname of your URL into an IP address. When doing this it doesn't take your timeout settings into account (those timeout values are only used when trying to actually make the socket connection). If you haven't got good Internet connectivity, your DNS lookup will just stall until it times out. Once that happens, your HTTP request should fail immediately with UnknownHostException.

Unfortunately you have no control over the DNS timeout, so the only way to solve your problem is to first determine if your DNS resolution is working. You should do this in a separate thread, and if you don't get successful host resolution within a few seconds, you know that your Internet connection is not reliable and you don't even have to attempt your HTTP request.

So you can try something like this:

HttpClient client = new DefaultHttpClient();
HttpParams httpParameters = client.getParams();

HttpRequestBase request = ...
request.setParams(httpParameters);

HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutReal);
HttpConnectionParams.setSoTimeout(httpParameters, timeoutReal);

request.setParams(httpParameters);
((DefaultHttpClient) client).setParams(httpParameters);

// The thread that is waiting to execute the HTTP request
final Thread waitingThread = Thread.currentThread();

Thread t = new Thread() {
    boolean running = true;
    public void run() {
        try {
            // Try to resolve the host name
            InetAddress addr = InetAddress.getByName (hostname);
            // Successful resolution, notify the waiting thread
            if (running) {
                // Signal the waiting thread that it can do the HTTP request now
                waitingThread.interrupt();
            }
        } catch (Exception e) {
            // Some problem, just ignore it
        }
    }
};

try {
    // Start name resolution
    t.start();
    // Sleep for as long as we are willing to wait for the DNS resolution
    Thread.sleep(4000);
    // If we slept the entire time without getting interrupted, the DNS resolution took too long
    //  so assume we have no connectivity.
    t.running = false; // We don't want to be interrupted anymore
    // Don't even bother trying the HTTP request now, we've used up all the time we have
} catch (InterruptedException ie) {
    // We got interrupted, so the DNS resolution must have been successful. Do the HTTP request now
    HttpResponse httpResponse = client.execute(request);
}

I'm writing this code without trying it, so please forgive any typos or missing semicolons. You should get the idea.

like image 89
David Wasser Avatar answered Feb 14 '23 02:02

David Wasser


You could try to use AndroidHttpClient instead of DefaultHttpClient. It has some specific settings for Android.

Alternatively, you could try to replace the following line:

HttpParams httpParameters = client.getParams();

with the following line

HttpParams httpParameters = new BasicHttpParams();

I don't know if this is the correct answer but, hope this helps.

like image 28
crowde Avatar answered Feb 14 '23 04:02

crowde