144

I want to return false if the URL takes more then 5 seconds to connect - how is this possible using Java? Here is the code I am using to check if the URL is valid

HttpURLConnection.setFollowRedirects(false);
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setRequestMethod("HEAD");
return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
Michael
  • 41,989
  • 11
  • 82
  • 128
Malachi
  • 33,142
  • 18
  • 63
  • 96

5 Answers5

224

HttpURLConnection has a setConnectTimeout method.

Just set the timeout to 5000 milliseconds, and then catch java.net.SocketTimeoutException

Your code should look something like this:


try {
   HttpURLConnection.setFollowRedirects(false);
   HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
   con.setRequestMethod("HEAD");

   con.setConnectTimeout(5000); //set timeout to 5 seconds

   return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
} catch (java.net.SocketTimeoutException e) {
   return false;
} catch (java.io.IOException e) {
   return false;
}


dbyrne
  • 59,111
  • 13
  • 86
  • 103
  • 6
    I set the value to 10 minutes. However it throws me a `java.net.ConnectException: Connection timed out: connect` before even 2 minutes is up. Do you know what is causing the problem? – Pacerier Feb 03 '12 at 10:16
  • @dbyrne this seems a bit confusing to me - since we set the timeout after `HttpURLConnection` object is created, I wonder what is the exact place in the code that shall raise `SocketTimeoutException` ? – Less Jul 24 '13 at 16:57
  • 5
    SocketTimeoutException is a subclass of IOException. If both catch blocks do the same thing, you could just catch IOException. – spaaarky21 Dec 05 '13 at 22:27
  • 2
    @spaaarky21 is correct. If however you are building a UI and you want to notify your users that a timeout occurred, you must catch SocketTimeoutException before IOException, if not, it will be unreachable. – Clocker Dec 29 '15 at 17:52
  • 5
    **N.B. !!!** you need to call `setConnectTimeout` before any of the methods that implicitly connect (basically all the methods that throw IllegalStateException if already connected). Ideally make setConnectTimeout (readTimeout) the first methods called. – Adam Gent Jan 26 '17 at 16:24
  • 7
    It didn't work for me. But, after adding `con.setReadTimeout()`, it worked as expected. – Paulo Jul 10 '17 at 16:11
  • @Pacerier the timeout might have been forced by some other part of the network such as a proxy. – john sullivan Nov 19 '19 at 19:19
  • is making HttpURLConnection.setFollowRedirects(false); has a direct impact on every url calls ? @dbyrne ? – gumuruh Mar 13 '22 at 17:49
128

You can set timeout like this,

con.setConnectTimeout(connectTimeout);
con.setReadTimeout(socketTimeout);
ZZ Coder
  • 74,484
  • 29
  • 137
  • 169
  • 2
    What's the maximum value of the timeout we can specify? – Pacerier Feb 03 '12 at 10:16
  • 10
    @Pacerier The docs do not state this explicitly. It does throw an IllegalArgumentException if the value is negative (a value of 0 would mean wait indefinitely). Since the timeout is an unsigned 32bit int, I would guess the max timeout would be about 49 days (though I seriously doubt this such a value would be helpful to anyone). – Jay Sidri Oct 25 '12 at 07:17
  • I used both the timeouts and still getting timeout error. but when I check logs my request is taking 20sec but I gave 100 sec for setConnectTimeout – Siva Sep 18 '20 at 00:19
1

If the HTTP Connection doesn't timeout, You can implement the timeout checker in the background thread itself (AsyncTask, Service, etc), the following class is an example for Customize AsyncTask which timeout after certain period

public abstract class AsyncTaskWithTimer<Params, Progress, Result> extends
    AsyncTask<Params, Progress, Result> {

private static final int HTTP_REQUEST_TIMEOUT = 30000;

@Override
protected Result doInBackground(Params... params) {
    createTimeoutListener();
    return doInBackgroundImpl(params);
}

private void createTimeoutListener() {
    Thread timeout = new Thread() {
        public void run() {
            Looper.prepare();

            final Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {

                    if (AsyncTaskWithTimer.this != null
                            && AsyncTaskWithTimer.this.getStatus() != Status.FINISHED)
                        AsyncTaskWithTimer.this.cancel(true);
                    handler.removeCallbacks(this);
                    Looper.myLooper().quit();
                }
            }, HTTP_REQUEST_TIMEOUT);

            Looper.loop();
        }
    };
    timeout.start();
}

abstract protected Result doInBackgroundImpl(Params... params);
}

A Sample for this

public class AsyncTaskWithTimerSample extends AsyncTaskWithTimer<Void, Void, Void> {

    @Override
    protected void onCancelled(Void void) {
        Log.d(TAG, "Async Task onCancelled With Result");
        super.onCancelled(result);
    }

    @Override
    protected void onCancelled() {
        Log.d(TAG, "Async Task onCancelled");
        super.onCancelled();
    }

    @Override
    protected Void doInBackgroundImpl(Void... params) {
        // Do background work
        return null;
    };
 }
bummi
  • 27,123
  • 14
  • 62
  • 101
Ayman Mahgoub
  • 4,152
  • 1
  • 30
  • 27
  • It's absolutely unnecessary to create a new looper thread just to schedule a call to cancel(). You can do that from the main thread in `onPreExecute()`. Also, if you cancel the task manually, you should also cancel the scheduled call to avoid leaks. – BladeCoder Sep 30 '15 at 07:44
  • Point here is to cancel AsyncTask in the middle of the doInBackground() when it takes too much time at execution not at onPreExecute(), also I want to cancel only this instance of AsyncTask which takes too much time and keep the others, much appreciate your feedback. – Ayman Mahgoub Sep 30 '15 at 12:03
  • 2
    I think my message was not clear enough. I didn't say you should cancel in onPreExecute(), I said you should create the Handler in onPreExecute() and post the delayed cancel from the main thread. This way you will use the main thread as looper thread and you can of course cancel the AsyncTask later while doInBackground() is executing because the main thread also runs concurrently with the background thread. – BladeCoder Sep 30 '15 at 12:36
0

The System property sun.net.client.defaultConnectTimeout can be set. The value is in milliseconds. This will set a default timeout for each request-

Either by setting in JVM options-

-Dsun.net.client.defaultConnectTimeout=5000

OR in java code-

System.setProperty("sun.net.client.defaultConnectTimeout", "5000");

shashank joshi
  • 140
  • 1
  • 4
-1

I could get solution for such a similar problem with addition of a simple line

HttpURLConnection hConn = (HttpURLConnection) url.openConnection();
hConn.setRequestMethod("HEAD");

My requirement was to know the response code and for that just getting the meta-information was sufficient, instead of getting the complete response body.

Default request method is GET and that was taking lot of time to return, finally throwing me SocketTimeoutException. The response was pretty fast when I set the Request Method to HEAD.

Subrata Nath
  • 195
  • 1
  • 3
  • 2
    This is not in any way the solution, you're changing the request method to a `HEAD` request which will not produce any response body. – Sveinung Kval Bakken Jun 14 '18 at 15:40
  • This does not add anything to the original question. OP has `.setRequestMethod("HEAD")` in their code. Oddly, this description was exactly what I needed to reduce my issue of "Too many open files." So thanks? – Joshua Pinter Oct 03 '19 at 03:44