dimi
dimi

Reputation: 1546

Effective measurement of dns lookup and site content download duration

I am implementing a Java method that measures a number of metrics while loading a webpage. The metrics include : resolve time, the connect time and download time.

The challenge seems to be the name resolution, since the code should never trigger two NS look-ups by any means (even when DNS caching is disabled).

My first thought was to trigger the name resolution before connecting to the server, and then prevent java from running a second one upon connect. Using InetAddress.getByName() for the name lookup and then HttpURLConnection and it's setRequestProperty method to set the a host header seemed to do the trick.

So here is my question: Do those two snippets below have the same effect? Do they always give the exact same result for all possible hosts? If not, what other options do I have?

VERSION 1: Implicit name resolution

/**
 * Site content download Test
 * 
 * @throws IOException
 */
public static void testMethod() throws IOException {

    String protocol = "http";
    String host = "stackoverflow.com";
    String file = "/";

    // create a URL object
    URL url = new URL(protocol, host, file);

    // create the connection object
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();

    // connect
    conn.connect();

    // create a stream reader
    BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    String inputLine;

    // read contents and print on std out
    while ((inputLine = in.readLine()) != null) {
        System.out.println(inputLine);
    }

    // close the stream
    in.close();
}

VERSION 2: Explicit name resolution

/**
 * Enhanced Site content download Test
 * 
 * @throws IOException
 */
public static void testMethod2() throws IOException {

    String protocol = "http";
    String host = "stackoverflow.com";
    String file = "/";

    // Do a name lookup.
    // If a literal IP address is supplied, only the validity of the address format is checked.
    InetAddress address = InetAddress.getByName(host);

    // create a URL object
    URL url = new URL(protocol, address.getHostAddress(), file);

    // create the connection object
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();

    // allow overriding Host and other restricted headers
    System.setProperty("sun.net.http.allowRestrictedHeaders", "true");

    // set the host header
    conn.setRequestProperty("Host", host);

    // connect
    conn.connect();

    // create a stream reader
    BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    String inputLine;

    // read contents and print on std out
    while ((inputLine = in.readLine()) != null) {
        System.out.println(inputLine);
    }

    // close the stream
    in.close();
}

TIA for the help. -Dimi

Upvotes: 1

Views: 1122

Answers (1)

Laurent Perrin
Laurent Perrin

Reputation: 14881

I've browsed through Java's source code to see what happens when you pass a domain name to HttpURLConnection and it eventually ends up in NetworkClient.doConnect:

 if (connectTimeout >= 0) {
            s.connect(new InetSocketAddress(server, port), connectTimeout);
        } else {
            if (defaultConnectTimeout > 0) {
                s.connect(new InetSocketAddress(server, port), defaultConnectTimeout);
            } else {
                s.connect(new InetSocketAddress(server, port));
            }   
        }

As you see, the domain resolution is always handled by InetSocketAddress:

public InetSocketAddress(String hostname, int port) {
    if (port < 0 || port > 0xFFFF) {
        throw new IllegalArgumentException("port out of range:" + port);
    }   
    if (hostname == null) {
        throw new IllegalArgumentException("hostname can't be null");
    }   
    try {
        addr = InetAddress.getByName(hostname);
    } catch(UnknownHostException e) {
        this.hostname = hostname;
        addr = null;
    }   
    this.port = port;
}

As you can see, InetAddress.getByName is called everytime. I think that you method is safe.

Upvotes: 1

Related Questions