Reputation: 1546
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
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