Wednesday, March 31, 2010

Chat Server (Working for the most part)

/** ChatServerSubject object
-- implementation of the Subject class, containing the user list, an observer list, and the message buffer
-- This class will notify each child thread of the server whenever a change is made on either of these 2 objects.
Authors:  Jon Fidler & Daniel Tanner
March 31st, 2010
CMPT 352 - Networking
*/
import java.util.*;
import java.net.*;
import java.awt.*;

public class ChatServerSubject extends Observable
{
    //members
    private Vector<String> userList;
    private Vector<MessageObject> messageBuffer;
    private String removedUser;
   
    //flags
    /* whatHappened flag = int representing the state of the object.
    0 = nothing's changed
    1 = user added
    2 = user removed
    3 = message added
    */
    private int whatHappened;
   
    //constructors
    public ChatServerSubject()
    {
        userList = new Vector<String>();
        messageBuffer = new Vector<MessageObject>();
        removedUser = "NOBODY";
        whatHappened = 0;
    }
   
    /** userList changing functions
    */
    //add a user
    public void addUser(String userName)
    {
        userList.add(userName);
       
        //set flags
        whatHappened = 1;
        setChanged();
        notifyObservers();
    }
   
    //remove a user from the list
    public void removeUser(String userName)
    {
        removedUser = userName;
        userList.remove(userName);
       
        //set flags
        whatHappened = 2;
        setChanged();
        notifyObservers();
    }
   
    //check if a user is on the list or not
    public boolean userExists(String userName)
    {
        return userList.contains(userName);
    }
   
    //get user list
    public Vector<String> getUserList(){return userList;}
   
    //get the name of the user who just disconnected
    public String getDCedUser() {return removedUser;}
   
    //returns the last user on the list (most recently added user
    public String getNewUser() {return userList.lastElement();}
   
    /**Message Buffer functions
    */
    //get message function
    public MessageObject getMessage() {return messageBuffer.firstElement();}
   
    //add message function
    public void addMessage(MessageObject message)
    {
        messageBuffer.add(message);
        whatHappened = 3;
        setChanged();
        notifyObservers();
    }
   
    //remove a message
    public void removeMessage(MessageObject message)
    {
        messageBuffer.remove(message);
    }
   
    /* Flag checking functions*/
    public boolean userAdded()
    {
        if(whatHappened == 1)
            return true;
        return false;
    }
   
    public boolean userRemoved()
    {
        if(whatHappened == 2)
            return true;
        return false;
    }
   
    public boolean messageAdded()
    {
        if(whatHappened == 3)
            return true;
        return false;
    }
}

/**
Daniel Tanner & Jon Fidler
CMPT 352 - Computer Networking
March 29th, 2010
Handler class for threaded Chat Server
 */

import java.net.*;
import java.lang.Object.*;
import java.util.*;
import java.text.*;
import javax.activation.*;
import java.io.*;
import java.util.regex.Pattern;

public class Handler implements Observer
{       
    //Create variables
    private BufferedReader fromClient = null;
    private BufferedOutputStream toClient = null;
    private ChatServerSubject buffer = null;
    private String myUserName = null;
    private String command = null;
    private boolean connectedProperly = false;
    private static Pattern alphaNum = Pattern.compile("^[A-Za-z0-9\\*]+$");

    //This method invoked as a separate thread
    public void process(Socket client, ChatServerSubject buffer) throws IOException
    {   
        //set up the method
        this.buffer = buffer;
       
        //try block for opening streams and getting hostname
        try
        {
            //open Streams
            fromClient = new BufferedReader(new InputStreamReader(client.getInputStream()));
            toClient = new BufferedOutputStream(client.getOutputStream());       
       
            //initial connection state
            command = fromClient.readLine();
            command = command.toLowerCase();
            if(!command.equalsIgnoreCase("connect"))
            {
                error("Bad Request.  Please input a proper command.", 2);
                return;
            }
           
            //get username, check against userList
            myUserName = fromClient.readLine();
            myUserName = myUserName.toUpperCase();
            if(buffer.userExists(myUserName))
            {
                error("Duplicate Handle.  Please try another name.", 1);
                return;
            }
           
            //check that username is within bounds
            if(!(myUserName.length() <= 16 && alphaNum.matcher(myUserName).matches()))
            {
                error("Invalid handle.  Handle must be an alphanumeric string with size less than or euqal to 16.", 3);
                return;
            }
            connectedProperly = true;
           
            //add username to userList
            buffer.addUser(myUserName);
            buffer.addObserver(this);
           
            //print userList to client
            printUserList();
           
            //listen for commands
            do
            {
                command = fromClient.readLine();
                command = command.toUpperCase();
               
                //getHandles command
                if(command.equalsIgnoreCase("gethandles"))
                    printUserList();
               
                //Post command
                else if(command.equalsIgnoreCase("post"))
                    constructMessage();
               
                //invalid command
                else if(!command.equalsIgnoreCase("disconnect"))
                    error("Invalid request.  Please input a proper command.", 2);
            }
            while(!command.equalsIgnoreCase("disconnect"));
        }
       
        catch(IOException ioe)
        {
            System.err.println(ioe);
            return;
        }
       
        finally
                {
                    //disconnection state
                    if(connectedProperly)
                    {
                            buffer.deleteObserver(this);
                            buffer.removeUser(myUserName);
                    }
                    // close streams and socket
                    if (fromClient != null)
                        fromClient.close();
                    if (toClient != null)
                        toClient.close();
                    if (client != null)
                        client.close();
                }
    }
   
    //broadcast method - this method is the basic broadcasting method
    //it assumes that we're sending a public message
    private void broadcast(MessageObject message) throws IOException
    {
        System.out.println("You've reached the broadcast method!");
        toClient.write("posted\n".getBytes());
        toClient.write((message.getFromUser() + "\n").getBytes());
        toClient.write("public\n".getBytes());
        toClient.write((message.getMessage() + "\n").getBytes());
        toClient.flush();
    }
   
    //private message broadcast method
    private void privateMessage(MessageObject message) throws IOException
    {
        System.out.println("privateMessage method!");
        toClient.write("posted\n".getBytes());
        toClient.write((message.getFromUser() + "\n").getBytes());
        toClient.write("private\n".getBytes());
        toClient.write((message.getMessage() + "/n").getBytes());
        toClient.flush();
    }
   
    //connected/disconnected message
    private void userConnected(String userName, boolean connected) throws IOException
    {
        if(connected)
            toClient.write("connected\n".getBytes());
        else
            toClient.write("disconnected\n".getBytes());
        toClient.write((userName + "\n").getBytes());
        toClient.flush();
    }
   
    //Error message
    private void error(String errorMessage, int errorNum) throws IOException
    {
        toClient.write("error\n".getBytes());
        toClient.write((errorNum + "\n").getBytes());
        toClient.write((errorMessage + "\n").getBytes());
        toClient.flush();
    }
   
    //Print user list function
    private void printUserList() throws IOException
    {
        Vector<String> currentUserList = new Vector<String>(buffer.getUserList());
        toClient.write("handles\n".getBytes());
        toClient.write((currentUserList.size() + "\n").getBytes());
        for(int index = 0; index < currentUserList.size(); index++)
            toClient.write((currentUserList.get(index) + "\n").getBytes());
        toClient.flush();
    }
   
    //CreateMessage method - goes to this state after receiving the post command from the user
    private void constructMessage() throws IOException
    {
        MessageObject newMessage = new MessageObject();
        newMessage.setFromUser(myUserName);
        String temp;
       
        //read the toUser
        temp = fromClient.readLine();
        temp = temp.toUpperCase();
        if(!(alphaNum.matcher(temp).matches() && temp.length() <= 16))
        {
            error("Invalid username entered.  Try again!.", 3);
            return;
        }
        if(!buffer.userExists(temp) && !temp.equalsIgnoreCase("*"))
        {
            error("User not found.", 4);
            return;
        }
        newMessage.setToUser(temp);
           
           
        //read the message and check to make sure it's within bounds
        temp = fromClient.readLine();
        if(temp.length() > 1024)
        {
            error("Message too Long", 5);
            return;
        }
        newMessage.setMessage(temp);
        buffer.addMessage(newMessage);
    }
   
    //update function - this is the observer functionanility implemented
    //it will check the flags when the subject has changed, and act appropriately
    public void update(Observable o, Object arg)
    {
        System.out.println("your observers are being updated!");
        try
        {
            //check if a user was added or removed
            if(buffer.userAdded())
                userConnected(buffer.getNewUser(), buffer.userAdded());
            else if(buffer.userRemoved())
                userConnected(buffer.getDCedUser(), buffer.userAdded());
               
           
            //here's the routine when we get a new message
            else if(buffer.messageAdded())
            {
                MessageObject newMessage = buffer.getMessage();
                //check if it's a private message
                if(newMessage.getToUser().equals(myUserName))
                    privateMessage(newMessage);
               
                //now check if it's a broadcast and not from me
                else if(newMessage.getToUser().equals("*") && !newMessage.getFromUser().equals(myUserName))
                    broadcast(newMessage);
               
                //now decrement and check if the counter is at 0, remove from the list if it is
                newMessage.viewed();
                if(newMessage.getCounter() == 0)
                    buffer.removeMessage(newMessage);
            }
        }
        catch(IOException ioe)
        {
            System.err.println(ioe);
            command = "disconnect";
            return;
        }
           
    }
}

/** Connection Class for threaded Chat Server
Daniel Tanner & John Fidler
CMPT 352 - Computer Networking
March 29th, 2010
 */

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

public class Connection implements Runnable
{
    //create variables
    private Socket    client;
    private ChatServerSubject buffer;
    private Handler handler = new Handler();

    public Connection(Socket client, ChatServerSubject buffer)
    {
        this.client = client;
        this.buffer = buffer;
    }

    //Separate thread
    public void run()
    {
        try
        {
            handler.process(client, buffer);
        }
       
        catch (java.io.IOException ioe)
        {
            System.err.println(ioe);
        }
    }
}

/** Chat Server
Authors: Daniel J. Tanner and Jon Fidler
March 29th, 2010
CMPT 352 - Networking

This program implements the Spring 2010 Networking class' Chat Protocol
**/

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


public class ChatServer
{
    //constants & globals
    public static final int PORT = 2222;
    private static ChatServerSubject buffer = null;
   
   
    public static void main(String[] args) throws java.io.IOException
    {
        //load everything up
        buffer = new ChatServerSubject();
       
        //Create socket
        ServerSocket sock = null;
        try
        {
            //establish socket
            sock = new ServerSocket(PORT);
           
            while(true)
            {
                Socket client = sock.accept();
                //debug testing
                InetAddress ipAddr = client.getInetAddress();
                System.out.print(ipAddr.getHostAddress() + " : ");
                System.out.println(client.getPort() + " connected.");
                Thread worker = new Thread(new Connection(client, buffer));
               
                worker.start();
            }
        }
        //catch any errors opening sockets
        catch(IOException ioe)
        {
            System.err.println(ioe);
            System.exit(0);
        }
       
        finally
        {
            //close streams
            if(sock != null)
                sock.close();
        }
    }
   
    //getUserList function - returns a pointer to the user List
    public static Vector<String> getUserList() {return buffer.getUserList();}
}

/**
BufferKeyEntry class

Contain fields to put into the key of the message buffer
The value will be the unformatted message

Members of the BufferEntry are as follows:

-Who sent the message (so we don't echo it back)

-A non-static counter which will tell us when all the threads have sent the message
so we know when to remove the message from our buffer.

-Who the message is to (* for broadcasts)
*/

/**
Authors: Daniel Tanner & Jon Fidler
March 29th, 2010
CMPT 352 - Networking
*/

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

public class MessageObject
{
    //create variables
    private String toUser;
    private String fromUser;
    private int counter;
    private String message;
   
    //constructor
    MessageObject()
    {
        counter = ChatServer.getUserList().size();
        toUser = null;
        fromUser = null;
        message = null;
    }
   
    //copy constructor
    MessageObject(MessageObject copy)
    {
        this.toUser = copy.toUser;
        this.fromUser = copy.fromUser;
        this.counter = copy.counter;
        this.message = copy.message;
    }
       
    //Constructor with arguments
    MessageObject(String toUser, String fromUser, String message)
    {
        counter = ChatServer.getUserList().size();
        this.toUser = toUser;
        this.fromUser = fromUser;
        this.message = message;
    }
   
    //get functions
    int getCounter() {return counter;}
    String getToUser() {return toUser;}
    String getFromUser() {return fromUser;}
    String getMessage() {return message;}
   
    //set functions
    void setToUser(String toUser) {this.toUser = toUser;}
    void setFromUser(String fromUser) {this.fromUser = fromUser;}
    void setMessage(String message) {this.message = message;}
   
    //viewed function - This function will decrement the counter
    void viewed() {counter--;}
   
    //equals function
    boolean equals(MessageObject rhs)
    {
        if(toUser.equals(rhs.toUser) && fromUser.equals(rhs.fromUser) && counter == rhs.counter && message.equals(rhs.message))
            return true;
        else
            return false;
    }
}
       
/**
Daniel Tanner & Jon Fidler
CMPT 352 - Computer Networking
March 29th, 2010
Handler class for threaded Chat Server
 */

import java.net.*;
import java.lang.Object.*;
import java.util.*;
import java.text.*;
import javax.activation.*;
import java.io.*;
import java.util.regex.Pattern;

public class Handler implements Observer
{       
    //Create variables
    private BufferedReader fromClient = null;
    private BufferedOutputStream toClient = null;
    private ChatServerSubject buffer = null;
    private String myUserName = null;
    private String command = null;
    private boolean connectedProperly = false;
    private static Pattern alphaNum = Pattern.compile("^[A-Za-z0-9\\*]+$");

    //This method invoked as a separate thread
    public void process(Socket client, ChatServerSubject buffer) throws IOException
    {   
        //set up the method
        this.buffer = buffer;
       
        //try block for opening streams and getting hostname
        try
        {
            //open Streams
            fromClient = new BufferedReader(new InputStreamReader(client.getInputStream()));
            toClient = new BufferedOutputStream(client.getOutputStream());       
       
            //initial connection state
            command = fromClient.readLine();
            command = command.toLowerCase();
            if(!command.equalsIgnoreCase("connect"))
            {
                error("Bad Request.  Please input a proper command.", 2);
                return;
            }
           
            //get username, check against userList
            myUserName = fromClient.readLine();
            myUserName = myUserName.toUpperCase();
            if(buffer.userExists(myUserName))
            {
                error("Duplicate Handle.  Please try another name.", 1);
                return;
            }
           
            //check that username is within bounds
            if(!(myUserName.length() <= 16 && alphaNum.matcher(myUserName).matches()))
            {
                error("Invalid handle.  Handle must be an alphanumeric string with size less than or euqal to 16.", 3);
                return;
            }
            connectedProperly = true;
           
            //add username to userList
            buffer.addUser(myUserName);
            buffer.addObserver(this);
           
            //print userList to client
            printUserList();
           
            //listen for commands
            do
            {
                command = fromClient.readLine();
                command = command.toUpperCase();
               
                //getHandles command
                if(command.equalsIgnoreCase("gethandles"))
                    printUserList();
               
                //Post command
                else if(command.equalsIgnoreCase("post"))
                    constructMessage();
               
                //invalid command
                else if(!command.equalsIgnoreCase("disconnect"))
                    error("Invalid request.  Please input a proper command.", 2);
            }
            while(!command.equalsIgnoreCase("disconnect"));
        }
       
        catch(IOException ioe)
        {
            System.err.println(ioe);
            return;
        }
       
        finally
                {
                    //disconnection state
                    if(connectedProperly)
                    {
                            buffer.deleteObserver(this);
                            buffer.removeUser(myUserName);
                    }
                    // close streams and socket
                    if (fromClient != null)
                        fromClient.close();
                    if (toClient != null)
                        toClient.close();
                    if (client != null)
                        client.close();
                }
    }
   
    //broadcast method - this method is the basic broadcasting method
    //it assumes that we're sending a public message
    private void broadcast(MessageObject message) throws IOException
    {
        System.out.println("You've reached the broadcast method!");
        toClient.write("posted\n".getBytes());
        toClient.write((message.getFromUser() + "\n").getBytes());
        toClient.write("public\n".getBytes());
        toClient.write((message.getMessage() + "\n").getBytes());
        toClient.flush();
    }
   
    //private message broadcast method
    private void privateMessage(MessageObject message) throws IOException
    {
        System.out.println("privateMessage method!");
        toClient.write("posted\n".getBytes());
        toClient.write((message.getFromUser() + "\n").getBytes());
        toClient.write("private\n".getBytes());
        toClient.write((message.getMessage() + "/n").getBytes());
        toClient.flush();
    }
   
    //connected/disconnected message
    private void userConnected(String userName, boolean connected) throws IOException
    {
        if(connected)
            toClient.write("connected\n".getBytes());
        else
            toClient.write("disconnected\n".getBytes());
        toClient.write((userName + "\n").getBytes());
        toClient.flush();
    }
   
    //Error message
    private void error(String errorMessage, int errorNum) throws IOException
    {
        toClient.write("error\n".getBytes());
        toClient.write((errorNum + "\n").getBytes());
        toClient.write((errorMessage + "\n").getBytes());
        toClient.flush();
    }
   
    //Print user list function
    private void printUserList() throws IOException
    {
        Vector<String> currentUserList = new Vector<String>(buffer.getUserList());
        toClient.write("handles\n".getBytes());
        toClient.write((currentUserList.size() + "\n").getBytes());
        for(int index = 0; index < currentUserList.size(); index++)
            toClient.write((currentUserList.get(index) + "\n").getBytes());
        toClient.flush();
    }
   
    //CreateMessage method - goes to this state after receiving the post command from the user
    private void constructMessage() throws IOException
    {
        MessageObject newMessage = new MessageObject();
        newMessage.setFromUser(myUserName);
        String temp;
       
        //read the toUser
        temp = fromClient.readLine();
        temp = temp.toUpperCase();
        if(!(alphaNum.matcher(temp).matches() && temp.length() <= 16))
        {
            error("Invalid username entered.  Try again!.", 3);
            return;
        }
        if(!buffer.userExists(temp) && !temp.equalsIgnoreCase("*"))
        {
            error("User not found.", 4);
            return;
        }
        newMessage.setToUser(temp);
           
           
        //read the message and check to make sure it's within bounds
        temp = fromClient.readLine();
        if(temp.length() > 1024)
        {
            error("Message too Long", 5);
            return;
        }
        newMessage.setMessage(temp);
        buffer.addMessage(newMessage);
    }
   
    //update function - this is the observer functionanility implemented
    //it will check the flags when the subject has changed, and act appropriately
    public void update(Observable o, Object arg)
    {
        System.out.println("your observers are being updated!");
        try
        {
            //check if a user was added or removed
            if(buffer.userAdded())
                userConnected(buffer.getNewUser(), buffer.userAdded());
            else if(buffer.userRemoved())
                userConnected(buffer.getDCedUser(), buffer.userAdded());
               
           
            //here's the routine when we get a new message
            else if(buffer.messageAdded())
            {
                MessageObject newMessage = buffer.getMessage();
                //check if it's a private message
                if(newMessage.getToUser().equals(myUserName))
                    privateMessage(newMessage);
               
                //now check if it's a broadcast and not from me
                else if(newMessage.getToUser().equals("*") && !newMessage.getFromUser().equals(myUserName))
                    broadcast(newMessage);
               
                //now decrement and check if the counter is at 0, remove from the list if it is
                newMessage.viewed();
                if(newMessage.getCounter() == 0)
                    buffer.removeMessage(newMessage);
            }
        }
        catch(IOException ioe)
        {
            System.err.println(ioe);
            command = "disconnect";
            return;
        }
           
    }
}

Monday, March 15, 2010

Updated Web Server Stuff (WORKING VERSION!!!!)

/** CacheEntry class
Stores a byte and a filename, allowing the cache
to be useful!  Method calls will get the filename or the byte array

Decided not to have set methods because you don't want to be able to fiddle
with a CacheEntry directly, should only construct via filename
and be able to get the file information and send it along
*/

import java.net.*;
import java.util.*;
import java.lang.Object.*;
import java.text.*;
import java.io.*;

public class CacheEntry
{
    //variables
    private byte [] byteFile_ = null;
    private File file_ = null;
    private InputStream fileReadStream = null;
    //constructors
    public CacheEntry()
    {
    }
   
    public CacheEntry(File file) throws IOException
    {
        System.out.println("Creating a new CacheEntry with filename: " + file.toString());
        file_ = file;
        byteFile_ = new byte[(int)file.length()];
        try
        {
            fileReadStream = new BufferedInputStream(new FileInputStream(file_));
            fileReadStream.read(byteFile_);
        }
       
        catch(IOException ioe)
        {
            System.err.println(ioe);
            return;
        }

        finally
        {
            if(fileReadStream != null)
                fileReadStream.close();
        }
    }
   
    //get functions
    public File getFile() {return file_;}
    public byte[] getBytes() {return byteFile_;}
}

/**
 * XML parser for configuration parameters.
 *
 * This maps configuration parameters to a HashMap and are retrieved
 * through the following getter methods:
 *
 *     public String getLogFile()
 *     public String getDocumentRoot()
 *     public String getDefaultDocument()
 *     public String getServerName()
 *
 * Usage:
 *     Configuration config = new Configuration();
 *
 *    config.getLogFile();
 *    config.getDocumentRoot();
 *    config.getDefaultDocument();
 *    config.getServerName();
 */

import java.io.*;

import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;

import java.util.Map;
import java.util.HashMap;

public class Configuration extends DefaultHandler
{
    private Map  map;
    private String configurationFile;

    /**
     * @param File configurationFile - The name of the configuration file
     */
    public Configuration(String configurationFile) throws ConfigurationException {
    this.configurationFile = configurationFile;

    map = new HashMap();

    try {       
            // Use the default (non-validating) parser
            SAXParserFactory factory = SAXParserFactory.newInstance();

            // Parse the input
            SAXParser saxParser = factory.newSAXParser();
            saxParser.parse( new File(configurationFile), this);
    }
    catch (javax.xml.parsers.ParserConfigurationException pce) {
        throw new ConfigurationException("javax.xml.parsers.ParserConfigurationException");
    }
    catch (org.xml.sax.SAXException se) {
        throw new ConfigurationException("org.xml.sax.SAXException");
    }
    catch (java.io.IOException ioe) {
        throw new ConfigurationException("java.io.IOException");
    }
    }


    /**
     * We will map each configuration attribute to its value
     *
     * @param namespaceURI the namespace
     * @param lName the local name of the element
     * @param qName the qualified name of the element
     * @param attrs the set of attributess associated with the element
      */
    public void startElement(String namespaceURI,
                             String lName,    
                             String qName,    
                             Attributes attrs)   
    throws SAXException
    {
        String elementName = lName; // element name
        if ("".equals(elementName))
        elementName = qName; // namespaceAware = false

    /**
     * Get the attributes associated with this ELEMENT.
      * Attributes are name/value pairs and are stored by index.
     */
        if (attrs != null) {
            for (int i = 0; i < attrs.getLength(); i++) {
                String aName = attrs.getLocalName(i); // Attr name
                if ("".equals(aName))
            aName = attrs.getQName(i);

        // map the element.attribute to its value
        map.put(elementName+"."+aName,attrs.getValue(i));
            }
        }
    }

    // getter methods for mapped configuration values

    /** Returns the location of the log file */
    public String getLogFile() {
        return map.get("logfile.log");
    }

    /** Returns the location of the document base */
    public String getDocumentRoot() {
        return map.get("context.documentRoot");
    }

    /** Returns the name of the default document */
    public String getDefaultDocument() {
        return map.get("context.defaultDocument");
    }

    /** Returns the name of the server */
    public String getServerName() {
        return map.get("webserver.title");
    }
   
    /** Returns the name of the 400 file */
    public String get400()
    {
        return map.get("context.fourHundredDocument");
    }
   
    /** Returns the name of the 404 file */
    public String get404()
    {
        return map.get("context.fourOhFourDocument");
    }
}

public class ConfigurationException extends Exception
{
    public ConfigurationException(String exception) {
        super(exception);
    }
}

/** HTTP Request Header
This Class represents a HTTP Request Header
It takes a byte array and Parses it, basically
Author: Daniel J. Tanner
Date:  March 9th, 2010
CMPT 352
*/
import java.io.*;
import java.net.*;
import java.lang.*;

public class HTTPRequestHeader
{
    //fields
    protected static final String DEFAULT_METHOD = "GET";
    protected static final String DEFAULT_PATH = "/index.html";
    protected static final String DEFAULT_PROTOCOL = "HTTP/1.0";
    protected static final int CHUNK = 5028;
    protected String method_;
    protected String path_;
    protected String protocol_;
   
    //Constructors
    public HTTPRequestHeader()
    {
        method_ = DEFAULT_METHOD;
        protocol_ = DEFAULT_PROTOCOL;
        path_ = DEFAULT_PATH;
    }
   
    //This one is basically the meat, it will parse the byte stream it receives
    public HTTPRequestHeader(InputStream fromClient) throws IOException
    {
        BufferedReader input = new BufferedReader(new InputStreamReader(fromClient));
        String requestLine = null;
        String requestParsed = null;
        StringBuffer request = new StringBuffer(CHUNK);
        int i = 0;
        try
        {
            requestParsed = input.readLine();
       
            while((requestLine = input.readLine()) != null)
            {
                if(requestLine.length() == 0) break;
                i += requestLine.length();
                request.append(requestLine);
            }
        }
        catch (IOException ioe)
        {
            ioe.printStackTrace();
            i = -1;
        }
       
        System.out.println("-->"+requestParsed+"<--");
       
        //Now we parse!
        //String requestParsed = request.toString();
        int index1, index2;
        i = requestParsed.length();
        index1 = requestParsed.indexOf(' ');
       
        //getMethod
        if(index1 != -1)
        {
            method_ = requestParsed.substring(0, index1);
        }
        index2 = requestParsed.indexOf(' ', index1 + 1);
       
        //getURI
        if(index2 > index1)
        {
            path_ = requestParsed.substring(index1 + 1, index2);
        }
        index1 = requestParsed.indexOf(requestParsed.length()-1, index2 + 1);
       
        //getProtocol
   
            protocol_ = requestParsed.substring(index2 + 1, requestParsed.length());
       
       
        System.out.println("-->"+method_+"<--");
        System.out.println("-->"+path_+"<--");              
        System.out.println("-->"+protocol_+"<--");
    }
   
    //Get Functions
    public String getMethod() {return method_;}
    public String getProtocol() {return protocol_;}
    public String getPath() {return path_;}
}

/** HTTP Response Header Class
This class takes a byte array and parses it into a HTTP Response Header

Author:  Daniel J. Tanner
Date:    March 9th, 2010
CMPT 352
*/

import java.io.*;
import java.net.*;
import java.lang.*;

public class HTTPResponseHeader
{
    //Fields
    protected String httpVersion_;
    protected String code_;
    protected String message_;
    protected String date_;
    protected String serverName_;
    protected String contentType_;
    protected long contentLength_;
   
    //constants - default values
    protected static final String DEFAULT_CODE = "200";
    protected static final String DEFAULT_MESSAGE = "Ok";
    protected static final String DEFAULT_HTTPVERSION = "HTTP/1.0";
    protected static final String DEFAULT_DATE = "January 1st, 1970";
    protected static final String DEFAULT_SERVERNAME = "So Long, And Thanks For All The Fish!";
    protected static final String DEFAULT_CONTENTTYPE = "matter/dark";
    protected static final long DEFAULT_CONTENTLENGTH = 42;
   
    //Constructors
    public HTTPResponseHeader()
    {
        code_ = DEFAULT_CODE;
        message_ = DEFAULT_MESSAGE;
        httpVersion_ = DEFAULT_HTTPVERSION;
        date_ = DEFAULT_DATE;
        serverName_ = DEFAULT_SERVERNAME;
        contentType_ = DEFAULT_CONTENTTYPE;
        contentLength_ = DEFAULT_CONTENTLENGTH;
    }
   
    public HTTPResponseHeader(String httpVersion, String code, String message, String date, String serverName, String contentType, long contentLength)
    {
        code_ = code;
        message_ = message;
        httpVersion_ = httpVersion;
        date_ = date;
        serverName_ = serverName;
        contentType_ = contentType;
        contentLength_ = contentLength;
    }
   
    //set functions
    public void setCode(String code) {code_ = code;}
    public void setMessage(String message) {message_ = message;}
    public void setHTTPVersion(String httpVersion) {httpVersion_ = httpVersion;}
    public void setDate(String date) {date_ = date;}
    public void setServerName(String serverName) {serverName_ = serverName;}
    public void setContentType(String contentType) {contentType_ = contentType;}
    public void setContentLength(long contentLength) {contentLength_ = contentLength;}
   
    //get functions
    public String getCode() {return code_;}
    public String getMessage() {return message_;}
    public String getHTTPVersion() {return httpVersion_;}
    public String getDate() {return date_;}
    public String getServerName() {return serverName_;}
    public String getContentType() {return contentType_;}
    public long getContentLength() {return contentLength_;}
   
    //print function
    public byte[] getBytes() throws IOException
    {
        String header = httpVersion_ + " " + code_ + " " + message_ + "\r\n"
            + "Date:  " + date_ + "\r\n"
            + "Server:  " + serverName_ + "\r\n"
            + "Content-Type:  " + contentType_ + "\r\n"
            + "Content-Length:  " + contentLength_ + "\r\n\r\n";
        return header.getBytes("US-ASCII");
    }
       
}

/**
Daniel Tanner
CMPT 352 - Computer Networking
January 26th, 2010
Connection Class for threaded DNS Server
(modified version of G. Gagne's code)
 */

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

public class TannConnection implements Runnable
{
    //create variables
    private Socket    client;
    private WeakHashMap cache = null;
    private Configuration configurator = null;
    private File logFile = null;
    private static TannHandler handler = new TannHandler();

    public TannConnection(Socket client, Configuration configurator, File logFile, WeakHashMap cache)
    {
        this.client = client;
        this.configurator = configurator;
        this.cache = cache;
        this.logFile = logFile;
    }

    //Separate thread
    public void run()
    {
        try
        {
            handler.process(client, configurator, logFile, cache);
        }
       
        catch (java.io.IOException ioe)
        {
            System.err.println(ioe);
        }
    }
}

/** Web Server
by Daniel J. Tanner
March 9th, 2010
CMPT 352

This program implements some basic features of the HTTP protocol
**/

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


public class TannerWebServer
{
    //constants & globals
    public static final int PORT = 2880;
    private static Configuration configurator = null;
    private static WeakHashMap cache = null;
    private static File logFile = null;
   
    public static void main(String[] args) throws java.io.IOException, ConfigurationException
    {
        //begin by initializng the configurator       
        try
        {
            configurator = new Configuration("../conf/config.xml");
        }
       
        catch (ConfigurationException ce)
        {
            System.out.println(ce);
            System.exit(0);
        }
       
        //load the initial files into a hashmap
        cache = new WeakHashMap(20);
        cache.put(configurator.getDefaultDocument(), new CacheEntry(new File(configurator.getDefaultDocument())));
        cache.put(configurator.get400(), new CacheEntry(new File(configurator.get400())));
        cache.put(configurator.get404(), new CacheEntry(new File(configurator.get404())));
       
        //load logfile
        logFile = new File(configurator.getLogFile());
       
        //Create socket
        ServerSocket sock = null;
        try
        {
            //establish socket
            sock = new ServerSocket(PORT);
           
            while(true)
            {
                Socket client = sock.accept();
                //debug testing
                InetAddress ipAddr = client.getInetAddress();
                System.out.print(ipAddr.getHostAddress() + " : ");
                System.out.println(client.getPort());
                Thread worker = new Thread(new TannConnection(client, configurator, logFile, cache));
               
                worker.start();
            }
        }
        //catch any errors opening sockets
        catch(IOException ioe)
        {
            System.err.println(ioe);
            System.exit(0);
        }
       
        finally
        {
            //close streams
            if(sock != null)
                sock.close();
        }
    }
}


/**
Daniel Tanner
CMPT 352 - Computer Networking
January 26th, 2010
Handler class for threaded DNS Server
 */

import java.net.*;
import java.lang.Object.*;
import java.util.*;
import java.text.*;
import javax.activation.*;
import java.io.*;

public class TannHandler
{       
    //Create variables
    private Configuration configurator = null;
    private WeakHashMap cache = null;
    private File logFile = null;
    private InputStream fromClient = null;
    private BufferedOutputStream toClient = null;
    private CacheEntry requestedFile = null;
    private HTTPRequestHeader requestHeader = null;
    private HTTPResponseHeader responseHeader = null;
   
    //This method invoked as a separate thread
    public void process(Socket client, Configuration configurator, File logFile, WeakHashMap cache) throws IOException
    {   
        this.configurator = configurator;
        this.logFile = logFile;
        this.cache = cache;
        //try block for opening streams and getting hostname
        try
        {
            //open Streams
            fromClient = (client.getInputStream());
            toClient = new BufferedOutputStream(client.getOutputStream());

            //Get Request
            requestHeader = new HTTPRequestHeader(fromClient);
            responseHeader = new HTTPResponseHeader();
           
            //process Request
            processRequest(client);
        }
       
        catch(IOException ioe)
        {
            System.err.println(ioe);
            return;
        }
       
                finally
                {
                    // close streams and socket
                    if (fromClient != null)
                        fromClient.close();
                    if (toClient != null)
                        toClient.close();
                    if (client != null)
                        client.close();
                }
    }
   
    /** This function will process the HTTP request
    If GET, will try to find the file in the cache.
    If the file isn't in the hashmap it will add it to the cache.
    If the file doesn't exist, will send the 404 error
    the default response is to send the 400 error
    */
    public void processRequest(Socket client) throws IOException
    {
        //GET command
        System.out.println("Currently the request has method " + requestHeader.getMethod());
        String docRoot = configurator.getDocumentRoot();
        //if(requestHeader.getMethod() == "GET" || requestHeader.getMethod() == "get")
        if( (requestHeader.getMethod()).equals("GET") || (requestHeader.getMethod().equals("get")) )
        {
            //check if the path is asking for the default
            if(requestHeader.getPath().equals("/"))
            {
                //check cache
                System.out.println("You have entered the default path.");
                String defaultDoc = configurator.getDefaultDocument();
                if(cache.containsKey(defaultDoc))
                {
                    requestedFile = cache.get(defaultDoc);
                }
               
                //not in cache, load it in
                else
                {
                    requestedFile = new CacheEntry(new File(defaultDoc));
                    cache.put(defaultDoc, requestedFile);
                }
            }
           
            //check if it's in cache, if it's not, add it to cache
            else if(cache.containsKey(requestHeader.getPath()))
            {               
                requestedFile = cache.get(requestHeader.getPath());
            }
            else
            {
                //open new File
                File requestedFileCheck = new File(docRoot + requestHeader.getPath());
                               
                //404 error
                if(!requestedFileCheck.exists())
                {
                    System.out.println("You have entered the 404 code");
                    responseHeader.setCode("404");
                    responseHeader.setMessage("File Not Found");
               
                    //Write 404 page to client
                    if(cache.containsKey(configurator.get404()))               
                        requestedFile = cache.get(configurator.get404());
                    else
                    {
                        requestedFile = new CacheEntry(new File(configurator.get404()));
                        cache.put(configurator.get404(), requestedFile);
                    }
                    System.out.println(requestedFile.getFile().toString());
                }
                else
                {
                    requestedFile = new CacheEntry(requestedFileCheck);
                    cache.put(requestHeader.getPath(), requestedFile);
                }
            }
        }
       
        //Bad request
        else
        {
            responseHeader.setCode("400");
            responseHeader.setMessage("Bad Request");
           
            //write 400 page to client
            if(cache.containsKey(configurator.get400()))
                requestedFile = cache.get(configurator.get400());
            else
            {
                requestedFile = new CacheEntry(new File(configurator.get400()));
                cache.put(configurator.get400(), requestedFile);
            }
        }
       
        //Fill out responseHeader headers
        DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
        Calendar cal = Calendar.getInstance(new SimpleTimeZone(0, "GMT"));
        df.setCalendar(cal);
        Date now = new Date();
        javax.activation.MimetypesFileTypeMap someMimes = new javax.activation.MimetypesFileTypeMap();
        responseHeader.setDate(df.format(now));
        responseHeader.setServerName(configurator.getServerName());
        responseHeader.setContentType(someMimes.getContentType(requestedFile.getFile()));
        responseHeader.setContentLength(requestedFile.getFile().length());
       
        //write the header and the file to the client
        System.out.print(responseHeader.getCode());
        System.out.println(requestedFile.getFile().toString());
        toClient.write(responseHeader.getBytes());
        toClient.write(requestedFile.getBytes());
        toClient.write("\r\n\r\n".getBytes("US-ASCII"));
        toClient.flush();
       
        //print to log file
        String logEntry = client.getInetAddress().getHostAddress() + ":" + client.getPort() + " [" + df.format(now) + "] \"" + requestHeader.getMethod() + " " + requestHeader.getPath() + " " + responseHeader.getHTTPVersion() + "\" " + responseHeader.getCode() + " " + requestedFile.getFile().length() + "\n";
        OutputStream logOut = null;
        try
        {
            System.out.println("If you've gotten here, we're printing the log!");
            if(logFile.exists())
                logOut = new BufferedOutputStream(new FileOutputStream(logFile, true));
            else
                logOut = new BufferedOutputStream(new FileOutputStream(logFile, false));
            logOut.write(logEntry.getBytes());
        }
        catch (IOException ioe)
        {
            System.err.println(ioe);
            return;
        }
       
        //cleanup any open straems
        finally
        {
            if(logOut != null)
            {
                logOut.flush();
                logOut.close();
            }
        }
    }
}

Wednesday, March 10, 2010

TannHandler (This is the meat of the program)

/**
Daniel Tanner
CMPT 352 - Computer Networking
January 26th, 2010
Handler class for threaded DNS Server
 */

import java.net.*;
import java.lang.Object.*;
import java.util.*;
import java.text.*;
import javax.activation.*;
import java.io.*;

public class TannHandler
{       
    //Create variables
    public static Configuration configurator = TannerWebServer.configurator;
    public static WeakHashMap cache = TannerWebServer.cache;
    public static File logFile = TannerWebServer.logFile;
    public static InputStream fromClient = null;
    public static BufferedOutputStream toClient = null;
    public static File requestedFile = null;
    public static HTTPRequestHeader requestHeader = null;
    public static HTTPResponseHeader responseHeader = null;
   
    public static long startTime = System.currentTimeMillis();
   
    //This method invoked as a separate thread
    public void process(Socket client) throws IOException
    {   
        //try block for opening streams and getting hostname
        try
        {
            //open Streams
            fromClient = (client.getInputStream());
            toClient = new BufferedOutputStream(client.getOutputStream());
       
       
            while(System.currentTimeMillis() - startTime < 60000)
            {
                //Get Request
                System.out.println("Now handing InputStream from client to request header");
                requestHeader = new HTTPRequestHeader(fromClient);
                responseHeader = new HTTPResponseHeader();
       
                //process Request
                processRequest(client);
            }
        }
       
        catch(IOException ioe)
        {
            System.err.println(ioe);
            System.exit(0);
        }
       
                finally
                {
                    // close streams and socket
                    if (fromClient != null)
                        fromClient.close();
                    if (toClient != null)
                        toClient.close();
                    if (client != null)
                        client.close();
                }
    }
   
    /** This function will process the HTTP request
    If GET, will try to find the file in the cache.
    If the file isn't in the hashmap it will add it to the cache.
    If the file doesn't exist, will send the 404 error
    the default response is to send the 400 error
    */
    public void processRequest(Socket client) throws IOException
    {
        //GET command
        if(requestHeader.getMethod() == "GET" || requestHeader.getMethod() == "get")
        {
            //check if the path is asking for the default
            if(requestHeader.getPath() == "/")
            {
                //check cache
                String defaultDoc = TannerWebServer.configurator.getDefaultDocument();
                if(cache.containsKey(defaultDoc))
                {
                    requestedFile = cache.get(defaultDoc);
                }
               
                //not in cache, load it in
                else
                {
                    requestedFile = new File(defaultDoc);
                    cache.put(defaultDoc, requestedFile);
                }
            }
           
            //check if it's in cache, if it's not, add it to cache
            else if(cache.containsKey(requestHeader.getPath()))
            {               
                requestedFile = cache.get(requestHeader.getPath());
            }
            else
            {
                //open new File
                try
                {
                    requestedFile = new File(requestHeader.getPath());
                    if(!requestedFile.exists())
                        throw new FileNotFoundException();
                    cache.put(requestHeader.getPath(), requestedFile);
                }
               
                //404 error
                catch (FileNotFoundException fnfe)
                {
                    responseHeader.setCode("404");
                    responseHeader.setMessage("File Not Found");
               
                    //Write 404 page to client
                    if(cache.containsKey("/404.html"))               
                        requestedFile = cache.get("/404.html");
                    else
                    {
                        requestedFile = new File(configurator.get404());
                        cache.put("/404.html", requestedFile);
                    }
                }
            }
        }
       
        //Bad request
        else
        {
            responseHeader.setCode("400");
            responseHeader.setMessage("Bad Request");
           
            //write 400 page to client
            if(cache.containsKey("/400.html"))
                requestedFile = cache.get("/400.html");
            else
            {
                requestedFile = new File(configurator.get404());
                cache.put("/404.html", requestedFile);
            }
        }
       
        //Fill out responseHeader headers
        DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
        Calendar cal = Calendar.getInstance(new SimpleTimeZone(0, "GMT"));
        df.setCalendar(cal);
        Date now = new Date();
        javax.activation.MimetypesFileTypeMap someMimes = new javax.activation.MimetypesFileTypeMap();
        responseHeader.setDate(df.format(now));
        responseHeader.setServerName(TannerWebServer.configurator.getServerName());
        responseHeader.setContentType(someMimes.getContentType(requestedFile));
        responseHeader.setContentLength(requestedFile.length());
       
        //write the header and the file to the client
        toClient.write(responseHeader.getBytes());
        InputStream requestedFileStream = null;
        try
        {
            requestedFileStream = new BufferedInputStream(new FileInputStream(requestedFile));
        }
        catch(IOException ioe)
        {
            System.err.println(ioe);
            System.exit(0);
        }
       
        while(requestedFileStream.available() > 0)
        {
            toClient.write(requestedFileStream.read());
        }
        toClient.write("\r\n\r\n".getBytes("US-ASCII"));
        toClient.flush();
       
        //print to log file
        String logEntry = client.getInetAddress().getHostAddress() + ":" + client.getPort() + " " + df.format(now) + "\"" + requestHeader.getMethod() + " " + requestHeader.getPath() + " " + responseHeader.getHTTPVersion() + responseHeader.getCode() + " " + requestedFile.length() + "\n";
        OutputStream logOut = null;
        try
        {
            if(logFile.exists())
                logOut = new BufferedOutputStream(new FileOutputStream(logFile, true));
            else
                logOut = new BufferedOutputStream(new FileOutputStream(logFile, false));
            logOut.write(logEntry.getBytes("US-ASCII"));
        }
        catch (IOException ioe)
        {
            System.err.println(ioe);
            System.exit(0);
        }
       
        //restart the timer
        startTime = System.currentTimeMillis();
    }
}

TannConnection

/**
Daniel Tanner
CMPT 352 - Computer Networking
January 26th, 2010
Connection Class for threaded DNS Server
(modified version of G. Gagne's code)
 */

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

public class TannConnection implements Runnable
{
    //create variables
    private Socket    client;
    private static TannHandler handler = new TannHandler();

    public TannConnection(Socket client)
    {
        this.client = client;
    }

    //Separate thread
    public void run()
    {
        try
        {
            System.out.println("Now handing control over to handler");
            handler.process(client);
        }
       
        catch (java.io.IOException ioe)
        {
            System.err.println(ioe);
        }
    }
}

HTTPResponseHeader

/** HTTP Response Header Class
This class takes a byte array and parses it into a HTTP Response Header

Author:  Daniel J. Tanner
Date:    March 9th, 2010
CMPT 352
*/

import java.io.*;
import java.net.*;
import java.lang.*;

public class HTTPResponseHeader
{
    //Fields
    protected String httpVersion_;
    protected String code_;
    protected String message_;
    protected String date_;
    protected String serverName_;
    protected String contentType_;
    protected long contentLength_;
    protected static final String DEFAULT_CODE = "200";
    protected static final String DEFAULT_MESSAGE = "Ok";
    protected static final String DEFAULT_HTTPVERSION = "HTTP/1.0";
    protected static final String DEFAULT_DATE = "January 1st, 1970";
    protected static final String DEFAULT_SERVERNAME = "So Long, And Thanks For All The Fish!";
    protected static final String DEFAULT_CONTENTTYPE = "matter/dark";
    protected static final long DEFAULT_CONTENTLENGTH = 42;
   
    //Constructors
    public HTTPResponseHeader()
    {
        code_ = DEFAULT_CODE;
        message_ = DEFAULT_MESSAGE;
        httpVersion_ = DEFAULT_HTTPVERSION;
        date_ = DEFAULT_DATE;
        serverName_ = DEFAULT_SERVERNAME;
        contentType_ = DEFAULT_CONTENTTYPE;
        contentLength_ = DEFAULT_CONTENTLENGTH;
    }
   
    public HTTPResponseHeader(String httpVersion, String code, String message, String date, String serverName, String contentType, long contentLength)
    {
        code_ = code;
        message_ = message;
        httpVersion_ = httpVersion;
        date_ = date;
        serverName_ = serverName;
        contentType_ = contentType;
        contentLength_ = contentLength;
    }
   
    //set functions
    public void setCode(String code) {code_ = code;}
    public void setMessage(String message) {message_ = message;}
    public void setHTTPVersion(String httpVersion) {httpVersion_ = httpVersion;}
    public void setDate(String date) {date_ = date;}
    public void setServerName(String serverName) {serverName_ = serverName;}
    public void setContentType(String contentType) {contentType_ = contentType;}
    public void setContentLength(long contentLength) {contentLength_ = contentLength;}
   
    //get functions
    public String getCode() {return code_;}
    public String getMessage() {return message_;}
    public String getHTTPVersion() {return httpVersion_;}
    public String getDate() {return date_;}
    public String getServerName() {return serverName_;}
    public String getContentType() {return contentType_;}
    public long getContentLength() {return contentLength_;}
   
    //print function
    public byte[] getBytes() throws IOException
    {
        String header = httpVersion_ + " " + code_ + " " + message_ + "\r\n"
            + "Date:  " + date_ + "\r\n"
            + "Server:  " + serverName_ + "\r\n"
            + "Content-Type:  " + contentType_ + "\r\n"
            + "Content-Length:  " + contentLength_ + "\r\n";
        return header.getBytes("US-ASCII");
    }
       
}

HTTPRequestHeader

/** HTTP Request Header
This Class represents a HTTP Request Header
It takes a byte array and Parses it, basically
Author: Daniel J. Tanner
Date:  March 9th, 2010
CMPT 352
*/
import java.io.*;
import java.net.*;
import java.lang.*;

public class HTTPRequestHeader
{
    //fields
    protected static final String DEFAULT_METHOD = "GET";
    protected static final String DEFAULT_PATH = "/index.html";
    protected static final String DEFAULT_PROTOCOL = "HTTP/1.0";
    protected static final int CHUNK = 5028;
    protected String method_;
    protected String path_;
    protected String protocol_;
   
    //Constructors
    public HTTPRequestHeader()
    {
        method_ = DEFAULT_METHOD;
        protocol_ = DEFAULT_PROTOCOL;
        path_ = DEFAULT_PATH;
    }
   
    //This one is basically the meat, it will parse the byte stream it receives
    public HTTPRequestHeader(InputStream fromClient)
    {
        BufferedReader input = new BufferedReader(new InputStreamReader(fromClient));
        String requestLine = null;
        String requestParsed = null;
        StringBuffer request = new StringBuffer(CHUNK);
        int i = 0;
        try
        {
            requestParsed = input.readLine();
       
            while((requestLine = input.readLine()) != null)
            {
                if(requestLine.length() == 0) break;
                i += requestLine.length();
                request.append(requestLine);
            }
        }
        catch (IOException ioe)
        {
            ioe.printStackTrace();
            i = -1;
        }
       
        //Now we parse!
        //String requestParsed = request.toString();
        int index1, index2;
        i = requestParsed.length();
        index1 = requestParsed.indexOf(' ');
       
        //getMethod
        if(index1 != -1)
        {
            method_ = requestParsed.substring(0, index1 - 1);
        }
        index2 = requestParsed.indexOf(' ', index1 + 1);
       
        //getURI
        if(index2 > index1)
        {
            path_ = requestParsed.substring(index1 + 1, index2 - 1);
        }
        index1 = requestParsed.indexOf(' ', index2 + 1);
       
        //getProtocol
        if(index1 > index2)
        {
            protocol_ = requestParsed.substring(index2 + 1, index1 - 1);
        }
       
    }
   
    //Get Functions
    public String getMethod() {return method_;}
    public String getProtocol() {return protocol_;}
    public String getPath() {return path_;}
}

ConfigurationException

public class ConfigurationException extends Exception
{
    public ConfigurationException(String exception) {
        super(exception);
    }
}

Configuration

/**
 * XML parser for configuration parameters.
 *
 * This maps configuration parameters to a HashMap and are retrieved
 * through the following getter methods:
 *
 *     public String getLogFile()
 *     public String getDocumentRoot()
 *     public String getDefaultDocument()
 *     public String getServerName()
 *
 * Usage:
 *     Configuration config = new Configuration();
 *
 *    config.getLogFile();
 *    config.getDocumentRoot();
 *    config.getDefaultDocument();
 *    config.getServerName();
 */

import java.io.*;

import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;

import java.util.Map;
import java.util.HashMap;

public class Configuration extends DefaultHandler
{
    private Map  map;
    private String configurationFile;

    /**
     * @param File configurationFile - The name of the configuration file
     */
    public Configuration(String configurationFile) throws ConfigurationException {
    this.configurationFile = configurationFile;

    map = new HashMap();

    try {       
            // Use the default (non-validating) parser
            SAXParserFactory factory = SAXParserFactory.newInstance();

            // Parse the input
            SAXParser saxParser = factory.newSAXParser();
            saxParser.parse( new File(configurationFile), this);
    }
    catch (javax.xml.parsers.ParserConfigurationException pce) {
        throw new ConfigurationException("javax.xml.parsers.ParserConfigurationException");
    }
    catch (org.xml.sax.SAXException se) {
        throw new ConfigurationException("org.xml.sax.SAXException");
    }
    catch (java.io.IOException ioe) {
        throw new ConfigurationException("java.io.IOException");
    }
    }


    /**
     * We will map each configuration attribute to its value
     *
     * @param namespaceURI the namespace
     * @param lName the local name of the element
     * @param qName the qualified name of the element
     * @param attrs the set of attributess associated with the element
      */
    public void startElement(String namespaceURI,
                             String lName,    
                             String qName,    
                             Attributes attrs)   
    throws SAXException
    {
        String elementName = lName; // element name
        if ("".equals(elementName))
        elementName = qName; // namespaceAware = false

    /**
     * Get the attributes associated with this ELEMENT.
      * Attributes are name/value pairs and are stored by index.
     */
        if (attrs != null) {
            for (int i = 0; i < attrs.getLength(); i++) {
                String aName = attrs.getLocalName(i); // Attr name
                if ("".equals(aName))
            aName = attrs.getQName(i);

        // map the element.attribute to its value
        map.put(elementName+"."+aName,attrs.getValue(i));
            }
        }
    }

    // getter methods for mapped configuration values

    /** Returns the location of the log file */
    public String getLogFile() {
        return map.get("logfile.log");
    }

    /** Returns the location of the document base */
    public String getDocumentRoot() {
        return map.get("context.documentRoot");
    }

    /** Returns the name of the default document */
    public String getDefaultDocument() {
        return map.get("context.defaultDocument");
    }

    /** Returns the name of the server */
    public String getServerName() {
        return map.get("webserver.title");
    }
   
    /** Returns the name of the 400 file */
    public String get400()
    {
        return map.get("context.fourHundredDocument");
    }
   
    /** Returns the name of the 404 file */
    public String get404()
    {
        return map.get("context.fourOhFourDocument");
    }
}

TannerWebServer (not working)

/** Web Server
by Daniel J. Tanner
March 9th, 2010
CMPT 352

This program implements some basic features of the HTTP protocol
**/

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


public class TannerWebServer
{
    //constants & globals
    public static final int PORT = 2880;
    public static Configuration configurator = null;
    public static WeakHashMap cache = null;
    public static File logFile = null;
   
    public static void main(String[] args) throws java.io.IOException, ConfigurationException
    {
        //begin by initializng the configurator       
        try
        {
            configurator = new Configuration("../conf/config.xml");
        }
       
        catch (ConfigurationException ce)
        {
            System.out.println(ce);
            System.exit(0);
        }
       
        //load the initial files into a hashmap
        cache = new WeakHashMap(20);
        cache.put("/index.html", new File(configurator.getDefaultDocument()));
        cache.put("/400.html", new File(configurator.get400()));
        cache.put("/404.html", new File(configurator.get404()));
       
        //load logfile
        logFile = new File(configurator.getLogFile());
       
        //Create socket
        ServerSocket sock = null;
        try
        {
            //establish socket
            sock = new ServerSocket(PORT);
           
            while(true)
            {
                Socket client = sock.accept();
                //debug testing
                InetAddress ipAddr = client.getInetAddress();
                System.out.print(ipAddr.getHostAddress() + " : ");
                System.out.println(client.getPort());
                Thread worker = new Thread(new TannConnection(client));
               
                worker.start();
            }
        }
        //catch any errors opening sockets
        catch(IOException ioe)
        {
            System.err.println(ioe);
            System.exit(0);
        }
       
        finally
        {
            //close streams
            if(sock != null)
                sock.close();
        }
    }
}