Question

I tried to implement a simple HTTP server with socket in Java. What it does is to accept a file name from the client browser, opens that file on the disk and prints it out in the browser. My current code is shown below:

public class HTTPServer {

    public String getFirstLine(Scanner s) {
        String line = "";
        if (s.hasNextLine()) {
            line = s.nextLine();
        }
        if (line.startsWith("GET /")) {
            return line;
        }
        return null;
    }

    public String getFilePath(String s) {
        int beginIndex = s.indexOf("/");
        int endIndex = s.indexOf(" ", beginIndex);
        return s.substring(beginIndex + 1, endIndex);
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException {
        Socket clientSocket = null;
        int serverPort = 7777; // the server port

        try {
            ServerSocket listenSocket = new ServerSocket(serverPort);

            while (true) {
                clientSocket = listenSocket.accept();
                Scanner in;
                PrintWriter out;
                HTTPServer hs;
                in = new Scanner(clientSocket.getInputStream());
                out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())));
                hs = new HTTPServer();
                // get the first line
                String httpGetLine = hs.getFirstLine(in);
                if (httpGetLine != null) {
                    // parse the file path
                    String filePath = hs.getFilePath(httpGetLine);
                    // open the file and read it
                    File f = new File(filePath);

                    if (f.exists()) {
                        // if the file exists, response to the client with its content
                        out.println("HTTP/1.1 200 OK\n\n");
                        out.flush();
                        BufferedReader br = new BufferedReader(new FileReader(filePath));
                        String fileLine;
                        while ((fileLine = br.readLine()) != null) {
                            out.println(fileLine);
                            out.flush();
                        }
                    } else {
                        out.println("HTTP/1.1 404 NotFound\n\nFile " + filePath + " not found.");
                        out.flush();
                    }
                }
            }

        } catch (IOException e) {
            System.out.println("IO Exception:" + e.getMessage());
        } finally {
            try {
                if (clientSocket != null) {
                    clientSocket.close();
                }
            } catch (IOException e) {
                // ignore exception on close
            }
        }
    }

}

After I run it in NetBeans, I open the browser and visit "localhost:7777/hello.html" (hello.html is a file in the project folder). It just shows the page is loading. Only after I stop my server in NetBeans will the content of hello.html be shown in the browser.

I want my server to work indefinitely, respond to GET requests one after another and display to the client the file content. I'm not sure which parts of my code should be put in the while(true) loop and which not.

Was it helpful?

Solution 2

You need to close the socket when you're done with it.

OTHER TIPS

Your code logic is very incomplete even for a minimalistic HTTP server. You are not following basic rules governed by RFC 2616.

You are not reading the client's HTTP request headers at all. You are only reading the first line. The headers dictate how the server needs to behave, what type of response it needs to send, how to send the response, etc.

You are not checking the client's HTTP request version. Do not send an HTTP 1.1 response to an HTTP 1.0 request, but you can send an HTTP 1.0 response to an HTTP 1.1 request.

You are not checking if the client's HTTP request has a Connection header. HTTP 1.0 connections do not use keep-alives by default, so an HTTP 1.0 client has to explicitly ask for a keep-alive by sending a Connection: keep-alive header. HTTP 1.1 connections use keep-alives by default, so an HTTP 1.1 client can explicitly disable a keep-alive by sending a Connection: close header instead. If an HTTP 1.0 request does not include a Connection: keep-alive header then the server MUST assume close was requested. If an HTTP 1.1 request does not include a Connection: close header then the server MUST assume keep-alive was requested. Either way, you should send back your own Connection header indicating whether keep-alive or close is being used by your server (if keep-alive is requested, you do not have to honor it, but you should if possible). In the case of close, you MUST close the socket after sending the response.

In your 200 response, you are not sending a Content-Length header (though the Transfer-Encoding: chunked approach would be more appropriate for the type of sending you are doing, but only if the client sent an HTTP 1.1 request. See RFC 2616 Section 3.6.1 for more details), so you MUST close the socket (and send a Connection: close header) after sending the response, otherwise the client has no way of knowing when EOF is reached. See RFC 2616 Section 4.4 for more details.

Your code only services one client and one request at a time. If you want your server is handle multiple clients at a time, especially if you want to support HTTP keep-alives (which you should), then you need to create a worker thread for each client that connects, and that thread can service has many requests as the client sends until it disconnects.

Try something more like this (might need some tweaking to compile, track threads, etc):

public class HTTPClientThread extends Thread {

    private Socket clientSocket;

    public HTTPClientThread(Socket client) {
        clientSocket = client;
    }

    public void run() {

        Scanner in = new Scanner(clientSocket.getInputStream());
        OutputStream out = clientSocket.getOutputStream();
        PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out)));
        bool keepAlive = false;

        do {
            String requestLine = s.nextLine();
            String line;
            String connection = "";
            keepAlive = false;

            do {
                line = s.nextLine();
                if (line == "") {
                    break;
                }
                if (line.startsWith("Connection:") {
                    connection = line.substring(11).trim();
                }
            }
            while (true);

            int idx = requestLine.indexOf(" ");
            if (idx == -1) {
                pw.println("HTTP/1.0 400 Bad Request");
                pw.println("Connection: close");
                pw.println("");
                pw.flush();
                continue;
            }

            String httpMethod = line.substring(0, idx);
            line = line.substring(idx+1);

            idx = line.indexOf(" ");
            if (idx == -1) {
                pw.println("HTTP/1.0 400 Bad Request");
                pw.println("Connection: close");
                pw.println("");
                pw.flush();
                continue;
            }

            String httpVersion = line.subString(endIndex+1);
            if (!httpVersion.equals("HTTP/1.0") && !httpVersion.equals("HTTP/1.1")) {
                pw.println("HTTP/1.0 505 HTTP Version Not Supported");
                pw.println("Connection: close");
                pw.println("");
                pw.flush();
                continue;
            }

            if (connection != "") {
                keepAlive = connection.equalsIgnoreCase("keep-alive");
            }
            else if (httpVersion.equals("HTTP/1.1")) {
                keepAlive = true;
            }
            else {
                keepAlive = false;
            }

            String filePath = line.substring(0, endIndex);
            if (filePath.startsWith("/")) {
                filePath = filePath.substring(1);
            }

            // open the file and read it
            File f = new File(filePath);

            if (!f.exists()) {
                pw.println(httpVersion + " 404 Not Found");
                pw.println("Content-Length: 0");
                if (keepAlive) {
                    pw.println("Connection: keep-alive");
                } else {
                    pw.println("Connection: close");
                }
                pw.println("");
                pw.flush();
                continue;
            }

            if (httpMethod != "GET") {
                pw.println(httpVersion +" 405 Method Not Allowed");
                pw.println("Allow: GET");
                if (keepAlive) {
                    pw.println("Connection: keep-alive");
                } else {
                    pw.println("Connection: close");
                }
                pw.println("");
                pw.flush();
                continue;
            }

            pw.println(httpVersion + " 200 OK");
            pw.println("Content-Type: application/octet-stream");
            pw.print("Content-Length: ");
            pw.println(f.length());
            if (keepAlive) {
                pw.println("Connection: keep-alive");
            } else {
                pw.println("Connection: close");
            }
            pw.println("");
            pw.flush();

            FileInputStream fis = new FileInputStream(f);
            BufferedOutputStream bw = new BufferedOutputStream(out);
            byte[] buffer = new byte[1024];
            int buflen;

            while ((buflen = fis.read(buffer)) > 0) {
                bw.write(buffer, 0, buflen);
                bw.flush();
            }
        }
        while (keepAlive);

        clientSocket.close();
    }
}
public class HTTPServer { /** * @param args the command line arguments */ public static void main(String[] args) throws IOException { Socket clientSocket = null; int serverPort = 7777; // the server port try { ServerSocket listenSocket = new ServerSocket(serverPort); while (true) { clientSocket = listenSocket.accept(); new HTTPClientThread(clientSocket); } } catch (IOException e) { System.out.println("IO Exception:" + e.getMessage()); } } }

With that said, Java has its own HTTP server class available.

while(true) statement will execute those two lines indefinitely. that would take all of CPU resource. there should be some event or thread sleep..

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top