Handlerpublic class Handler extends Object Called from Httpd in a Thread to handle one connection.
We are created with just a Socket, and read the
HTTP request, extract a name, read it (saving it
in Hashtable h for next time), and write it back.
TODO split into general handler stuff and "FileServlet",
then handle w/ either user HttpServlet subclasses or FileServlet. |
Fields Summary |
---|
protected BufferedReader | isinputStream, from Viewer | protected PrintStream | osoutputStream, to Viewer | protected Httpd | parentMain program | protected static final String | DEF_NAMEThe default filename in a directory. | private static Hashtable | hThe Hashtable used to cache all URLs we've read.
Static, shared by all instances of Handler (one Handler per request;
this is probably quite inefficient, but simple. Need ThreadPool).
Note that Hashtable methods *are* synchronized. | protected static final int | RQ_INVALID | protected static final int | RQ_GET | protected static final int | RQ_HEAD | protected static final int | RQ_POST | static final String | UNKNOWNThe type for unguessable files |
Constructors Summary |
---|
Handler(Httpd parent)Construct a Handler
h.put("", "<html><body><b>Unknown server error</b>".getBytes());
this.parent = parent;
|
Methods Summary |
---|
void | doDirList(java.lang.String rqName, java.io.File dir, boolean justAHead, java.io.PrintStream os)
os.println("HTTP/1.0 200 directory found");
os.println("Content-type: text/html");
os.println("Date: " + new Date().toString());
os.println("");
if (justAHead)
return;
os.println("<HTML>");
os.println("<TITLE>Contents of directory " + rqName + "</TITLE>");
os.println("<H1>Contents of directory " + rqName + "</H1>");
String fl[] = dir.list();
Arrays.sort(fl);
for (int i=0; i<fl.length; i++)
os.println("<br/><a href=\"" + rqName + File.separator + fl[i] + "\">" +
"<img align='center' border='0' src=\"/images/file.jpg\">" +
' " + fl[i] + "</a>");
| void | doFile(java.lang.String rqName, boolean headerOnly, java.io.PrintStream os)Processes one file request
File f;
byte[] content = null;
Object o = h.get(rqName);
if (o != null && o instanceof byte[]) {
content = (byte[])o;
System.out.println("Using cached file " + rqName);
sendFile(rqName, headerOnly, content, os);
} else if ((f = new File(parent.getRootDir() + rqName)).isDirectory()) {
// Directory with index.html? Process it.
File index = new File(f, DEF_NAME);
if (index.isFile()) {
doFile(rqName + DEF_NAME, index, headerOnly, os);
return;
}
else {
// Directory? Do not cache; always make up dir list.
System.out.println("DIRECTORY FOUND");
doDirList(rqName, f, headerOnly, os);
sendEnd();
}
} else if (f.canRead()) {
// REGULAR FILE
doFile(rqName, f, headerOnly, os);
}
else {
errorResponse(404, "File not found");
}
| void | doFile(java.lang.String rqName, java.io.File f, boolean headerOnly, java.io.PrintStream os)Send one file, given a File object.
System.out.println("Loading file " + rqName);
InputStream in = new FileInputStream(f);
byte c_content[] = new byte[(int)f.length()];
// Single large read, should be fast.
int n = in.read(c_content);
h.put(rqName, c_content);
sendFile(rqName, headerOnly, c_content, os);
in.close();
| protected void | errorResponse(int errNum, java.lang.String errMsg)Sends an error response, by number, hopefully localized.
// Check for localized messages
ResourceBundle messages = ResourceBundle.getBundle("errors");
String response;
try { response = messages.getString(Integer.toString(errNum)); }
catch (MissingResourceException e) { response=errMsg; }
// Generate and send the response
os.println("HTTP/1.0 " + errNum + " " + response);
os.println("Content-type: text/html");
os.println();
os.println("<html>");
os.println("<head><title>Error " + errNum + "--" + response +
"</title></head>");
os.println("<h1>" + errNum + " " + response + "</h1>");
sendEnd();
| protected java.lang.String | guessMime(java.lang.String fn)
String lcname = fn.toLowerCase();
int extenStartsAt = lcname.lastIndexOf('.");
if (extenStartsAt<0) {
if (fn.equalsIgnoreCase("makefile"))
return "text/plain";
return UNKNOWN;
}
String exten = lcname.substring(extenStartsAt);
String guess = parent.getMimeType(exten, UNKNOWN);
return guess;
| public void | process(java.net.Socket clntSock)
String request; // what Viewer sends us.
int methodType = RQ_INVALID;
try {
System.out.println("Connection accepted from " +
clntSock.getInetAddress());
is = new BufferedReader(new InputStreamReader(
clntSock.getInputStream()));
// Must do before any chance of errorResponse being called!
os = new PrintStream(clntSock.getOutputStream());
request = is.readLine();
if (request == null || request.length() == 0) {
// No point nattering: the sock died, nobody will hear
// us if we scream into cyberspace... Could log it though.
return;
}
// Use a StringTokenizer to break the request into its three parts:
// HTTP method, resource name, and HTTP version
StringTokenizer st = new StringTokenizer(request);
if (st.countTokens() != 3) {
errorResponse(444, "Unparseable input " + request);
return;
}
String rqCode = st.nextToken();
String rqName = st.nextToken();
String rqHttpVer = st.nextToken();
System.out.println("Request: Command " + rqCode +
", file " + rqName + ", version " + rqHttpVer);
// Read headers, up to the null line before the body,
// so the body can be read directly if it's a POST.
HashMap map = new HashMap();
String hdrLine;
while ((hdrLine = is.readLine()) != null &&
hdrLine.length() != 0) {
int ix;
if ((ix=hdrLine.indexOf(':")) != -1) {
String hdrName = hdrLine.substring(0, ix);
String hdrValue = hdrLine.substring(ix+1).trim();
Debug.println("hdr", hdrName+","+hdrValue);
map.put(hdrName, hdrValue);
} else {
System.err.println("INVALID HEADER: " + hdrLine);
}
}
// check that rqCode is either GET or HEAD or ...
if ("get".equalsIgnoreCase(rqCode))
methodType = RQ_GET;
else if ("head".equalsIgnoreCase(rqCode))
methodType = RQ_HEAD;
else if ("post".equalsIgnoreCase(rqCode))
methodType = RQ_POST;
else {
errorResponse(400, "invalid method: " + rqCode);
return;
}
// A bit of paranoia may be a good thing...
if (rqName.indexOf("..") != -1) {
errorResponse(404, "can't seem to find: " + rqName);
return;
}
// XXX new MyRequest(clntSock, rqName, methodType);
// XXX new MyResponse(clntSock, os);
// XXX if (isServlet(rqName)) [
// doServlet(rqName, methodType, map);
// else
doFile(rqName, methodType == RQ_HEAD, os /*, map */);
os.flush();
clntSock.close();
} catch (IOException e) {
System.out.println("IOException " + e);
}
System.out.println("END OF REQUEST");
| protected void | sendEnd()Send the tail end of any page we make up.
os.println("<hr>");
os.println("<address>Java Web Server,");
String myAddr = "http://www.darwinsys.com/freeware/";
os.println("<a href=\"" + myAddr + "\">" +
myAddr + "</a>");
os.println("</address>");
os.println("</html>");
os.println();
| void | sendFile(java.lang.String fname, boolean justHead, byte[] content, java.io.PrintStream os)Send one file, given the filename and contents.
os.println("HTTP/1.0 200 Here's your file");
os.println("Content-type: " + guessMime(fname));
os.println("Content-length: " + content.length);
os.println();
if (justHead)
return;
os.write(content);
|
|