Wednesday, October 1, 2008

Free/Sample HTTP Server

In this software world, everything has to be built our own or it has to be opensource code which helps us to tune the performance our application. I was in the need of testing my product which makes more than 100 HTTP request per minute. I initially thought of installing freely available any of the HTTP server. But, none of the HTTPServer comes with the flavour of light weight, everything starts minimum of 10 MB and it needs separate installation which will not be started just like that.

Building a HTTP Server:

We have to have little bit of knowledge about the HTTP protocol. This protocol is nothing but built on top of TCP with specific headers, which leads us to important details easily using some kind of getter method.

To build HTTPServer, we have to know the threading concept with socket programming knowledge. Don't worry, I won't ask you to write the HTTPServer code here, I will share with you full code which is ready to use.

No configuration is required for this server. Just add this class in your classpath and create instance of this Class by passing port number.

Here you go with HTTPServer implementation code.

HTTPServer has two inner classes - one is HTTPRequest, HTTPWorker.


import java.io.BufferedInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Properties;

public class HTTPServer implements Runnable {

 public static final String CONTENT_LENGTH = "Content-length";
 public static final String CONTENT_TYPE = "Content-type";
 private static char NEW_LINE = '\n';
 private static final String EOL = "\r\n";
 private static final String SUCCESS_HDR_MESSAGE = "HTTP/1.1 200 OK";
 private static final String SC_NO_CONTENT = 
 "HTTP/1.1 204 No Content" + EOL
   + "Content-Type: text/plain" 
   + EOL + "Cache-Control: private" + EOL
   + EOL;

 private static final String 
 INTERNAL_ERROR = "HTTP/1.1 500 Internal " +
   "Krishna's Server Error"
   + EOL
   + "Content-Type: text/html"
   + EOL
   + EOL
   + "<html><head>BLite Internal Error</head><body>";

 private static int DEFAULT_PORT = 7777;
 private static String SOAP_ACTION = "SOAPAction";

 private int port;

 private boolean isShutdown = false;

 private static HTTPServer httpServer = null;

 public static HTTPServer newInstance(int port) {

  if (null == httpServer) {
   httpServer = new HTTPServer();
   if (port > 0)
    httpServer.port = port;
  }

  return httpServer;
 }

 private HTTPServer() {
  this.port = DEFAULT_PORT;
 }

 public void run() {

  System.out.println(" STARTING HTTP SERVER ");
  System.out.println("HTTP Server Listens on port " + httpServer.port);

  try {
   // ServerSocket
   // ss=ServerSocketFactory.getDefault().createServerSocket(port,1500);
   // ss.setReceiveBufferSize(2048);
   // ss.setReuseAddress(true);
   ServerSocket ss = new ServerSocket(port, 1500);
   long startTime = System.currentTimeMillis();

   while (!isShutdown) {
    try {
     System.out
       .println("SocketTime :"
         + ((startTime = System.currentTimeMillis()) - startTime)
         + "s");
     HTTPWorker httpWorker = new HTTPWorker(ss.accept());
     Thread thread = new Thread(httpWorker);
     thread.start();
     thread = null;
    } catch (Throwable e) {
     System.out.println(e);
    }
   }

  } catch (Throwable e) {
   System.out.println(e);
  }
  // finally
  // {
  // tpe.shutdown();
  // }
 }

 public static String getPropertyIgnoreCase(Properties props, 
   String key) {
  String value = null;

  Enumeration enum0 = props.propertyNames();
  while (enum0.hasMoreElements()) {
   String name = (String) enum0.nextElement();
   if (key.equalsIgnoreCase(name)) {
    value = props.getProperty(name);
    break;
   }
  }

  return value;
 }

 
 public void shutdown() {

  this.isShutdown = true;
 }

 private class HTTPRequest {

  private byte[] request;
  private int headerIndex = 0;
  private Properties headers;
  private byte[] bodyContent;

  public HTTPRequest(byte[] request) {
   this.request = request;
   init();
  }

  private void init() {

   if (request == null)
    return;

   for (int i = 0; i < request.length; i++) {
    if ((NEW_LINE == (char) request[i])
       && (13 == request[i + 1])) {
     headerIndex = i;
     break;
    }
   }

   initHeaders();
   initBodyContent();
  }

  private void initHeaders() {
   String httpMethod;
   String httpVersion;
   String path;
   String headerContent = new String(request, 0, headerIndex);

   String[] availbleHeaders = headerContent.split("\n");

   if (availbleHeaders != null && 
   availbleHeaders.length > 0) {
    String[] typeContainer = availbleHeaders[0].split(" ");

    if (typeContainer != null && 
       typeContainer.length > 2) {
     httpMethod = typeContainer[0];
     path = typeContainer[1];
     httpVersion = typeContainer[2];
    }
   }

   if (availbleHeaders.length > 1) {
    this.headers = new Properties();

    for (int index = 1; index < availbleHeaders.length; index++) {
     String key = availbleHeaders[index].substring(0,
       availbleHeaders[index].indexOf(':'));
     String value = availbleHeaders[index].substring(
       availbleHeaders[index].indexOf(':') + 1,
       availbleHeaders[index].length() - 1);
     this.headers.put(key, value);
    }
   }
  }

  private void initBodyContent() {

   int bodyIndex = headerIndex + 3;
   bodyContent = new byte[request.length - bodyIndex];
   for (int i = (bodyIndex), j = 0; 
   i < request.length; i++, j++) {
    bodyContent[j] = request[i];
   }

  }

  public Properties getHeaders() {
   return headers;
  }

  public byte[] getBodyContent() {
   return bodyContent;
  }

 }

 private class HTTPWorker implements Runnable {

  private String ret = null;
  private BufferedInputStream is;
  private DataOutputStream os;
  private Socket socket = null;
  
  public HTTPWorker(Socket socket) {
   this.socket = socket;
   try {
    this.socket.setTcpNoDelay(true);
    // this.socket.setSoLinger(true, 60);

    is = new BufferedInputStream(socket.getInputStream());
    os = new DataOutputStream(socket.getOutputStream());

   } catch (Exception e) {
   }
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.lang.Runnable#run()
   */
  public void run() {

   try {
    readwrite();
   } catch (Throwable e) {
    System.out.println("\n\n***\n" + ret + "\n****\n\n");
    System.out.println(e);
   }

  }

  private void readwrite() throws Throwable {

   try {
    ret = readHTTPMessage();

    //Here HTTP request string is 
 ready to do your business magic 
 
 
    
    
    StringBuffer strbuffer = new StringBuffer();
    strbuffer.append(SC_NO_CONTENT);
    writebytes(socket, strbuffer.toString());

   } catch (Throwable e) {
    System.out.println(e);
   } finally {

    // Following code has no use, However to make double check
    // whether socket is closed
    if (!socket.isClosed())
     socket.close();
   }
  }

  public String readHTTPMessage() throws IOException {
   String result = null;
   StringBuffer strb = new StringBuffer();
   int bufferSize = 1024; // is.available()==0 ? 1024:is.available();
   int contentLength = 0;
   int headerLength = 0;
   int bodyLength = 0;
   try {
    byte[] buf = new byte[bufferSize];
    int nread = 0;
    while ((nread = is.read(buf)) != -1) {
     if (nread == 0)
      continue;
     strb.append(new String(buf, 0, nread));
     // System.out.println(strb);
     result = strb.toString();

     if (contentLength == 0) {

      // atleast 50 bytes required to identify content length
      if (result.length() < 50)
       continue;

      HTTPRequest request = new HTTPRequest(result.getBytes());
      String contentStr = getPropertyIgnoreCase(request
        .getHeaders(), CONTENT_LENGTH);

      // if length specified
      if (null == contentStr || "".equals(contentStr))
       break;

      contentLength = Integer
        .parseInt("" + contentStr.trim());
      bodyLength = request.getBodyContent().length;
      headerLength = result.length() - bodyLength;
     } else {
      bodyLength = result.length() - headerLength;
     }

     if (bodyLength < contentLength) {
      bufferSize = contentLength - bodyLength;
      buf = new byte[bufferSize];
     } else {

      if (bodyLength >= contentLength 
        || result.endsWith(EOL)) {

       // try{socket.shutdownInput();}catch(Exception e){}
       break;
      }

     }
    }
   } catch (Exception e) {
    System.out.println(e);
   } finally {
    // try{is.close();}catch(Exception e){}

   }
   return result;
  }

  private void writebytes(Socket socket, String data) {
   try {
    //
    os.write(data.getBytes());
    os.flush();
    if (!socket.isClosed())
     is.close();
    if (!socket.isClosed())
     os.close();

   } catch (Throwable e) {
    System.out.println(e);
   }

   try {
    if (!socket.isClosed())
     socket.close();
   } catch (Throwable e) {
    System.out.println("Problem in data writing in channel");
    System.out.println(e);
   }
  }

  @Override
  protected void finalize() throws Throwable {
   // TODO Auto-generated method stub
   super.finalize();
   socket = null;
   ret = null;
  }
 }
}

If you like this blog and posting, please feel free to leave your comments, which will help me to post more useful blogs.. :)

No comments:

Post a Comment

Recent Posts

Unix Commands | List all My Posts

Texts

This blog intended to share the knowledge and contribute to JAVA Community such a way that by providing samples and pointing right documents/webpages. We try to give our knowledege level best and no guarantee can be claimed on truth. Copyright and Terms of Policy refer blogspot.com