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
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
private Configuration configurator = null;
private File logFile = null;
private static TannHandler handler = new TannHandler();
public TannConnection(Socket client, Configuration configurator, File logFile, WeakHashMap
{
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
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
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
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
{
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();
}
}
}
}