0

I am trying to implement sample HTTP server using Java socket and executor service for concurrency. However every 2nd request is failing when I run the test using JMeter with 2 or more requests or browser for example.

How to properly handle the request? Here is the sample source code:

public class Service {
    public static void main(String[] args) throws Exception {
        var serverSocket = new ServerSocket(8080);
        var executors = Executors.newFixedThreadPool(4);
        while(true) {
            try {
                var server = serverSocket.accept();

                executors.submit(() -> {
                    try {
                        var text = "sample";
                        System.out.println("Waiting for client on port " +
                                serverSocket.getLocalPort() + "...");

                        System.out.println("Getting empty request");
                        var response = "HTTP/1.1 200 OK\r\n" +
                                    "Content-Type: text/plain\r\n" +
                                    "Content-Length: " + text.length() + "\r\n\r\n"
                                    + text;
                        server.getOutputStream().write(response.getBytes(StandardCharsets.UTF_8));
                    } catch (Exception e) {
                        System.out.println("Executor error:" + e.toString());
                        e.printStackTrace();
                    } finally {
                        try {
                            System.out.println("Closing server");
                            server.close();
                        } catch (Exception e) {
                            System.out.println("Executor error2: ");
                            e.printStackTrace();
                        }
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
        }

        serverSocket.close();
    }
}
2
  • This works fine for me. Why do you say it fails? Commented Nov 21, 2022 at 10:49
  • Something you can do to make this better is to include a JMeter test plan. Eg. A jmeter xyz localhost:8080 that demostrates how the request fails. I wouldn't be surprised if JMeter will tell you when/why the request fails. Also, is it possible this is an OS issue? Maybe include the OS and JDK you're using. It works fine for me using Linux + JDK17. I used wget and made about 20 requests. They all returned with the text sample Commented Nov 21, 2022 at 11:09

1 Answer 1

1

Your first problem lies in your response.

"HTTP/1.1 200 OK\r\n"

That allows for keep-alive, which you're not handling. A basic JMeter sampler tries to use keep alive, that is why you always fail on the second attempt.

You can change it to

"HTTP/1.0 200 OK\r\n"

That does not support keep alive, so you'll get a lot more successes with your current code. For me I only get a couple 1000 responses before JMeter has another error, but I don't know what the error is.

To support keep alive, I need to parse the request. Here is an example.

    int clients = 0;
    while(true) {
        try {
            System.out.println("Waiting for client on port " +
                            serverSocket.getLocalPort() + "...");
            var server = serverSocket.accept();
            final int client_no = clients++;
            System.out.println("handling: " + client_no);
            executors.submit(() -> {
                int sent = 0;
                try {
                    var is = server.getInputStream();
                    var os = server.getOutputStream();
                    
                    var text = "sample";
                    byte[] tb = text.getBytes(StandardCharsets.UTF_8);

                    char[] buffer = new char[256];
                    int cr_count = 0;
                    while( true ){
                        
                        int i=0;
                        int r = is.read();
                        if(r == -1) break;
                        
                        while( r != -1 ){
                            char c = (char)r;
                            if( c == '\n' ){
                                cr_count++;
                            } else if( c != '\r' ){
                                cr_count = 0;
                            }
                            buffer[i++] = c;
                            if(cr_count == 2) break;
                            r = is.read();
                        }
                        //System.out.println("request: " + new String(buffer));
                        var response = "HTTP/1.1 200 OK\r\n" +
                                    "Content-Type: text/plain\r\n" +
                                    "Content-Length: " + tb.length + "\r\n\r\n";
                        os.write(response.getBytes(StandardCharsets.UTF_8));
                        os.write(tb);
                        os.flush();
                        sent++;
                    }
                } catch (Exception e) {
                    System.out.println("Executor error:" + e.toString());
                    e.printStackTrace();
                } finally {
                    try {
                        System.out.println("Closing connection!");
                        server.close();
                    } catch (Exception e) {
                        System.out.println("Executor error2: ");
                        e.printStackTrace();
                    }
                    System.out.println("sent " + sent + " responses to client " + client_no);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            break;
        }
    }

This will run a JMeter test for me. It can use either 1.0 or 1.1 and finish 10's of thousands of requests. If I use keep alive (1.1) each client handles many requests, if I don't use keep alive (1.0) each client handles 1 request.

If I dont read the request header, then the http 1.0 version will stop after a couple thousand requests.

It is also a separate issue from your original "dies after the second request." which is because your are not actually using HTTP 1.1 !

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for you answer. Could you elaborate what is cr_count and why are you stopping reading at 2?
@Sabyrzhan carriage return count, after 2 consecutive \r\n the request is complete. I couldnt find a spec for that though.
Got it. But i am curious: is it a requirement that we have to read headers? Is it required to comply to keep-alive?
If you support http 1.1, yes. That is why changing to 1.0 also works. You have to at least read the request to know the response.
FYI I also found this link stackoverflow.com/a/21635476/544561 which also has more details explanations and more clear code. Anyway i am upvoting your answer since it solved my issue. Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.