// 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 minutes
protected 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.jar
try
{
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 nulled
for (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());
else
arguments.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] : "");
}
else
usage();
++argn;
}
if (argn != argc)
usage();
/** format path mapping
from=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);
}
}
else
System.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));
else
try
{
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);
// port
arguments.put(ARG_PORT, new Integer(port));
// nohup
if (!consoleQuit)
{
arguments.put(ARG_NOHUP, ARG_NOHUP);
}
// logging
PrintStream 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);
// servlets
for (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 format
servlet.<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 file
servlet_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);
}
else
System.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 sessions
int 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 directory
public 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 from
public 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 handler
if (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 impl
as = (AcmeSession) sessions.remove(sid);
if (as != null && as.isValid())
try
{
as.invalidate();
}
catch (IllegalStateException ise)
{
}
}
}
}
}
}
}, "Session cleaner");
ssclThread.setPriority(Thread.MIN_PRIORITY);
ssclThread.start();
}
else
expiredIn = -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 exist
public 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 sessions
Enumeration 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 log
public 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 log
public 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 translated
public 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-path
Object[] 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);
else
path = "";
}
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 required
public 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";
// Image
if (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 ";
// Audio
if (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";
// Application
if (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/x
if (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";
// Video
if (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";
// Chemical
if (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);
else
attributes.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: implement
return 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 implementation
public 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 context
public 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 name
public 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 name
public Enumeration getInitParameterNames()
{
// This server does:) support servlet init params.
if (init_params != null)
return init_params.keys();
return new Vector().elements();
}
// 2.2
public 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$AD5RQ0IAADJAZAMTA1LU5YQ
private String reqMethod; // == null by default
private String reqUriPath;
private String reqProtocol;
private String reqCharEncoding;
private String remoteUser;
private String authType;
private boolean oneOne; // HTTP/1.1 or better
private 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);
}
else
serve.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);
}
else
servlete.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 authenification
String 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;
}
else
result.append(c);
}
else if (c == '+')
result.append(' ');
else
result.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 tokenizers
while (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";
else
return "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 errors
public 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 errors
public 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());
}
else
try
{
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 name
public 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 name
public 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 invalid
public 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 invalid
public 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 invalid
public 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.1
public HttpSession getSession()
{
return getSession(true);
}
public boolean isRequestedSessionIdFromURL()
{
3000行代码实现一个servlet server
最新推荐文章于 2023-08-16 15:20:06 发布
本文介绍了一个小型嵌入式HTTP服务器的实现,该服务器能够运行与JavaSoft JavaServer API兼容的Servlet,提供基本的HTTP服务,如文件返回和目录列表显示。文章详细说明了服务器的工作原理及其主要组件。
摘要由CSDN通过智能技术生成