FileDocCategorySizeDatePackage
Handler.javaAPI DocExample7867Sat Oct 27 15:57:20 BST 2001None

Handler

public 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.
author
Ian F. Darwin, ian@darwinsys.com
version
$Id: Handler.java,v 1.10 2001/10/27 18:57:20 ian Exp $

Fields Summary
Socket
clntSock
The Socket that we read from and write to.
BufferedReader
is
inputStream, from Viewer
PrintStream
os
outputStream, to Viewer
Httpd
parent
Main program
static final String
DEF_NAME
The default filename in a directory.
static Hashtable
h
The 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
UNKNOWN
The 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
voiddoDirList(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>");
	
voiddoFile(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");
		}
	
voiddoFile(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 voiderrorResponse(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.StringguessMime(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 voidrun()

 

	   
		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 voidsendEnd()
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();
	
voidsendFile(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);