FileDocCategorySizeDatePackage
Handler.javaAPI DocExample7914Sun Mar 14 11:47:22 GMT 2004None

Handler

public 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.

version
$Id: Handler.java,v 1.14 2004/03/14 17:47:22 ian Exp $

Fields Summary
protected BufferedReader
is
inputStream, from Viewer
protected PrintStream
os
outputStream, to Viewer
protected Httpd
parent
Main program
protected static final String
DEF_NAME
The default filename in a directory.
private static Hashtable
h
The 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
UNKNOWN
The 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
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=\"" + rqName + File.separator + fl[i] + "\">" +
			"<img align='center' border='0' src=\"/images/file.jpg\">" +
			' " + 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.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");
		}
	
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();
	
protected 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.getMimeType(exten, UNKNOWN);

		return guess;
	
public voidprocess(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 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.

param
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);