We are using the internal HttpServer
class in a project to exchange data between a client and a server over HTTP. As we switched to Java 7, we realized a delay in the delivery of the results. We could reduce the problem to the following sample:
Class EchoServer
creates the context /echo
which simply returns the current date and the request URI upon each request. This service is then invoked by a client in a loop.
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.Date;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class EchoServer {
public static void main(String[] args) throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(80), 0);
server.createContext("/echo", new EchoHandler());
server.start();
}
static class EchoHandler implements HttpHandler {
public void handle(HttpExchange httpExchange) throws IOException {
httpExchange.getResponseHeaders().add("Content-type", "text/html");
String response = "<b>" + new Date() + "</b> for " + httpExchange.getRequestURI();
httpExchange.sendResponseHeaders(200, response.length());
OutputStream os = httpExchange.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
}
The following client invokes the service in an infinite loop using class URL
and prints the first character from the returned stream (which will be the <
sign). In addition, the client prints the current time.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
public class EchoClient {
public static void main(String[] args) throws Exception{
while(true) {
URL url = new URL("http://localhost:80/echo");
BufferedReader rd = new BufferedReader(new InputStreamReader(url.openStream()));
int res = rd.read();
System.out.println((char)res);
System.out.println(System.currentTimeMillis());
}
}
}
If this code is executed on Java6, everything works fine and a result is printed approx. every 5 ms.
% java -version
java version "1.6.0_24"
Java(TM) SE Runtime Environment (build 1.6.0_24-b07)
Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02, mixed mode)
% java EchoClient
<
1362515635677
<
1362515635682
<
1362515635687
<
1362515635691
If the code is executed on Java7, then each request uses 1000ms approx.
% java -version
java version "1.7.0_17"
Java(TM) SE Runtime Environment (build 1.7.0_17-b02)
Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)
% java EchoClient
<
1362517297845
<
1362517298844
<
1362517299845
<
1362517300845
It seems that a timeout of 1000ms is hidden somewhere. If the character is read on the InputStreamReader
instead over the BufferedReader
, the same delay happens.
If a byte is read from the input stream directly, then no delay can be seen. On the other hand, if the EchoClient
program is
run against a servlet, then everything works fine, independent of whether the BufferedReader
or the InputStreamReader
is used.
It seems, that class InputStreamReader
is expecting something from the server which is no longer delivered by the Java 7 implementation
of HttpServer. Do you have an idea what exactly happens here and how this problem could be resolved? A workaround? Or is this a bug?
Thanks!
I have added further timings to the client code:
public static void main(String[] args) throws Exception{
while(true) {
System.out.println("0: "+System.currentTimeMillis());
URL url = new URL("http://localhost:80/echo");
System.out.println("1: "+System.currentTimeMillis());
InputStream in = url.openStream();
System.out.println("2: "+System.currentTimeMillis());
InputStreamReader isr = new InputStreamReader(in);
System.out.println("3: "+System.currentTimeMillis());
char res = (char)isr.read(); // character read is `<`
System.out.println(res + ": "+System.currentTimeMillis());
}
}
with the following result:
% java EchoClient
0: 1362532555535
1: 1362532555537
2: 1362532555608
3: 1362532555609
<: 1362532555611
0: 1362532555612
1: 1362532555613
2: 1362532556608
3: 1362532556609
<: 1362532556610
0: 1362532556611
1: 1362532556612
2: 1362532557609
3: 1362532557610
<: 1362532557611
0: 1362532557612
1: 1362532557613
The first invocation of openStream
takes some time (70ms), but all further invocations of openStream
take much longer (996ms approx).
url.openStream()
which takes 1000ms, but only at its second (and later) invocation in the loop, and only if later anInputStreamReader
is generated and used to read a byte. Thus, theBufferedReader
seems to be innocent. – WhistlinggetInputStream
on classsun.net.www.protocol.http.HttpURLConnection
. – WhistlingSocketInputStream.socketRead0
which is native. This method is invoked fromjava.net.SocketInputStream.read(byte b[], int off, int length)
withtimeout
= 0. – WhistlingHttpServer
. Have you tried cross-calling from Java 6 to Java 7 and Java 7 to Java 6? You should run your code in a profiler and try to identify where it's getting stuck. It may help to actually add a sleep into the server portion so that you can actually see where the code is going. – Pentathlon