Link to home
Start Free TrialLog in
Avatar of jmcdonald69124
jmcdonald69124Flag for United States of America

asked on

HTTP1.1 keep-alive support for simple server

Hello,

I would like to know if anyone can give me an example of how to extend this simple server to allow support for HTTP keep- alive (1.1 only)

import java.io.*;
import java.net.*;
import java.util.*;

public final class WebServer {
    public static void main(String args[]) throws Exception {

        //Establish the listen socket
        int PORT = 777;    
        ServerSocket listenSocket = new ServerSocket(PORT);
       

        //Process HTTP service requests in an infinite loop
        while(true) {
            //listen for TCP connection request
            //Construct an object to process the HTTP request message
            HttpRequest request = new HttpRequest(listenSocket.accept());
            Thread thread = new Thread(request);
            thread.start();
        }
    }
}
final class HttpRequest implements Runnable {

    final static String CRLF ="\r\n";
    Socket socket;

    public HttpRequest(Socket socket) throws Exception {
        this.socket = socket;
    }


    //Implement the run() method of the Runnable interface
    public void run() {
        try {
            processRequest();
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    private void processRequest() throws Exception {
        //Get references to sockets input and output streams
        InputStream is = this.socket.getInputStream();
        DataOutputStream os = new DataOutputStream(this.socket.getOutputStream());
       
        //Set up input stream filter
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
       
        //Get the request line of HTTP message
        String requestLine = br.readLine();
        //Make the server directory C:\Webserver
        String root_dir;
        root_dir = "C:\\Webserver\\";
// Extract the filename from the request line. Assume a GET command
        StringTokenizer tokens = new StringTokenizer(requestLine);
        tokens.nextToken();
        String fileName = tokens.nextToken();
        // Drop the slash at the begginning.
        if(fileName.startsWith("/"))
            fileName = root_dir + fileName.substring(1,fileName.length());

        // Open the requested file.  
        FileInputStream fis = null;
        boolean fileExists = true;
        try {
            fis = new FileInputStream(fileName);
        } catch (FileNotFoundException e) {
            fileExists = false;
        }
   
        // Construct the response message.
        String statusLine = null;
        String contentTypeLine = null;
        String entityBody = null;
           
        if (fileExists) {
            statusLine = "HTTP/1.0 200 OK" + CRLF;
            contentTypeLine = "Content-type: " + contentType(fileName) + CRLF;
        } else {
            statusLine = "HTTP/1.0 404 Not Found" + CRLF;
            contentTypeLine = "NONE";
            entityBody = "\n\n Sorry The File You Requested Is Not There!!";
        }
       
        // Send the status line.
        os.writeBytes(statusLine);
       
        // Send the content type line.
        os.writeBytes(contentTypeLine);
       
        // Send a blank line to indicate the end of the header lines.
        os.writeBytes(CRLF);
       
        // Send the entity body.
        if (fileExists) {
            sendBytes(fis, os);
            fis.close();
        } else {
            os.writeBytes(entityBody);
        }
       
        //Close the streams
        os.close();
        br.close();
        socket.close();
    }

    private String contentType(String fileName) {
        if(fileName.endsWith(".htm") || fileName.endsWith(".html"))
            return "text/html";
        else if(fileName.endsWith(".jpg") || fileName.endsWith(".jpeg"))
            return "image/jpeg";
        else if(fileName.endsWith(".gif"))
            return "image/gif";
        else if(fileName.endsWith(".txt"))
            return "text/plain";
        else
            return "application/octet-stream";
    }
         
    private static void sendBytes(FileInputStream fis, OutputStream os) throws Exception {
        // Construct a 1K buffer to hold bytes on their way to the socket.
        byte[] buffer = new byte[1024];
        int bytes = 0;
       
        // Copy requested file into the socket's output stream.
        while((bytes = fis.read(buffer)) != -1 )
            os.write(buffer, 0, bytes);
    }
}


I have found several sites on the subject but have not been able to find an in depth example.

Thanks

Avatar of helloexpert
helloexpert

In HTTP 1.1, by default keep alive must be enabled, unless there is an HTTP header stating that
Connection: close

You must parse the http request headers to check for such a message from the client.

To keep alive, i.e, to not close the connection, you will have to modify the processRequest() method in your program.

Here, at the end of the method, you are closing the input streams, which essentially closes the socket connection. You must move the contents of this method into an infinite loop. Then try to read the stream using a non-blocking technique...i.e, check the availability of bytes to read. Otherwise sleep for a small time (say 100ms) and check again.  Have a time-out of say 60 seconds.  i.e, if there has been no activity for some 600 loops, then close the connection.

if the HTTP header requesting not to keep alive is set (Connection: close), then do not go into the loop. Break out after the first iteration.

That is a primitive way of doing things. But given the primitive web server, it should be good enough :-)
Avatar of Mick Barry
ignore my comment :)
perhaps have a look at jigsawand see how they handle it:
http://www.w3.org/Jigsaw/
You would be better off doing this with NIO:

http://javaalmanac.com/egs/java.nio/NbServer.html
SOLUTION
Avatar of Tommy Braas
Tommy Braas
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
The changes models the connection to the client as something separate from the request itself. You will need to make changes in the HttpRequest class, or the HttpConnection class to cater for whether keepAlive should be on. Currently the default setting is keepAlive = true.

\t
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial