0001: // Serve - minimal Java HTTP server class
0002: //
0003: // Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
0004: //
0005: // Redistribution and use in source and binary forms, with or without
0006: // modification, are permitted provided that the following conditions
0007: // are met:
0008: // 1. Redistributions of source code must retain the above copyright
0009: // notice, this list of conditions and the following disclaimer.
0010: // 2. Redistributions in binary form must reproduce the above copyright
0011: // notice, this list of conditions and the following disclaimer in the
0012: // documentation and/or other materials provided with the distribution.
0013: //
0014: // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
0015: // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0016: // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0017: // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
0018: // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0019: // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0020: // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0021: // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0022: // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0023: // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0024: // SUCH DAMAGE.
0025: //
0026: // Visit the ACME Labs Java page for up-to-date versions of this and other
0027: // fine Java utilities: http://www.acme.com/java/
0028: //
0029:
0030: // All enhancments Copyright (C)1998-2002 by Dmitriy Rogatkin
0031: // this version is compatible with JSDK 2.3
0032: // http://tjws.sourceforge.net
0033: // $Id: Serve.java,v 1.1 2004/09/08 22:00:24 drogatkin Exp $
0034:
0035: package Acme.Serve;
0036:
0037: import java.io.*;
0038: import java.util.*;
0039: import java.net.*;
0040: import java.text.*;
0041: import javax.servlet.*;
0042: import javax.servlet.http.*;
0043:
0044: /// Minimal Java HTTP server class.
0045: // <P>
0046: // This class implements a very small embeddable HTTP server.
0047: // It runs Servlets compatible with the API used by JavaSoft's
0048: // <A HREF="http://java.sun.com/products/java-server/">JavaServer</A> server.
0049: // It comes with default Servlets which provide the usual
0050: // httpd services, returning files and directory listings.
0051: // <P>
0052: // This is not in any sense a competitor for JavaServer.
0053: // JavaServer is a full-fledged HTTP server and more.
0054: // Acme.Serve is tiny, about 1500 lines, and provides only the
0055: // functionality necessary to deliver an Applet's .class files
0056: // and then start up a Servlet talking to the Applet.
0057: // They are both written in Java, they are both web servers, and
0058: // they both implement the Servlet API; other than that they couldn't
0059: // be more different.
0060: // <P>
0061: // This is actually the second HTTP server I've written.
0062: // The other one is called
0063: // <A HREF="http://www.acme.com/software/thttpd/">thttpd</A>,
0064: // it's written in C, and is also pretty small although much more
0065: // featureful than this.
0066: // <P>
0067: // Other Java HTTP servers:
0068: // <UL>
0069: // <LI> The above-mentioned <A HREF="http://java.sun.com/products/java-server/">JavaServer</A>.
0070: // <LI> W3C's <A HREF="http://www.w3.org/pub/WWW/Jigsaw/">Jigsaw</A>.
0071: // <LI> David Wilkinson's <A HREF="http://www.netlink.co.uk/users/cascade/http/">Cascade</A>.
0072: // <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>.
0073: // </UL>
0074: // <P>
0075: // A <A HREF="http://www.byte.com/art/9706/sec8/art1.htm">June 1997 BYTE magazine article</A> mentioning this server.<BR>
0076: // 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>
0077: // <A HREF="/resources/classes/Acme/Serve/Serve.java">Fetch the software.</A><BR>
0078: // <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>
0079: // <P>
0080: // @see Acme.Serve.servlet.http.HttpServlet
0081: // @see FileServlet
0082: // @see CgiServlet
0083:
0084: // make it final?
0085: public class Serve implements ServletContext, RequestDispatcher {
0086:
0087: private static final String progName = "Serve";
0088: public static final String ARG_PORT = "port";
0089: public static final String ARG_THROTTLES = "throttles";
0090: public static final String ARG_SERVLETS = "servlets";
0091: public static final String ARG_REALMS = "realms";
0092: public static final String ARG_ALIASES = "aliases";
0093: public static final String ARG_CGI_PATH = "cgi-path";
0094: public static final String ARG_SESSION_TIMEOUT = "session-timeout";
0095: public static final String ARG_LOG_OPTIONS = "log-options";
0096: public static final String ARG_SOCKET_FACTORY = "socketFactory";
0097:
0098: protected static final int DEF_SESSION_TIMEOUT = 30; // in minutes
0099: protected static final int DEF_PORT = 9090;
0100:
0101: /// Main routine, if you want to run this directly as an application.
0102: public static void main(String[] args) {
0103: Hashtable arguments = new Hashtable(20);
0104:
0105: int argc = args.length;
0106: int argn;
0107: // Parse args.
0108: if (argc == 0) // a try to read from file for java -jar server.jar
0109: try {
0110: BufferedReader br = new BufferedReader(new FileReader(
0111: "cmdparams"));
0112: StringTokenizer st = new StringTokenizer(br.readLine(),
0113: " ");
0114: args = new String[st.countTokens()];
0115: argc = args.length; // tail can be nulled
0116: for (int i = 0; i < argc && st.hasMoreTokens(); i++)
0117: args[i] = st.nextToken();
0118: } catch (Exception e) { // many can happen
0119: }
0120: for (argn = 0; argn < argc && args[argn].charAt(0) == '-';) {
0121: if (args[argn].equals("-p") && argn + 1 < argc) {
0122: ++argn;
0123: arguments.put(ARG_PORT, new Integer(args[argn]));
0124: } else if (args[argn].equals("-t") && argn + 1 < argc) {
0125: ++argn;
0126: arguments.put(ARG_THROTTLES, args[argn]);
0127: } else if (args[argn].equals("-s") && argn + 1 < argc) {
0128: ++argn;
0129: arguments.put(ARG_SERVLETS, args[argn]);
0130: } else if (args[argn].equals("-r") && argn + 1 < argc) {
0131: ++argn;
0132: arguments.put(ARG_REALMS, args[argn]);
0133: } else if (args[argn].equals("-a") && argn + 1 < argc) {
0134: ++argn;
0135: arguments.put(ARG_ALIASES, args[argn]);
0136: } else if (args[argn].equals("-c") && argn + 1 < argc) {
0137: ++argn;
0138: arguments.put(ARG_CGI_PATH, args[argn]);
0139: } else if (args[argn].equals("-e") && argn + 1 < argc) {
0140: ++argn;
0141: try {
0142: arguments.put(ARG_SESSION_TIMEOUT, new Integer(
0143: args[argn]));
0144: } catch (NumberFormatException nfe) {
0145: }
0146: } else if (args[argn].startsWith("-l")) {
0147: if (args[argn].length() > 2)
0148: arguments.put(ARG_LOG_OPTIONS, args[argn]
0149: .substring(2).toUpperCase());
0150: else
0151: arguments.put(ARG_LOG_OPTIONS, "");
0152: } else if (args[argn].startsWith("-")) {
0153: if (args[argn].length() > 1)
0154: arguments.put(args[argn].substring(1),//.toUpperCase(),
0155: argn < argc - 1 ? args[++argn] : "");
0156: } else
0157: usage();
0158:
0159: ++argn;
0160: }
0161: if (argn != argc)
0162: usage();
0163: /** format path mapping
0164: from=givenpath;dir=realpath
0165: */
0166: PrintStream printstream = System.err;
0167: try {
0168: printstream = new PrintStream(new FileOutputStream("AWS-"
0169: + System.currentTimeMillis() + ".log"), true);
0170: System.setErr(printstream);
0171: } catch (IOException e) {
0172: System.err.println("IO problem at setting a log stream "
0173: + e);
0174: }
0175: PathTreeDictionary mappingtable = new PathTreeDictionary();
0176: if (arguments.get(ARG_ALIASES) != null) {
0177: File file = new File((String) arguments.get(ARG_ALIASES));
0178: if (file.exists() && file.canRead()) {
0179: try {
0180: DataInputStream in = new DataInputStream(
0181: new FileInputStream(file));
0182: do {
0183: String mappingstr = in.readLine();
0184: if (mappingstr == null)
0185: break;
0186: StringTokenizer maptokenzr = new StringTokenizer(
0187: mappingstr, "=;");
0188: if (maptokenzr.hasMoreTokens()) {
0189: if (maptokenzr.nextToken("=")
0190: .equalsIgnoreCase("from")) {
0191: if (maptokenzr.hasMoreTokens()) {
0192: String srcpath = maptokenzr
0193: .nextToken("=;");
0194: if (maptokenzr.hasMoreTokens()
0195: && maptokenzr.nextToken(
0196: ";=")
0197: .equalsIgnoreCase(
0198: "dir"))
0199: try {
0200: if (maptokenzr
0201: .hasMoreTokens())
0202: mappingtable
0203: .put(
0204: srcpath,
0205: maptokenzr
0206: .nextToken());
0207: } catch (NullPointerException e) {
0208: }
0209: }
0210: }
0211: }
0212: } while (true);
0213: } catch (IOException e) {
0214: System.err.println("Problem reading aliases file: "
0215: + arguments.get(ARG_ALIASES) + "/" + e);
0216: }
0217: } else
0218: System.err.println("FIle " + arguments.get(ARG_ALIASES)
0219: + " doesn't exist or not readable.");
0220: }
0221: /** format realmname=path,user:password,,,,
0222: */
0223: PathTreeDictionary realms = new PathTreeDictionary();
0224: if (arguments.get(ARG_REALMS) != null) {
0225: try {
0226: DataInputStream in = new DataInputStream(
0227: new FileInputStream((String) arguments
0228: .get(ARG_REALMS)));
0229:
0230: do {
0231: String realmstr = in.readLine();
0232: if (realmstr == null)
0233: break;
0234: StringTokenizer rt = new StringTokenizer(realmstr,
0235: "=,:");
0236: String realmname = null;
0237: BasicAuthRealm realm = null;
0238: if (rt.hasMoreTokens())
0239: realmname = rt.nextToken();
0240: else
0241: continue;
0242: if (rt.hasMoreTokens()) {
0243: realm = new BasicAuthRealm(realmname);
0244: realms.put(rt.nextToken(), realm);
0245: } else
0246: continue;
0247: if (rt.hasMoreTokens()) {
0248: String user = rt.nextToken();
0249: if (rt.hasMoreTokens())
0250: realm.put(user, rt.nextToken());
0251: }
0252: } while (true);
0253: } catch (IOException ioe) {
0254: System.err.println("Problem in reading realms file "
0255: + arguments.get(ARG_REALMS) + "/ " + ioe);
0256: }
0257: }
0258:
0259: // Create the server.
0260: final Serve serve = new Serve(arguments, printstream);
0261: final String cf = (String) arguments.get(ARG_SERVLETS);
0262: serve.setMappingTable(mappingtable);
0263: serve.setRealms(realms);
0264:
0265: new Thread(new Runnable() {
0266: public void run() {
0267: serve.readServlets(cf);
0268: }
0269: }).start();
0270: // And add the standard Servlets.
0271: String throttles = (String) arguments.get(ARG_THROTTLES);
0272: if (throttles == null)
0273: serve.addDefaultServlets((String) arguments
0274: .get(ARG_CGI_PATH));
0275: else
0276: try {
0277: serve.addDefaultServlets((String) arguments
0278: .get(ARG_CGI_PATH), throttles);
0279: } catch (IOException e) {
0280: System.err.println("Problem reading throttles file: "
0281: + e);
0282: System.exit(1);
0283: }
0284:
0285: // And run.
0286: serve.serve();
0287:
0288: System.exit(0);
0289: }
0290:
0291: private static void usage() {
0292: System.err
0293: .println("usage: "
0294: + progName
0295: + " [-p port] [-s servletpropertiesfile] [-a aliasmappingfile] [-l[a][r]] [-c cgi-bin-dir] [-e [-]duration_in_minutes] [-socketFactory class name and other parameters}");
0296: System.exit(1);
0297: }
0298:
0299: private void readServlets(String cfgfile) {
0300: /** servlet.properties file format
0301: servlet.<servletname>.code=<servletclass>
0302: servlet.<servletname>.initArgs=<name=value>,<name=value>
0303: */
0304: Hashtable servletstbl, parameterstbl;
0305: servletstbl = new Hashtable();
0306: parameterstbl = new Hashtable();
0307: if (cfgfile != null) {
0308: File file = new File(cfgfile);
0309: if (file.exists() && file.canRead()) {
0310: try {
0311: DataInputStream in = new DataInputStream(
0312: new FileInputStream(file));
0313: /** format of servlet.cfg file
0314: servlet_name;servlet_class;init_parameter1=value1;init_parameter2=value2...
0315: */
0316: do {
0317: String servletdsc = in.readLine();
0318: if (servletdsc == null)
0319: break;
0320: StringTokenizer dsctokenzr = new StringTokenizer(
0321: servletdsc, ".=,", false);
0322: if (dsctokenzr.hasMoreTokens()) {
0323: if (!dsctokenzr.nextToken()
0324: .equalsIgnoreCase("servlet")) {
0325: System.err
0326: .println("No leading 'servlet' keyword, the sentence is skipped");
0327: break;
0328: }
0329: if (dsctokenzr.hasMoreTokens()) {
0330: String servletname = dsctokenzr
0331: .nextToken();
0332:
0333: if (dsctokenzr.hasMoreTokens()) {
0334: String lt = dsctokenzr.nextToken();
0335: if (lt.equalsIgnoreCase("code")) {
0336: if (dsctokenzr.hasMoreTokens())
0337: servletstbl
0338: .put(
0339: servletname,
0340: dsctokenzr
0341: .nextToken("="));
0342: } else if (lt
0343: .equalsIgnoreCase("initArgs")) {
0344: Hashtable initparams = new Hashtable();
0345: while (dsctokenzr
0346: .hasMoreTokens()) {
0347: String key = dsctokenzr
0348: .nextToken("=,");
0349: if (dsctokenzr
0350: .hasMoreTokens())
0351: initparams
0352: .put(
0353: key,
0354: dsctokenzr
0355: .nextToken(",="));
0356: }
0357: parameterstbl.put(servletname,
0358: initparams);
0359: } else
0360: System.err
0361: .println("Unrecognized token "
0362: + lt
0363: + " in "
0364: + servletdsc
0365: + ", the line's skipped");
0366: }
0367: }
0368: }
0369: } while (true);
0370: } catch (IOException e) {
0371: System.err
0372: .println("Problem reading cfg file: " + e);
0373: }
0374: Enumeration se = servletstbl.keys();
0375: String servletname;
0376: while (se.hasMoreElements()) {
0377: servletname = (String) se.nextElement();
0378: addServlet(servletname, (String) servletstbl
0379: .get(servletname),
0380: (Hashtable) parameterstbl.get(servletname));
0381: }
0382: }
0383: }
0384: }
0385:
0386: int port;
0387: String hostName;
0388: private PrintStream logStream;
0389: private boolean useAccLog;
0390: private boolean showUserAgent;
0391: private boolean showReferer;
0392: protected PathTreeDictionary registry;
0393: protected PathTreeDictionary realms;
0394: private PathTreeDictionary mappingtable;
0395: private Hashtable attributes;
0396: sun.misc.BASE64Decoder base64Dec = new sun.misc.BASE64Decoder();
0397: // for sessions
0398: int uniqer;
0399: HttpSessionContextImpl sessions;
0400: static int expiredIn;
0401: protected Hashtable arguments;
0402:
0403: /// Constructor.
0404: public Serve(Hashtable arguments, PrintStream logStream) {
0405: this .arguments = arguments;
0406: this .logStream = logStream;
0407: registry = new PathTreeDictionary();
0408: realms = new PathTreeDictionary();
0409: attributes = new Hashtable();
0410: sessions = new HttpSessionContextImpl();
0411: setAccessLogged();
0412: expiredIn = arguments.get(ARG_SESSION_TIMEOUT) != null ? ((Integer) arguments
0413: .get(ARG_SESSION_TIMEOUT)).intValue()
0414: : DEF_SESSION_TIMEOUT;
0415: port = arguments.get(ARG_PORT) != null ? ((Integer) arguments
0416: .get(ARG_PORT)).intValue() : DEF_PORT;
0417: }
0418:
0419: public Serve() {
0420: this (new Hashtable(), System.err);
0421: }
0422:
0423: void setAccessLogged() {
0424: String logflags = (String) arguments.get(ARG_LOG_OPTIONS);
0425: if (logflags != null) {
0426: useAccLog = true;
0427: showUserAgent = logflags.indexOf('A') >= 0;
0428: showReferer = logflags.indexOf('R') >= 0;
0429: }
0430: }
0431:
0432: boolean isAccessLogged() {
0433: return useAccLog;
0434: }
0435:
0436: boolean isShowReferer() {
0437: return showReferer;
0438: }
0439:
0440: boolean isShowUserAgent() {
0441: return showUserAgent;
0442: }
0443:
0444: /// Register a Servlet by class name. Registration consists of a URL
0445: // pattern, which can contain wildcards, and the class name of the Servlet
0446: // to launch when a matching URL comes in. Patterns are checked for
0447: // matches in the order they were added, and only the first match is run.
0448: public void addServlet(String urlPat, String className) {
0449: addServlet(urlPat, className, (Hashtable) null);
0450: }
0451:
0452: public void addServlet(String urlPat, String className,
0453: Hashtable initParams) {
0454: // Check if we're allowed to make one of these.
0455: SecurityManager security = System.getSecurityManager();
0456: if (security != null) {
0457: int i = className.lastIndexOf('.');
0458: if (i != -1) {
0459: security.checkPackageAccess(className.substring(0, i));
0460: security.checkPackageDefinition(className.substring(0,
0461: i));
0462: }
0463: }
0464:
0465: // Make a new one.
0466: try {
0467: addServlet(urlPat, (Servlet) Class.forName(className)
0468: .newInstance(), initParams);
0469: return;
0470: } catch (ClassNotFoundException e) {
0471: log("Class not found: " + className);
0472: } catch (ClassCastException e) {
0473: log("Class cast problem: " + e.getMessage());
0474: } catch (InstantiationException e) {
0475: log("Instantiation problem: " + e.getMessage());
0476: } catch (IllegalAccessException e) {
0477: log("Illegal class access: " + e.getMessage());
0478: } catch (Exception e) {
0479: log("Unexpected problem creating servlet: " + e, e);
0480: }
0481: }
0482:
0483: /// Register a Servlet. Registration consists of a URL pattern,
0484: // which can contain wildcards, and the Servlet to
0485: // launch when a matching URL comes in. Patterns are checked for
0486: // matches in the order they were added, and only the first match is run.
0487: public void addServlet(String urlPat, Servlet servlet) {
0488: addServlet(urlPat, servlet, (Hashtable) null);
0489: }
0490:
0491: public void addServlet(String urlPat, Servlet servlet,
0492: Hashtable initParams) {
0493: try {
0494: servlet.init(new ServeConfig((ServletContext) this ,
0495: initParams, urlPat));
0496: registry.put(urlPat, servlet);
0497: } catch (ServletException e) {
0498: log("Problem initializing servlet: " + e);
0499: }
0500: }
0501:
0502: /// Register a standard set of Servlets. These will return
0503: // files or directory listings, and run CGI programs, much like a
0504: // standard HTTP server.
0505: // <P>
0506: // Because of the pattern checking order, this should be called
0507: // <B>after</B> you've added any custom Servlets.
0508: // <P>
0509: // The current set of default servlet mappings:
0510: // <UL>
0511: // <LI> If enabled, *.cgi goes to CgiServlet, and gets run as a CGI program.
0512: // <LI> * goes to FileServlet, and gets served up as a file or directory.
0513: // </UL>
0514: // @param cgi whether to run CGI programs
0515: // TODO: provide user specified CGI directory
0516: public void addDefaultServlets(String cgi) {
0517: if (cgi != null)
0518: addServlet("/" + cgi, new Acme.Serve.CgiServlet());
0519: addServlet("/", new Acme.Serve.FileServlet());
0520: }
0521:
0522: /// Register a standard set of Servlets, with throttles.
0523: // @param cgi whether to run CGI programs
0524: // @param throttles filename to read FileServlet throttle settings from
0525: public void addDefaultServlets(String cgi, String throttles)
0526: throws IOException {
0527: if (cgi != null)
0528: addServlet("/" + cgi, new Acme.Serve.CgiServlet());
0529: addServlet("/", new Acme.Serve.FileServlet(throttles));
0530: }
0531:
0532: // Run the server. Returns only on errors.
0533: boolean running = true;
0534:
0535: public void serve() {
0536: final ServerSocket serverSocket;
0537: try {
0538: serverSocket = createServerSocket();
0539: } catch (IOException e) {
0540: log("Server socket: " + e);
0541: return;
0542: }
0543: hostName = serverSocket.getInetAddress().getHostName();
0544: new Thread(new Runnable() {
0545: public void run() {
0546: BufferedReader in = new BufferedReader(
0547: new InputStreamReader(System.in));
0548: String line;
0549: while (true) {
0550: try {
0551: System.out
0552: .print("Press \"q\" <ENTER>, for gracefully stopping the server ");
0553: line = in.readLine();
0554: if (line != null && line.length() > 0
0555: && line.charAt(0) == 'q') {
0556: running = false;
0557: serverSocket.close();
0558: break;
0559: }
0560: } catch (IOException e) {
0561: }
0562: }
0563: }
0564: }, "Stop Monitor").start();
0565: if (expiredIn > 0) {
0566: Thread t = new Thread(new Runnable() {
0567: public void run() {
0568: while (running) {
0569: try {
0570: Thread.sleep(expiredIn * 60 * 1000);
0571: } catch (InterruptedException ie) {
0572: }
0573: Enumeration e = sessions.keys();
0574: while (e.hasMoreElements()) {
0575: Object sid = e.nextElement();
0576: if (sid != null) {
0577: AcmeSession as = (AcmeSession) sessions
0578: .get(sid);
0579: if (as != null
0580: && (as.getMaxInactiveInterval() * 1000 < System
0581: .currentTimeMillis()
0582: - as
0583: .getLastAccessedTime() || !as
0584: .isValid())) { //log("sesion is timeouted, last accessed " + new Date(as.getLastAccessedTime()));
0585: // hashtable is synchronized impl
0586: as = (AcmeSession) sessions
0587: .remove(sid);
0588: if (as != null && as.isValid())
0589: try {
0590: as.invalidate();
0591: } catch (IllegalStateException ise) {
0592:
0593: }
0594: }
0595: }
0596: }
0597: }
0598: }
0599: }, "Session cleaner");
0600: t.setPriority(Thread.MIN_PRIORITY);
0601: t.start();
0602: } else
0603: expiredIn = -expiredIn;
0604: System.out.println("WebServer :" + port + " is ready.");
0605: try {
0606: while (running) {
0607: Socket socket = serverSocket.accept();
0608: new ServeConnection(socket, this );
0609: }
0610: } catch (IOException e) {
0611: log("Accept: " + e);
0612: } finally {
0613: try {
0614: serverSocket.close();
0615: } catch (IOException e) {
0616: }
0617: destroyAllServlets();
0618: }
0619: }
0620:
0621: public static interface SocketFactory {
0622: public ServerSocket createSocket(Hashtable arguments)
0623: throws IOException, IllegalArgumentException;
0624: }
0625:
0626: protected ServerSocket createServerSocket() throws IOException {
0627: String socketFactoryClass = (String) arguments
0628: .get(ARG_SOCKET_FACTORY);
0629: if (socketFactoryClass != null)
0630: try {
0631: return ((SocketFactory) Class.forName(
0632: socketFactoryClass).newInstance())
0633: .createSocket(arguments);
0634: } catch (Exception e) {
0635: System.err
0636: .println("Couldn't create custom socket factory "
0637: + socketFactoryClass
0638: + " or call creation method. Standard socket will be created. "
0639: + e);
0640: }
0641: return new ServerSocket(port, 1000);
0642: }
0643:
0644: // Methods from ServletContext.
0645:
0646: /// Gets a servlet by name.
0647: // @param name the servlet name
0648: // @return null if the servlet does not exist
0649: public Servlet getServlet(String name) {
0650: try {
0651: return (Servlet) ((Object[]) registry.get(name))[0];
0652: } catch (NullPointerException npe) {
0653: return null;
0654: }
0655: }
0656:
0657: /// Enumerates the servlets in this context (server). Only servlets that
0658: // are accesible will be returned. This enumeration always includes the
0659: // servlet itself.
0660: public Enumeration getServlets() {
0661: return registry.elements();
0662: }
0663:
0664: /// Enumerates the names of the servlets in this context (server). Only
0665: // servlets that are accesible will be returned. This enumeration always
0666: // includes the servlet itself.
0667: public Enumeration getServletNames() {
0668: return registry.keys();
0669: }
0670:
0671: /// Destroys all currently-loaded servlets.
0672: public void destroyAllServlets() {
0673: Enumeration en = registry.elements();
0674: while (en.hasMoreElements()) {
0675: Servlet servlet = (Servlet) en.nextElement();
0676: servlet.destroy();
0677: }
0678: registry = new PathTreeDictionary();
0679: // invalidate all sessions?
0680: }
0681:
0682: public void setMappingTable(PathTreeDictionary mappingtable) {
0683: this .mappingtable = mappingtable;
0684: }
0685:
0686: public void setRealms(PathTreeDictionary realms) {
0687: this .realms = realms;
0688: }
0689:
0690: Object getSession(String id) {
0691: return sessions.get(id);
0692: }
0693:
0694: HttpSession createSession() {
0695: HttpSession result = new AcmeSession(generateSessionId(),
0696: expiredIn * 60, this , sessions);
0697: sessions.put(result.getId(), result);
0698: return result;
0699: }
0700:
0701: void removeSession(String id) {
0702: sessions.remove(id);
0703: }
0704:
0705: /// Write information to the servlet log.
0706: // @param message the message to log
0707: public void log(String message) {
0708: Date date = new Date(System.currentTimeMillis());
0709: logStream.println("[" + date.toString() + "] " + message);
0710: }
0711:
0712: public void log(String message, Throwable throwable) {
0713: StringWriter sw = new StringWriter();
0714: throwable.printStackTrace(new PrintWriter(sw));
0715: log(message + '\n' + sw);
0716: }
0717:
0718: /// Write a stack trace to the servlet log.
0719: // @param exception where to get the stack trace
0720: // @param message the message to log
0721: public void log(Exception exception, String message) {
0722: StringWriter sw = new StringWriter();
0723: exception.printStackTrace(new PrintWriter(sw));
0724: log("" + sw + '\n' + message);
0725: }
0726:
0727: /// Applies alias rules to the specified virtual path and returns the
0728: // corresponding real path. It returns null if the translation
0729: // cannot be performed.
0730: // @param path the path to be translated
0731: public String getRealPath(String path) {
0732: //System.err.print("["+path+"]->[");
0733: try { // this code only for debug purpose
0734: // check if it absolute URL
0735: URL url = new URL(path);
0736: path = url.getFile();
0737: new Exception("URL " + path + " specified in getRealPath")
0738: .printStackTrace();
0739: path = null;
0740: } catch (MalformedURLException mfue) {
0741: }
0742: if (mappingtable != null) {
0743: // try find first sub-path
0744: Object[] os = mappingtable.get(path);
0745: if (os[0] == null)
0746: return null;
0747: int slpos = ((Integer) os[1]).intValue();
0748: if (path.length() > slpos && slpos > 0) {
0749: path = path.substring(slpos + 1);
0750: } else if (path.length() > 0) {
0751: char s = path.charAt(0);
0752: if (s == '/' || s == '\\')
0753: path = path.substring(1);
0754: }
0755: //System.err.println("Base:"+((String)os[0])+"\npath="+path+"\n pos="+slpos+']');
0756:
0757: return ((String) os[0]) + File.separatorChar + path;
0758: }
0759: return path;
0760: }
0761:
0762: /// Returns the MIME type of the specified file.
0763: // @param file file name whose MIME type is required
0764: public String getMimeType(String file) {
0765: file = file.toUpperCase();
0766:
0767: if (file.endsWith(".HTML") || file.endsWith(".HTM"))
0768: return "text/html";
0769: if (file.endsWith(".TXT"))
0770: return "text/plain";
0771: if (file.endsWith(".XML"))
0772: return "text/xml";
0773: if (file.endsWith(".CSS"))
0774: return "text/css";
0775: if (file.endsWith(".SGML") || file.endsWith(".SGM"))
0776: return "text/x-sgml";
0777: // Image
0778: if (file.endsWith(".GIF"))
0779: return "image/gif";
0780: if (file.endsWith(".JPG") || file.endsWith(".JPEG")
0781: || file.endsWith(".JPE"))
0782: return "image/jpeg";
0783: if (file.endsWith(".PNG"))
0784: return "image/png";
0785: if (file.endsWith(".TIF") || file.endsWith(".TIFF"))
0786: return "image/tiff";
0787: if (file.endsWith(".RGB"))
0788: return "image/x-rgb";
0789: if (file.endsWith(".XPM"))
0790: return "image/x-xpixmap";
0791: if (file.endsWith(".XBM"))
0792: return "image/x-xbitmap";
0793: if (file.endsWith(".SVG"))
0794: return "image/svg-xml ";
0795: if (file.endsWith(".SVGZ"))
0796: return "image/svg-xml ";
0797: // Audio
0798: if (file.endsWith(".AU") || file.endsWith(".SND"))
0799: return "audio/basic";
0800: if (file.endsWith(".MID") || file.endsWith(".MIDI")
0801: || file.endsWith(".RMI") || file.endsWith(".KAR"))
0802: return "audio/mid";
0803: if (file.endsWith(".MPGA") || file.endsWith(".MP2")
0804: || file.endsWith(".MP3"))
0805: return "audio/mpeg";
0806: if (file.endsWith(".WAV"))
0807: return "audio/wav";
0808: if (file.endsWith(".AIFF") || file.endsWith(".AIFC"))
0809: return "audio/aiff";
0810: if (file.endsWith(".AIF"))
0811: return "audio/x-aiff";
0812: if (file.endsWith(".RA"))
0813: return "audio/x-realaudio";
0814: if (file.endsWith(".RPM"))
0815: return "audio/x-pn-realaudio-plugin";
0816: if (file.endsWith(".RAM"))
0817: return "audio/x-pn-realaudio";
0818: if (file.endsWith(".SD2"))
0819: return "audio/x-sd2";
0820: // Application
0821: if (file.endsWith(".BIN") || file.endsWith(".DMS")
0822: || file.endsWith(".LHA") || file.endsWith(".LZH")
0823: || file.endsWith(".EXE") || file.endsWith(".CLASS"))
0824: return "application/octet-stream";
0825: if (file.endsWith(".HQX"))
0826: return "application/mac-binhex40";
0827: if (file.endsWith(".PS") || file.endsWith(".AI")
0828: || file.endsWith(".EPS"))
0829: return "application/postscript";
0830: if (file.endsWith(".PDF"))
0831: return "application/pdf";
0832: if (file.endsWith(".RTF"))
0833: return "application/rtf";
0834: if (file.endsWith(".DOC"))
0835: return "application/msword";
0836: if (file.endsWith(".PPT"))
0837: return "application/powerpoint";
0838: if (file.endsWith(".FIF"))
0839: return "application/fractals";
0840: if (file.endsWith(".P7C"))
0841: return "application/pkcs7-mime";
0842: // Application/x
0843: if (file.endsWith(".JS"))
0844: return "application/x-javascript";
0845: if (file.endsWith(".Z"))
0846: return "application/x-compress";
0847: if (file.endsWith(".GZ"))
0848: return "application/x-gzip";
0849: if (file.endsWith(".TAR"))
0850: return "application/x-tar";
0851: if (file.endsWith(".TGZ"))
0852: return "application/x-compressed";
0853: if (file.endsWith(".ZIP"))
0854: return "application/x-zip-compressed";
0855: if (file.endsWith(".DIR") || file.endsWith(".DCR")
0856: || file.endsWith(".DXR"))
0857: return "application/x-director";
0858: if (file.endsWith(".DVI"))
0859: return "application/x-dvi";
0860: if (file.endsWith(".TEX"))
0861: return "application/x-tex";
0862: if (file.endsWith(".LATEX"))
0863: return "application/x-latex";
0864: if (file.endsWith(".TCL"))
0865: return "application/x-tcl";
0866: if (file.endsWith(".CER") || file.endsWith(".CRT")
0867: || file.endsWith(".DER"))
0868: return "application/x-x509-ca-cert";
0869: // Video
0870: if (file.endsWith(".MPG") || file.endsWith(".MPE")
0871: || file.endsWith(".MPEG"))
0872: return "video/mpeg";
0873: if (file.endsWith(".QT") || file.endsWith(".MOV"))
0874: return "video/quicktime";
0875: if (file.endsWith(".AVI"))
0876: return "video/x-msvideo";
0877: if (file.endsWith(".MOVIE"))
0878: return "video/x-sgi-movie";
0879: // Chemical
0880: if (file.endsWith(".PDB") || file.endsWith(".XYZ"))
0881: return "chemical/x-pdb";
0882: // X-
0883: if (file.endsWith(".ICE"))
0884: return "x-conference/x-cooltalk";
0885: if (file.endsWith(".WRL") || file.endsWith(".VRML"))
0886: return "x-world/x-vrml";
0887: if (file.endsWith(".WML"))
0888: return "text/vnd.wap.wml";
0889: if (file.endsWith(".WMLC"))
0890: return "application/vnd.wap.wmlc";
0891: if (file.endsWith(".WMLS"))
0892: return "text/vnd.wap.wmlscript";
0893: if (file.endsWith(".WMLSC"))
0894: return "application/vnd.wap.wmlscriptc";
0895: if (file.endsWith(".WBMP"))
0896: return "image/vnd.wap.wbmp";
0897:
0898: return null;
0899: }
0900:
0901: /// Returns the name and version of the web server under which the servlet
0902: // is running.
0903: // Same as the CGI variable SERVER_SOFTWARE.
0904: public String getServerInfo() {
0905: return Serve.Identification.serverName + " "
0906: + Serve.Identification.serverVersion + " ("
0907: + Serve.Identification.serverUrl + ")";
0908: }
0909:
0910: /// Returns the value of the named attribute of the network service, or
0911: // null if the attribute does not exist. This method allows access to
0912: // additional information about the service, not already provided by
0913: // the other methods in this interface.
0914: public Object getAttribute(String name) {
0915: // This server does not support attributes.
0916: return attributes.get(name);
0917: }
0918:
0919: /////////////////// JSDK 2.1 extensions //////////////////////////
0920: public void removeAttribute(String name) {
0921: attributes.remove(name);
0922: }
0923:
0924: public void setAttribute(String name, Object object) {
0925: attributes.put(name, object);
0926: }
0927:
0928: public Enumeration getAttributeNames() {
0929: return attributes.keys();
0930: }
0931:
0932: public ServletContext getContext(String uripath) {
0933: return this ; // only root context supported
0934: }
0935:
0936: public int getMajorVersion() {
0937: return 2; // support 2.x
0938: }
0939:
0940: public int getMinorVersion() {
0941: return 3; // support 2.3
0942: }
0943:
0944: // 2.3
0945:
0946: /**
0947: * 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
0948: * of the web application and have a leading '/'. For example, for a web application containing
0949: * <p>
0950: * /welcome.html <br>
0951: * /catalog/index.html<br>
0952: * /catalog/products.html<br>
0953: * /catalog/offers/books.html<br>
0954: * /catalog/offers/music.html<br>
0955: * /customer/login.jsp<br>
0956: * /WEB-INF/web.xml<br>
0957: * /WEB-INF/classes/com.acme.OrderServlet.class,
0958: * <p>
0959: * getResourcePaths("/") returns {"/welcome.html", "/catalog/", "/customer/", "/WEB-INF/"}<br>
0960: * getResourcePaths("/catalog/") returns {"/catalog/index.html", "/catalog/products.html", "/catalog/offers/"}.
0961: * <p>
0962: *
0963: * @param the - partial path used to match the resources, which must start with a /
0964: * @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.
0965: * @since Servlet 2.3
0966: *
0967: */
0968: public java.util.Set getResourcePaths(java.lang.String path) {
0969: // TODO: implement
0970: return null;
0971: }
0972:
0973: /**
0974: * 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.
0975: * @return The name of the web application or null if no name has been declared in the deployment descriptor.
0976: *
0977: * @since Servlet 2.3
0978: */
0979: public java.lang.String getServletContextName() {
0980: return null;//"ROOT";
0981: }
0982:
0983: // only root relative in this implementation
0984: public URL getResource(String path) throws MalformedURLException {
0985: return new URL("http", hostName, port, path);
0986: }
0987:
0988: public InputStream getResourceAsStream(String path) {
0989: return null; // we don't provide resources in this way
0990: }
0991:
0992: public RequestDispatcher getRequestDispatcher(String urlpath) {
0993: return this ; // we don't provide resource dispatching in this way
0994: }
0995:
0996: // no way to specify parameters for context
0997: public String getInitParameter(String param) {
0998: return null;
0999: }
1000:
1001: public Enumeration getInitParameterNames() {
1002: return null;
1003: }
1004:
1005: public RequestDispatcher getNamedDispatcher(String name) {
1006: return this ;
1007: }
1008:
1009: synchronized String generateSessionId() {
1010: return "-" + System.currentTimeMillis() + '-' + (uniqer++)
1011: + '-' + Math.round(Math.random() * 1000);
1012: }
1013:
1014: public void forward(ServletRequest _request,
1015: ServletResponse _response) throws ServletException,
1016: java.io.IOException {
1017: }
1018:
1019: public void include(ServletRequest _request,
1020: ServletResponse _response) throws ServletException,
1021: java.io.IOException {
1022: }
1023:
1024: final static class Identification {
1025: public static final String serverName = "Rogatkin's JWS based on Acme.Serve";
1026: public static final String serverVersion = "$Revision: 1.1 $";
1027: public static final String serverUrl = "http://tjws.sourceforge.net";
1028:
1029: /// Write a standard-format HTML address for this server.
1030: public static void writeAddress(OutputStream o)
1031: throws IOException {
1032: PrintStream p = new PrintStream(o);
1033: p.println("<ADDRESS><A HREF=\"" + serverUrl + "\">"
1034: + serverName + " " + serverVersion
1035: + "</A></ADDRESS>");
1036: }
1037:
1038: public static void writeAddress(StringBuffer sb)
1039: throws IOException {
1040: sb.append("<ADDRESS><A HREF=\"" + serverUrl + "\">"
1041: + serverName + " " + serverVersion
1042: + "</A></ADDRESS>");
1043: }
1044: }
1045: }
1046:
1047: class ServeConfig implements ServletConfig {
1048:
1049: private ServletContext context;
1050: private Hashtable init_params;
1051: private String servletName;
1052:
1053: public ServeConfig(ServletContext context) {
1054: this (context, null, "undefined");
1055: }
1056:
1057: public ServeConfig(ServletContext context, Hashtable initParams,
1058: String servletName) {
1059: this .context = context;
1060: this .init_params = initParams;
1061: this .servletName = servletName;
1062: }
1063:
1064: // Methods from ServletConfig.
1065:
1066: /// Returns the context for the servlet.
1067: public ServletContext getServletContext() {
1068: return context;
1069: }
1070:
1071: /// Gets an initialization parameter of the servlet.
1072: // @param name the parameter name
1073: public String getInitParameter(String name) {
1074: // This server supports servlet init params. :)
1075: if (init_params != null)
1076: return (String) init_params.get(name);
1077: return null;
1078: }
1079:
1080: /// Gets the names of the initialization parameters of the servlet.
1081: // @param name the parameter name
1082: public Enumeration getInitParameterNames() {
1083: // This server does:) support servlet init params.
1084: if (init_params != null)
1085: return init_params.keys();
1086: return new Vector().elements();
1087: }
1088:
1089: // 2.2
1090: public String getServletName() {
1091: return servletName;
1092: }
1093: }
1094:
1095: ///////////////////////////////////////////////////////////////////////
1096: /**
1097: * provides request/response
1098: */
1099: class ServeConnection implements Runnable, HttpServletRequest,
1100: HttpServletResponse {
1101:
1102: private Socket socket;
1103: private Serve serve;
1104:
1105: private ServletInputStream in;
1106: private ServletOutputStream out;
1107:
1108: private Hashtable formParameters;
1109: private Hashtable attributes = new Hashtable();
1110:
1111: public final static String WWWFORMURLENCODE = "application/x-www-form-urlencoded";
1112: public final static String TRANSFERENCODING = "Transfer-Encoding";
1113: public final static String CHUNKED = "chunked";
1114: public final static String CONTENTLENGTH = "Content-Length";
1115: public final static String CONTENTTYPE = "Content-Type";
1116: public final static String SETCOOKIE = "Set-Cookie";
1117: public final static String COOKIE = "Cookie";
1118:
1119: public final static String SESSION_COOKIE_NAME = "JSESSIONID";
1120: // URL rewriting http://www.myserver.com/catalog/index.html;jsessionid=mysession1928
1121: // like: http://www.sun.com/2001-0227/sunblade/;$sessionid$AD5RQ0IAADJAZAMTA1LU5YQ
1122:
1123: private String reqMethod; // == null by default
1124: private String reqUriPath;
1125: private String reqProtocol;
1126: private String reqCharEncoding;
1127: private String remoteUser;
1128: private String authType;
1129: private boolean oneOne; // HTTP/1.1 or better
1130: private boolean reqMime;
1131: String reqQuery = null;
1132: private Vector reqHeaderNames = new Vector();
1133: private Vector reqHeaderValues = new Vector();
1134: private Locale locale; // = java.util.Locale.getDefault();
1135: private int uriLen;
1136: private static final Hashtable EMPTYHASHTABLE = new Hashtable();
1137:
1138: private Vector outCookies;
1139: private Vector inCookies;
1140:
1141: private String sessionCookieValue;
1142:
1143: /// Constructor.
1144: public ServeConnection(Socket socket, Serve serve) {
1145: // Save arguments.
1146: this .socket = socket;
1147: this .serve = serve;
1148:
1149: // Start a separate thread to read and handle the request.
1150: Thread thread = new Thread(this , "Request handler");
1151: thread.start();
1152: }
1153:
1154: // Methods from Runnable.
1155: public void run() {
1156: try {
1157: // Get the streams.
1158: in = new ServeInputStream(socket.getInputStream());
1159: out = new ServeOutputStream(socket.getOutputStream(), this );
1160: } catch (IOException e) {
1161: problem("Getting streams: " + e.getMessage(),
1162: SC_BAD_REQUEST);
1163: }
1164:
1165: parseRequest();
1166:
1167: if (serve.isAccessLogged()) {
1168: serve.log(socket.getInetAddress().toString()
1169: + ' '
1170: + reqMethod
1171: + ' '
1172: + reqUriPath
1173: + ' '
1174: + resCode
1175: + (serve.isShowReferer() ? "| "
1176: + getHeader("Referer") : "")
1177: + (serve.isShowUserAgent() ? "| "
1178: + getHeader("User-Agent") : ""));
1179: }
1180:
1181: try {
1182: socket.close();
1183: } catch (IOException e) { /* ignore */
1184: }
1185: }
1186:
1187: private void parseRequest() {
1188: byte[] lineBytes = new byte[4096];
1189: int len;
1190: String line;
1191:
1192: try {
1193: // Read the first line of the request.
1194: len = in.readLine(lineBytes, 0, lineBytes.length);
1195: if (len == -1 || len == 0) {
1196: problem("Empty request", SC_BAD_REQUEST);
1197: return;
1198: }
1199: line = new String(lineBytes, 0, len);
1200: StringTokenizer ust = new StringTokenizer(line);
1201: reqProtocol = null;
1202: if (ust.hasMoreTokens()) {
1203: reqMethod = ust.nextToken();
1204: if (ust.hasMoreTokens()) {
1205: reqUriPath = Acme.Utils.urlDecoder(ust.nextToken());
1206: if (ust.hasMoreTokens()) {
1207: reqProtocol = ust.nextToken();
1208: oneOne = !reqProtocol.toUpperCase().equals(
1209: "HTTP/1.0");
1210: reqMime = true;
1211: // Read the rest of the lines.
1212: String s;
1213: while ((s = ((ServeInputStream) in).readLine()) != null) {
1214: if (s.length() == 0)
1215: break;
1216:
1217: int c = s.indexOf(':', 0);
1218: if (c > 0) {
1219: String key = s.substring(0, c).trim();
1220: String value = s.substring(c + 1,
1221: s.length()).trim();
1222: reqHeaderNames.addElement(key
1223: .toLowerCase());
1224: reqHeaderValues.addElement(value);
1225: } else
1226: serve.log("header field without ':'");
1227: }
1228: } else {
1229: reqProtocol = "HTTP/0.9";
1230: oneOne = false;
1231: reqMime = false;
1232: }
1233: }
1234: }
1235: if (reqProtocol == null) {
1236: problem("Malformed request line", SC_BAD_REQUEST);
1237: return;
1238: }
1239: // Check Host: header in HTTP/1.1 requests.
1240: if (oneOne) {
1241: String host = getHeader("host");
1242: if (host == null) {
1243: problem("Host header missing on HTTP/1.1 request",
1244: SC_BAD_REQUEST);
1245: return;
1246: }
1247: }
1248:
1249: // Decode %-sequences.
1250: //reqUriPath = decode( reqUriPath );
1251: // Split off query string, if any.
1252: int qmark = reqUriPath.indexOf('?');
1253: if (qmark > -1) {
1254: reqQuery = reqUriPath.substring(qmark + 1);
1255: reqUriPath = reqUriPath.substring(0, qmark);
1256: }
1257: if (CHUNKED.equals(getHeader(TRANSFERENCODING))) {
1258: setHeader(CONTENTLENGTH, null);
1259: ((ServeInputStream) in).chunking(true);
1260: }
1261:
1262: Object[] os = serve.registry.get(reqUriPath);
1263: if (os != null) {
1264: uriLen = ((Integer) os[1]).intValue();
1265: runServlet((HttpServlet) os[0]);
1266: }
1267: } catch (IOException e) {
1268: problem("Reading request: " + e.getMessage(),
1269: SC_BAD_REQUEST);
1270: }
1271: }
1272:
1273: private void runServlet(HttpServlet servlete) {
1274: // Set default response fields.
1275: setStatus(SC_OK);
1276: setDateHeader("Date", System.currentTimeMillis());
1277: setHeader("Server", Serve.Identification.serverName + "/"
1278: + Serve.Identification.serverVersion);
1279: setHeader("MIME-Version", "1.0");
1280: try {
1281: parseCookies();
1282: authenificate();
1283: if (servlete instanceof SingleThreadModel)
1284: synchronized (servlete) {
1285: servlete.service((ServletRequest) this ,
1286: (ServletResponse) this );
1287: }
1288: else
1289: servlete.service((ServletRequest) this ,
1290: (ServletResponse) this );
1291: } catch (IOException e) {
1292: e.printStackTrace();
1293: problem("IO problem running servlet: " + e.toString(),
1294: SC_BAD_REQUEST);
1295: } catch (ServletException e) {
1296: problem("problem running servlet: " + e.toString(),
1297: SC_BAD_REQUEST);
1298: } catch (Exception e) {
1299: problem("unexpected problem running servlet: "
1300: + e.toString(), SC_INTERNAL_SERVER_ERROR);
1301: e.printStackTrace();
1302: }
1303: }
1304:
1305: private boolean authenificate() throws IOException {
1306: Object[] o = serve.realms.get(getPathInfo());
1307: BasicAuthRealm realm = null;
1308: if (o == null)
1309: realm = (BasicAuthRealm) o[0];
1310: String credentials = getHeader("Authorization");
1311:
1312: if (credentials != null) {
1313: credentials = credentials.substring(credentials
1314: .indexOf(' ') + 1);
1315: try {
1316: ByteArrayOutputStream baos = new ByteArrayOutputStream();
1317: serve.base64Dec.decodeBuffer(
1318: new StringBufferInputStream(credentials), baos);
1319: credentials = baos.toString();
1320: } catch (IOException e) {
1321: e.printStackTrace();
1322: }
1323: int i = credentials.indexOf(':');
1324: String user = credentials.substring(0, i);
1325: String password = credentials.substring(i + 1);
1326: remoteUser = user;
1327: authType = "Basic"; // support only basic authenification
1328: if (realm == null)
1329: return true;
1330: String realPassword = (String) realm.get(user);
1331: System.err.println("User " + user + " Password " + password
1332: + " real " + realPassword);
1333: if (realPassword != null && realPassword.equals(password))
1334: return true;
1335: }
1336: if (realm == null)
1337: return true;
1338:
1339: setStatus(SC_UNAUTHORIZED);
1340: setHeader("WWW-Authenticate", "basic realm=\"" + realm.name()
1341: + '"');
1342: writeHeaders();
1343: return false;
1344: }
1345:
1346: private void problem(String logMessage, int resCode) {
1347: serve.log(logMessage);
1348: try {
1349: sendError(resCode);
1350: } catch (IllegalStateException e) { /* ignore */
1351: } catch (IOException e) { /* ignore */
1352: }
1353: }
1354:
1355: private String decode(String str) {
1356: StringBuffer result = new StringBuffer();
1357: int l = str.length();
1358: for (int i = 0; i < l; ++i) {
1359: char c = str.charAt(i);
1360: if (c == '%' && i + 2 < l) {
1361: char c1 = str.charAt(i + 1);
1362: char c2 = str.charAt(i + 2);
1363: if (isHexit(c1) && isHexit(c2)) {
1364: result.append((char) (hexit(c1) * 16 + hexit(c2)));
1365: i += 2;
1366: } else
1367: result.append(c);
1368: } else if (c == '+')
1369: result.append(' ');
1370: else
1371: result.append(c);
1372: }
1373: return result.toString();
1374: }
1375:
1376: private boolean isHexit(char c) {
1377: String legalChars = "0123456789abcdefABCDEF";
1378: return (legalChars.indexOf(c) != -1);
1379: }
1380:
1381: private int hexit(char c) {
1382: if (c >= '0' && c <= '9')
1383: return c - '0';
1384: if (c >= 'a' && c <= 'f')
1385: return c - 'a' + 10;
1386: if (c >= 'A' && c <= 'F')
1387: return c - 'A' + 10;
1388: return 0; // shouldn't happen, we're guarded by isHexit()
1389: }
1390:
1391: void parseCookies() {
1392: if (inCookies == null)
1393: inCookies = new Vector();
1394: try {
1395: String cookie_name;
1396: String cookie_value;
1397: String cookie_path;
1398: String cookies = getHeader(COOKIE);
1399: if (cookies == null)
1400: return;
1401: //Enumeration e = getHeaders(COOKIE);
1402: //while(e.hasMoreElements())
1403: // cookies += (String)e.nextElement();
1404: StringTokenizer st = new StringTokenizer(cookies, ";", true);
1405: // TODO: write a parser to avoid tokenizers
1406: while (st.hasMoreTokens()) {
1407: StringTokenizer st2 = new StringTokenizer(st
1408: .nextToken(), "=");
1409: if (st2.hasMoreTokens()) {
1410: cookie_name = st2.nextToken().trim();
1411: if (st2.hasMoreTokens()) {
1412: cookie_value = st2.nextToken(",").trim();
1413: if (cookie_value.length() > 0
1414: && cookie_value.charAt(0) == '=')
1415: cookie_value = cookie_value.substring(1);
1416: cookie_path = "/";
1417: while (st2.hasMoreTokens()) {
1418: String cookie_atr = st2.nextToken();
1419: if ("$Version".equalsIgnoreCase(cookie_atr)
1420: || "$Path"
1421: .equalsIgnoreCase(cookie_atr)
1422: || "$Domain"
1423: .equalsIgnoreCase(cookie_atr))
1424: continue;
1425: cookie_path = st2.nextToken();
1426: }
1427: Cookie cookie = new Cookie(cookie_name,
1428: cookie_value);
1429: //System.err.println("Cookie set:"+cookie_name+':'+cookie_value);
1430: cookie.setPath(cookie_path);
1431: inCookies.addElement(cookie);
1432: if (SESSION_COOKIE_NAME.equals(cookie_name)
1433: && sessionCookieValue == null) {
1434: sessionCookieValue = cookie_value;
1435: try {
1436: ((AcmeSession) serve
1437: .getSession(sessionCookieValue))
1438: .userTouch();
1439: } catch (Throwable t) {
1440: }
1441: }
1442: }
1443: }
1444: }
1445: } catch (Throwable e) {
1446: System.err.println("prepareCookies(): " + e);
1447: e.printStackTrace();
1448: }
1449: }
1450:
1451: // Methods from ServletRequest.
1452:
1453: /// Returns the size of the request entity data, or -1 if not known.
1454: // Same as the CGI variable CONTENT_LENGTH.
1455: public int getContentLength() {
1456: return getIntHeader(CONTENTLENGTH, -1);
1457: }
1458:
1459: /// Returns the MIME type of the request entity data, or null if
1460: // not known.
1461: // Same as the CGI variable CONTENT_TYPE.
1462: public String getContentType() {
1463: return getHeader(CONTENTTYPE);
1464: }
1465:
1466: /// Returns the protocol and version of the request as a string of
1467: // the form <protocol>/<major version>.<minor version>.
1468: // Same as the CGI variable SERVER_PROTOCOL.
1469: public String getProtocol() {
1470: return reqProtocol;
1471: }
1472:
1473: /// Returns the scheme of the URL used in this request, for example
1474: // "http", "https", or "ftp". Different schemes have different rules
1475: // for constructing URLs, as noted in RFC 1738. The URL used to create
1476: // a request may be reconstructed using this scheme, the server name
1477: // and port, and additional information such as URIs.
1478: public String getScheme() {
1479: if (socket.getClass().getName().toUpperCase().indexOf("SSL") < 0)
1480: return "http";
1481: else
1482: return "https";
1483: }
1484:
1485: /// Returns the host name of the server as used in the <host> part of
1486: // the request URI.
1487: // Same as the CGI variable SERVER_NAME.
1488: public String getServerName() {
1489: String serverName;
1490: int serverPort = 80;
1491: serverName = getHeader("Host");
1492: if (serverName != null && serverName.length() > 0) {
1493: int colon = serverName.indexOf(':');
1494: if (colon >= 0) {
1495: if (colon < serverName.length())
1496: serverPort = Integer.parseInt(serverName
1497: .substring(colon + 1));
1498: serverName = serverName.substring(0, colon);
1499: }
1500: }
1501:
1502: if (serverName == null) {
1503: try {
1504: serverName = InetAddress.getLocalHost().getHostName();
1505: } catch (java.net.UnknownHostException ignore) {
1506: serverName = "127.0.0.0";
1507: }
1508: }
1509:
1510: int slash = serverName.indexOf("/");
1511: if (slash >= 0)
1512: serverName = serverName.substring(slash + 1);
1513: return serverName;
1514: }
1515:
1516: /// Returns the port number on which this request was received as used in
1517: // the <port> part of the request URI.
1518: // Same as the CGI variable SERVER_PORT.
1519: public int getServerPort() {
1520: return socket.getLocalPort();
1521: }
1522:
1523: /// Returns the IP address of the agent that sent the request.
1524: // Same as the CGI variable REMOTE_ADDR.
1525: public String getRemoteAddr() {
1526: return socket.getInetAddress().toString();
1527: }
1528:
1529: /// Returns the fully qualified host name of the agent that sent the
1530: // request.
1531: // Same as the CGI variable REMOTE_HOST.
1532: public String getRemoteHost() {
1533: String result = socket.getInetAddress().getHostName();
1534: return result != null ? result : getRemoteAddr();
1535: }
1536:
1537: /// Applies alias rules to the specified virtual path and returns the
1538: // corresponding real path, or null if the translation can not be
1539: // performed for any reason. For example, an HTTP servlet would
1540: // resolve the path using the virtual docroot, if virtual hosting is
1541: // enabled, and with the default docroot otherwise. Calling this
1542: // method with the string "/" as an argument returns the document root.
1543: public String getRealPath(String path) {
1544: return serve.getRealPath(path);
1545: }
1546:
1547: /// Returns an input stream for reading request data.
1548: // @exception IllegalStateException if getReader has already been called
1549: // @exception IOException on other I/O-related errors
1550: public ServletInputStream getInputStream() throws IOException {
1551: synchronized (in) {
1552: if (((ServeInputStream) in).isReturnedAsReader())
1553: throw new IllegalStateException(
1554: "Already returned as a reader.");
1555: ((ServeInputStream) in).setReturnedAsReader(true);
1556: }
1557: return in;
1558: }
1559:
1560: /// Returns a buffered reader for reading request data.
1561: // @exception UnsupportedEncodingException if the character set encoding isn't supported
1562: // @exception IllegalStateException if getInputStream has already been called
1563: // @exception IOException on other I/O-related errors
1564: public BufferedReader getReader() {
1565: synchronized (in) {
1566: if (((ServeInputStream) in).isReturnedAsStream())
1567: throw new IllegalStateException(
1568: "Already returned as a stream.");
1569: ((ServeInputStream) in).setReturnedAsStream(true);
1570: }
1571: if (reqCharEncoding != null)
1572: try {
1573: return new BufferedReader(new InputStreamReader(in,
1574: reqCharEncoding));
1575: } catch (UnsupportedEncodingException uee) {
1576: }
1577: return new BufferedReader(new InputStreamReader(in));
1578: }
1579:
1580: private Hashtable getParametersFromRequest() {
1581: Hashtable result = null;
1582: //System.out.println("Req:"+reqMethod+" con:"+getContentType()+" eq "+WWWFORMURLENCODE.equals(getContentType()));
1583: if ("GET".equals(reqMethod)) {
1584: if (reqQuery != null)
1585: try {
1586: result = HttpUtils.parseQueryString(reqQuery);
1587: } catch (IllegalArgumentException ex) {
1588: }
1589: } else if ("POST".equals(reqMethod))
1590: if (WWWFORMURLENCODE.equals(getContentType()))
1591: try {
1592: result = HttpUtils.parsePostData(
1593: getContentLength(), getInputStream());
1594: if (reqQuery != null && reqQuery.length() > 0) {
1595: Acme.Utils.putAll(result, HttpUtils
1596: .parseQueryString(reqQuery));
1597: }
1598: } catch (Exception ex) {
1599: serve.log("Exception " + ex
1600: + " at parsing post data of length "
1601: + getContentLength());
1602: }
1603: else
1604: try {
1605: if (reqQuery != null)
1606: result = HttpUtils.parseQueryString(reqQuery);
1607: } catch (Exception ex) {
1608: }
1609: return result != null ? result : EMPTYHASHTABLE;
1610: }
1611:
1612: /// Returns the parameter names for this request.
1613: public Enumeration getParameterNames() {
1614: if (formParameters == null)
1615: formParameters = getParametersFromRequest();
1616: return formParameters.keys();
1617: }
1618:
1619: /// Returns the value of the specified query string parameter, or null
1620: // if not found.
1621: // @param name the parameter name
1622: public String getParameter(String name) {
1623: String[] params = getParameterValues(name);
1624: if (params == null || params.length == 0)
1625: return null;
1626:
1627: return params[0];
1628: }
1629:
1630: /// Returns the values of the specified parameter for the request as an
1631: // array of strings, or null if the named parameter does not exist.
1632: public String[] getParameterValues(String name) {
1633: if (formParameters == null)
1634: getParameterNames();
1635:
1636: return (String[]) formParameters.get(name);
1637: }
1638:
1639: /// Returns the value of the named attribute of the request, or null if
1640: // the attribute does not exist. This method allows access to request
1641: // information not already provided by the other methods in this interface.
1642: public Object getAttribute(String name) {
1643: return attributes.get(name);
1644: }
1645:
1646: // Methods from HttpServletRequest.
1647:
1648: /// Gets the array of cookies found in this request.
1649: public Cookie[] getCookies() {
1650: Cookie[] cookieArray = new Cookie[inCookies.size()];
1651: inCookies.copyInto(cookieArray);
1652: return cookieArray;
1653: }
1654:
1655: /// Returns the method with which the request was made. This can be "GET",
1656: // "HEAD", "POST", or an extension method.
1657: // Same as the CGI variable REQUEST_METHOD.
1658: public String getMethod() {
1659: return reqMethod;
1660: }
1661:
1662: /***
1663: Returns the part of this request's URL from the protocol name up to
1664: the query string in the first line of the HTTP request.
1665: To reconstruct an URL with a scheme and host,
1666: use HttpUtils.getRequestURL(javax.servlet.http.HttpServletRequest).
1667: */
1668: /// Returns the full request URI.
1669: public String getRequestURI() {
1670: return reqUriPath;
1671: }
1672:
1673: /** Reconstructs the URL the client used to make the request.
1674: * The returned URL contains a protocol, server name, port number,
1675: * and server path, but it does not include query string parameters. <br>
1676: * Because this method returns a StringBuffer, not a string, you can modify the
1677: * URL easily, for example, to append query parameters.
1678: * <p>
1679: * This method is useful for creating redirect messages and for reporting errors.
1680: *
1681: * @return a StringBuffer object containing the reconstructed URL
1682: * @since 2.3
1683: */
1684: public java.lang.StringBuffer getRequestURL() {
1685: return new StringBuffer().append(getScheme()).append("://")
1686: .append(serve.hostName).append(
1687: serve.port == 80 ? "" : String
1688: .valueOf(serve.port)).append(
1689: getRequestURI());
1690: }
1691:
1692: /// Returns the part of the request URI that referred to the servlet being
1693: // invoked.
1694: // Analogous to the CGI variable SCRIPT_NAME.
1695: public String getServletPath() {
1696: // In this server, the entire path is regexp-matched against the
1697: // servlet pattern, so there's no good way to distinguish which
1698: // part refers to the servlet.
1699: return uriLen > 0 ? reqUriPath.substring(0, uriLen) : "";
1700: }
1701:
1702: /// Returns optional extra path information following the servlet path, but
1703: // immediately preceding the query string. Returns null if not specified.
1704: // Same as the CGI variable PATH_INFO.
1705: public String getPathInfo() {
1706: // In this server, the entire path is regexp-matched against the
1707: // servlet pattern, so there's no good way to distinguish which
1708: // part refers to the servlet.
1709: return uriLen >= reqUriPath.length() ? null : reqUriPath
1710: .substring(uriLen);
1711: }
1712:
1713: /// Returns extra path information translated to a real path. Returns
1714: // null if no extra path information was specified.
1715: // Same as the CGI variable PATH_TRANSLATED.
1716: public String getPathTranslated() {
1717: // In this server, the entire path is regexp-matched against the
1718: // servlet pattern, so there's no good way to distinguish which
1719: // part refers to the servlet.
1720: return getRealPath(getPathInfo());
1721: }
1722:
1723: /// Returns the query string part of the servlet URI, or null if not known.
1724: // Same as the CGI variable QUERY_STRING.
1725: public String getQueryString() {
1726: return reqQuery;
1727: }
1728:
1729: /// Returns the name of the user making this request, or null if not known.
1730: // Same as the CGI variable REMOTE_USER.
1731: public String getRemoteUser() {
1732: return remoteUser;
1733: }
1734:
1735: /// Returns the authentication scheme of the request, or null if none.
1736: // Same as the CGI variable AUTH_TYPE.
1737: public String getAuthType() {
1738: return authType;
1739: }
1740:
1741: /// Returns the value of a header field, or null if not known.
1742: // Same as the information passed in the CGI variabled HTTP_*.
1743: // @param name the header field name
1744: public String getHeader(String name) {
1745: int i = reqHeaderNames.indexOf(name.toLowerCase());
1746: if (i == -1)
1747: return null;
1748: return (String) reqHeaderValues.elementAt(i);
1749: }
1750:
1751: public int getIntHeader(String name) {
1752: return getIntHeader(name, 0);
1753: }
1754:
1755: /// Returns the value of an integer header field.
1756: // @param name the header field name
1757: // @param def the integer value to return if header not found or invalid
1758: public int getIntHeader(String name, int def) {
1759: String val = getHeader(name);
1760: if (val == null)
1761: return def;
1762: try {
1763: return Integer.parseInt(val);
1764: } catch (Exception e) {
1765: return def;
1766: }
1767: }
1768:
1769: /// Returns the value of a long header field.
1770: // @param name the header field name
1771: // @param def the long value to return if header not found or invalid
1772: public long getLongHeader(String name, long def) {
1773: String val = getHeader(name);
1774: if (val == null)
1775: return def;
1776: try {
1777: return Long.parseLong(val);
1778: } catch (Exception e) {
1779: return def;
1780: }
1781: }
1782:
1783: public long getDateHeader(String name) {
1784: String val = getHeader(name);
1785: if (val == null)
1786: return 0;
1787: try {
1788: return headerdateformat.parse(val).getTime();
1789: } catch (Exception e) {
1790: throw new IllegalArgumentException("Value " + val
1791: + " can't be converted to Date using "
1792: + headerdateformat.toPattern());
1793: }
1794:
1795: }
1796:
1797: /// Returns the value of a date header field.
1798: // @param name the header field name
1799: // @param def the date value to return if header not found or invalid
1800: public long getDateHeader(String name, long def) {
1801: String val = getHeader(name);
1802: if (val == null)
1803: return def;
1804: try {
1805: return DateFormat.getDateInstance().parse(val).getTime();
1806: } catch (Exception e) {
1807: return def;
1808: }
1809: }
1810:
1811: /// Returns an Enumeration of the header names.
1812: public Enumeration getHeaderNames() {
1813: return reqHeaderNames.elements();
1814: }
1815:
1816: /// Gets the current valid session associated with this request, if
1817: // create is false or, if necessary, creates a new session for the
1818: // request, if create is true.
1819: // <P>
1820: // Note: to ensure the session is properly maintained, the servlet
1821: // developer must call this method (at least once) before any output
1822: // is written to the response.
1823: // <P>
1824: // Additionally, application-writers need to be aware that newly
1825: // created sessions (that is, sessions for which HttpSession.isNew
1826: // returns true) do not have any application-specific state.
1827: public HttpSession getSession(boolean create) {
1828: // look for session
1829: // get session cookie
1830: HttpSession result = getSession();
1831: if (result == null && create) {
1832: result = serve.createSession();
1833: }
1834: if (result != null)
1835: sessionCookieValue = result.getId();
1836: return result;
1837: }
1838:
1839: // JSDK 2.1
1840: public HttpSession getSession() {
1841: AcmeSession result = null;
1842: if (sessionCookieValue != null) {
1843: result = (AcmeSession) serve.getSession(sessionCookieValue);
1844: if (result != null && !result.isValid()) {
1845: serve.removeSession(sessionCookieValue);
1846: result = null;
1847: }
1848: }
1849: return result;
1850: }
1851:
1852: public boolean isRequestedSessionIdFromURL() {
1853: return false;
1854: }
1855:
1856: // from ServletRequest
1857: public Enumeration getAttributeNames() {
1858: return attributes.keys();
1859: }
1860:
1861: public void setAttribute(String key, Object o) {
1862: attributes.put(key, o);
1863: }
1864:
1865: /// Gets the session id specified with this request. This may differ
1866: // from the actual session id. For example, if the request specified
1867: // an id for an invalid session, then this will get a new session with
1868: // a new id.
1869: public String getRequestedSessionId() {
1870: return sessionCookieValue;
1871: }
1872:
1873: /// Checks whether this request is associated with a session that is
1874: // valid in the current session context. If it is not valid, the
1875: // requested session will never be returned from the getSession
1876: // method.
1877: public boolean isRequestedSessionIdValid() {
1878: if (sessionCookieValue != null) {
1879: AcmeSession session = (AcmeSession) serve
1880: .getSession(sessionCookieValue);
1881: if (session != null && session.isValid()) {
1882: return true;
1883: }
1884: }
1885: return false;
1886: }
1887:
1888: /** Checks whether the session id specified by this request came in as
1889: * a cookie. (The requested session may not be one returned by the
1890: * getSession method.) */
1891: public boolean isRequestedSessionIdFromCookie() {
1892: return true;
1893: }
1894:
1895: /// Checks whether the session id specified by this request came in as
1896: // part of the URL. (The requested session may not be the one returned
1897: // by the getSession method.)
1898: public boolean isRequestedSessionIdFromUrl() {
1899: return false;
1900: }
1901:
1902: // Methods from ServletResponse.
1903:
1904: /// Sets the content length for this response.
1905: // @param length the content length
1906: public void setContentLength(int length) {
1907: setIntHeader(CONTENTLENGTH, length);
1908: }
1909:
1910: /// Sets the content type for this response.
1911: // @param type the content type
1912: public void setContentType(String type) {
1913: setHeader(CONTENTTYPE, type != null ? type : "Unknown");
1914: }
1915:
1916: /// Returns an output stream for writing response data.
1917: public ServletOutputStream getOutputStream() {
1918: synchronized (out) {
1919: if (((ServeOutputStream) out).isReturnedAsWriter())
1920: throw new IllegalStateException(
1921: "Already returned as a writer");
1922: ((ServeOutputStream) out).setReturnedAsStream(true);
1923: }
1924: return out;
1925: }
1926:
1927: /// Returns a print writer for writing response data. The MIME type of
1928: // the response will be modified, if necessary, to reflect the character
1929: // encoding used, through the charset=... property. This means that the
1930: // content type must be set before calling this method.
1931: // @exception UnsupportedEncodingException if no such encoding can be provided
1932: // @exception IllegalStateException if getOutputStream has been called
1933: // @exception IOException on other I/O errors
1934: public PrintWriter getWriter() throws IOException {
1935: synchronized (out) {
1936: if (((ServeOutputStream) out).isReturnedAsStream())
1937: throw new IllegalStateException(
1938: "Already was returned as servlet output stream");
1939: ((ServeOutputStream) out).setReturnedAsWriter(true);
1940: }
1941: String encoding = getCharacterEncoding();
1942: if (encoding != null)
1943: return new PrintWriter(
1944: new OutputStreamWriter(out, encoding));
1945: else
1946: return new PrintWriter(out);
1947: }
1948:
1949: /// Returns the character set encoding used for this MIME body. The
1950: // character encoding is either the one specified in the assigned
1951: // content type, or one which the client understands. If no content
1952: // type has yet been assigned, it is implicitly set to text/plain.
1953: public String getCharacterEncoding() {
1954: String ct = (String) resHeaderNames.get(CONTENTTYPE);
1955: if (ct != null) {
1956: int scp = ct.indexOf(';');
1957: if (scp > 0) {
1958: scp = ct.toLowerCase().indexOf("charset=", scp);
1959: if (scp >= 0) {
1960: ct = ct.substring(scp + 8);
1961: scp = ct.indexOf(' ');
1962: if (scp > 0)
1963: ct = ct.substring(0, scp);
1964: scp = ct.indexOf(';');
1965: if (scp > 0)
1966: ct = ct.substring(0, scp);
1967: return ct;
1968: }
1969: }
1970: }
1971: return null;
1972: }
1973:
1974: // 2.2
1975: // do not use buffer
1976: public void flushBuffer() {
1977: }
1978:
1979: /**
1980: * Clears the content of the underlying buffer in the response without clearing
1981: * headers or status code. If the response has been committed,
1982: * this method throws an IllegalStateException.
1983: * @since 2.3
1984: */
1985: public void resetBuffer() {
1986: throw new IllegalStateException("The method not implemented");
1987: }
1988:
1989: public int getBufferSize() {
1990: return 0;
1991: }
1992:
1993: public void setBufferSize(int size) {
1994: }
1995:
1996: /**
1997: * Returns a boolean indicating if the response has been committed.
1998: * A commited response has already had its status code and headers written.
1999: * @return
2000: * a boolean indicating if the response has been committed
2001: * @see
2002: * setBufferSize(int), getBufferSize(), flushBuffer(), reset()
2003: */
2004: // a caller should think about syncronization
2005: public boolean isCommitted() {
2006: return headersWritten;
2007: }
2008:
2009: /** Clears any data that exists in the buffer as well as the status code and headers.
2010: * If the response has been committed, this method throws an IllegalStateException.
2011: * @throws java.lang.IllegalStateException - if the response has already been committed
2012: * @see
2013: * setBufferSize(int), getBufferSize(), flushBuffer(), isCommitted()
2014: */
2015: public void reset() throws IllegalStateException {
2016: if (!isCommitted()) {
2017: outCookies.removeAllElements();
2018: resHeaderNames.clear();
2019: } else
2020: throw new IllegalStateException(
2021: "Header have already been committed.");
2022: }
2023:
2024: /**
2025: * Sets the locale of the response, setting the headers (including the Content-Type's charset)
2026: * as appropriate. This method should be called before a call to getWriter().
2027: * By default, the response locale is the default locale for the server.
2028: * @param loc - the locale of the response
2029: * @see
2030: * getLocale()
2031: */
2032: public void setLocale(java.util.Locale locale) {
2033: this .locale = locale;
2034: }
2035:
2036: public java.util.Locale getLocale() {
2037: return locale;
2038: }
2039:
2040: // should decide about supported locales and charsets, no a good decision yet
2041: public Enumeration getLocales() {
2042: // TODO: get locales from Accept-Language (RFC 2616)
2043: //Locale.getAvailableLocales()
2044: return null;
2045: }
2046:
2047: /**
2048: * Overrides the name of the character encoding used in the body of this request.
2049: * This method must be called prior to reading request parameters or reading input using getReader().
2050: * @param a - String containing the name of the chararacter encoding.
2051: * @throws java.io.UnsupportedEncodingException - if this is not a valid encoding
2052: * @since JSDK 2.3
2053: */
2054: public void setCharacterEncoding(String _enc)
2055: throws java.io.UnsupportedEncodingException {
2056: reqCharEncoding = _enc;
2057: }
2058:
2059: public void addDateHeader(String header, long date) {
2060: addHeader(header, expdatefmt.format(new Date(date)));
2061: }
2062:
2063: public void addHeader(String header, String value) {
2064: Object o = resHeaderNames.get(header);
2065: if (o == null)
2066: setHeader(header, value);
2067: else {
2068: if (o instanceof String[]) {
2069: String[] oldVal = (String[]) o;
2070: String[] newVal = new String[oldVal.length + 1];
2071: System.arraycopy(oldVal, 0, newVal, 0, oldVal.length);
2072: newVal[oldVal.length] = value;
2073: resHeaderNames.put(header, newVal);
2074: } else if (o instanceof String) {
2075: String[] newVal = new String[2];
2076: newVal[0] = (String) o;
2077: newVal[1] = value;
2078: resHeaderNames.put(header, newVal);
2079: } else
2080: throw new RuntimeException(
2081: "Invalid content of header hash - "
2082: + o.getClass().getName());
2083: }
2084: }
2085:
2086: public void addIntHeader(String header, int value) {
2087: addHeader(header, Integer.toString(value));
2088: }
2089:
2090: public RequestDispatcher getRequestDispatcher(String urlpath) {
2091: // TODO: calculate dispacter relatively the current path
2092: return null; // we don't provide resource dispatching in this way
2093: }
2094:
2095: public boolean isSecure() {
2096: return false;
2097: }
2098:
2099: public void removeAttribute(String name) {
2100: }
2101:
2102: // only root context supported
2103: public String getContextPath() {
2104: return "";
2105: }
2106:
2107: public Enumeration getHeaders(String header) {
2108: Vector result = new Vector();
2109: int i = -1;
2110: while ((i = reqHeaderNames.indexOf(header.toLowerCase(), i + 1)) >= 0)
2111: result.addElement(reqHeaderValues.elementAt(i));
2112: return result.elements();
2113: }
2114:
2115: public java.security.Principal getUserPrincipal() {
2116: return null;
2117: }
2118:
2119: public boolean isUserInRole(String user) {
2120: return true;
2121: }
2122:
2123: /**
2124: * Returns a java.util.Map of the parameters of this request.
2125: * Request parameters are extra information sent with the request.
2126: * For HTTP servlets, parameters are contained in the query string or posted form data.
2127: * @return an immutable java.util.Map containing parameter names as keys
2128: * and parameter values as map values. The keys in the parameter map are of type String.
2129: * The values in the parameter map are of type String array.
2130: * @since 2.3
2131: */
2132: public java.util.Map getParameterMap() {
2133: return (java.util.Map) formParameters;
2134: }
2135:
2136: // Methods from HttpServletResponse.
2137:
2138: /// Adds the specified cookie to the response. It can be called
2139: // multiple times to set more than one cookie.
2140: public void addCookie(Cookie cookie) {
2141: if (outCookies == null)
2142: outCookies = new Vector();
2143:
2144: outCookies.addElement(cookie);
2145: }
2146:
2147: /// Checks whether the response message header has a field with the
2148: // specified name.
2149: public boolean containsHeader(String name) {
2150: return resHeaderNames.contains(name);
2151: }
2152:
2153: // JSDK 2.1 extension
2154: public String encodeURL(String url) {
2155: return url; // as is
2156: }
2157:
2158: public String encodeRedirectURL(String url) {
2159: return url; // as is
2160: }
2161:
2162: private int resCode = -1;
2163: private String resMessage = null;
2164: private Hashtable resHeaderNames = new Hashtable();
2165:
2166: protected static final SimpleDateFormat expdatefmt = new SimpleDateFormat(
2167: "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'");
2168: protected static final SimpleDateFormat headerdateformat = new SimpleDateFormat(
2169: "EEE, dd MMM yyyy HH:mm:ss z");
2170: static {
2171: TimeZone tz = TimeZone.getTimeZone("GMT");
2172: tz.setID("GMT");
2173: expdatefmt.setTimeZone(tz);
2174: }
2175:
2176: /// Sets the status code and message for this response.
2177: // @param resCode the status code
2178: // @param resMessage the status message
2179: public void setStatus(int resCode, String resMessage) {
2180: // if (this.resCode > 0 && this.resCode != SC_OK)
2181: // throw new IllegalStateException("Result code "+this.resCode+" was already set.");
2182: this .resCode = resCode;
2183: this .resMessage = resMessage;
2184: }
2185:
2186: /// Sets the status code and a default message for this response.
2187: // @param resCode the status code
2188: public void setStatus(int resCode) {
2189: switch (resCode) {
2190: case SC_CONTINUE:
2191: setStatus(resCode, "Continue");
2192: break;
2193: case SC_SWITCHING_PROTOCOLS:
2194: setStatus(resCode, "Switching protocols");
2195: break;
2196: case SC_OK:
2197: setStatus(resCode, "Ok");
2198: break;
2199: case SC_CREATED:
2200: setStatus(resCode, "Created");
2201: break;
2202: case SC_ACCEPTED:
2203: setStatus(resCode, "Accepted");
2204: break;
2205: case SC_NON_AUTHORITATIVE_INFORMATION:
2206: setStatus(resCode, "Non-authoritative");
2207: break;
2208: case SC_NO_CONTENT:
2209: setStatus(resCode, "No content");
2210: break;
2211: case SC_RESET_CONTENT:
2212: setStatus(resCode, "Reset content");
2213: break;
2214: case SC_PARTIAL_CONTENT:
2215: setStatus(resCode, "Partial content");
2216: break;
2217: case SC_MULTIPLE_CHOICES:
2218: setStatus(resCode, "Multiple choices");
2219: break;
2220: case SC_MOVED_PERMANENTLY:
2221: setStatus(resCode, "Moved permanentently");
2222: break;
2223: case SC_MOVED_TEMPORARILY:
2224: setStatus(resCode, "Moved temporarily");
2225: break;
2226: case SC_SEE_OTHER:
2227: setStatus(resCode, "See other");
2228: break;
2229: case SC_NOT_MODIFIED:
2230: setStatus(resCode, "Not modified");
2231: break;
2232: case SC_USE_PROXY:
2233: setStatus(resCode, "Use proxy");
2234: break;
2235: case SC_BAD_REQUEST:
2236: setStatus(resCode, "Bad request");
2237: break;
2238: case SC_UNAUTHORIZED:
2239: setStatus(resCode, "Unauthorized");
2240: break;
2241: case SC_PAYMENT_REQUIRED:
2242: setStatus(resCode, "Payment required");
2243: break;
2244: case SC_FORBIDDEN:
2245: setStatus(resCode, "Forbidden");
2246: break;
2247: case SC_NOT_FOUND:
2248: setStatus(resCode, "Not found");
2249: break;
2250: case SC_METHOD_NOT_ALLOWED:
2251: setStatus(resCode, "Method not allowed");
2252: break;
2253: case SC_NOT_ACCEPTABLE:
2254: setStatus(resCode, "Not acceptable");
2255: break;
2256: case SC_PROXY_AUTHENTICATION_REQUIRED:
2257: setStatus(resCode, "Proxy auth required");
2258: break;
2259: case SC_REQUEST_TIMEOUT:
2260: setStatus(resCode, "Request timeout");
2261: break;
2262: case SC_CONFLICT:
2263: setStatus(resCode, "Conflict");
2264: break;
2265: case SC_GONE:
2266: setStatus(resCode, "Gone");
2267: break;
2268: case SC_LENGTH_REQUIRED:
2269: setStatus(resCode, "Length required");
2270: break;
2271: case SC_PRECONDITION_FAILED:
2272: setStatus(resCode, "Precondition failed");
2273: break;
2274: case SC_REQUEST_ENTITY_TOO_LARGE:
2275: setStatus(resCode, "Request entity too large");
2276: break;
2277: case SC_REQUEST_URI_TOO_LONG:
2278: setStatus(resCode, "Request URI too large");
2279: break;
2280: case SC_UNSUPPORTED_MEDIA_TYPE:
2281: setStatus(resCode, "Unsupported media type");
2282: break;
2283: case SC_INTERNAL_SERVER_ERROR:
2284: setStatus(resCode, "Internal server error");
2285: break;
2286: case SC_NOT_IMPLEMENTED:
2287: setStatus(resCode, "Not implemented");
2288: break;
2289: case SC_BAD_GATEWAY:
2290: setStatus(resCode, "Bad gateway");
2291: break;
2292: case SC_SERVICE_UNAVAILABLE:
2293: setStatus(resCode, "Service unavailable");
2294: break;
2295: case SC_GATEWAY_TIMEOUT:
2296: setStatus(resCode, "Gateway timeout");
2297: break;
2298: case SC_HTTP_VERSION_NOT_SUPPORTED:
2299: setStatus(resCode, "HTTP version not supported");
2300: break;
2301: default:
2302: setStatus(resCode, "");
2303: break;
2304: }
2305: }
2306:
2307: /// Sets the value of a header field.
2308: // @param name the header field name
2309: // @param value the header field value
2310: public void setHeader(String name, String value) {
2311: resHeaderNames.put(name, value);
2312: }
2313:
2314: /// Sets the value of an integer header field.
2315: // @param name the header field name
2316: // @param value the header field integer value
2317: public void setIntHeader(String name, int value) {
2318: setHeader(name, Integer.toString(value));
2319: }
2320:
2321: /// Sets the value of a long header field.
2322: // @param name the header field name
2323: // @param value the header field long value
2324: public void setLongHeader(String name, long value) {
2325: setHeader(name, Long.toString(value));
2326: }
2327:
2328: /// Sets the value of a date header field.
2329: // @param name the header field name
2330: // @param value the header field date value
2331: public void setDateHeader(String name, long value) {
2332: setHeader(name, expdatefmt.format(new Date(value)));
2333: }
2334:
2335: private static final String[] weekdays = { "Sun", "Mon", "Tue",
2336: "Wed", "Thu", "Fri", "Sat" };
2337: /*
2338: /// Converts a Date into an RFC-1123 string.
2339: private static String to1123String( Date date )
2340: {
2341: // We have to go through some machinations here to get the
2342: // correct day of the week in GMT. getDay() gives the day in
2343: // local time. getDate() gives the day of the month in local
2344: // time. toGMTString() gives a formatted string in GMT. So, we
2345: // extract the day of the month from the GMT string, and if it
2346: // doesn't match the local one we change the local day of the
2347: // week accordingly.
2348: //
2349: // The Date class sucks.
2350: int localDay = date.getDay();
2351: int localDate = date.getDate();
2352: String gmtStr = date.toGMTString();
2353: int blank = gmtStr.indexOf( ' ' );
2354: int gmtDate = Integer.parseInt( gmtStr.substring( 0, blank ) );
2355: int gmtDay;
2356: if ( gmtDate > localDate || ( gmtDate < localDate && gmtDate == 1 ) )
2357: gmtDay = ( localDay + 1 ) % 7;
2358: else if ( localDate > gmtDate || ( localDate < gmtDate && localDate == 1 ) )
2359: gmtDay = ( localDay + 6 ) % 7;
2360: else
2361: gmtDay = localDay;
2362: return weekdays[gmtDay] + ( gmtDate < 10 ? ", 0" : ", " ) + gmtStr;
2363: }
2364: */
2365: private boolean headersWritten = false;
2366:
2367: /// Writes the status line and message headers for this response to the
2368: // output stream.
2369: // @exception IOException if an I/O error has occurred
2370: void writeHeaders() throws IOException {
2371: synchronized (this ) {
2372: if (headersWritten)
2373: return;
2374:
2375: headersWritten = true;
2376: }
2377: if (reqMime) {
2378: //boolean chunked_out = false;
2379: out.println(reqProtocol + " " + resCode + " " + resMessage);
2380:
2381: Enumeration he = resHeaderNames.keys();
2382: while (he.hasMoreElements()) {
2383: String name = (String) he.nextElement();
2384: Object o = resHeaderNames.get(name);
2385: if (o instanceof String) {
2386: String value = (String) o;
2387: if (value != null) // just in case
2388: out.println(name + ": " + value);
2389: /* moved to servlet
2390: if (TRANSFERENCODING.equals(name) && CHUNKED.equals(value))
2391: chunked_out = true;*/
2392: } else if (o instanceof String[]) {
2393: String[] values = (String[]) o;
2394: out.println(name + ": " + values[0]);
2395: for (int i = 0; i < values.length; i++)
2396: out.print("," + values[i]);
2397: out.println();
2398: }
2399: }
2400: StringBuffer sb = null;
2401: Cookie cc = null;
2402: // add session cookie
2403: // if cookie based session
2404: if (sessionCookieValue != null) {
2405: if (outCookies == null)
2406: outCookies = new Vector();
2407: cc = new Cookie(SESSION_COOKIE_NAME, sessionCookieValue);
2408: cc.setMaxAge(Math.abs(Serve.expiredIn) * 60);
2409: outCookies.addElement(cc);
2410: }
2411:
2412: // how to remove a cookie
2413: // cc = new Cookie(cookieName, "");
2414: // cc.setMaxAge(0);
2415: //
2416: for (int i = 0; outCookies != null && i < outCookies.size(); i++) {
2417: if (sb == null)
2418: sb = new StringBuffer(SETCOOKIE + ": ");
2419: else
2420: //sb.append(',');
2421: sb.append("\r\n" + SETCOOKIE + ": "); // for IE not understanding the standard
2422: cc = (Cookie) outCookies.elementAt(i);
2423: sb.append(cc.getName());
2424: sb.append('=');
2425: sb.append(cc.getValue());
2426: if (cc.getComment() != null) {
2427: sb.append("; Comment=" + cc.getComment());
2428: }
2429: if (cc.getDomain() != null) {
2430: sb.append("; Domain=" + cc.getDomain());
2431: }
2432:
2433: if (cc.getMaxAge() >= 0) {
2434: sb.append("; expires=");
2435: sb.append(expdatefmt.format(new Date(System
2436: .currentTimeMillis()
2437: + 1000 * cc.getMaxAge())));
2438: }
2439: if (cc.getPath() != null) {
2440: sb.append("; Path=" + cc.getPath());
2441: }
2442: if (cc.getSecure()) {
2443: sb.append("; Secure");
2444: }
2445: if (cc.getVersion() > 0) {
2446: sb.append("; Version=" + cc.getVersion());
2447: }
2448: }
2449: if (sb != null) {
2450: out.println(sb.toString());
2451: //System.err.println("We sent cookies: "+sb);
2452: }
2453: out.println("");
2454: out.flush();
2455: //if (chunked_out) ((ServeOutputStream)out).setChunked(true);
2456: }
2457: }
2458:
2459: /// Writes an error response using the specified status code and message.
2460: // @param resCode the status code
2461: // @param resMessage the status message
2462: // @exception IOException if an I/O error has occurred
2463: public void sendError(int resCode, String resMessage)
2464: throws IOException {
2465: setStatus(resCode, resMessage);
2466: realSendError();
2467: }
2468:
2469: /// Writes an error response using the specified status code and a default
2470: // message.
2471: // @param resCode the status code
2472: // @exception IOException if an I/O error has occurred
2473: public void sendError(int resCode) throws IOException {
2474: setStatus(resCode);
2475: realSendError();
2476: }
2477:
2478: private void realSendError() throws IOException {
2479: if (isCommitted())
2480: throw new IllegalStateException(
2481: "Can not send error, headers have been already written");
2482: synchronized (out) {
2483: // no more out after error
2484: ((ServeOutputStream) out).setReturnedAsStream(true);
2485: ((ServeOutputStream) out).setReturnedAsWriter(true);
2486: }
2487: setContentType("text/html");
2488: StringBuffer sb = new StringBuffer(100);
2489: sb.append("<HTML><HEAD>").append(
2490: "<TITLE>" + resCode + " " + resMessage + "</TITLE>")
2491: .append("</HEAD><BODY BGCOLOR=\"#F1D0F2\">").append(
2492: "<H2>" + resCode + " " + resMessage + "</H2>")
2493: .append("<HR>");
2494: Serve.Identification.writeAddress(sb);
2495: sb.append("</BODY></HTML>");
2496: setContentLength(sb.length());
2497: out.print(sb.toString());
2498: out.flush();
2499: }
2500:
2501: /// Sends a redirect message to the client using the specified redirect
2502: // location URL.
2503: // @param location the redirect location URL
2504: // @exception IOException if an I/O error has occurred
2505: public void sendRedirect(String location) throws IOException {
2506: if (isCommitted())
2507: throw new IllegalStateException(
2508: "Can not redirect, headers have been already written");
2509: synchronized (out) {
2510: // no more out after redirect
2511: ((ServeOutputStream) out).setReturnedAsStream(true);
2512: ((ServeOutputStream) out).setReturnedAsWriter(true);
2513: }
2514: setHeader("Location", location);
2515: setStatus(SC_MOVED_TEMPORARILY);
2516: setContentType("text/html");
2517: StringBuffer sb = new StringBuffer(200);
2518: sb.append("<HTML><HEAD>" + "<TITLE>" + SC_MOVED_TEMPORARILY
2519: + " Moved</TITLE>"
2520: + "</HEAD><BODY BGCOLOR=\"#F1D0F2\">" + "<H2>"
2521: + SC_MOVED_TEMPORARILY + " Moved</H2>"
2522: + "This document has moved <a href=" + location
2523: + ">here.<HR>");
2524: Serve.Identification.writeAddress(sb);
2525: sb.append("</BODY></HTML>");
2526: setContentLength(sb.length());
2527: // to avoid further out
2528: out.print(sb.toString());
2529: out.flush();
2530: }
2531:
2532: // URL session-encoding stuff. Not implemented, but the API is here
2533: // for compatibility.
2534:
2535: /// Encodes the specified URL by including the session ID in it, or, if
2536: // encoding is not needed, returns the URL unchanged. The
2537: // implementation of this method should include the logic to determine
2538: // whether the session ID needs to be encoded in the URL. For example,
2539: // if the browser supports cookies, or session tracking is turned off,
2540: // URL encoding is unnecessary.
2541: // <P>
2542: // All URLs emitted by a Servlet should be run through this method.
2543: // Otherwise, URL rewriting cannot be used with browsers which do not
2544: // support cookies.
2545: public String encodeUrl(String url) {
2546: return url;
2547: }
2548:
2549: /// Encodes the specified URL for use in the sendRedirect method or, if
2550: // encoding is not needed, returns the URL unchanged. The
2551: // implementation of this method should include the logic to determine
2552: // whether the session ID needs to be encoded in the URL. Because the
2553: // rules for making this determination differ from those used to
2554: // decide whether to encode a normal link, this method is seperate
2555: // from the encodeUrl method.
2556: // <P>
2557: // All URLs sent to the HttpServletResponse.sendRedirect method should be
2558: // run through this method. Otherwise, URL rewriting cannot be used with
2559: // browsers which do not support cookies.
2560: public String encodeRedirectUrl(String url) {
2561: return url;
2562: }
2563:
2564: }
2565:
2566: class BasicAuthRealm extends Hashtable {
2567: String name;
2568:
2569: BasicAuthRealm(String name) {
2570: this .name = name;
2571: }
2572:
2573: String name() {
2574: return name;
2575: }
2576: }
2577:
2578: class ServeInputStream extends ServletInputStream {
2579: /* ------------------------------------------------------------ */
2580: /** The actual input stream.
2581: */
2582: private BufferedInputStream in;
2583: private int chunksize = 0;
2584: private boolean chunking = false;
2585: private boolean returnedAsReader, returnedAsStream;
2586:
2587: /* ------------------------------------------------------------ */
2588: /** Constructor
2589: */
2590: public ServeInputStream(InputStream in) {
2591: this .in = new BufferedInputStream(in);
2592: }
2593:
2594: /* ------------------------------------------------------------ */
2595: /**
2596: * @param chunking
2597: */
2598: public void chunking(boolean chunking) {
2599: this .chunking = chunking;
2600: }
2601:
2602: /* ------------------------------------------------------------ */
2603: /** Read a line ended by CR or CRLF or LF.
2604: * More forgiving of line termination than ServletInputStream.readLine().
2605: * This method only read raw data, that may be chunked. Calling
2606: * ServletInputStream.readLine() will always return unchunked data.
2607: */
2608: // TODO: won't work with encoding
2609: public String readLine() throws IOException {
2610: StringBuffer buf = new StringBuffer(1024);
2611:
2612: int c;
2613: boolean cr = false;
2614: boolean lf = false;
2615:
2616: LineLoop: while ((c = chunking ? read() : in.read()) != -1) {
2617: switch (c) {
2618: case 10:
2619: lf = true;
2620: break LineLoop;
2621:
2622: case 13:
2623: cr = true;
2624: if (!chunking)
2625: in.mark(2);
2626: break;
2627:
2628: default:
2629: if (cr) {
2630: //if (chunking)
2631: //log("Cannot handle CR in chunking mode");
2632: in.reset();
2633: break LineLoop;
2634: } else
2635: buf.append((char) c);
2636: break;
2637: }
2638: }
2639:
2640: if (c == -1 && buf.length() == 0)
2641: return null;
2642: //System.err.println(buf.toString()); //###
2643:
2644: return buf.toString();
2645: }
2646:
2647: /* ------------------------------------------------------------ */
2648: public int read() throws IOException {
2649: if (chunking) {
2650: int b = -1;
2651: if (chunksize <= 0 && getChunkSize() <= 0)
2652: return -1;
2653: b = in.read();
2654: chunksize = (b < 0) ? -1 : (chunksize - 1);
2655: return b;
2656: }
2657:
2658: return in.read();
2659: }
2660:
2661: /* ------------------------------------------------------------ */
2662: public int read(byte b[]) throws IOException {
2663: return read(b, 0, b.length);
2664: }
2665:
2666: /* ------------------------------------------------------------ */
2667: public int read(byte b[], int off, int len) throws IOException {
2668: if (chunking) {
2669: if (chunksize <= 0 && getChunkSize() <= 0)
2670: return -1;
2671: if (len > chunksize)
2672: len = chunksize;
2673: len = in.read(b, off, len);
2674: chunksize = (len < 0) ? -1 : (chunksize - len);
2675: } else
2676: len = in.read(b, off, len);
2677: //System.err.println(new String(b,off,len)); //###
2678:
2679: return len;
2680: }
2681:
2682: /* ------------------------------------------------------------ */
2683: public long skip(long len) throws IOException {
2684: if (chunking) {
2685: if (chunksize <= 0 && getChunkSize() <= 0)
2686: return -1;
2687: if (len > chunksize)
2688: len = chunksize;
2689: len = in.skip(len);
2690: chunksize = (len < 0) ? -1 : (chunksize - (int) len);
2691: } else
2692: len = in.skip(len);
2693: return len;
2694: }
2695:
2696: /* ------------------------------------------------------------ */
2697: /** Available bytes to read without blocking.
2698: * If you are unlucky may return 0 when there are more
2699: */
2700: public int available() throws IOException {
2701: if (chunking) {
2702: int len = in.available();
2703: if (len <= chunksize)
2704: return len;
2705: return chunksize;
2706: }
2707:
2708: return in.available();
2709: }
2710:
2711: /* ------------------------------------------------------------ */
2712: public void close() throws IOException {
2713: in.close();
2714: chunksize = -1;
2715: }
2716:
2717: /* ------------------------------------------------------------ */
2718: /** Mark is not supported
2719: * @return false
2720: */
2721: public boolean markSupported() {
2722: return false;
2723: }
2724:
2725: /* ------------------------------------------------------------ */
2726: /** Not Implemented
2727: */
2728: public void reset() {
2729: }
2730:
2731: /* ------------------------------------------------------------ */
2732: /** Not Implemented
2733: * @param readlimit
2734: */
2735: public void mark(int readlimit) {
2736: }
2737:
2738: /* ------------------------------------------------------------ */
2739: private int getChunkSize() throws IOException {
2740: if (chunksize < 0)
2741: return -1;
2742:
2743: chunksize = -1;
2744:
2745: // Get next non blank line
2746: chunking = false;
2747: String line = readLine();
2748: while (line != null && line.length() == 0)
2749: line = readLine();
2750: chunking = true;
2751:
2752: // Handle early EOF or error in format
2753: if (line == null)
2754: return -1;
2755:
2756: // Get chunksize
2757: int i = line.indexOf(';');
2758: if (i > 0)
2759: line = line.substring(0, i).trim();
2760: chunksize = Integer.parseInt(line, 16);
2761:
2762: // check for EOF
2763: if (chunksize == 0) {
2764: chunksize = -1;
2765: // Look for footers
2766: chunking = false;
2767: }
2768: return chunksize;
2769: }
2770:
2771: boolean isReturnedAsStream() {
2772: return returnedAsStream;
2773: }
2774:
2775: void setReturnedAsStream(boolean _on) {
2776: returnedAsStream = _on;
2777: }
2778:
2779: boolean isReturnedAsReader() {
2780: return returnedAsReader;
2781: }
2782:
2783: void setReturnedAsReader(boolean _on) {
2784: returnedAsReader = _on;
2785: }
2786:
2787: }
2788:
2789: class ServeOutputStream extends ServletOutputStream {
2790:
2791: // underneath stream
2792: private OutputStream out;
2793: //private BufferedWriter writer; // for top speed
2794: private ServeConnection conn;
2795: private boolean returnedAsStream, returnedAsWriter;
2796:
2797: public ServeOutputStream(OutputStream out, ServeConnection conn) {
2798:
2799: this .out = out;
2800: this .conn = conn;
2801: }
2802:
2803: public void print(String s) throws IOException {
2804: write(s.getBytes());
2805: }
2806:
2807: public void write(int b) throws IOException {
2808: conn.writeHeaders();
2809: out.write(b);
2810: }
2811:
2812: public void write(byte[] b) throws IOException {
2813: write(b, 0, b.length);
2814: }
2815:
2816: public void write(byte[] b, int off, int len) throws IOException {
2817: conn.writeHeaders();
2818: out.write(b, off, len);
2819: }
2820:
2821: public void flush() throws IOException {
2822: conn.writeHeaders();
2823: out.flush();
2824: }
2825:
2826: public void close() throws IOException {
2827: conn.writeHeaders();
2828: out.close();
2829: }
2830:
2831: boolean isReturnedAsStream() {
2832: return returnedAsStream;
2833: }
2834:
2835: void setReturnedAsStream(boolean _set) {
2836: returnedAsStream = _set;
2837: }
2838:
2839: boolean isReturnedAsWriter() {
2840: return returnedAsWriter;
2841: }
2842:
2843: void setReturnedAsWriter(boolean _set) {
2844: returnedAsWriter = _set;
2845: }
2846: }
2847:
2848: /**
2849: * Class PathTreeDictionary - this class allows to put path elements
2850: * in format n1/n2/n2[/*.ext]
2851: * and get match to a pattern and a unmatched tail
2852: */
2853: class PathTreeDictionary {
2854: Node root_node;
2855:
2856: PathTreeDictionary() {
2857: root_node = new Node();
2858: }
2859:
2860: void put(String path, Object value) {
2861: StringTokenizer st = new StringTokenizer(path, "\\/");
2862: Node cur_node = root_node;
2863: while (st.hasMoreTokens()) {
2864: String nodename = st.nextToken();
2865: Node node = (Node) cur_node.get(nodename);
2866: if (node == null) {
2867: node = new Node();
2868: cur_node.put(nodename, node);
2869: }
2870: cur_node = node;
2871: }
2872: cur_node.object = value;
2873: }
2874:
2875: /**
2876: * This function looks up in the directory to find the perfect match
2877: * and remove matching part from path, so if you need to keep
2878: * original path, save it somewhere
2879: */
2880: Object[] get(String path) {
2881: Object[] result = new Object[2];
2882: if (path == null)
2883: return result;
2884: char[] ps = path.toCharArray();
2885: Node cur_node = root_node;
2886: int p0 = 0, lm = 0; // last match
2887: result[0] = cur_node.object;
2888: boolean div_state = true;
2889: for (int i = 0; i < ps.length; i++) {
2890: if (ps[i] == '/' || ps[i] == '\\') {
2891: if (div_state)
2892: continue;
2893: Node node = (Node) cur_node.get(new String(ps, p0, i
2894: - p0));
2895: if (node == null) {
2896: result[1] = new Integer(lm);
2897: return result;
2898: }
2899: if (node.object != null) {
2900: result[0] = node.object;
2901: lm = i;
2902: }
2903: cur_node = node;
2904: div_state = true;
2905: } else {
2906: if (div_state) {
2907: p0 = i;
2908: div_state = false;
2909: }
2910: }
2911: }
2912: cur_node = (Node) cur_node.get(new String(ps, p0, ps.length
2913: - p0));
2914: if (cur_node != null && cur_node.object != null) {
2915: result[0] = cur_node.object;
2916: lm = ps.length;
2917: }
2918: result[1] = new Integer(lm);
2919: return result;
2920: }
2921:
2922: Enumeration keys() {
2923: Vector result = new Vector();
2924: addSiblingNames(root_node, result, "");
2925: return result.elements();
2926: }
2927:
2928: void addSiblingNames(Node node, Vector result, String path) {
2929: Enumeration e = node.keys();
2930: while (e.hasMoreElements()) {
2931: String pc = (String) e.nextElement();
2932: Node childNode = (Node) node.get(pc);
2933: pc = path + '/' + pc;
2934: if (childNode.object != null)
2935: result.addElement(pc);
2936: addSiblingNames(childNode, result, pc);
2937: }
2938: }
2939:
2940: Enumeration elements() {
2941: Vector result = new Vector();
2942: addSiblingObjects(root_node, result);
2943: return result.elements();
2944: }
2945:
2946: void addSiblingObjects(Node node, Vector result) {
2947: Enumeration e = node.keys();
2948: while (e.hasMoreElements()) {
2949: Node childNode = (Node) node.get(e.nextElement());
2950: if (childNode.object != null)
2951: result.addElement(childNode.object);
2952: addSiblingObjects(childNode, result);
2953: }
2954: }
2955:
2956: class Node extends Hashtable {
2957: Object object;
2958: }
2959: }
2960:
2961: /**
2962: * Http session support
2963: */
2964: class AcmeSession extends Hashtable implements HttpSession {
2965: private long createTime;
2966: private long lastAccessTime;
2967: private String id;
2968: private int inactiveInterval; // in seconds
2969: private boolean expired;
2970: private ServletContext servletContext;
2971: private HttpSessionContext sessionContext;
2972:
2973: // TODO: check in documentation what is default inactive interval and what means 0
2974: // and what is mesurement unit
2975: AcmeSession(String id, ServletContext servletContext,
2976: HttpSessionContext sessionContext) {
2977: this (id, 0, servletContext, sessionContext);
2978: }
2979:
2980: AcmeSession(String id, int inactiveInterval,
2981: ServletContext servletContext,
2982: HttpSessionContext sessionContext) {
2983: createTime = System.currentTimeMillis();
2984: this .id = id;
2985: this .inactiveInterval = inactiveInterval;
2986: this .servletContext = servletContext;
2987: this .sessionContext = sessionContext;
2988: }
2989:
2990: public long getCreationTime() {
2991: return createTime;
2992: }
2993:
2994: public String getId() {
2995: return id;
2996: }
2997:
2998: public long getLastAccessedTime() {
2999: return lastAccessTime;
3000: }
3001:
3002: public void setMaxInactiveInterval(int interval) {
3003: inactiveInterval = interval;
3004: }
3005:
3006: public int getMaxInactiveInterval() {
3007: return inactiveInterval;
3008: }
3009:
3010: /**
3011: * @deprecated
3012: */
3013: public HttpSessionContext getSessionContext() {
3014:
3015: return sessionContext;
3016: }
3017:
3018: /**
3019: * Returns the ServletContext to which this session belongs.
3020: * @return The ServletContext object for the web application
3021: * @ince 2.3
3022: */
3023: public ServletContext getServletContext() {
3024: return servletContext;
3025: }
3026:
3027: public java.lang.Object getAttribute(java.lang.String name)
3028: throws IllegalStateException {
3029: if (expired)
3030: throw new IllegalStateException();
3031: return get((Object) name);
3032: }
3033:
3034: public java.lang.Object getValue(java.lang.String name)
3035: throws IllegalStateException {
3036: return getAttribute(name);
3037: }
3038:
3039: public java.util.Enumeration getAttributeNames()
3040: throws IllegalStateException {
3041: if (expired)
3042: throw new IllegalStateException();
3043: return keys();
3044: }
3045:
3046: public java.lang.String[] getValueNames()
3047: throws IllegalStateException {
3048: Enumeration e = getAttributeNames();
3049: Vector names = new Vector();
3050: while (e.hasMoreElements())
3051: names.addElement(e.nextElement());
3052: String[] result = new String[names.size()];
3053: names.copyInto(result);
3054: return result;
3055: }
3056:
3057: public void setAttribute(String name, Object value)
3058: throws IllegalStateException {
3059: if (expired)
3060: throw new IllegalStateException();
3061: Object oldValue = put((Object) name, value);
3062: if (oldValue != null
3063: && oldValue instanceof HttpSessionBindingListener)
3064: ((HttpSessionBindingListener) oldValue)
3065: .valueUnbound(new HttpSessionBindingEvent(this ,
3066: name));
3067: if (value instanceof HttpSessionBindingListener)
3068: ((HttpSessionBindingListener) value)
3069: .valueBound(new HttpSessionBindingEvent(this , name));
3070: }
3071:
3072: public void putValue(String name, Object value)
3073: throws IllegalStateException {
3074: setAttribute(name, value);
3075: }
3076:
3077: public void removeAttribute(java.lang.String name)
3078: throws IllegalStateException {
3079: if (expired)
3080: throw new IllegalStateException();
3081: Object value = remove((Object) name);
3082: if (value != null
3083: && value instanceof HttpSessionBindingListener)
3084: ((HttpSessionBindingListener) value)
3085: .valueUnbound(new HttpSessionBindingEvent(this ,
3086: name));
3087: }
3088:
3089: public void removeValue(java.lang.String name)
3090: throws IllegalStateException {
3091: removeAttribute(name);
3092: }
3093:
3094: public synchronized void invalidate() throws IllegalStateException {
3095: if (expired)
3096: throw new IllegalStateException();
3097: Enumeration e = getAttributeNames();
3098: while (e.hasMoreElements()) {
3099: removeAttribute((String) e.nextElement());
3100: }
3101: setExpired(true);
3102: // would be nice remove it from hash table also
3103: }
3104:
3105: public boolean isNew() throws IllegalStateException {
3106: if (expired)
3107: throw new IllegalStateException();
3108: return lastAccessTime == 0;
3109: }
3110:
3111: private void setExpired(boolean expired) {
3112: this .expired = expired;
3113: }
3114:
3115: boolean isValid() {
3116: return !expired;
3117: }
3118:
3119: void userTouch() {
3120: lastAccessTime = System.currentTimeMillis();
3121: }
3122: }
3123:
3124: // TODO: reconsider implementation by providing
3125: // inner class implementing HttpSessionContext
3126: // and returning it on request
3127: // to avoid casting this class to Hashtable
3128: class HttpSessionContextImpl extends Hashtable implements
3129: HttpSessionContext {
3130:
3131: public java.util.Enumeration getIds() {
3132: return keys();
3133: }
3134:
3135: public HttpSession getSession(java.lang.String sessionId) {
3136: return (HttpSession) get(sessionId);
3137: }
3138: }
|