[ Team LiB ] Previous Section Next Section

5.8 A Simple Web Server

Example 5-8 shows a very simple web server, HttpMirror. Instead of returning a requested file, however, this server simply "mirrors" the request back to the client as its reply. This can be useful when debugging web clients and can be interesting if you are just curious about the details of HTTP client requests. To run the program, specify the port that it should listen on as an argument. For example, I can run the server like this:

oxymoron% java je3.net.HttpMirror 4444

Then, in my web browser, I can load http://localhost:4444/testing.html. The server ignores the request for the file testing.html, but it echoes back the request that my web browser sent. It might look something like this:

GET /testing.html HTTP/1.1
Host: localhost:4444
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.3) Gecko/20030312
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate,compress;q=0.9
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive

The main new feature introduced in Example 5-8 is the ServerSocket class. This class is used by a server, or any other program, that wants to sit and wait for a connection request from a client. When you create a ServerSocket, you specify the port to listen on. To connect to a client, call the accept( ) method of the ServerSocket. This method blocks until a client attempts to connect to the port that the ServerSocket is listening on. When such a connection attempt occurs, the ServerSocket establishes a connection to the client and returns a Socket object that can communicate with the client. Your code can then call the getInputStream( ) and getOutputStream( ) methods of the socket to get streams for reading bytes from the client and for writing bytes to the client.

Note that the ServerSocket is not used for communication between the server and its client; it is used only to wait for and establish the connection to the client. Typically, a single ServerSocket object is used over and over again to establish connections to any number of clients.

Example 5-8 is quite straightforward. It creates a ServerSocket and calls its accept( ) method, as outlined previously. When a client connects, it sets up the streams and then sends some HTTP headers to the client, telling it that the request has been received successfully and that the reply is text/plain data. Next, it reads all the HTTP headers of the client's request and sends them back to the client as the body of its reply. When it reads a blank line from the client, this indicates the end of the client's headers, so it closes the connection.

Note that the body of the HttpMirror program is a big infinite loop. It connects to a client, handles the request, and then loops and waits for another client connection. Although this simple server works perfectly well for the testing purposes for which it is designed, there is a flaw in it: it is a single-threaded server and can talk to only one client at a time. Later in this chapter, we'll see examples of servers that use multiple threads and can maintain connections to any number of clients.

Example 5-8. HttpMirror.java
package je3.net;
import java.io.*;
import java.net.*;

/**
 * This program is a very simple web server.  When it receives a HTTP request
 * it sends the request back as the reply.  This can be of interest when
 * you want to see just what a web client is requesting, or what data is
 * being sent when a form is submitted, for example.
 **/
public class HttpMirror {
    public static void main(String args[  ]) {
        try {
            // Get the port to listen on
            int port = Integer.parseInt(args[0]);
            // Create a ServerSocket to listen on that port.
            ServerSocket ss = new ServerSocket(port);
            // Now enter an infinite loop, waiting for & handling connections.
            for(;;) {
                // Wait for a client to connect.  The method will block;
                // when it returns the socket will be connected to the client
                Socket client = ss.accept( );

                // Get input and output streams to talk to the client 
                BufferedReader in = new BufferedReader(
                               new InputStreamReader(client.getInputStream( )));
                PrintWriter out = new PrintWriter(client.getOutputStream( ));

                // Start sending our reply, using the HTTP 1.1 protocol
                out.print("HTTP/1.1 200 \r\n");        // Version & status code
                out.print("Content-Type: text/plain\r\n"); // The type of data
                out.print("Connection: close\r\n");        // Will close stream
                out.print("\r\n");                         // End of headers

                // Now, read the HTTP request from the client, and send it
                // right back to the client as part of the body of our
                // response.  The client doesn't disconnect, so we never get
                // an EOF.  It does sends an empty line at the end of the
                // headers, though.  So when we see the empty line, we stop
                // reading.  This means we don't mirror the contents of POST
                // requests, for example.  Note that the readLine( ) method 
                // works with Unix, Windows, and Mac line terminators.
                String line;
                while((line = in.readLine( )) != null) {
                    if (line.length( ) == 0) break;
                    out.print(line + "\r\n");
                }

                // Close socket, breaking the connection to the client, and
                // closing the input and output streams
                out.close( );     // Flush and close the output stream
                in.close( );      // Close the input stream
                client.close( );  // Close the socket itself
            } // Now loop again, waiting for the next connection
        }
        // If anything goes wrong, print an error message
        catch (Exception e) {
            System.err.println(e);
            System.err.println("Usage: java HttpMirror <port>");
        }
    }
}
    [ Team LiB ] Previous Section Next Section