3000行代码实现一个servlet server
// All enhancments Copyright (C)1998-2003 by Dmitriy Rogatkin// this version is compatible with JSDK 2.4// http://tjws.sourceforge.net// $Id: Serve.java,v 1.49 2004/02/06 07:10:20 rogatkin Exp $package Acme.Serve;import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.ByteArrayOutputStream;import java.io.DataInputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.FileReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintStream;import java.io.PrintWriter;import java.io.StringBufferInputStream;import java.io.StringWriter;import java.io.UnsupportedEncodingException;import java.net.InetAddress;import java.net.MalformedURLException;import java.net.ServerSocket;import java.net.Socket;import java.net.URL;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Enumeration;import java.util.HashMap;import java.util.Hashtable;import java.util.Locale;import java.util.Map;import java.util.StringTokenizer;import java.util.TimeZone;import java.util.Vector;import javax.servlet.RequestDispatcher;import javax.servlet.Servlet;import javax.servlet.ServletConfig;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.ServletInputStream;import javax.servlet.ServletOutputStream;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.SingleThreadModel;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import javax.servlet.http.HttpSessionBindingEvent;import javax.servlet.http.HttpSessionBindingListener;import javax.servlet.http.HttpSessionContext;import javax.servlet.http.HttpUtils;/// Minimal Java HTTP server class.// <P>// This class implements a very small embeddable HTTP server.// It runs Servlets compatible with the API used by JavaSoft's// <A HREF="http://java.sun.com/products/java-server/">JavaServer</A> server.// It comes with default Servlets which provide the usual// httpd services, returning files and directory listings.// <P>// This is not in any sense a competitor for JavaServer.// JavaServer is a full-fledged HTTP server and more.// Acme.Serve is tiny, about 1500 lines, and provides only the// functionality necessary to deliver an Applet's .class files// and then start up a Servlet talking to the Applet.// They are both written in Java, they are both web servers, and// they both implement the Servlet API; other than that they couldn't// be more different.// <P>// This is actually the second HTTP server I've written.// The other one is called// <A HREF="http://www.acme.com/software/thttpd/">thttpd</A>,// it's written in C, and is also pretty small although much more// featureful than this.// <P>// Other Java HTTP servers:// <UL>// <LI> The above-mentioned <A HREF="http://java.sun.com/products/java-server/">JavaServer</A>.// <LI> W3C's <A HREF="http://www.w3.org/pub/WWW/Jigsaw/">Jigsaw</A>.// <LI> David Wilkinson's <A HREF="http://www.netlink.co.uk/users/cascade/http/">Cascade</A>.// <LI> Yahoo's <A HREF="http://www.yahoo.com/Computers_and_Internet/Software/Internet/World_Wide_Web/Servers/Java/">list of Java web servers</A>.// </UL>// <P>// A <A HREF="http://www.byte.com/art/9706/sec8/art1.htm">June 1997 BYTE magazine article</A> mentioning this server.<BR>// A <A HREF="http://www.byte.com/art/9712/sec6/art7.htm">December 1997 BYTE magazine article</A> giving it an Editor's Choice Award of Distinction.<BR>// <A HREF="/resources/classes/Acme/Serve/Serve.java">Fetch the software.</A><BR>// <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>// <P>// @see Acme.Serve.servlet.http.HttpServlet// @see FileServlet// @see CgiServlet// make it final?public class Serve implements ServletContext, RequestDispatcher{private static final String progName = "Serve";public static final String ARG_PORT = "port";public static final String ARG_THROTTLES = "throttles";public static final String ARG_SERVLETS = "servlets";public static final String ARG_REALMS = "realms";public static final String ARG_ALIASES = "aliases";public static final String ARG_CGI_PATH = "cgi-path";public static final String ARG_SESSION_TIMEOUT = "session-timeout";public static final String ARG_LOG_OPTIONS = "log-options";public static final String ARG_SOCKET_FACTORY = "socketFactory";public static final String ARG_QUIET = "quiet";public static final String ARG_NOHUP = "nohup";protected static final int DEF_SESSION_TIMEOUT = 30; // in minutesprotected static final int DEF_PORT = 8888;/// Main routine, if you want to run this directly as an application.public static void main(String[] args){Map arguments = new HashMap(20);int argc = args.length;int argn;// Parse args.workPath = System.getProperty("user.dir", ".");if (argc == 0) // a try to read from file for java -jar server.jartry{BufferedReader br = new BufferedReader(new FileReader(new File(workPath, "cmdparams")));StringTokenizer st = new StringTokenizer(br.readLine(), " ");args = new String[st.countTokens()];argc = args.length; // tail can be nulledfor (int i = 0; i < argc && st.hasMoreTokens(); i++)args[i] = st.nextToken();}catch (Exception e){ // many can happen}for (argn = 0; argn < argc && args[argn].charAt(0) == '-';){if (args[argn].equals("-p") && argn + 1 < argc){++argn;arguments.put(ARG_PORT, new Integer(args[argn]));}else if (args[argn].equals("-t") && argn + 1 < argc){++argn;arguments.put(ARG_THROTTLES, args[argn]);}else if (args[argn].equals("-s") && argn + 1 < argc){++argn;arguments.put(ARG_SERVLETS, args[argn]);}else if (args[argn].equals("-r") && argn + 1 < argc){++argn;arguments.put(ARG_REALMS, args[argn]);}else if (args[argn].equals("-a") && argn + 1 < argc){++argn;arguments.put(ARG_ALIASES, args[argn]);}else if (args[argn].equals("-c") && argn + 1 < argc){++argn;arguments.put(ARG_CGI_PATH, args[argn]);}else if (args[argn].equals("-e") && argn + 1 < argc){++argn;try{arguments.put(ARG_SESSION_TIMEOUT, new Integer(args[argn]));}catch (NumberFormatException nfe){}}else if (args[argn].startsWith("-l")){if (args[argn].length() > 2)arguments.put(ARG_LOG_OPTIONS, args[argn].substring(2).toUpperCase());elsearguments.put(ARG_LOG_OPTIONS, "");}else if (args[argn].startsWith("-nohup")){arguments.put(ARG_NOHUP, ARG_NOHUP);}else if (args[argn].equals("-q")){arguments.put(ARG_QUIET, ARG_QUIET);}else if (args[argn].startsWith("-")){if (args[argn].length() > 1)arguments.put(args[argn].substring(1),//.toUpperCase(),argn < argc - 1 ? args[++argn] : "");}elseusage();++argn;}if (argn != argc)usage();/** format path mappingfrom=givenpath;dir=realpath*/PrintStream printstream = System.err;try{printstream =new PrintStream(new FileOutputStream(new File(workPath, "AWS-" + System.currentTimeMillis() + ".log")),true);System.setErr(printstream);}catch (IOException e){System.err.println("IO problem at setting a log stream " + e);}PathTreeDictionary mappingtable = new PathTreeDictionary();if (arguments.get(ARG_ALIASES) != null){File file = new File((String) arguments.get(ARG_ALIASES));if (file.isAbsolute() == false)file = new File(workPath, file.getName());if (file.exists() && file.canRead()){try{DataInputStream in = new DataInputStream(new FileInputStream(file));do{String mappingstr = in.readLine();if (mappingstr == null)break;StringTokenizer maptokenzr = new StringTokenizer(mappingstr, "=;");if (maptokenzr.hasMoreTokens()){if (maptokenzr.nextToken("=").equalsIgnoreCase("from")){if (maptokenzr.hasMoreTokens()){String srcpath = maptokenzr.nextToken("=;");if (maptokenzr.hasMoreTokens()&& maptokenzr.nextToken(";=").equalsIgnoreCase("dir"))try{if (maptokenzr.hasMoreTokens()){String value = maptokenzr.nextToken();File mapFile = new File(value);if (mapFile.isAbsolute() == false)mapFile = new File(workPath, mapFile.getName());mappingtable.put(srcpath, mapFile);}}catch (NullPointerException e){}}}}}while (true);}catch (IOException e){System.err.println("Problem reading aliases file: " + arguments.get(ARG_ALIASES) + "/" + e);}}elseSystem.err.println("File " + arguments.get(ARG_ALIASES) + " doesn't exist or not readable.");}/** format realmname=path,user:password,,,,*/PathTreeDictionary realms = new PathTreeDictionary();if (arguments.get(ARG_REALMS) != null){try{File file = new File((String) arguments.get(ARG_REALMS));if (file.isAbsolute() == false)file = new File(workPath, file.getName());DataInputStream in = new DataInputStream(new FileInputStream(file));do{String realmstr = in.readLine();if (realmstr == null)break;StringTokenizer rt = new StringTokenizer(realmstr, "=,:");if (rt.hasMoreTokens()){String realmname = null;realmname = rt.nextToken();if (rt.hasMoreTokens()){String realmPath = null;realmPath = rt.nextToken();if (rt.hasMoreTokens()){String user = rt.nextToken();if (rt.hasMoreTokens()){String password = rt.nextToken();BasicAuthRealm realm = null;Object o[] = realms.get(realmPath);if (o != null && o[0] != null)realm = (BasicAuthRealm) o[0];else{realm = new BasicAuthRealm(realmname);realms.put(realmPath, realm);}realm.put(user, password);}}}}}while (true);}catch (IOException ioe){System.err.println("Problem in reading realms file " + arguments.get(ARG_REALMS) + "/ " + ioe);}}// Create the server.serve = new Serve(arguments, printstream);File tempFile = arguments.get(ARG_SERVLETS) == null ? null : new File((String) arguments.get(ARG_SERVLETS));if (tempFile != null && tempFile.isAbsolute() == false)tempFile = new File(workPath, tempFile.getName());final File servFile = tempFile;// PathTreeDictionary mappingtable = new PathTreeDictionary();// File mapFile = new File("D:\\webfiles");// mappingtable.put("/", mapFile);serve.setMappingTable(mappingtable);serve.setRealms(realms);new Thread(new Runnable(){public void run(){serve.readServlets(servFile);}}).start();// And add the standard Servlets.String throttles = (String) arguments.get(ARG_THROTTLES);if (throttles == null)serve.addDefaultServlets((String) arguments.get(ARG_CGI_PATH));elsetry{serve.addDefaultServlets((String) arguments.get(ARG_CGI_PATH), throttles);}catch (IOException e){System.err.println("Problem reading throttles file: " + e);System.exit(1);}// And run.serve.serve();//System.exit( 0 );}private static void usage(){System.err.println("usage: "+ progName+ " [-p port] [-s servletpropertiesfile] [-a aliasmappingfile] [-l[a][r]] [-c cgi-bin-dir] [-e [-]duration_in_minutes] [-nohup] [-socketFactory class name and other parameters}");System.exit(1);}/** * Stops the server * * @throws IOException */public static void stop() throws IOException{if (serve != null){serve.notifyStop();}}/** * Starts the server. * * @param port the port to use * @param servlets an array of strings of the form {"servletName", "servletClass"} for example * {"RequestHeader", "com.datasweep.compatibility.servlet.RequestHeaderServlet"} * @param aliases an array of strings of the form {"/", "fully qualified path to webroot directory"} * @param consoleQuit to have server listen for a 'q' on the console to quit */public static void start(int port, String[] servlets, String[] aliases, boolean consoleQuit, boolean quiet){Map arguments = new HashMap(20);// portarguments.put(ARG_PORT, new Integer(port));// nohupif (!consoleQuit){arguments.put(ARG_NOHUP, ARG_NOHUP);}// loggingPrintStream printstream = System.err;if (quiet){arguments.put(ARG_QUIET, ARG_QUIET);}// aliases// PathTreeDictionary mappingtable = new PathTreeDictionary();// File mapFile = new File("D:\\webfiles");// mappingtable.put("/", mapFile);PathTreeDictionary mappingtable = new PathTreeDictionary();for (int i = 0; i < aliases.length; i = i + 2){mappingtable.put(aliases[i], new File(aliases[i + 1]));}// Create the server.serve = new Serve(arguments, printstream);serve.setMappingTable(mappingtable);// servletsfor (int i = 0; i < servlets.length; i = i + 2){serve.addServlet(servlets[i], servlets[i + 1]);}// And add the standard Servlets.serve.addDefaultServlets(null);serve.serve();}private void readServlets(File servFile){/** servlet.properties file formatservlet.<servletname>.code=<servletclass>servlet.<servletname>.initArgs=<name=value>,<name=value> */Hashtable servletstbl, parameterstbl;servletstbl = new Hashtable();parameterstbl = new Hashtable();if (servFile != null && servFile.exists() && servFile.canRead()){try{DataInputStream in = new DataInputStream(new FileInputStream(servFile));/** format of servlet.cfg fileservlet_name;servlet_class;init_parameter1=value1;init_parameter2=value2...*/do{String servletdsc = in.readLine();if (servletdsc == null)break;StringTokenizer dsctokenzr = new StringTokenizer(servletdsc, ".=,", false);if (dsctokenzr.hasMoreTokens()){if (!dsctokenzr.nextToken().equalsIgnoreCase("servlet")){System.err.println("No leading 'servlet' keyword, the sentence is skipped");break;}if (dsctokenzr.hasMoreTokens()){String servletname = dsctokenzr.nextToken();if (dsctokenzr.hasMoreTokens()){String lt = dsctokenzr.nextToken();if (lt.equalsIgnoreCase("code")){if (dsctokenzr.hasMoreTokens())servletstbl.put(servletname, dsctokenzr.nextToken("="));}else if (lt.equalsIgnoreCase("initArgs")){Hashtable initparams = new Hashtable();while (dsctokenzr.hasMoreTokens()){String key = dsctokenzr.nextToken("=,");if (dsctokenzr.hasMoreTokens())initparams.put(key, dsctokenzr.nextToken(",="));}parameterstbl.put(servletname, initparams);}elseSystem.err.println("Unrecognized token " + lt + " in " + servletdsc + ", the line's skipped");}}}}while (true);}catch (IOException e){System.err.println("Problem reading cfg file: " + e);}Enumeration se = servletstbl.keys();String servletname;while (se.hasMoreElements()){servletname = (String) se.nextElement();addServlet(servletname,(String) servletstbl.get(servletname),(Hashtable) parameterstbl.get(servletname));}}}int port;String hostName;private PrintStream logStream;private boolean useAccLog;private boolean showUserAgent;private boolean showReferer;private boolean quiet = false;protected PathTreeDictionary registry;protected PathTreeDictionary realms;private PathTreeDictionary mappingtable;private Hashtable attributes;sun.misc.BASE64Decoder base64Dec = new sun.misc.BASE64Decoder();// for sessionsint uniqer;HttpSessionContextImpl sessions;static int expiredIn;protected Map arguments;protected static Serve serve;protected static String workPath;/// Constructor.public Serve(Map arguments, PrintStream logStream){this.arguments = arguments;this.logStream = logStream;registry = new PathTreeDictionary();realms = new PathTreeDictionary();attributes = new Hashtable();sessions = new HttpSessionContextImpl();setAccessLogged();expiredIn =arguments.get(ARG_SESSION_TIMEOUT) != null? ((Integer) arguments.get(ARG_SESSION_TIMEOUT)).intValue(): DEF_SESSION_TIMEOUT;port = arguments.get(ARG_PORT) != null ? ((Integer) arguments.get(ARG_PORT)).intValue() : DEF_PORT;quiet = (arguments.get(ARG_QUIET) != null);}public Serve(){this(new HashMap(), System.err);}void setAccessLogged(){String logflags = (String) arguments.get(ARG_LOG_OPTIONS);if (logflags != null){useAccLog = true;showUserAgent = logflags.indexOf('A') >= 0;showReferer = logflags.indexOf('R') >= 0;}}boolean isAccessLogged(){return useAccLog;}boolean isShowReferer(){return showReferer;}boolean isShowUserAgent(){return showUserAgent;}/// Register a Servlet by class name. Registration consists of a URL// pattern, which can contain wildcards, and the class name of the Servlet// to launch when a matching URL comes in. Patterns are checked for// matches in the order they were added, and only the first match is run.public void addServlet(String urlPat, String className){addServlet(urlPat, className, (Hashtable) null);}public void addServlet(String urlPat, String className, Hashtable initParams){// Check if we're allowed to make one of these.SecurityManager security = System.getSecurityManager();if (security != null){int i = className.lastIndexOf('.');if (i != -1){security.checkPackageAccess(className.substring(0, i));security.checkPackageDefinition(className.substring(0, i));}}// Make a new one.try{addServlet(urlPat, (Servlet) Class.forName(className).newInstance(), initParams);return;}catch (ClassNotFoundException e){log("Class not found: " + className);}catch (ClassCastException e){log("Class cast problem: " + e.getMessage());}catch (InstantiationException e){log("Instantiation problem: " + e.getMessage());}catch (IllegalAccessException e){log("Illegal class access: " + e.getMessage());}catch (Exception e){log("Unexpected problem creating servlet: " + e, e);}}/// Register a Servlet. Registration consists of a URL pattern,// which can contain wildcards, and the Servlet to// launch when a matching URL comes in. Patterns are checked for// matches in the order they were added, and only the first match is run.public void addServlet(String urlPat, Servlet servlet){addServlet(urlPat, servlet, (Hashtable) null);}public void addServlet(String urlPat, Servlet servlet, Hashtable initParams){try{servlet.init(new ServeConfig((ServletContext) this, initParams, urlPat));registry.put(urlPat, servlet);}catch (ServletException e){log("Problem initializing servlet: " + e);}}/// Register a standard set of Servlets. These will return// files or directory listings, and run CGI programs, much like a// standard HTTP server.// <P>// Because of the pattern checking order, this should be called// <B>after</B> you've added any custom Servlets.// <P>// The current set of default servlet mappings:// <UL>// <LI> If enabled, *.cgi goes to CgiServlet, and gets run as a CGI program.// <LI> * goes to FileServlet, and gets served up as a file or directory.// </UL>// @param cgi whether to run CGI programs// TODO: provide user specified CGI directorypublic void addDefaultServlets(String cgi){if (cgi != null)addServlet("/" + cgi, new Acme.Serve.CgiServlet());addServlet("/", new Acme.Serve.FileServlet());}/// Register a standard set of Servlets, with throttles.// @param cgi whether to run CGI programs// @param throttles filename to read FileServlet throttle settings frompublic void addDefaultServlets(String cgi, String throttles) throws IOException{if (cgi != null)addServlet("/" + cgi, new Acme.Serve.CgiServlet());addServlet("/", new Acme.Serve.FileServlet(throttles, null));}// Run the server. Returns only on errors.boolean running = true;protected ServerSocket serverSocket;protected Thread ssclThread;boolean do_exit = false;public void serve(){try{serverSocket = createServerSocket();}catch (IOException e){log("Server socket: " + e);return;}hostName = serverSocket.getInetAddress().getHostName();if (arguments.get(ARG_NOHUP) == null)new Thread(new Runnable(){public void run(){BufferedReader in = new BufferedReader(new InputStreamReader(System.in));String line;while (true){try{System.out.print("Press \"q\" <ENTER>, for gracefully stopping the server ");line = in.readLine();if (line != null && line.length() > 0 && line.charAt(0) == 'q'){notifyStop();do_exit = true;break;}}catch (IOException e){System.err.println("Exception in reading from console " + e);break;}}}}, "Stop Monitor").start();// else create kill signal handlerif (expiredIn > 0){ssclThread = new Thread(new Runnable(){public void run(){while (running){try{Thread.sleep(expiredIn * 60 * 1000);}catch (InterruptedException ie){if (running == false)break;}Enumeration e = sessions.keys();while (e.hasMoreElements()){Object sid = e.nextElement();if (sid != null){AcmeSession as = (AcmeSession) sessions.get(sid);if (as != null&& (as.getMaxInactiveInterval() * 1000< System.currentTimeMillis() - as.getLastAccessedTime()|| !as.isValid())){ //log("sesion is timeouted, last accessed " + new Date(as.getLastAccessedTime()));// hashtable is synchronized implas = (AcmeSession) sessions.remove(sid);if (as != null && as.isValid())try{as.invalidate();}catch (IllegalStateException ise){}}}}}}}, "Session cleaner");ssclThread.setPriority(Thread.MIN_PRIORITY);ssclThread.start();}elseexpiredIn = -expiredIn;if (!quiet){System.out.println("WebServer :" + port + " is ready.");}try{while (running){Socket socket = serverSocket.accept();new ServeConnection(socket, this);}}catch (IOException e){log("Accept: " + e);}finally{try{serverSocket.close();}catch (IOException e){}destroyAllServlets();if (do_exit){System.exit(0);}}}protected void notifyStop() throws IOException{running = false;if (serverSocket != null)serverSocket.close();if (ssclThread != null)ssclThread.interrupt();}public static interface SocketFactory{public ServerSocket createSocket(Map arguments) throws IOException, IllegalArgumentException;}protected ServerSocket createServerSocket() throws IOException{String socketFactoryClass = (String) arguments.get(ARG_SOCKET_FACTORY);if (socketFactoryClass != null)try{return ((SocketFactory) Class.forName(socketFactoryClass).newInstance()).createSocket(arguments);}catch (Exception e){System.err.println("Couldn't create custom socket factory "+ socketFactoryClass+ " or call creation method. Standard socket will be created. "+ e);}return new ServerSocket(port, 1000);}// Methods from ServletContext./// Gets a servlet by name.// @param name the servlet name// @return null if the servlet does not existpublic Servlet getServlet(String name){try{return (Servlet) ((Object[]) registry.get(name))[0];}catch (NullPointerException npe){return null;}}/// Enumerates the servlets in this context (server). Only servlets that// are accesible will be returned. This enumeration always includes the// servlet itself.public Enumeration getServlets(){return registry.elements();}/// Enumerates the names of the servlets in this context (server). Only// servlets that are accesible will be returned. This enumeration always// includes the servlet itself.public Enumeration getServletNames(){return registry.keys();}/// Destroys all currently-loaded servlets.public void destroyAllServlets(){Enumeration en = registry.elements();while (en.hasMoreElements()){Servlet servlet = (Servlet) en.nextElement();servlet.destroy();}registry = new PathTreeDictionary();// invalidate all sessionsEnumeration e = sessions.keys();while (e.hasMoreElements()){Object sid = e.nextElement();if (sid != null){AcmeSession as = (AcmeSession) sessions.get(sid);if (as != null){as = (AcmeSession) sessions.remove(sid);if (as != null && as.isValid())try{as.invalidate();}catch (IllegalStateException ise){}}}}}public void setMappingTable(PathTreeDictionary mappingtable){this.mappingtable = mappingtable;}public void setRealms(PathTreeDictionary realms){this.realms = realms;}Object getSession(String id){return sessions.get(id);}HttpSession createSession(){HttpSession result = new AcmeSession(generateSessionId(), expiredIn * 60, this, sessions);sessions.put(result.getId(), result);return result;}void removeSession(String id){sessions.remove(id);}/// Write information to the servlet log.// @param message the message to logpublic void log(String message){if (logStream != null && !quiet){Date date = new Date(System.currentTimeMillis());logStream.println("[" + date.toString() + "] " + message);}}public void log(String message, Throwable throwable){StringWriter sw = new StringWriter();throwable.printStackTrace(new PrintWriter(sw));log(message + '\n' + sw);}/// Write a stack trace to the servlet log.// @param exception where to get the stack trace// @param message the message to logpublic void log(Exception exception, String message){StringWriter sw = new StringWriter();exception.printStackTrace(new PrintWriter(sw));log("" + sw + '\n' + message);}/// Applies alias rules to the specified virtual path and returns the// corresponding real path. It returns null if the translation// cannot be performed.// @param path the path to be translatedpublic String getRealPath(String path){//System.err.print("["+path+"]->[");try{path = new String(path.getBytes("ISO-8859-1"), "UTF-8");}catch (Exception ee){ // no encoding}if (mappingtable != null){// try find first sub-pathObject[] os = mappingtable.get(path);if (os[0] == null)return null;int slpos = ((Integer) os[1]).intValue();if (slpos > 0){if (path.length() > slpos)path = path.substring(slpos + 1);elsepath = "";}else if (path.length() > 0){char s = path.charAt(0);if (s == '/' || s == '\\')path = path.substring(1);}//System.err.println("Base:"+((String)os[0])+"\npath="+path+"\n pos="+slpos+']');return (os[0].toString()) + File.separatorChar + path;}return path;}/// Returns the MIME type of the specified file.// @param file file name whose MIME type is requiredpublic String getMimeType(String file){file = file.toUpperCase();if (file.endsWith(".HTML") || file.endsWith(".HTM"))return "text/html";if (file.endsWith(".TXT"))return "text/plain";if (file.endsWith(".XML"))return "text/xml";if (file.endsWith(".CSS"))return "text/css";if (file.endsWith(".SGML") || file.endsWith(".SGM"))return "text/x-sgml";// Imageif (file.endsWith(".GIF"))return "image/gif";if (file.endsWith(".JPG") || file.endsWith(".JPEG") || file.endsWith(".JPE"))return "image/jpeg";if (file.endsWith(".PNG"))return "image/png";if (file.endsWith(".BMP"))return "image/bmp";if (file.endsWith(".TIF") || file.endsWith(".TIFF"))return "image/tiff";if (file.endsWith(".RGB"))return "image/x-rgb";if (file.endsWith(".XPM"))return "image/x-xpixmap";if (file.endsWith(".XBM"))return "image/x-xbitmap";if (file.endsWith(".SVG"))return "image/svg-xml ";if (file.endsWith(".SVGZ"))return "image/svg-xml ";// Audioif (file.endsWith(".AU") || file.endsWith(".SND"))return "audio/basic";if (file.endsWith(".MID") || file.endsWith(".MIDI") || file.endsWith(".RMI") || file.endsWith(".KAR"))return "audio/mid";if (file.endsWith(".MPGA") || file.endsWith(".MP2") || file.endsWith(".MP3"))return "audio/mpeg";if (file.endsWith(".WAV"))return "audio/wav";if (file.endsWith(".AIFF") || file.endsWith(".AIFC"))return "audio/aiff";if (file.endsWith(".AIF"))return "audio/x-aiff";if (file.endsWith(".RA"))return "audio/x-realaudio";if (file.endsWith(".RPM"))return "audio/x-pn-realaudio-plugin";if (file.endsWith(".RAM"))return "audio/x-pn-realaudio";if (file.endsWith(".SD2"))return "audio/x-sd2";// Applicationif (file.endsWith(".BIN")|| file.endsWith(".DMS")|| file.endsWith(".LHA")|| file.endsWith(".LZH")|| file.endsWith(".EXE")|| file.endsWith(".DLL")|| file.endsWith(".CLASS"))return "application/octet-stream";if (file.endsWith(".HQX"))return "application/mac-binhex40";if (file.endsWith(".PS") || file.endsWith(".AI") || file.endsWith(".EPS"))return "application/postscript";if (file.endsWith(".PDF"))return "application/pdf";if (file.endsWith(".RTF"))return "application/rtf";if (file.endsWith(".DOC"))return "application/msword";if (file.endsWith(".PPT"))return "application/powerpoint";if (file.endsWith(".FIF"))return "application/fractals";if (file.endsWith(".P7C"))return "application/pkcs7-mime";// Application/xif (file.endsWith(".JS"))return "application/x-javascript";if (file.endsWith(".Z"))return "application/x-compress";if (file.endsWith(".GZ"))return "application/x-gzip";if (file.endsWith(".TAR"))return "application/x-tar";if (file.endsWith(".TGZ"))return "application/x-compressed";if (file.endsWith(".ZIP"))return "application/x-zip-compressed";if (file.endsWith(".DIR") || file.endsWith(".DCR") || file.endsWith(".DXR"))return "application/x-director";if (file.endsWith(".DVI"))return "application/x-dvi";if (file.endsWith(".TEX"))return "application/x-tex";if (file.endsWith(".LATEX"))return "application/x-latex";if (file.endsWith(".TCL"))return "application/x-tcl";if (file.endsWith(".CER") || file.endsWith(".CRT") || file.endsWith(".DER"))return "application/x-x509-ca-cert";// Videoif (file.endsWith(".MPG") || file.endsWith(".MPE") || file.endsWith(".MPEG"))return "video/mpeg";if (file.endsWith(".QT") || file.endsWith(".MOV"))return "video/quicktime";if (file.endsWith(".AVI"))return "video/x-msvideo";if (file.endsWith(".MOVIE"))return "video/x-sgi-movie";// Chemicalif (file.endsWith(".PDB") || file.endsWith(".XYZ"))return "chemical/x-pdb";// X-if (file.endsWith(".ICE"))return "x-conference/x-cooltalk";if (file.endsWith(".WRL") || file.endsWith(".VRML"))return "x-world/x-vrml";if (file.endsWith(".WML"))return "text/vnd.wap.wml";if (file.endsWith(".WMLC"))return "application/vnd.wap.wmlc";if (file.endsWith(".WMLS"))return "text/vnd.wap.wmlscript";if (file.endsWith(".WMLSC"))return "application/vnd.wap.wmlscriptc";if (file.endsWith(".WBMP"))return "image/vnd.wap.wbmp";return null;}/// Returns the name and version of the web server under which the servlet// is running.// Same as the CGI variable SERVER_SOFTWARE.public String getServerInfo(){return Serve.Identification.serverName+ " "+ Serve.Identification.serverVersion+ " ("+ Serve.Identification.serverUrl+ ")";}/// Returns the value of the named attribute of the network service, or// null if the attribute does not exist. This method allows access to// additional information about the service, not already provided by// the other methods in this interface.public Object getAttribute(String name){// This server does not support attributes.return attributes.get(name);}/////////////////// JSDK 2.1 extensions //////////////////////////public void removeAttribute(String name){attributes.remove(name);}public void setAttribute(String name, Object object){if (object != null)attributes.put(name, object);elseattributes.remove(name);}public Enumeration getAttributeNames(){return attributes.keys();}public ServletContext getContext(String uripath){return this; // only root context supported}public int getMajorVersion(){return 2; // support 2.x}public int getMinorVersion(){return 3; // support 2.3}// 2.3/** * Returns a directory-like listing of all the paths to resources within the web application whose longest sub-path matches the supplied path argument. Paths indicating subdirectory paths end with a '/'. The returned paths are all relative to the root * of the web application and have a leading '/'. For example, for a web application containing * <p> * /welcome.html <br> * /catalog/index.html<br> * /catalog/products.html<br> * /catalog/offers/books.html<br> * /catalog/offers/music.html<br> * /customer/login.jsp<br> * /WEB-INF/web.xml<br> * /WEB-INF/classes/com.acme.OrderServlet.class, * <p> * getResourcePaths("/") returns {"/welcome.html", "/catalog/", "/customer/", "/WEB-INF/"}<br> * getResourcePaths("/catalog/") returns {"/catalog/index.html", "/catalog/products.html", "/catalog/offers/"}. * <p> * * @param the - partial path used to match the resources, which must start with a / * @return a Set containing the directory listing, or null if there are no resources in the web application whose path begins with the supplied path. * @since Servlet 2.3 * */public java.util.Set getResourcePaths(java.lang.String path){// TODO: implementreturn null;}/** * Returns the name of this web application correponding to this ServletContext as specified in the deployment descriptor for this web application by the display-name element. * @return The name of the web application or null if no name has been declared in the deployment descriptor. * * @since Servlet 2.3 */public java.lang.String getServletContextName(){//return null;//"ROOT";throw new RuntimeException("getServletContextName is not supported.");}// only root relative in this implementationpublic URL getResource(String path) throws MalformedURLException{return new URL("http", hostName, port, path);}public InputStream getResourceAsStream(String path){return null; // we don't provide resources in this way}public RequestDispatcher getRequestDispatcher(String urlpath){return this; // we don't provide resource dispatching in this way}// no way to specify parameters for contextpublic String getInitParameter(String param){return null;}public Enumeration getInitParameterNames(){return null;}public RequestDispatcher getNamedDispatcher(String name){return this;}synchronized String generateSessionId(){return "-" + System.currentTimeMillis() + '-' + (uniqer++) + '-' + Math.round(Math.random() * 1000);}public void forward(ServletRequest _request, ServletResponse _response)throws ServletException, java.io.IOException{}public void include(ServletRequest _request, ServletResponse _response)throws ServletException, java.io.IOException{}final static class Identification{public static final String serverName = "Rogatkin's JWS based on Acme.Serve";public static final String serverVersion = "$Revision: 1.49 $";public static final String serverUrl = "http://tjws.sourceforge.net";/// Write a standard-format HTML address for this server.public static void writeAddress(OutputStream o) throws IOException{PrintStream p = new PrintStream(o);p.println("<ADDRESS><A HREF=\"" + serverUrl + "\">" + serverName + " " + serverVersion + "</A></ADDRESS>");}public static void writeAddress(StringBuffer sb) throws IOException{sb.append("<ADDRESS><A HREF=\"" + serverUrl + "\">" + serverName + " " + serverVersion + "</A></ADDRESS>");}}}class ServeConfig implements ServletConfig{private ServletContext context;private Hashtable init_params;private String servletName;public ServeConfig(ServletContext context){this(context, null, "undefined");}public ServeConfig(ServletContext context, Hashtable initParams, String servletName){this.context = context;this.init_params = initParams;this.servletName = servletName;}// Methods from ServletConfig./// Returns the context for the servlet.public ServletContext getServletContext(){return context;}/// Gets an initialization parameter of the servlet.// @param name the parameter namepublic String getInitParameter(String name){// This server supports servlet init params. :)if (init_params != null)return (String) init_params.get(name);return null;}/// Gets the names of the initialization parameters of the servlet.// @param name the parameter namepublic Enumeration getInitParameterNames(){// This server does:) support servlet init params.if (init_params != null)return init_params.keys();return new Vector().elements();}// 2.2public String getServletName(){return servletName;}}////////////////////////////////////////////////////////////////////////** * provides request/response */class ServeConnection implements Runnable, HttpServletRequest, HttpServletResponse{private Socket socket;private Serve serve;private ServletInputStream in;private ServletOutputStream out;private Hashtable formParameters;private Hashtable attributes = new Hashtable();public final static String WWWFORMURLENCODE = "application/x-www-form-urlencoded";public final static String TRANSFERENCODING = "Transfer-Encoding";public final static String CHUNKED = "chunked";public final static String CONTENTLENGTH = "Content-Length";public final static String CONTENTTYPE = "Content-Type";public final static String SETCOOKIE = "Set-Cookie";public final static String COOKIE = "Cookie";public final static String SESSION_COOKIE_NAME = "JSESSIONID";// URL rewriting http://www.myserver.com/catalog/index.html;jsessionid=mysession1928// like: http://www.sun.com/2001-0227/sunblade/;$sessionid$AD5RQ0IAADJAZAMTA1LU5YQprivate String reqMethod; // == null by defaultprivate String reqUriPath;private String reqProtocol;private String reqCharEncoding;private String remoteUser;private String authType;private boolean oneOne; // HTTP/1.1 or betterprivate boolean reqMime;String reqQuery = null;private Vector reqHeaderNames = new Vector();private Vector reqHeaderValues = new Vector();private Locale locale; // = java.util.Locale.getDefault();private int uriLen;private static final Hashtable EMPTYHASHTABLE = new Hashtable();private Vector outCookies;private Vector inCookies;private String sessionCookieValue;/// Constructor.public ServeConnection(Socket socket, Serve serve){// Save arguments.this.socket = socket;this.serve = serve;// Start a separate thread to read and handle the request.Thread thread = new Thread(this, "Request handler");thread.start();}// Methods from Runnable.public void run(){try{// Get the streams.in = new ServeInputStream(socket.getInputStream());out = new ServeOutputStream(socket.getOutputStream(), this);}catch (IOException e){problem("Getting streams: " + e.getMessage(), SC_BAD_REQUEST);}parseRequest();if (serve.isAccessLogged()){serve.log(socket.getInetAddress().toString()+ ' '+ reqMethod+ ' '+ reqUriPath+ ' '+ resCode+ (serve.isShowReferer() ? "| " + getHeader("Referer") : "")+ (serve.isShowUserAgent() ? "| " + getHeader("User-Agent") : ""));}try{socket.close();}catch (IOException e){ /* ignore */}}private void parseRequest(){byte[] lineBytes = new byte[4096];int len;String line;try{// Read the first line of the request.len = in.readLine(lineBytes, 0, lineBytes.length);if (len == -1 || len == 0){problem("Empty request", SC_BAD_REQUEST);return;}line = new String(lineBytes, 0, len);StringTokenizer ust = new StringTokenizer(line);reqProtocol = null;if (ust.hasMoreTokens()){reqMethod = ust.nextToken();if (ust.hasMoreTokens()){reqUriPath = Acme.Utils.urlDecoder(ust.nextToken());if (ust.hasMoreTokens()){reqProtocol = ust.nextToken();oneOne = !reqProtocol.toUpperCase().equals("HTTP/1.0");reqMime = true;// Read the rest of the lines.String s;while ((s = ((ServeInputStream) in).readLine()) != null){if (s.length() == 0)break;int c = s.indexOf(':', 0);if (c > 0){String key = s.substring(0, c).trim();String value = s.substring(c + 1, s.length()).trim();reqHeaderNames.addElement(key.toLowerCase());reqHeaderValues.addElement(value);}elseserve.log("header field without ':'");}}else{reqProtocol = "HTTP/0.9";oneOne = false;reqMime = false;}}}if (reqProtocol == null){problem("Malformed request line", SC_BAD_REQUEST);return;}// Check Host: header in HTTP/1.1 requests.if (oneOne){String host = getHeader("host");if (host == null){problem("Host header missing on HTTP/1.1 request", SC_BAD_REQUEST);return;}}// Decode %-sequences.//reqUriPath = decode( reqUriPath );// Split off query string, if any.int qmark = reqUriPath.indexOf('?');if (qmark > -1){reqQuery = reqUriPath.substring(qmark + 1);reqUriPath = reqUriPath.substring(0, qmark);}if (CHUNKED.equals(getHeader(TRANSFERENCODING))){setHeader(CONTENTLENGTH, null);((ServeInputStream) in).chunking(true);}Object[] os = serve.registry.get(reqUriPath);if (os != null){uriLen = ((Integer) os[1]).intValue();runServlet((HttpServlet) os[0]);}}catch (IOException e){problem("Reading request: " + e.getMessage(), SC_BAD_REQUEST);}}private void runServlet(HttpServlet servlete){// Set default response fields.setStatus(SC_OK);setDateHeader("Date", System.currentTimeMillis());setHeader("Server", Serve.Identification.serverName + "/" + Serve.Identification.serverVersion);setHeader("MIME-Version", "1.0");try{parseCookies();if (authenificate()){if (servlete instanceof SingleThreadModel)synchronized (servlete){servlete.service((ServletRequest) this, (ServletResponse) this);}elseservlete.service((ServletRequest) this, (ServletResponse) this);}}catch (IOException e){e.printStackTrace();problem("IO problem running servlet: " + e.toString(), SC_BAD_REQUEST);}catch (ServletException e){problem("problem running servlet: " + e.toString(), SC_BAD_REQUEST);}catch (Exception e){problem("unexpected problem running servlet: " + e.toString(), SC_INTERNAL_SERVER_ERROR);e.printStackTrace();}}private boolean authenificate() throws IOException{Object[] o = serve.realms.get(getPathInfo());BasicAuthRealm realm = null;if (o != null)realm = (BasicAuthRealm) o[0];//System.err.println("looking for realm for path "+getPathInfo()+" in "+serve.realms+" found "+realm);if (realm == null)return true;String credentials = getHeader("Authorization");if (credentials != null){credentials = credentials.substring(credentials.indexOf(' ') + 1);try{ByteArrayOutputStream baos = new ByteArrayOutputStream();serve.base64Dec.decodeBuffer(new StringBufferInputStream(credentials), baos);credentials = baos.toString();}catch (IOException e){e.printStackTrace();}int i = credentials.indexOf(':');String user = credentials.substring(0, i);String password = credentials.substring(i + 1);remoteUser = user;authType = "Basic"; // support only basic authenificationString realPassword = (String) realm.get(user);//System.err.println("User "+user+" Password "+password+" real "+realPassword);if (realPassword != null && realPassword.equals(password))return true;}setStatus(SC_UNAUTHORIZED);setHeader("WWW-Authenticate", "basic realm=\"" + realm.name() + '"');writeHeaders();return false;}private void problem(String logMessage, int resCode){serve.log(logMessage);try{sendError(resCode);}catch (IllegalStateException e){ /* ignore */}catch (IOException e){ /* ignore */}}private String decode(String str){StringBuffer result = new StringBuffer();int l = str.length();for (int i = 0; i < l; ++i){char c = str.charAt(i);if (c == '%' && i + 2 < l){char c1 = str.charAt(i + 1);char c2 = str.charAt(i + 2);if (isHexit(c1) && isHexit(c2)){result.append((char) (hexit(c1) * 16 + hexit(c2)));i += 2;}elseresult.append(c);}else if (c == '+')result.append(' ');elseresult.append(c);}return result.toString();}private boolean isHexit(char c){String legalChars = "0123456789abcdefABCDEF";return (legalChars.indexOf(c) != -1);}private int hexit(char c){if (c >= '0' && c <= '9')return c - '0';if (c >= 'a' && c <= 'f')return c - 'a' + 10;if (c >= 'A' && c <= 'F')return c - 'A' + 10;return 0; // shouldn't happen, we're guarded by isHexit()}void parseCookies(){if (inCookies == null)inCookies = new Vector();try{String cookie_name;String cookie_value;String cookie_path;String cookies = getHeader(COOKIE);if (cookies == null)return;//Enumeration e = getHeaders(COOKIE);//while(e.hasMoreElements())//cookies += (String)e.nextElement();StringTokenizer st = new StringTokenizer(cookies, ";", true);// TODO: write a parser to avoid tokenizerswhile (st.hasMoreTokens()){StringTokenizer st2 = new StringTokenizer(st.nextToken(), "=");if (st2.hasMoreTokens()){cookie_name = st2.nextToken().trim();if (st2.hasMoreTokens()){cookie_value = st2.nextToken(",").trim();if (cookie_value.length() > 0 && cookie_value.charAt(0) == '=')cookie_value = cookie_value.substring(1);cookie_path = "/";while (st2.hasMoreTokens()){String cookie_atr = st2.nextToken();if ("$Version".equalsIgnoreCase(cookie_atr)|| "$Path".equalsIgnoreCase(cookie_atr)|| "$Domain".equalsIgnoreCase(cookie_atr))continue;cookie_path = st2.nextToken();}Cookie cookie = new Cookie(cookie_name, cookie_value);//System.err.println("Cookie set:"+cookie_name+':'+cookie_value);cookie.setPath(cookie_path);inCookies.addElement(cookie);if (SESSION_COOKIE_NAME.equals(cookie_name) && sessionCookieValue == null){sessionCookieValue = cookie_value;try{((AcmeSession) serve.getSession(sessionCookieValue)).userTouch();}catch (Throwable t){}}}}}}catch (Throwable e){System.err.println("prepareCookies(): " + e);e.printStackTrace();}}// Methods from ServletRequest./// Returns the size of the request entity data, or -1 if not known.// Same as the CGI variable CONTENT_LENGTH.public int getContentLength(){return getIntHeader(CONTENTLENGTH, -1);}/// Returns the MIME type of the request entity data, or null if// not known.// Same as the CGI variable CONTENT_TYPE.public String getContentType(){return getHeader(CONTENTTYPE);}/// Returns the protocol and version of the request as a string of// the form <protocol>/<major version>.<minor version>.// Same as the CGI variable SERVER_PROTOCOL.public String getProtocol(){return reqProtocol;}/// Returns the scheme of the URL used in this request, for example// "http", "https", or "ftp". Different schemes have different rules// for constructing URLs, as noted in RFC 1738. The URL used to create// a request may be reconstructed using this scheme, the server name// and port, and additional information such as URIs.public String getScheme(){if (socket.getClass().getName().toUpperCase().indexOf("SSL") < 0)return "http";elsereturn "https";}/// Returns the host name of the server as used in the <host> part of// the request URI.// Same as the CGI variable SERVER_NAME.public String getServerName(){String serverName;int serverPort = 80;serverName = getHeader("Host");if (serverName != null && serverName.length() > 0){int colon = serverName.indexOf(':');if (colon >= 0){if (colon < serverName.length())serverPort = Integer.parseInt(serverName.substring(colon + 1));serverName = serverName.substring(0, colon);}}if (serverName == null){try{serverName = InetAddress.getLocalHost().getHostName();}catch (java.net.UnknownHostException ignore){serverName = "127.0.0.0";}}int slash = serverName.indexOf("/");if (slash >= 0)serverName = serverName.substring(slash + 1);return serverName;}/// Returns the port number on which this request was received as used in// the <port> part of the request URI.// Same as the CGI variable SERVER_PORT.public int getServerPort(){return socket.getLocalPort();}/// Returns the IP address of the agent that sent the request.// Same as the CGI variable REMOTE_ADDR.public String getRemoteAddr(){return socket.getInetAddress().toString();}/// Returns the fully qualified host name of the agent that sent the// request.// Same as the CGI variable REMOTE_HOST.public String getRemoteHost(){String result = socket.getInetAddress().getHostName();return result != null ? result : getRemoteAddr();}/// Applies alias rules to the specified virtual path and returns the// corresponding real path, or null if the translation can not be// performed for any reason. For example, an HTTP servlet would// resolve the path using the virtual docroot, if virtual hosting is// enabled, and with the default docroot otherwise. Calling this// method with the string "/" as an argument returns the document root.public String getRealPath(String path){return serve.getRealPath(path);}/// Returns an input stream for reading request data.// @exception IllegalStateException if getReader has already been called// @exception IOException on other I/O-related errorspublic ServletInputStream getInputStream() throws IOException{synchronized (in){if (((ServeInputStream) in).isReturnedAsReader())throw new IllegalStateException("Already returned as a reader.");((ServeInputStream) in).setReturnedAsReader(true);}return in;}/// Returns a buffered reader for reading request data.// @exception UnsupportedEncodingException if the character set encoding isn't supported// @exception IllegalStateException if getInputStream has already been called// @exception IOException on other I/O-related errorspublic BufferedReader getReader(){synchronized (in){if (((ServeInputStream) in).isReturnedAsStream())throw new IllegalStateException("Already returned as a stream.");((ServeInputStream) in).setReturnedAsStream(true);}if (reqCharEncoding != null)try{return new BufferedReader(new InputStreamReader(in, reqCharEncoding));}catch (UnsupportedEncodingException uee){}return new BufferedReader(new InputStreamReader(in));}private Hashtable getParametersFromRequest(){Hashtable result = null;//System.out.println("Req:"+reqMethod+" con:"+getContentType()+" eq "+WWWFORMURLENCODE.equals(getContentType()));if ("GET".equals(reqMethod)){if (reqQuery != null)try{result = HttpUtils.parseQueryString(reqQuery);}catch (IllegalArgumentException ex){}}else if ("POST".equals(reqMethod))if (WWWFORMURLENCODE.equals(getContentType()))try{result = HttpUtils.parsePostData(getContentLength(), getInputStream());if (reqQuery != null && reqQuery.length() > 0){result.putAll(HttpUtils.parseQueryString(reqQuery));}}catch (Exception ex){serve.log("Exception " + ex + " at parsing post data of length " + getContentLength());}elsetry{if (reqQuery != null)result = HttpUtils.parseQueryString(reqQuery);}catch (Exception ex){}return result != null ? result : EMPTYHASHTABLE;}/// Returns the parameter names for this request.public Enumeration getParameterNames(){if (formParameters == null)formParameters = getParametersFromRequest();return formParameters.keys();}/// Returns the value of the specified query string parameter, or null// if not found.// @param name the parameter namepublic String getParameter(String name){String[] params = getParameterValues(name);if (params == null || params.length == 0)return null;return params[0];}/// Returns the values of the specified parameter for the request as an// array of strings, or null if the named parameter does not exist.public String[] getParameterValues(String name){if (formParameters == null)getParameterNames();return (String[]) formParameters.get(name);}/// Returns the value of the named attribute of the request, or null if// the attribute does not exist. This method allows access to request// information not already provided by the other methods in this interface.public Object getAttribute(String name){return attributes.get(name);}// Methods from HttpServletRequest./// Gets the array of cookies found in this request.public Cookie[] getCookies(){Cookie[] cookieArray = new Cookie[inCookies.size()];inCookies.copyInto(cookieArray);return cookieArray;}/// Returns the method with which the request was made. This can be "GET",// "HEAD", "POST", or an extension method.// Same as the CGI variable REQUEST_METHOD.public String getMethod(){return reqMethod;}/*** Returns the part of this request's URL from the protocol name up to the query string in the first line of the HTTP request. To reconstruct an URL with a scheme and host, use HttpUtils.getRequestURL(javax.servlet.http.HttpServletRequest).*//// Returns the full request URI.public String getRequestURI(){return reqUriPath;}/** Reconstructs the URL the client used to make the request. * The returned URL contains a protocol, server name, port number, * and server path, but it does not include query string parameters. <br> * Because this method returns a StringBuffer, not a string, you can modify the * URL easily, for example, to append query parameters. * <p> * This method is useful for creating redirect messages and for reporting errors. * * @return a StringBuffer object containing the reconstructed URL * @since 2.3 */public java.lang.StringBuffer getRequestURL(){return new StringBuffer().append(getScheme()).append("://").append(serve.hostName).append(serve.port == 80 ? "" : String.valueOf(serve.port)).append(getRequestURI());}/// Returns the part of the request URI that referred to the servlet being// invoked.// Analogous to the CGI variable SCRIPT_NAME.public String getServletPath(){// In this server, the entire path is regexp-matched against the// servlet pattern, so there's no good way to distinguish which// part refers to the servlet.return uriLen > 0 ? reqUriPath.substring(0, uriLen) : "";}/// Returns optional extra path information following the servlet path, but// immediately preceding the query string. Returns null if not specified.// Same as the CGI variable PATH_INFO.public String getPathInfo(){// In this server, the entire path is regexp-matched against the// servlet pattern, so there's no good way to distinguish which// part refers to the servlet.return uriLen >= reqUriPath.length() ? null : reqUriPath.substring(uriLen);}/// Returns extra path information translated to a real path. Returns// null if no extra path information was specified.// Same as the CGI variable PATH_TRANSLATED.public String getPathTranslated(){// In this server, the entire path is regexp-matched against the// servlet pattern, so there's no good way to distinguish which// part refers to the servlet.return getRealPath(getPathInfo());}/// Returns the query string part of the servlet URI, or null if not known.// Same as the CGI variable QUERY_STRING.public String getQueryString(){return reqQuery;}/// Returns the name of the user making this request, or null if not known.// Same as the CGI variable REMOTE_USER.public String getRemoteUser(){return remoteUser;}/// Returns the authentication scheme of the request, or null if none.// Same as the CGI variable AUTH_TYPE.public String getAuthType(){return authType;}/// Returns the value of a header field, or null if not known.// Same as the information passed in the CGI variabled HTTP_*.// @param name the header field namepublic String getHeader(String name){int i = reqHeaderNames.indexOf(name.toLowerCase());if (i == -1)return null;return (String) reqHeaderValues.elementAt(i);}public int getIntHeader(String name){return getIntHeader(name, 0);}/// Returns the value of an integer header field.// @param name the header field name// @param def the integer value to return if header not found or invalidpublic int getIntHeader(String name, int def){String val = getHeader(name);if (val == null)return def;try{return Integer.parseInt(val);}catch (Exception e){return def;}}/// Returns the value of a long header field.// @param name the header field name// @param def the long value to return if header not found or invalidpublic long getLongHeader(String name, long def){String val = getHeader(name);if (val == null)return def;try{return Long.parseLong(val);}catch (Exception e){return def;}}public long getDateHeader(String name){String val = getHeader(name);if (val == null)return 0;try{return headerdateformat.parse(val).getTime();}catch (Exception e){throw new IllegalArgumentException("Value " + val + " can't be converted to Date using " + headerdateformat.toPattern());}}/// Returns the value of a date header field.// @param name the header field name// @param def the date value to return if header not found or invalidpublic long getDateHeader(String name, long def){String val = getHeader(name);if (val == null)return def;try{return DateFormat.getDateInstance().parse(val).getTime();}catch (Exception e){return def;}}/// Returns an Enumeration of the header names.public Enumeration getHeaderNames(){return reqHeaderNames.elements();}/// Gets the current valid session associated with this request, if// create is false or, if necessary, creates a new session for the// request, if create is true.// <P>// Note: to ensure the session is properly maintained, the servlet// developer must call this method (at least once) before any output// is written to the response.// <P>// Additionally, application-writers need to be aware that newly// created sessions (that is, sessions for which HttpSession.isNew// returns true) do not have any application-specific state.public synchronized HttpSession getSession(boolean create){HttpSession result = null;if (sessionCookieValue != null){result = (HttpSession) serve.getSession(sessionCookieValue);if (result != null && ((AcmeSession) result).isValid() == false){serve.removeSession(sessionCookieValue);result = null;}}if (result == null && create){result = serve.createSession();}if (result != null)sessionCookieValue = result.getId();return result;}// JSDK 2.1public HttpSession getSession(){return getSession(true);}public boolean isRequestedSessionIdFromURL(){</p