Interview Questions

Example : An HTTP Server

Java Network Programming - Sockets for Servers


(Continued from previous question...)

Example : An HTTP Server

HTTP is a large protocol. A full-featured HTTP server must respond to requests for files, convert URLs into filenames on the local system, respond to POST and GET requests, handle requests for files that don't exist, interpret MIME types, launch CGI programs, and much, much more. However, many HTTP servers don't need all of these features. For example, many sites simply display an "under construction" message. Clearly, Apache is overkill for a site like this. Such a site is a candidate for a custom server that does only one thing. Java's network class library makes writing simple servers like this almost trivial.

Custom servers aren't useful only for small sites. High-traffic sites like Yahoo! are also candidates for custom servers because a server that does only one thing can often be much faster than a general purpose server such as Apache or Netscape. It is easy to optimize a special purpose server for a particular task; the result is often much more efficient than a general purpose server that needs to respond to many different kinds of requests. For instance, icons and images that are used repeatedly across many pages or on high-traffic pages might be better handled by a server that read all the image files into memory on startup, and then served them straight out of RAM rather than having to read them off disk for each request. Furthermore, this server could avoid wasting time on logging if you didn't want to track the image request separately from the requests for the pages they were included in.

Finally, Java isn't a bad language for feature-full web servers meant to compete with the likes of Apache or AOLServer. Although CPU-intensive Java programs are demonstrably slower than CPU-intensive C and C++ programs, even when run under a JIT, most HTTP servers are limited by bandwidth, not by CPU speed. Consequently, Java's other advantages, such as its half-compiled/half-interpreted nature, dynamic class loading, garbage collection, and memory protection, really get a chance to shine. In particular, sites that make heavy use of dynamic content through CGI scripts, PHP pages, or other mechanisms can often run much faster when reimplemented on top of a pure or mostly pure Java web server. Indeed, there are several production web servers written in Java such as the W3C's testbed server Jigsaw (http://www.w3.org/Jigsaw/). Many other web servers written in C now include substantial Java components to support the Java Servlet API and Java Server Pages. On many sites, these are replacing the traditional CGIs, ASPs, and server-side includes, mostly because the Java equivalents are faster and less resource-intensive.

Investigation of HTTP servers begins with a server that always sends out the same file, no matter who or what the request. This is shown in Example, SingleFileHTTPServer. The filename, local port, and content encoding are read from the command line. If the port is omitted, port 80 is assumed. If the encoding is omitted, ASCII is assumed.

Example : An HTTP Server
That Chunks Out the Same File

    import java.net.*;
    import java.io.*;
    import java.util.*;
     
public class SingleFileHTTPServer 
extends Thread {
     
      private byte[] content;
      private byte[] header;
      private int port = 80;
     
public SingleFileHTTPServer
String data, String encoding, 
String MIMEType, int port) 
throws UnsupportedEncodingException {    
this(data.getBytes(encoding), 
encoding, MIMEType, port);
      }
     
public SingleFileHTTPServer(byte[]
data, String encoding, 
String MIMEType, int port) 
throws UnsupportedEncodingException {
        
        this.content = data;
        this.port = port;
        String header = "HTTP 1.0 200 OK\r\n"
         + "Server: OneFile 1.0\r\n"
+ "Content-length: " + this.content.length + 
"\r\n" + "Content-type: " + MIMEType + 
\r\n\r\n"; 
    this.header = header.getBytes("ASCII");
     
      }
     
      
      public void run(  ) {
      
        try {
ServerSocket server = new ServerSocket(this.port); 
System.out.println("Accepting connections on port " 
            + server.getLocalPort(  ));
        System.out.println("Data to be sent:");
          System.out.write(this.content);
          while (true) {
            
            Socket connection = null;
            try {
              connection = server.accept(  );
  OutputStream out = new BufferedOutputStream(
          connection.getOutputStream(  )
                                     );
InputStream in   = new BufferedInputStream(
              connection.getInputStream(  )
                                     );
// read the first line only; that's all we need
   StringBuffer request = new StringBuffer(80);
              while (true) {
                int c = in.read(  );
 if (c == '\r' || c == '\n' || c == -1) break;
                request.append((char) c);
// If this is HTTP 1.0 or later send a MIME header
     
              }
if (request.toString(  ).indexOf("HTTP/") != -1)
 {
                out.write(this.header);
              }         
              out.write(this.content);
              out.flush(  );
            }  // end try
            catch (IOException e) {   
            }
            finally {
if (connection != null) connection.close(  ); 
            }
            
          } // end while
        } // end try
        catch (IOException e) {
System.err.println
"Could not start server. Port Occupied");
        }
     
      } // end run
     
     
 public static void main(String[] args) {
     
        try {
            
          String contentType = "text/plain";
 if (args[0].endsWith(".html") || 
 args[0].endsWith(".htm")) {
            contentType = "text/html";
          }
          
InputStream in = new FileInputStream(args[0]);
ByteArrayOutputStream out =
 new ByteArrayOutputStream(  );
          int b;
while ((b = in.read(  )) != -1) out.write(b);
          byte[] data = out.toByteArray(  );
            
          // set the port to listen on
          int port;
          try {
            port = Integer.parseInt(args[1]);
if (port < 1 || port > 65535) port = 80;
          }  
          catch (Exception e) {
            port = 80;
          }  
          
          String encoding = "ASCII";
if (args.length >= 2) encoding = args[2]; 
           
Thread t = new SingleFileHTTPServer
(data, encoding,
           contentType, port);
          t.start(  );         
     
        }
catch (ArrayIndexOutOfBoundsException e) {
          System.out.println(
"Usage: java SingleFileHTTPServer 
filename port encoding");
        }
        catch (Exception e) {
          System.err.println(e);
        }
      
      }
     
    }

The constructors set up the data to be sent along with an HTTP header that includes information about content length and content encoding. The header and the body of the response are stored in byte arrays in the desired encoding so that they can be blasted to clients very quickly.

The SingleFileHTTPServer class itself is a subclass of Thread. Its run( ) method processes incoming connections. Chances are this server will serve only small files and will support only low-volume web sites. Since all the server needs to do for each connection is check whether the client supports HTTP 1.0 and spew one or two relatively small byte arrays over the connection, chances are this will be sufficient. On the other hand, if you find clients are getting refused, you could use multiple threads instead. A lot depends on the size of the file being served, the peak number of connections expected per minute, and the thread model of Java on the host machine. Using multiple threads would be a clear win for a server that was even slightly more sophisticated than this one.

The run( ) method creates a ServerSocket on the specified port. Then it enters an infinite loop that continually accepts connections and processes them. When a socket is accepted, an InputStream reads the request from the client. It looks at the first line to see whether it contains the string HTTP. If it sees this, the server assumes that the client understands HTTP 1.0 or later and therefore sends a MIME header for the file; then it sends the data. If the client request doesn't contain the string HTTP, the server omits the header, sending the data by itself. Finally, the server closes the connection and tries to accept the next connection.

The main( ) method just reads parameters from the command line. The name of the file to be served is read from the first command-line argument. If no file is specified or the file cannot be opened, an error message is printed and the program exits. Assuming the file can be read, its contents are read into the byte array data. A reasonable guess is made about the content type of the file, and that guess is stored in the contentType variable. Next, the port number is read from the second command-line argument. If no port is specified, or if the second argument is not an integer from 0 to 65,535, then port 80 is used. The encoding is read from the third command-line argument if present. Otherwise, ASCII is assumed. (Surprisingly, some VMs don't support ASCII, so you might want to pick 8859-1 instead.) Then these values are used to construct a SingleFileHTTPServer object and start it running. This is only one possible interface. You could easily use this class as part of some other program. If you added a setter method to change the content, you could easily use it to provide simple status information about a running server or system.

Here's what you see when you connect to this server via Telnet; the specifics depend on the exact server and file:

 % telnet macfaq.dialup.cloud9.net 80
    Trying 168.100.203.234...
 Connected to macfaq.dialup.cloud9.net.
    Escape character is '^]'.
    GET / HTTP 1.0
    HTTP 1.0 200 OK
    Server: OneFile 1.0
    Content-length: 959
    Content-type: text/html
     
<!DOCTYPE HTML PUBLIC 
"-//W3C//DTD HTML 3.2//EN">
    <HTML>
    <HEAD>
 <TITLE>Under Construction</TITLE>
    </HEAD>
     
   <BODY>
    ...

(Continued on next question...)

Other Interview Questions