As part of a concurrency blog series, I was building the simplest HTTP server in different languages (Java, Kotlin, Rust, Go, JS, TS) and everything works fine for everything except Java/Kotlin, aka on the JVM. All the code can be found here. The below is the server code in Java, I tried a traditional Thread based one and an AsynchronousServerSocketChannel
based one, but regardless when I run a benchmark with ApacheBench it fails with Broken pipe
and apr_socket_recv: Connection reset by peer (104)
this is weird as similar setup in other languages works fine. The problem here happens only with ApacheBench, coz when I access the URL in a browser it just works fine. SO I'm banging my head to figure out what is going on. I tried to play with keep-alive etc but doesn't seem to help. I looked at a bunch of examples of something similar and I don't see anything special being done anywhere. I'm hoping someone can figure out what is going wrong here as it definitely seems to be something to do with JVM + APacheBench. I have tried this with Java 11 and 15 but it's the same result.
Java Thread Sample (hello.html
can be any HTML file)
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class JavaHTTPServerCopy {
public static void main(String[] args) {
int port = 8080;
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("Server is listening on port " + port);
while (true) {
new ServerThreadCopy(serverSocket.accept()).start();
}
} catch (IOException ex) {
System.out.println("Server exception: " + ex.getMessage());
}
}
}
class ServerThreadCopy extends Thread {
private final Socket socket;
public ServerThreadCopy(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
var file = new File("hello.html");
try (
// we get character output stream to client (for headers)
var out = new PrintWriter(socket.getOutputStream());
// get binary output stream to client (for requested data)
var dataOut = new BufferedOutputStream(socket.getOutputStream());
var fileIn = new FileInputStream(file)
) {
var fileLength = (int) file.length();
var fileData = new byte[fileLength];
int read = fileIn.read(fileData);
System.out.println("Responding with Content-length: " + read);
var contentMimeType = "text/html";
// send HTTP Headers
out.println("HTTP/1.1 200 OK");
out.println("Connection: keep-alive");
out.println("Content-type: " + contentMimeType);
out.println("Content-length: " + fileLength);
out.println(); // blank line between headers and content, very important !
out.flush(); // flush character output stream buffer
dataOut.write(fileData, 0, fileLength);
dataOut.flush();
} catch (Exception ex) {
System.err.println("Error with exception : " + ex);
} finally {
try {
socket.close(); // we close socket connection
} catch (Exception e) {
System.err.println("Error closing stream : " + e.getMessage());
}
}
}
}
Error on console
Responding with Content-length: 176
Error with exception : java.net.SocketException: Broken pipe (Write failed)
Error with exception : java.net.SocketException: Broken pipe (Write failed)
Error with exception : java.net.SocketException: Broken pipe (Write failed)
Error with exception : java.net.SocketException: Broken pipe (Write failed)
Error with exception : java.net.SocketException: Broken pipe (Write failed)
ApacheBench output
ab -c 100 -n 1000 http://localhost:8080/
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
apr_socket_recv: Connection reset by peer (104)
Java Async Sample
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class JavaAsyncHTTPServer {
public static void main(String[] args) throws Exception {
new JavaAsyncHTTPServer().go();
Thread.currentThread().join();//Wait forever
}
private void go() throws IOException {
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open();
InetSocketAddress hostAddress = new InetSocketAddress("localhost", 8080);
server.bind(hostAddress);
server.setOption(StandardSocketOptions.SO_REUSEADDR, true);
System.out.println("Server channel bound to port: " + hostAddress.getPort());
if (server.isOpen()) {
server.accept(null, new CompletionHandler<>() {
@Override
public void completed(final AsynchronousSocketChannel result, final Object attachment) {
if (server.isOpen()) {
server.accept(null, this);
}
handleAcceptConnection(result);
}
@Override
public void failed(final Throwable exc, final Object attachment) {
if (server.isOpen()) {
server.accept(null, this);
System.out.println("Connection handler error: " + exc);
}
}
});
}
}
private void handleAcceptConnection(final AsynchronousSocketChannel ch) {
var content = "Hello Java!";
var message = ("HTTP/1.0 200 OK
" +
"Connection: keep-alive
" +
"Content-length: " + content.length() + "
" +
"Content-Type: text/html; charset=utf-8
" +
content).getBytes();
var buffer = ByteBuffer.wrap(message);
ch.write(buffer);
try {
ch.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
No error on console
ApacheBench output
? ab -c 100 -n 1000 http://localhost:8080/
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
apr_socket_recv: Connection reset by peer (104)
ApacheBench output with keep-alive
ab -k -c 100 -n 1000 http://localhost:8080/
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
Send request failed!
apr_socket_recv: Connection reset by peer (104)
Total of 37 requests completed
question from:
https://stackoverflow.com/questions/65849156/a-simple-java-http-server-fails-with-apachebench-but-works-fine-on-a-browser