// From Exploring Java, 2nd.Ed., by P. Niemeyer & J. Peck, O'Reilly Pub.
import java.net.*;
import java.io.*;
import java.util.*;

public class TinyHttpd {
  public static void main( String argv[] ) throws IOException {

    // Get the port number from the command line.
    // Create a ServerSocket object to watch that port for clients
    ServerSocket ss = new ServerSocket( Integer.parseInt(argv[0]) );
    System.out.println("starting...");

    // Here we loop indefinitely, just waiting for clients to connect
    while ( true ) {

      // accept() does not return until a client requests a connection
      // Now that a client has arrived, create an instance of our special
      // thread subclass to respond to it.
      new TinyHttpdConnection( ss.accept() ).start();
      System.out.println("new connection");

    } // Now loop back around and wait for next client to be accepted.
  }
}

class TinyHttpdConnection extends Thread {
  Socket client;

   // Pass the socket as a argument to the constructor
  TinyHttpdConnection ( Socket client ) throws SocketException {
    this.client = client;

    // Set the thread priority down so that the ServerSocket
    // will be responsive to new clients.
    setPriority( NORM_PRIORITY - 1 );
  }

  public void run() {
    try {
      // Use the client socket to obtain an input stream from it.
      // For text input we wrap an InputStreamReader around
      // the raw input stream and set ASCII character encoding.
      // Finally, use a BufferReader wrapper to obtain
      // buffering and higher order read methods.
      BufferedReader in = new BufferedReader(
        new InputStreamReader(client.getInputStream(), "8859_1") );

      // Now get an output stream to the client.
      OutputStream out = client.getOutputStream();

      // For text output we wrap an OutputStreamWriter around
      // the raw output stream and set ASCII character encoding.
      // Finally, we use a PrintWriter wrapper to obtain its
      // higher level output methods.
      // Open in append mode.
      PrintWriter pout = new PrintWriter(
        new OutputStreamWriter(out, "8859_1"), true );


      // First read the request line from the client
      String request = in.readLine();
      System.out.println( "Request: "+request );

      // Check if HTTP/1.0 or later. If so, add MIME header
      boolean sendMIME = false;
      if ( request.indexOf("HTTP/") != -1) sendMIME = true;

      // Use aStringTokenizer to examine the request text.
      StringTokenizer st = new StringTokenizer( request );

      // Check that the request has a minimun number of words
      // and that the first word is the GET command.
      if ( (st.countTokens() >= 2) && st.nextToken().equals("GET") ) {

       // Ignore the leading "/" on the file name.
        if ( (request = st.nextToken()).startsWith("/") )
          request = request.substring( 1 );

        // If no file name is there, use index.html default.
        if ( request.endsWith("/") || request.equals("") )
          request = request + "index.html";

        // Check if the file is hypertext or plain text
        String ContentType;
        if (request.endsWith(".html") || request.endsWith(".htm")) {
           ContentType = "text/html";
        }
        else {
          ContentType = "text/plain";
        }
        // Now read the file from the disk and write it to the
        // output stream to the client.
        try {

          // Open a stream to the file. Remember that by this
          // all the text but the file name has been stripped
          // from the "request" string.
          FileInputStream fis = new FileInputStream ( request );

          if( sendMIME) {
            // Follow example in "Java Network Programming", chp.8
            PrintStream os = new PrintStream(out);
            os.print("HTTP/1.0 200 OK\r\n");
            Date now = new Date();
            os.print("Date: " + now + "\r\n");
            os.print("Server: TinyHttpd 1.0\r\n");
            os.print("Content-length: " + fis.available() + "\r\n");
            os.print("Content-type: " + ContentType + "\r\n\r\n");
          }


          // Creat a byte array to hold the file.
          byte [] data = new byte [ fis.available() ];

          fis.read( data );  // Read file into the byte array
          out.write( data ); // Write it to client output stream
          out.flush();       // Remember to flush output buffer
          fis.close();

        } catch ( FileNotFoundException e ) {
          // If no such file, then send the famous 404 message.
          pout.println( "404 Object Not Found" ); }
      } else
        pout.println( "400 Bad Request" );
      client.close();
    } catch ( IOException e ) {
      System.out.println( "I/O error " + e ); }
  // On return from run() the thread process will stop.
  }
}


