import java.io.*;
import java.net.*;
import java.util.*;

// HTTPServer class. This server sends named HTML files
// (ending in .htm or html) as long as the client request
// indicates it can handle html docs.

public class HTTPServer {

    public static void main(String[] args) {
	String RootDir;

        try {
	    // command line arg is the directory in which we want
	    // to look for files when we get an HTTP request.
	    // This is used later to create an HTTPResponse object
	    // that will server the specified directory.

	    RootDir = args[0];
	    System.out.println("Document Root: " + RootDir);


            // Connect to the specified host and port
            ServerSocket s = new ServerSocket(0);
	    
	    // Print out our port number
	    System.out.println("Port: " + s.getLocalPort());

	    // process clients forever...
	    while (true) {
		System.out.println("Looking for new client");
		// get the next client connection
		Socket client = s.accept();
		
		HTTPRequest r = new HTTPRequest(client);

		// The request object can tell us about the request,
		// including the URI and headers. For this sample
		// program we just print them out.

		System.out.println("METHOD: " + r.getMethod());
		System.out.println("URI: " + r.getURI());
		System.out.println("Version: " + r.getVersion());

		// The headers are stored in a hash table
		Hashtable hdrs = r.headers;
		// Find out what the client can handle - Accept header
		System.out.println("Client can handle: " + hdrs.get("Accept"));
		HTTPResponse resp = new HTTPResponse(client,r,RootDir);
		client.close();
	    }

        // If anything goes wrong, print an error message
	} catch (IOException e) {
	    System.err.println(e);
	} catch (ArrayIndexOutOfBoundsException e) { 
	    System.err.println("Usage: HTTPServer <documnet root>");
        }
    }
}

// HTTPResponse generates an HTTP response 
// This class includes methods that take care of a single request.
// The constructor actually creates the response and sends it
// to the client, so creating an HTTPResponse object is all you need
// to do...
//
// The constructor needs a socket (current connected to the client),
//    an HTTPRequest object that hold the current request, and
//    a string that specifies the root directory served by this
//    HTTP server (where to look for files). The root directory is
//    prepended to all received URIs to resolve the actual file name.


class HTTPResponse  {
    HTTPRequest request;
    Socket client;
    OutputStream toClient;
    String RootDir;

    HTTPResponse(Socket client,HTTPRequest req, String rootdir) throws IOException{
	this.client = client;
	this.request = req;
	toClient = client.getOutputStream();
	RootDir = rootdir;
	SendFile();
    }

    void SendFile() {
	try {
	    // We need to prepend the root directory to the requested file
	    // path - the server only looks in that directory.

	    // A "real" server would make sure that the URI does
	    // not include things like ".." so that the server
	    // wouldn't send back anything not contained in the root
	    // directory (but this sample doesn't handle this).

	    String path = RootDir + request.getURI();

	    // open the file and associate an input stream with it.
	    // (The file could hold anything, so we don't assume it is
	    // just text we are reading - if it was just text we would
	    // probably want to use a reader).

	    FileInputStream f = new FileInputStream(path);

	    // Some debugging info - log this request to stdout
	    System.out.println("REQUEST FOR: " + path );
	    
	    StatusLine(200,"OK");
	    // Send back content-type header. We assume the file is
	    // and HTML file!
	    Header("Content-type","text/HTML");
	    // blank line ends the headers
	    WriteLine("");
	    
	    // Now send the file (as raw bytes)
	    byte b[] = new byte[1000];
	    int n;
	    while ((n=f.read(b))>0) {
		toClient.write(b,0,n);
	    }

	    f.close();

	} catch (WriteAbortedException e) {
	    System.out.println(e);
	} catch (FileNotFoundException e) {
	    // Generate HTTP error 400
	    try {
		StatusLine(400,"No such resource: " + request.getURI());
		WriteLine("");
		WriteLine("<H2>Error: No such resource found: " + request.getURI());
	    } catch (IOException ie) {
		System.out.println(ie);
	    }
	} catch (IOException e) {
	    System.out.println(e);
	}
    }

    void StatusLine(int ecode,String msg) throws IOException {
	StringBuffer cd = new StringBuffer();
	cd.append(ecode/100);
	cd.append((ecode%100)/10);
	cd.append(ecode%10);
	WriteLine(request.getVersion() + " " + cd.toString()+ " " + msg);
    }

    void Header(String name, String value) throws IOException {
	WriteLine(name+": "+value);
    }

    void WriteLine(String s) throws IOException {
	toClient.write(s.getBytes());
	toClient.write("\r\n".getBytes());
    }

}



// HTTPRequest extracts a HTTP request from a socket
// and can report on the various parts of the request
// (URI, headers, method, etc.)

class HTTPRequest {
    String Method;             // hold the method extracted from the request
    String URI;                // the requested URI
    String Version;            // HTTP version 
    public Hashtable headers;  // has table that holds all found request headers

    // constructor gets a socket that corresponds to an
    // active client connection.

    HTTPRequest(Socket c) throws IOException {
	InputStream in = c.getInputStream();

	// The first line is the request line
	// NOTE: Using my own method GetLine - should probably use a 
	// a reader and readLine (although a buffered reader could make 
	// it difficult to read raw data that comes as part of the content 
	// of the request).

	StringBuffer request = GetLine(in);

	System.out.println("Request was: " + request);
	headers = new Hashtable();

	try {
	    StringTokenizer st = new StringTokenizer(request.toString());
	    Method = st.nextToken();
	    URI = st.nextToken();
	    // If there is no HTTP version found in the request line,
	    // this is a version 0.9 request
	    if (st.hasMoreElements()) {
		Version = st.nextToken();
		// read in headers and store in Hash Table
		StringBuffer header = GetLine(in);
		// keep processing until we see an empty header line
		while (header.length()>0) {
		    StringTokenizer hst = new StringTokenizer(header.toString(),":");
		    String name=hst.nextToken();
		    String val=hst.nextToken();

		    // remove leading whitespace from header value
		    while (val.charAt(0) == ' ') {
			val = val.substring(1);
		    }

		    headers.put(name,val);
		    header = GetLine(in);
		}
	    } else {
		Version = "HTTP/0.9";
		// Old style request - no headers
	    }
	} catch (NoSuchElementException e) {
	    System.out.println("Invalid Request");
	    throw e;
	}
    }
    
    StringBuffer GetLine(InputStream in) throws IOException {
	StringBuffer b = new StringBuffer();

	int x;

	x=in.read();
	while (x!='\n') {
	    if (x == -1) break;
	    if (x != '\r')
		b.append((char)x);
	    x = in.read();
	}
	return(b);
    }

    public String getMethod() {
	return(Method);
    }

    public String getURI() {
	return(URI);
    }
    public String getVersion() {
	return(Version);
    }

}


















