Hadi Moloodi
Hadi Moloodi

Reputation: 639

Multi-Threaded Java HTTP-Server

I use sun.net.httpServer to run an HTTP-server in my application (don't ask me why).

The thing is it processes my requests in just one thread so my throughput is a disaster.

I thought if I setExecutor to my httpServer this problem would be resolved but I started to get exception both on my server-side and client-side (SOAP-UI). I test all kinds of executors including Executors.newCachedThreadPool , Executors.newFixedThreadPool, Executors.newWorkStealingPool, Executors.newScheduledThreadPool without any luck.

As I said my code works fine when I setExecutor to null but in this configuration, my code process requests sequentially.

I don't know what to do. Here is my code:

public class HTTPListener {
    private HttpServer httpServer;
    private int port = 1253;

    public void stop() {
        if (httpServer != null) {
            httpServer.stop(0);
            httpServer = null;
        }
    }
    public void startHTTPServer() {
        try {
            httpServer = HttpServer.create(new InetSocketAddress(1252), 100);
            httpServer.setExecutor(null);
            httpServer.createContext("/", new RequestHandler());
            httpServer.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

when I set Executors I get this error

java.io.IOException: stream closed
    at sun.net.httpserver.FixedLengthOutputStream.write(FixedLengthOutputStream.java:68)
    at sun.net.httpserver.PlaceholderOutputStream.write(ExchangeImpl.java:444)
    at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
    at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
    at java.io.FilterOutputStream.close(FilterOutputStream.java:158)
    at com.RequestHandler.sendResponse(RequestHandler.java:61)
    at com..RequestHandler.handle(RequestHandler.java:18)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79)
    at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:82)
    at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:675)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79)
    at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:645)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Here is my RequestHandler Class:

public class RequestHandler implements HttpHandler {
    private HttpExchange sender;

    @Override
    public void handle(HttpExchange httpExchange) throws IOException {
        this.sender = httpExchange;
        try {
            String request = readRequestBody();
            sendResponse(200, "Hello World !");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            this.sender.close();
        }
    }

    private String readRequestBody() throws Exception {
        try (InputStream isr = sender.getRequestBody()) {
            byte[] buffer = new byte[isr.available()];
            isr.read(buffer);
            return Arrays.toString(buffer);
        } catch (IOException ex) {
            throw new Exception(ex.getMessage());
        }
    }

    private void sendResponse(int httpResponseCode, String response) throws IOException {
        OutputStream outputStream = null;
        try {
            sender.getResponseHeaders().set("Content-Type", "text/html; charset=UTF-8");
            sender.sendResponseHeaders(httpResponseCode, response.length());
            outputStream = sender.getResponseBody();
            outputStream.write(response.getBytes());
        } finally {
            if (outputStream != null)
                outputStream.close();
        }

    }
}

And I also get this error codes on Client-Side

org.apache.http.ConnectionClosedException: Premature end of Content-Length delimited message body (expected: 13; received: 0
    at org.apache.http.impl.io.ContentLengthInputStream.read(ContentLengthInputStream.java:180)
    at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:137)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
    at java.io.InputStreamReader.read(InputStreamReader.java:184)
    at java.io.Reader.read(Reader.java:140)
    at org.apache.http.util.EntityUtils.toString(EntityUtils.java:247)
    at org.apache.http.util.EntityUtils.toString(EntityUtils.java:291)
    at Business.HTTPHandler.Client.send(Client.java:68)
    at main.Main.lambda$main$1(Main.java:86)
    at java.lang.Thread.run(Thread.java:745)

    org.apache.http.client.ClientProtocolException
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:186)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
    at Business.HTTPHandler.Client.send(Client.java:67)
    at main.Main.lambda$main$1(Main.java:86)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.http.ProtocolException: Invalid header: *
    at org.apache.http.impl.io.AbstractMessageParser.parseHeaders(AbstractMessageParser.java:232)
    at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:268)
    at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:165)
    at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:167)
    at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:272)
    at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:124)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:271)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
    ... 5 more

    Caused by: org.apache.http.ProtocolException: Invalid header: 13
    at org.apache.http.impl.io.AbstractMessageParser.parseHeaders(AbstractMessageParser.java:232)
    at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:268)
    at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:165)
    at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:167)
    at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:272)
    at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:124)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:271)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
    ... 5 more

Upvotes: 1

Views: 3036

Answers (1)

Kayaman
Kayaman

Reputation: 73558

Your RequestHandler class is not thread safe, that's why it works when you're running single threaded and fails when using an executor.

Do not store the HttpExchange object in the sender variable. It causes a race condition when all threads are overwriting the same variable, and may see the wrong object in the wrong state (e.g. one with streams closed already).

You can pass it as a parameter to your methods instead. You really don't need the extra variable.

Upvotes: 3

Related Questions