Handlerpublic class Handler extends Thread Called from Httpd 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. |
Fields Summary |
---|
Socket | clntSockThe Socket that we read from and write to. | BufferedReader | isinputStream, from Viewer | PrintStream | osoutputStream, to Viewer | Httpd | parentMain program | static final String | DEF_NAMEThe default filename in a directory. | static Hashtable | hThe Hashtable used to cache all URLs we've read.
Static, shared by all instances of Handler (one per request). | 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 prnt, Socket sock)Construct a Handler
super("client thread");
parent = prnt;
clntSock = sock;
// First time, put in null handler.
if (h.size() == 0) {
h.put("", "<HTML><BODY><B>Unknown server error".getBytes());
}
|
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=\"" + fl[i] + "\">" +
"<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-unknown\">" +
' " + 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.rootDir + 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();
| 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.mimeTypes.getProperty(exten, UNKNOWN);
// System.out.println("guessMime: input " + fn +
// ", extention " + exten + ", result " + guess);
return guess;
| public void | run()
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();
System.out.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.
boolean justHead - if true, send heading and return.
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);
|
|