Simple HTTP Server

 < Day Day Up > 



Now, let’s look at a simple HTTP server written in Java. The entire source listing for the Java simple HTTP server is provided in Listing 17.7. Let’s now walk through this listing to understand the sample implementation.

Listing 17.7 Java Simple HTTP server source.

start example
  1   import java.net.*;   2   import java.io.*;   3   import java.util.*;   4   5   public class shttp extends Thread {   6   7     Socket theConnection;   8     static File webRoot;   9     static String indexFile = "index.html";  10  11     public shttp(Socket s) {  12       theConnection = s;  13     }  14  15  16     //  17     //  Determine the content type based upon the   18     //  filename.  19     //  20     public String define_content_type(String name) {  21  22       if (name.endsWith(".html") ||   23           name.endsWith(".htm"))   24         return "text/html";  25       else if (name.endsWith(".txt") ||   26                name.endsWith(".java"))   27         return "text/plain";  28       else if (name.endsWith(".jpg") ||   29                name.endsWith(".jpeg"))   30         return "image/jpeg";  31       else   32         return "text/plain";  33  34     }  35  36  37     //  38     //  Emit the standard HTTP response message header  39     //  40     public void emit_response_header(   41                   PrintStream os, String ct ) {  42  43       os.print("Server: Java shttp\r\n");  44       os.print("Connection: close\r\n");  45       os.print("Content-type: " );  46       os.print( ct );  47       os.print("\r\n\r\n");  48  49     }  50  51  52     //  53     //  HTTP Connection Handler (for GET)  54     //  55     public void handle_connection(   56                   PrintStream os, String file,   57                   String ct ) {  58  59       File theFile;  60  61       try {  62  63         theFile = new File(webRoot,   64                             file.substring(1, file.length()));  65  66         System.out.println("Looking for file " + theFile);  67  68         // Read the data into a byte array  69         FileInputStream fis = new FileInputStream(theFile);  70         byte[] theData = new byte[(int)theFile.length()];  71         fis.read(theData);  72         fis.close();  73  74         os.print("HTTP/1.0 200 OK\r\n");  75         emit_response_header( os, ct );  76  77         os.write(theData);  78         os.close();  79  80       } catch (IOException e) {  81  82           os.print("HTTP/1.0 404 File Not Found\r\n\r\n");  83  84       }  85  86     }  87  88  89     //  90     //  Simple HTTP Server thread  91     //  92     public void run() {  93  94       String ct;  95  96       System.out.println("\n---------------------");  97       System.out.println("Request from " +   98          theConnection.getInetAddress().getHostAddress() +   99          " " + 100          theConnection.getInetAddress().getHostName()); 101 102       try { 103 104         PrintStream os =  105            new PrintStream(theConnection.getOutputStream()); 106         DataInputStream is =  107            new DataInputStream(theConnection.getInputStream()); 108         String get = is.readLine(); 109         String version = ""; 110 111         StringTokenizer st = new StringTokenizer(get); 112         String method = st.nextToken(); 113         String file = st.nextToken(); 114 115         if (method.equals("GET")) { 116 117           if (file.endsWith("/")) file += indexFile; 118           ct = define_content_type(file); 119           if (st.hasMoreTokens()) { 120             version = st.nextToken(); 121           } 122 123           while ((get = is.readLine()) != null) { 124             if (get.trim().equals("")) break; 125           } 126 127           handle_connection( os, file, ct ); 128 129           os.close(); 130 131         } else { // Method is not GET 132 133           os.print("HTTP/1.0 501 Not Implemented\r\n\r\n"); 134           os.close(); 135 136         } 137 138       } catch (IOException e) { 139       } 140 141       try { 142         theConnection.close(); 143       } catch (IOException e) { 144       } 145 146     } 147 148 149     // 150     //  Initialization method for the simple HTTP server 151     // 152     public static void main(String[] args) { 153       int thePort; 154       ServerSocket server; 155 156       webRoot = new File("."); 157       thePort = 80; 158 159       try { 160 161         // Create the HTTP server socket 162         server = new ServerSocket(thePort); 163 164         System.out.println( 165               "Accepting connections on port " + 166                            server.getLocalPort()); 167         System.out.println("Doc Root: " + webRoot); 168 169         // Start the server accept loop 170         while (true) { 171 172           // Upon accept of new socket, start a new  173           // HTTP thread. 174           shttp t = new shttp(server.accept()); 175           t.start(); 176 177         } 178       } catch (IOException e) { 179         System.err.println("Server aborted prematurely"); 180       } 181 182     } 183 184   }
end example

This implementation of the simple HTTP server will be based upon a simple class that provides the HTTP server functionality using Java threads. The only visible method is the Java application’s main method, which starts the HTTP server.

The Java HTTP server is made up of six methods: the main method (lines 152–182), run (lines 92–146), handle_connection (lines 55–86), emit_response_header (lines 40–49), define_content_type (lines 20–34), and the shttp constructor method (lines 11–13). We’ll look at the methods in their order of use.

The class, shttp, is defined at line 5 and extends the Thread class (using threads to provide multiple client capabilities). A few local variable objects are created at lines 7–9; the client socket connection (theConnection), a String identifying the local file system path (webRoot), and finally the default file that will be loaded if one isn’t specified in the HTTP GET request (indexFile).

The first method invoked in the simple HTTP server class is main method (lines 152–182). The purpose of the main method is to set up the HTTP server socket and prepare for incoming connections. At line 156, the root of the HTTP server’s file system is defined as the current path, and stored as a File object into webRoot (we’ll see this used in the handle_connection method later). We also define the default port to use for the server as port 80 (thePort) at line 157. The serverSocket is created at line 162 using the ServerSocket constructor (passing the desired port to bind to as the single argument). A simple debug message is then emitted to standard-out at lines 164–167.

The HTTP server loop begins at line 170. At line 174, the accept method is invoked for the server socket and upon completion of the call (an incoming client connection arrives), the new client socket is passed to the shttp constructor method of the shttp class. Note that a new thread is created here because the shttp class extends the Thread class. The shttp constructor is shown at lines 11–13, with the only action being performed that the passed socket is stored into local variable, theConnection. The run method is then invoked for the newly created thread (lines 92–146).

The run method is where the HTTP serving functionality begins. Within run, we parse the incoming HTTP GET request and parse it to its basic elements. Some debugging information is emitted identifying the client that has connected (lines 96–100) using the getHostAddress and getHostName methods.

At lines 104–107, we create our input and output streams for reading and writing to the client socket. For the output stream, we create a PrintStream over the OutputStream of the socket, and for the input stream, we create a DataInputStream over the InputStream of the socket. The HTTP GET request is then read from the socket using the readLine method and the resulting String stored into get. We then create a StringTokenizer object over the HTTP GET request (stored in get) at line 111, and then parse out the HTTP request method (storing it in method) at line 112 and the requested file at line 113 (storing it in file). The tokenizer simply picks apart the String using space characters as the delimiter, returning the next available token using the nextToken method.

At line 115, we test the method String to see if the request was a GET request, and if so we perform the necessary elements for satisfying this HTTP request. We first test the filename to see if it ends with ‘/’ (line 117). In this case, the default file is requested, so we append the index filename to the end of this string (index.html). We then call the define_content_type method to determine the type of file that’s being requested (and should be returned). We’ll discuss this method shortly. We check the tokenizer object again to see if any other tokens are available (hasMoreTokens), and if so, we grab the version from the tokenizer (because it should follow next on the HTTP GET request). We then read the remainder of the HTTP GET request, which we’ll ignore (lines 123–125). Now that we have the file that’s being requested, we call handle_connection to handle the specific GET request (line 127) and then close the client socket with the close method (line 129).

Note that if a request other than GET was made to the HTTP server (such as HEAD or POST), we return an error message to the connected client (line 133). This is done using the print method of the PrintStream object. The client socket is then closed with the close method (line 134).

The define_content_type method (lines 20–34) is used to identify the type of content being returned based upon the file extension being requested. For example, if the file being requested is an HTML file (identified by the .html file extension), then the content type of the HTTP response is “text/html”. This is necessary for the HTTP client to know how to properly render the content being returned. This method is passed to the String filename and returns a new String containing the content type. The endswith method of the String class is used to identify the type of file. We test three unique cases; .html and .htm result in “text/html” (lines 22–24), .txt and .java result in “text/plain” (lines 25–27), and .jpg and .jpeg result in “image/jpeg” (lines 28–30). If the content type can’t be determined, then the “text/plain” content type is returned by default (line 32).

The handle_connection method (lines 55–86) handles a GET request from a client socket (called by the run method). The handle_connection method is called with the PrintStream object (so that handle_connection can send the requested file to the client through the socket), the filename being requested, and the content type. The first task is to open the file that the client is requesting. This is a good example of the exception handling capabilities of Java. At lines 63–64, a new File object is created, specifying the file path (the current subdirectory in which the Java application was started), and then the filename (ignoring the first character). This new File object creation also attempts to open the file. If the file is not present, an exception is raised and is caught at lines 80–84 (the catch block). In this section of code, we emit an error message back to the client—in this case, an HTTP 404 error code (indicating that the file could not be found). Otherwise, we continue on within the try block at line 69 where we create a FileInputStream of the File object and create a byte array of the size of the file (theFile.length()). We then read the file (line 71) placing the data read into the previously created byte array, and then close the file (line 72). Finally, we emit a standard HTTP response header (more on the emit_response_header method next) and then write the data array to the peer using the write method of the PrintStream object (line 77). The client socket is then closed with the close method and the process is complete (line 78).

HTTP response headers are the portion of the response message that indicates to the client what is going to follow. Server responses include first a header that includes the HTTP response code, and then a set of optional lines that provide more information to the client. In emit_response_header (lines 40–49), we emit some sample information to the client, including the HTTP server name (line 43), a status line indicating that the connection should be closed once this request is complete (line 44), and, finally, the content type (lines 45–46), which tells the HTTP client how to render the data that is contained in the HTTP response message.



 < Day Day Up > 



BSD Sockets Programming from a Multi-Language Perspective
Network Programming for Microsoft Windows , Second Edition (Microsoft Programming Series)
ISBN: 1584502681
EAN: 2147483647
Year: 2003
Pages: 225
Authors: Jim Ohlund

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net