Preferred Java way to ping an HTTP URL for availability

196,104

Solution 1

Is this any good at all (will it do what I want?)

You can do so. Another feasible way is using java.net.Socket.

public static boolean pingHost(String host, int port, int timeout) {
    try (Socket socket = new Socket()) {
        socket.connect(new InetSocketAddress(host, port), timeout);
        return true;
    } catch (IOException e) {
        return false; // Either timeout or unreachable or failed DNS lookup.
    }
}

There's also the InetAddress#isReachable():

boolean reachable = InetAddress.getByName(hostname).isReachable();

This however doesn't explicitly test port 80. You risk to get false negatives due to a Firewall blocking other ports.


Do I have to somehow close the connection?

No, you don't explicitly need. It's handled and pooled under the hoods.


I suppose this is a GET request. Is there a way to send HEAD instead?

You can cast the obtained URLConnection to HttpURLConnection and then use setRequestMethod() to set the request method. However, you need to take into account that some poor webapps or homegrown servers may return HTTP 405 error for a HEAD (i.e. not available, not implemented, not allowed) while a GET works perfectly fine. Using GET is more reliable in case you intend to verify links/resources not domains/hosts.


Testing the server for availability is not enough in my case, I need to test the URL (the webapp may not be deployed)

Indeed, connecting a host only informs if the host is available, not if the content is available. It can as good happen that a webserver has started without problems, but the webapp failed to deploy during server's start. This will however usually not cause the entire server to go down. You can determine that by checking if the HTTP response code is 200.

HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("HEAD");
int responseCode = connection.getResponseCode();
if (responseCode != 200) {
    // Not OK.
}

// < 100 is undetermined.
// 1nn is informal (shouldn't happen on a GET/HEAD)
// 2nn is success
// 3nn is redirect
// 4nn is client error
// 5nn is server error

For more detail about response status codes see RFC 2616 section 10. Calling connect() is by the way not needed if you're determining the response data. It will implicitly connect.

For future reference, here's a complete example in flavor of an utility method, also taking account with timeouts:

/**
 * Pings a HTTP URL. This effectively sends a HEAD request and returns <code>true</code> if the response code is in 
 * the 200-399 range.
 * @param url The HTTP URL to be pinged.
 * @param timeout The timeout in millis for both the connection timeout and the response read timeout. Note that
 * the total timeout is effectively two times the given timeout.
 * @return <code>true</code> if the given HTTP URL has returned response code 200-399 on a HEAD request within the
 * given timeout, otherwise <code>false</code>.
 */
public static boolean pingURL(String url, int timeout) {
    url = url.replaceFirst("^https", "http"); // Otherwise an exception may be thrown on invalid SSL certificates.

    try {
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        connection.setConnectTimeout(timeout);
        connection.setReadTimeout(timeout);
        connection.setRequestMethod("HEAD");
        int responseCode = connection.getResponseCode();
        return (200 <= responseCode && responseCode <= 399);
    } catch (IOException exception) {
        return false;
    }
}

Solution 2

Instead of using URLConnection use HttpURLConnection by calling openConnection() on your URL object.

Then use getResponseCode() will give you the HTTP response once you've read from the connection.

here is code:

    HttpURLConnection connection = null;
    try {
        URL u = new URL("http://www.google.com/");
        connection = (HttpURLConnection) u.openConnection();
        connection.setRequestMethod("HEAD");
        int code = connection.getResponseCode();
        System.out.println("" + code);
        // You can determine on HTTP return code received. 200 is success.
    } catch (MalformedURLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } finally {
        if (connection != null) {
            connection.disconnect();
        }
    }

Also check similar question How to check if a URL exists or returns 404 with Java?

Hope this helps.

Solution 3

You could also use HttpURLConnection, which allows you to set the request method (to HEAD for example). Here's an example that shows how to send a request, read the response, and disconnect.

Solution 4

The following code performs a HEAD request to check whether the website is available or not.

public static boolean isReachable(String targetUrl) throws IOException
{
    HttpURLConnection httpUrlConnection = (HttpURLConnection) new URL(
            targetUrl).openConnection();
    httpUrlConnection.setRequestMethod("HEAD");

    try
    {
        int responseCode = httpUrlConnection.getResponseCode();

        return responseCode == HttpURLConnection.HTTP_OK;
    } catch (UnknownHostException noInternetConnection)
    {
        return false;
    }
}

Solution 5

public boolean isOnline() {
    Runtime runtime = Runtime.getRuntime();
    try {
        Process ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8");
        int     exitValue = ipProcess.waitFor();
        return (exitValue == 0);
    } catch (IOException | InterruptedException e) { e.printStackTrace(); }
    return false;
}

Possible Questions

  • Is this really fast enough?Yes, very fast!
  • Couldn’t I just ping my own page, which I want to request anyways? Sure! You could even check both, if you want to differentiate between “internet connection available” and your own servers beeing reachable What if the DNS is down? Google DNS (e.g. 8.8.8.8) is the largest public DNS service in the world. As of 2013 it serves 130 billion requests a day. Let ‘s just say, your app not responding would probably not be the talk of the day.

read the link. its seems very good

EDIT: in my exp of using it, it's not as fast as this method:

public boolean isOnline() {
    NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
    return netInfo != null && netInfo.isConnectedOrConnecting();
}

they are a bit different but in the functionality for just checking the connection to internet the first method may become slow due to the connection variables.

Share:
196,104

Related videos on Youtube

Sean Patrick Floyd
Author by

Sean Patrick Floyd

About me: Austrian / American Java developer / architect with 25+ years of experience, working in the Seattle Area for Domino Data Lab (views are my own). 50 y/o, married to a lovely German wife and father of three. Hold a 5th degree black belt in Taekwondo (Kukkiwon), love Monty Python and appreciate a good glass of Whisk(e)y. He / him. Java aficionado, dabbling in Scala / Kotlin / Groovy, off and on also JS, and Python or GoLang when I have to. Connect: Twitter, LinkedIn

Updated on November 11, 2021

Comments

  • Sean Patrick Floyd
    Sean Patrick Floyd over 2 years

    I need a monitor class that regularly checks whether a given HTTP URL is available. I can take care of the "regularly" part using the Spring TaskExecutor abstraction, so that's not the topic here. The question is: What is the preferred way to ping a URL in java?

    Here is my current code as a starting point:

    try {
        final URLConnection connection = new URL(url).openConnection();
        connection.connect();
        LOG.info("Service " + url + " available, yeah!");
        available = true;
    } catch (final MalformedURLException e) {
        throw new IllegalStateException("Bad URL: " + url, e);
    } catch (final IOException e) {
        LOG.info("Service " + url + " unavailable, oh no!", e);
        available = false;
    }
    
    1. Is this any good at all (will it do what I want)?
    2. Do I have to somehow close the connection?
    3. I suppose this is a GET request. Is there a way to send HEAD instead?
  • Sean Patrick Floyd
    Sean Patrick Floyd over 13 years
    Thanks for the details, answers like these are what makes SO a great place. Testing the server for availability is not enough in my case, I need to test the URL (the webapp may not be deployed), so I'll stick with the HttpURLConnection. About HEAD not being a good test: it is a good method if I know the target URL supports HEAD, I'll check that.
  • BalusC
    BalusC over 13 years
    If that's the functional requirement, then the HttpURLConnection is the best way to go. You'd namely like to determine the response code as well. You can also do this using Socket but that only adds verbosity and requires knowledge of HTTP spec.
  • Marcin Waśniowski
    Marcin Waśniowski about 11 years
    It's possible to get java.io.IOException: unexpected end of stream on some servers, to fix it you need to add connection.setRequestProperty("Accept-Encoding", "musixmatch"); It's known problem and reported at code.google.com
  • BalusC
    BalusC about 11 years
    @Muhd: really? :) Please test/experiment it yourself with values in various ranges before making such a ridiculous statement, if you can't tell based on the code.
  • Muhd
    Muhd about 11 years
    @BalusC, ah wait, sorry I read it wrong, since you put the variable on the right side of the comparison. Apparently that was too much for my brain to handle.
  • Erk
    Erk over 10 years
    Some sites may require that you spoof a user-agent string in order to get a reliable result: connection.setRequestProperty("User-Agent", "...");
  • metator
    metator over 10 years
    @BalusC Are you sure the return condition is correct? Shouldn't it be return (200 >= responseCode && responseCode <= 399); ?
  • metator
    metator about 10 years
    @BalusC Because (200 <= responseCode && responseCode <= 399) will be true if and only if (response <= 399), which means that (200 <= responseCode) condition is redundant. So i thought it was a mistake.
  • BalusC
    BalusC about 10 years
    @metator: huh??? It's absolutely not redundant. Response codes lower than 200 are not considered valid.
  • AndreaF
    AndreaF over 9 years
    @BalusC Im some situation these method seems that doesn't works well. give a look here stackoverflow.com/questions/25805580/…
  • BalusC
    BalusC over 9 years
    @Andrea: you're dependent on the target server whether it properly supports HEAD. The question is about server monitoring which thus implies that the server is full under OP's control. But if you intend to "ping" completely arbitrary servers, then odds are indeed high that it won't always succeed. Almost half of Internet servers are really really badly configured. They then either don't support HEAD, or return completely wrong response codes, or even auto-block IPs, etcetera. You'd then better switch to GET and swallow the (much) higher network payload.
  • AndreaF
    AndreaF over 9 years
    @BalusC as said in the linked question the server responds correctly and supports HEAD, the problem happens when for some reason the connection is lost during the request causing the method stucks for more than 10 seconds ignoring all timeouts before throwing an exception that catched leads to return false value
  • AndreaF
    AndreaF over 9 years
    No the issue is related to Java HttpURLConnection implementation thushw.blogspot.it/2010/10/…
  • Erik Kaju
    Erik Kaju over 9 years
    Which one of covered ones is the fastest in terms of performance.
  • charliebrownie
    charliebrownie over 9 years
    @BalusC nice to see you've also included timeouts!
  • Xiv
    Xiv about 8 years
    This first method of just opening a socket is wrong. All it will do is do a DNS lookup on that hostname, and see if it exists. It will not actually send any packets over the network. You can see the source is here: grepcode.com/file/repository.grepcode.com/java/root/jdk/open‌​jdk/…
  • BalusC
    BalusC about 8 years
    @Xiv: Thanks for heads up. DNS lookup alone is indeed not sufficient to test if it's physically reachable. I removed the snippet.
  • Xiv
    Xiv about 8 years
    @BalusC Ah actually my mistake! I read the constructor wrong, but it seems like the original solution had a mistake but not the one I thought. It did in fact make a full TCP connection to the socket except that it had a timeout of 0, so if the host was unreachable the line would just hang indefinitely. Here's a gist that I've tested and it should work: gist.github.com/lingz/e25914bea8019e08a1cf
  • BalusC
    BalusC about 8 years
    @Xiv: Right. I've put back Socket snippet and improved it.
  • Jerec TheSith
    Jerec TheSith about 8 years
    HEADmight actually only indicate if the proxy is up (if there is a proxy)
  • Maytham Fahmi
    Maytham Fahmi over 7 years
    @BalusC I have one question would this practice be ok, if I have a list of 10000 urls/domains I want to check there status periodically. OR would you suggest some thing else?
  • Luis Carlos
    Luis Carlos over 5 years
    I'm not entirely agreed with the threshold of used ports numbers. For example http code 401,403 doesn't mean that the resource doesn't exist but that you don't have privileges to access to the Url. I think that is a little difficult to evaluate what code will be take as OK or not, depending on the server you connect to. Hope this help
  • gumuruh
    gumuruh about 2 years
    what if the target is actually a local IP Address ?
  • gumuruh
    gumuruh about 2 years
    is this work for local network ip-address target checking mechanism ?