0001: package com.quadcap.http.server22;
0002:
0003: /* Copyright 1997 - 2003 Quadcap Software. All rights reserved.
0004: *
0005: * This software is distributed under the Quadcap Free Software License.
0006: * This software may be used or modified for any purpose, personal or
0007: * commercial. Open Source redistributions are permitted. Commercial
0008: * redistribution of larger works derived from, or works which bundle
0009: * this software requires a "Commercial Redistribution License"; see
0010: * http://www.quadcap.com/purchase.
0011: *
0012: * Redistributions qualify as "Open Source" under one of the following terms:
0013: *
0014: * Redistributions are made at no charge beyond the reasonable cost of
0015: * materials and delivery.
0016: *
0017: * Redistributions are accompanied by a copy of the Source Code or by an
0018: * irrevocable offer to provide a copy of the Source Code for up to three
0019: * years at the cost of materials and delivery. Such redistributions
0020: * must allow further use, modification, and redistribution of the Source
0021: * Code under substantially the same terms as this license.
0022: *
0023: * Redistributions of source code must retain the copyright notices as they
0024: * appear in each source code file, these license terms, and the
0025: * disclaimer/limitation of liability set forth as paragraph 6 below.
0026: *
0027: * Redistributions in binary form must reproduce this Copyright Notice,
0028: * these license terms, and the disclaimer/limitation of liability set
0029: * forth as paragraph 6 below, in the documentation and/or other materials
0030: * provided with the distribution.
0031: *
0032: * The Software is provided on an "AS IS" basis. No warranty is
0033: * provided that the Software is free of defects, or fit for a
0034: * particular purpose.
0035: *
0036: * Limitation of Liability. Quadcap Software shall not be liable
0037: * for any damages suffered by the Licensee or any third party resulting
0038: * from use of the Software.
0039: */
0040:
0041: import java.util.Collections;
0042: import java.util.Comparator;
0043: import java.util.Date;
0044: import java.util.Enumeration;
0045: import java.util.Hashtable;
0046: import java.util.Locale;
0047: import java.util.Vector;
0048:
0049: import java.io.BufferedReader;
0050: import java.io.ByteArrayInputStream;
0051: import java.io.IOException;
0052: import java.io.InputStream;
0053: import java.io.InputStreamReader;
0054: import java.io.PushbackInputStream;
0055:
0056: import java.net.InetAddress;
0057:
0058: import java.text.DateFormat;
0059:
0060: import java.security.Principal;
0061:
0062: import javax.servlet.RequestDispatcher;
0063: import javax.servlet.ServletInputStream;
0064: import javax.servlet.http.Cookie;
0065: import javax.servlet.http.HttpServletRequest;
0066: import javax.servlet.http.HttpSession;
0067:
0068: import com.quadcap.http.util.HeaderParser;
0069:
0070: import com.quadcap.util.Debug;
0071: import com.quadcap.util.Util;
0072:
0073: import com.quadcap.io.URLDecodeInputStream;
0074:
0075: import com.quadcap.util.text.OctetMap;
0076: import com.quadcap.util.text.Scanner;
0077:
0078: /**
0079: * This class encapsulates the information that makes up a single HTTP
0080: * request, including the method, URI, and the headers.
0081: *
0082: * @author Stan Bailes
0083: */
0084: public class HttpRequest implements HttpServletRequest {
0085: WebWorker w;
0086: HttpResponse res;
0087: HttpInputStream his;
0088: HttpDispatcher rd;
0089: HSession session = null;
0090: BufferedReader reader = null;
0091:
0092: Scanner scanner;
0093: static OctetMap mapM = new OctetMap('&');
0094: static {
0095: mapM.include('\r');
0096: mapM.include('\n');
0097: }
0098: static OctetMap mapE = new OctetMap('=');
0099: static OctetMap mapQuote = new OctetMap('"');
0100:
0101: String method = null;
0102: String uri = null;
0103: String pathInfo = null;
0104: String queryString = null;
0105: int queryStringStart = 0;
0106: int queryStringLen = 0;
0107: String protocol = null;
0108: byte[] headers = new byte[4096];
0109: int[] hOffsets = new int[32];
0110: Hashtable parameters = new Hashtable();
0111: Hashtable attributes = null;
0112: boolean getInputStreamCalled = false;
0113: boolean getReaderCalled = false;
0114:
0115: static DateFormat dateFormat = DateFormat.getInstance();
0116:
0117: static final int CR = '\r';
0118:
0119: Cookie[] cookies = null;
0120: boolean badRequest = false;
0121:
0122: static final String methodGET = "GET";
0123: static final String methodHEAD = "HEAD";
0124: static final String methodPOST = "POST";
0125:
0126: static final String proto_09 = "HTTP/0.9";
0127: static final String proto_10 = "HTTP/1.0";
0128: static final String proto_11 = "HTTP/1.1";
0129:
0130: /**
0131: * Using the specified worker's input stream, read an HTTP request,
0132: * and construct a new <code>HttpRequest</code> object to represent it.
0133: *
0134: * @param w the worker
0135: */
0136: public HttpRequest(WebWorker w) {
0137: this .w = w;
0138: this .scanner = new Scanner(null);
0139: }
0140:
0141: /**
0142: * Reset the request object and bind it to the input stream.
0143: */
0144: public void reset(HttpInputStream is) throws IOException {
0145: //Jni j2 = new Jni("HttpRequest");
0146: //j2.reset();
0147: this .his = is;
0148: parameters.clear();
0149: attributes = null;
0150: method = null;
0151: uri = null;
0152: pathInfo = null;
0153: queryString = null;
0154: protocol = null;
0155: reader = null;
0156: session = null;
0157: rd = null;
0158: cookies = null;
0159: queryStringStart = 0;
0160: getInputStreamCalled = false;
0161: getReaderCalled = false;
0162:
0163: //j2.dump("clear");
0164: int hsize = is.getInputStream().readHeaders(headers, hOffsets);
0165: //Debug.println(Util.strBytes(headers, 0, hsize));
0166: //j2.dump("readHeaders");
0167: int lim = hOffsets[1];
0168: int pos = 0;
0169: while (headers[pos] == '\r' || headers[pos] == '\n')
0170: pos++;
0171: int start = pos;
0172: while (headers[pos] != ' ' && pos < lim)
0173: pos++;
0174:
0175: final byte b1 = headers[start];
0176: if (b1 == 'G') {
0177: if (headers[start + 1] == 'E') {
0178: if (headers[start + 2] == 'T') {
0179: method = methodGET;
0180: }
0181: }
0182: } else if (b1 == 'P') {
0183: if (headers[start + 1] == 'O') {
0184: if (headers[start + 2] == 'S') {
0185: if (headers[start + 3] == 'T') {
0186: method = methodPOST;
0187: }
0188: }
0189: }
0190: } else if (b1 == 'H') {
0191: if (headers[start + 1] == 'E') {
0192: if (headers[start + 2] == 'A') {
0193: if (headers[start + 3] == 'D') {
0194: method = methodHEAD;
0195: }
0196: }
0197: }
0198: }
0199: if (method == null) {
0200: method = new String(headers, start, pos - start);
0201: if (method.equalsIgnoreCase("get")) {
0202: method = methodGET;
0203: } else if (method.equalsIgnoreCase("post")) {
0204: method = methodPOST;
0205: } else if (method.equalsIgnoreCase("head")) {
0206: method = methodHEAD;
0207: }
0208: }
0209:
0210: //j2.dump("getMethod");
0211: while (headers[pos] == ' ' && pos < lim)
0212: pos++;
0213: start = pos;
0214: while (headers[pos] != ' ' && headers[pos] != '?' && pos < lim)
0215: pos++;
0216: uri = new String(headers, start, pos - start);
0217: //j2.dump("getUri");
0218: if (headers[pos] == '?') {
0219: start = ++pos;
0220: while (headers[pos] != ' ' && headers[pos] != '\r'
0221: && pos < lim) {
0222: pos++;
0223: }
0224: queryStringStart = start;
0225: queryStringLen = pos - start;
0226: parseParameters(parameters, new ByteArrayInputStream(
0227: headers, start, pos - start));
0228:
0229: }
0230: while (headers[pos] == ' ' && pos < lim)
0231: pos++;
0232: start = pos;
0233: if (pos < lim) {
0234: for (byte c = headers[pos]; c != '\r' && c != '\n'
0235: && c != ' '; c = headers[pos]) {
0236: if (++pos >= lim)
0237: break;
0238: }
0239: }
0240: protocol = proto_10;
0241: try {
0242: if (headers[pos - 1] == '0') {
0243: protocol = proto_10;
0244: } else if (headers[pos - 1] == '1') {
0245: protocol = proto_11;
0246: }
0247: if (protocol == null) {
0248: protocol = new String(headers, start, pos - start);
0249: }
0250: } catch (Throwable t) {
0251: }
0252: //j2.dump("getProto");
0253:
0254: int cl = getContentLength();
0255: if (cl >= 0) {
0256: is.setContentLength(cl);
0257: }
0258: }
0259:
0260: final void maybeParsePostData() {
0261: if (!getReaderCalled && !getInputStreamCalled
0262: && method == methodPOST) {
0263: String type = getContentType();
0264: if (type != null
0265: && type.equals("application/x-www-form-urlencoded")) {
0266: scanner.reset(his);
0267: parseParameters(parameters, scanner);
0268: }
0269: }
0270: }
0271:
0272: boolean badRequest() {
0273: return badRequest;
0274: }
0275:
0276: public void setURI(String s) {
0277: try {
0278: Scanner scanner = new Scanner(new ByteArrayInputStream(s
0279: .getBytes()));
0280: this .uri = scanner.parseWhile(OctetMap.uriChars);
0281: if (scanner.peek() == '?') {
0282: scanner.matchChar('?');
0283: queryString = scanner.parseWhile(OctetMap.uriChars);
0284: parseParameters(parameters, new ByteArrayInputStream(
0285: queryString.getBytes()));
0286: } else {
0287: queryString = "";
0288: }
0289: } catch (IOException e) {
0290: }
0291: }
0292:
0293: /**
0294: * ----- ServletRequest methods
0295: */
0296:
0297: /**
0298: * Returns the size of the request entity data, or -1 if not known.
0299: * Same as the CGI variable CONTENT_LENGTH.
0300: */
0301: public int getContentLength() {
0302: String s = getHeader("Content-Length");
0303: if (s == null)
0304: return -1;
0305: try {
0306: return Integer.parseInt(s);
0307: } catch (Exception e) {
0308: return -1;
0309: }
0310: }
0311:
0312: /**
0313: * Returns the Internet Media Type of the request entity data, or
0314: * null if not known. Same as the CGI variable CONTENT_TYPE.
0315: */
0316: public String getContentType() {
0317: return getHeader("Content-Type");
0318: }
0319:
0320: /**
0321: * Returns the protocol and version of the request as a string of
0322: * the form <code><protocol>/<major version>.<minor
0323: * version></code>. Same as the CGI variable SERVER_PROTOCOL.
0324: */
0325: public String getProtocol() {
0326: return protocol;
0327: }
0328:
0329: /**
0330: * Returns the scheme of the URL used in this request, for example
0331: * "http", "https", or "ftp". Different schemes have different
0332: * rules for constructing URLs, as noted in RFC 1738. The URL used
0333: * to create a request may be reconstructed using this scheme, the
0334: * server name and port, and additional information such as URIs.
0335: */
0336: public String getScheme() {
0337: return "HTTP";
0338: }
0339:
0340: /**
0341: * Returns the host name of the server that received the request.
0342: * Same as the CGI variable SERVER_NAME.
0343: */
0344: public String getServerName() {
0345: return w.getHostName();
0346: }
0347:
0348: /**
0349: * Returns the port number on which this request was received.
0350: * Same as the CGI variable SERVER_PORT.
0351: */
0352: public int getServerPort() {
0353: return w.getPort();
0354: }
0355:
0356: /**
0357: * Returns the IP address of the agent that sent the request.
0358: * Same as the CGI variable REMOTE_ADDR.
0359: */
0360: public String getRemoteAddr() {
0361: return w.getRemoteAddr();
0362: }
0363:
0364: /**
0365: * Returns the fully qualified host name of the agent that sent the
0366: * request. Same as the CGI variable REMOTE_HOST.
0367: */
0368: public String getRemoteHost() {
0369: return w.getRemoteHost();
0370: }
0371:
0372: /**
0373: * Applies alias rules to the specified virtual path and returns
0374: * the corresponding real path, or null if the translation can not
0375: * be performed for any reason. For example, an HTTP servlet would
0376: * resolve the path using the virtual docroot, if virtual hosting
0377: * is enabled, and with the default docroot otherwise. Calling
0378: * this method with the string "/" as an argument returns the
0379: * document root.
0380: *
0381: * @param path the virtual path to be translated to a real path
0382: */
0383: public String getRealPath(String path) {
0384: WebApplication app = rd.getContext();
0385: return app.getRealPath(path);
0386: }
0387:
0388: /**
0389: * Returns an input stream for reading binary data in the request body.
0390: *
0391: * @exception IllegalStateException if getReader has been
0392: * called on this same request.
0393: * @exception IOException on other I/O related errors.
0394: */
0395: public ServletInputStream getInputStream() throws IOException {
0396: if (getReaderCalled) {
0397: throw new IllegalStateException(
0398: "getReader() already called");
0399: }
0400: getInputStreamCalled = true;
0401: return w.getHttpInputStream();
0402: }
0403:
0404: /**
0405: * Returns a string containing the lone value of the specified
0406: * parameter, or null if the parameter does not exist. For example,
0407: * in an HTTP servlet this method would return the value of the
0408: * specified query string parameter. Servlet writers should use
0409: * this method only when they are sure that there is only one value
0410: * for the parameter. If the parameter has (or could have)
0411: * multiple values, servlet writers should use
0412: * getParameterValues. If a multiple valued parameter name is
0413: * passed as an argument, the return value is implementation
0414: * dependent.
0415: *
0416: *
0417: * @param name the name of the parameter whose value is required.
0418: */
0419: public String getParameter(String name) {
0420: maybeParsePostData();
0421: String[] s = (String[]) parameters.get(name);
0422: if (s == null)
0423: return null;
0424: return s[0];
0425: }
0426:
0427: /**
0428: * Returns the values of the specified parameter for the request as
0429: * an array of strings, or null if the named parameter does not
0430: * exist. For example, in an HTTP servlet this method would return
0431: * the values of the specified query string or posted form as an
0432: * array of strings.
0433: *
0434: * @param name the name of the parameter whose value is required.
0435: */
0436: public String[] getParameterValues(String name) {
0437: maybeParsePostData();
0438: String[] ret = (String[]) parameters.get(name);
0439: return ret;
0440: }
0441:
0442: /**
0443: * Returns the parameter names for this request as an enumeration
0444: * of strings, or an empty enumeration if there are no parameters
0445: * or the input stream is empty. The input stream would be empty
0446: * if all the data had been read from the stream returned by the
0447: * method getInputStream.
0448: */
0449: public Enumeration getParameterNames() {
0450: maybeParsePostData();
0451: return parameters.keys();
0452: }
0453:
0454: /**
0455: * Returns the value of the named attribute of the request, or
0456: * null if the attribute does not exist.
0457: *
0458: * @param name the name of the attribute whose value is required
0459: */
0460: public Object getAttribute(String name) {
0461: Object obj = null;
0462: if (attributes != null)
0463: obj = attributes.get(name);
0464: return obj;
0465: }
0466:
0467: /**
0468: * Set the value of the named attribute
0469: *
0470: * @param name the name of the attribute
0471: * @param obj the value
0472: */
0473: public void setAttribute(String name, Object obj) {
0474: if (attributes == null)
0475: attributes = new Hashtable();
0476: attributes.put(name, obj);
0477: }
0478:
0479: /**
0480: * Return an enumeration of all attribute names of this service
0481: */
0482: public Enumeration getAttributeNames() {
0483: if (attributes == null)
0484: return new Vector().elements();
0485: return attributes.keys();
0486: }
0487:
0488: /**
0489: * Remove an attribute from the request
0490: */
0491: public void removeAttribute(String name) {
0492: attributes.remove(name);
0493: }
0494:
0495: /**
0496: * Returns a buffered reader for reading text in the request body.
0497: * This translates character set encodings as appropriate.
0498: *
0499: *
0500: * @exception UnsupportedEncodingException if the character set encoding
0501: * is unsupported, so the text can't be correctly decoded.
0502: * @exception IllegalStateException if getInputStream has been
0503: * called on this same request.
0504: * @exception IOException on other I/O related errors.
0505: */
0506: public BufferedReader getReader() throws IOException {
0507: if (getInputStreamCalled) {
0508: throw new IllegalStateException(
0509: "getInputStream() already called");
0510: }
0511: if (reader == null) {
0512: reader = new BufferedReader(new InputStreamReader(
0513: getInputStream()));
0514: }
0515: getReaderCalled = true;
0516: return reader;
0517: }
0518:
0519: /**
0520: * Returns the character set encoding for the input of this request.
0521: */
0522: public String getCharacterEncoding() {
0523: String s = getContentType();
0524: if (s == null)
0525: return null;
0526:
0527: int idx = s.indexOf(';');
0528: while (idx > 0) {
0529: s = s.substring(idx + 1).trim();
0530: idx = s.indexOf(';');
0531: String c = s;
0532: if (idx > 0) {
0533: c = s.substring(0, idx).trim();
0534: }
0535: if (c.startsWith("charset=")) {
0536: return c.substring(8);
0537: }
0538: }
0539: return null;
0540: }
0541:
0542: /**
0543: * ----- HttpServletRequest methods -----
0544: */
0545:
0546: /**
0547: * Gets the array of cookies found in this request.
0548: *
0549: * @return the array of cookies found in this request
0550: */
0551: public Cookie[] getCookies() {
0552: if (cookies == null) {
0553: CookieParser cp = new CookieParser(getHeader("Cookie"));
0554: try {
0555: cookies = cp.parseCookies();
0556: } catch (IOException e) {
0557: Debug.print(e);
0558: cookies = new Cookie[0];
0559: }
0560: }
0561: return cookies;
0562: }
0563:
0564: /**
0565: * Gets the HTTP method (for example, GET, POST, PUT) with which
0566: * this request was made. Same as the CGI variable REQUEST_METHOD.
0567: *
0568: * @return the HTTP method with which this request was made
0569: */
0570: public String getMethod() {
0571: return method;
0572: }
0573:
0574: /**
0575: * Gets, from the first line of the HTTP request, the part of this
0576: * request's URI that is to the left of any query string.
0577: * For example,
0578: *
0579: * <blockquote>
0580: * <table>
0581: * <tr align=left><th>First line of HTTP request<th>
0582: * <th>Return from <code>getRequestURI</code>
0583: * <tr><td>POST /some/path.html HTTP/1.1<td><td>/some/path.html
0584: * <tr><td>GET http://foo.bar/a.html HTTP/1.0
0585: * <td><td>http://foo.bar/a.html
0586: * <tr><td>HEAD /xyz?a=b HTTP/1.1<td><td>/xyz
0587: * </table>
0588: * </blockquote>
0589: *
0590: * <p>To reconstruct a URL with a URL scheme and host, use the
0591: * method javax.servlet.http.HttpUtils.getRequestURL, which returns
0592: * a StringBuffer.
0593: *
0594: * @return this request's URI
0595: */
0596: public String getRequestURI() {
0597: return uri;
0598: }
0599:
0600: /**
0601: * Gets the part of this request's URI that refers to the servlet
0602: * being invoked. Analogous to the CGI variable SCRIPT_NAME.
0603: *
0604: * @return the servlet being invoked, as contained in this
0605: * request's URI
0606: */
0607: public String getServletPath() {
0608: return rd.getServletPath();
0609: }
0610:
0611: /**
0612: * Gets any optional extra path information following the servlet
0613: * path of this request's URI, but immediately preceding its query
0614: * string. Same as the CGI variable PATH_INFO.
0615: *
0616: * @return the optional path information following the servlet
0617: * path, but before the query string, in this request's URI; null
0618: * if this request's URI contains no extra path information
0619: */
0620: public String getPathInfo() {
0621: return rd.getPathInfo();
0622: }
0623:
0624: /**
0625: * Gets any optional extra path information following the servlet
0626: * path of this request's URI, but immediately preceding its query
0627: * string, and translates it to a real path. Similar to the CGI
0628: * variable PATH_TRANSLATED
0629: *
0630: * @return extra path information translated to a real path or null
0631: * if no extra path information is in the request's URI
0632: */
0633: public String getPathTranslated() {
0634: return null;
0635: }
0636:
0637: /**
0638: * Gets any query string that is part of the HTTP request URI.
0639: * Same as the CGI variable QUERY_STRING.
0640: *
0641: * @return query string that is part of this request's URI, or null
0642: * if it contains no query string
0643: */
0644: public String getQueryString() {
0645: if (queryString == null) {
0646: if (queryStringStart > 0) {
0647: queryString = new String(headers, queryStringStart,
0648: (queryStringLen));
0649: }
0650: }
0651: return queryString;
0652: }
0653:
0654: /**
0655: * Gets the name of the user making this request. The user name is
0656: * set with HTTP authentication. Whether the user name will
0657: * continue to be sent with each subsequent communication is
0658: * browser-dependent. Same as the CGI variable REMOTE_USER.
0659: *
0660: * @return the name of the user making this request, or null if not
0661: * known.
0662: */
0663: public String getRemoteUser() {
0664: return null;
0665: }
0666:
0667: /**
0668: * Gets the authentication scheme of this request. Same as the CGI
0669: * variable AUTH_TYPE.
0670: *
0671: * @return this request's authentication scheme, or null if none.
0672: */
0673: public String getAuthType() {
0674: return null;
0675: }
0676:
0677: /**
0678: * Gets the value of the requested header field of this request.
0679: * The case of the header field name is ignored.
0680: *
0681: * @param name the String containing the name of the requested
0682: * header field
0683: * @return the value of the requested header field, or null if not
0684: * known.
0685: */
0686: public String getHeader(String name) {
0687: String ret = null;
0688: int hcnt = hOffsets[0];
0689: for (int i = 1; i < hcnt; i++) {
0690: int off = hOffsets[i];
0691: int lim = hOffsets[i + 1];
0692: int pos = off;
0693: while (headers[pos] != ':' && pos < lim)
0694: pos++;
0695: String hdr = new String(headers, off, pos - off);
0696: if (name.equalsIgnoreCase(hdr)) {
0697: ret = new String(headers, pos + 1, lim - pos - 1)
0698: .trim();
0699: break;
0700: }
0701: }
0702: return ret;
0703: }
0704:
0705: /**
0706: * Gets the value of the specified integer header field of this
0707: * request. The case of the header field name is ignored. If the
0708: * header can't be converted to an integer, the method throws a
0709: * NumberFormatException.
0710: *
0711: * @param name the String containing the name of the requested
0712: * header field
0713: * @return the value of the requested header field, or -1 if not
0714: * found.
0715: */
0716: public int getIntHeader(String name) {
0717: int ret = -1;
0718: String val = getHeader(name);
0719: if (val != null) {
0720: ret = Integer.parseInt(val);
0721: }
0722: return ret;
0723: }
0724:
0725: /**
0726: * Gets the value of the requested date header field of this
0727: * request. If the header can't be converted to a date, the method
0728: * throws an IllegalArgumentException. The case of the header
0729: * field name is ignored.
0730: *
0731: * @param name the String containing the name of the requested
0732: * header field
0733: * @return the value the requested date header field, or -1 if not
0734: * found.
0735: */
0736: public long getDateHeader(String name) {
0737: long ret = -1;
0738: String val = getHeader(name);
0739: if (val != null) {
0740: try {
0741: synchronized (dateFormat) {
0742: Date d = dateFormat.parse(val);
0743: ret = d.getTime();
0744: }
0745: } catch (Exception e) {
0746: }
0747: }
0748: return ret;
0749: }
0750:
0751: /**
0752: * Gets the header names for this request.
0753: *
0754: * @return an enumeration of strings representing the header names
0755: * for this request. Some server implementations do not allow
0756: * headers to be accessed in this way, in which case this method
0757: * will return null.
0758: */
0759: public Enumeration getHeaders() {
0760: return getHeaderNames();
0761: }
0762:
0763: public Enumeration getHeaders(String name) {
0764: Vector v = new Vector();
0765: int hcnt = hOffsets[0];
0766: for (int i = 1; i < hcnt; i++) {
0767: int off = hOffsets[i];
0768: int lim = hOffsets[i + 1];
0769: int pos = off;
0770: while (headers[pos] != ':' && pos < lim)
0771: pos++;
0772: if (name.equalsIgnoreCase(new String(headers, off, pos
0773: - off))) {
0774: pos++;
0775: int len = lim - pos;
0776: v.addElement(new String(headers, pos, len).trim());
0777: }
0778: }
0779: return v.elements();
0780: }
0781:
0782: public Enumeration getHeaderNames() {
0783: Vector v = new Vector();
0784: int hcnt = hOffsets[0];
0785: for (int i = 1; i < hcnt; i++) {
0786: int off = hOffsets[i];
0787: int lim = hOffsets[i + 1];
0788: int pos = off;
0789: while (headers[pos] != ':' && pos < lim)
0790: pos++;
0791: v.addElement(new String(headers, off, pos - off));
0792: }
0793: return v.elements();
0794: }
0795:
0796: /**
0797: * Gets the current valid session associated with this request, if
0798: * create is false or, if necessary, creates a new session for the
0799: * request, if create is true.
0800: *
0801: * <p><b>Note</b>: to ensure the session is properly maintained,
0802: * the servlet developer must call this method (at least once)
0803: * before any output is written to the response.
0804: *
0805: * <p>Additionally, application-writers need to be aware that newly
0806: * created sessions (that is, sessions for which
0807: * <code>HttpSession.isNew</code> returns true) do not have any
0808: * application-specific state.
0809: *
0810: * @return the session associated with this request or null if
0811: * create was false and no valid session is associated
0812: * with this request.
0813: */
0814: public HttpSession getSession(boolean create) {
0815: if (session == null) {
0816: WebApplication app = rd.getContext();
0817: String sessionId = getRequestedSessionId();
0818: if (sessionId != null) {
0819: session = app.getSession(sessionId);
0820: } else if (create) {
0821: session = app.createSession();
0822: sessionId = session.getId();
0823: }
0824: if (sessionId != null) {
0825: Cookie c = new Cookie("sessionId", sessionId);
0826: c.setPath(getContextPath());
0827: res.addCookie(c);
0828: }
0829: }
0830: if (session != null)
0831: session.updateLastAccess();
0832: return session;
0833: }
0834:
0835: /**
0836: * Gets the current valid session associated with this request, and
0837: * if necessary, creates a new session for the request.
0838: *
0839: * @return the session associated with this request.
0840: */
0841: public HttpSession getSession() {
0842: return getSession(true);
0843: }
0844:
0845: /**
0846: * Gets the session id specified with this request. This may
0847: * differ from the actual session id. For example, if the request
0848: * specified an id for an invalid session, then this will get a new
0849: * session with a new id.
0850: *
0851: * @return the session id specified by this request, or null if the
0852: * request did not specify a session id
0853: *
0854: */
0855: public String getRequestedSessionId() {
0856: String sessionId = null;
0857: getCookies();
0858: WebApplication app = rd.getContext();
0859: boolean foundSession = false;
0860: for (int i = 0; i < cookies.length; i++) {
0861: Cookie c = cookies[i];
0862: if (c.getName().equals("sessionId")) {
0863: sessionId = c.getValue();
0864: if (app.getSession(sessionId) != null) {
0865: foundSession = true;
0866: break;
0867: // } else {
0868: // Debug.println("invalid session id: " + sessionId);
0869: }
0870: }
0871: }
0872: if (sessionId != null && !foundSession) {
0873: sessionId = app.createSession().getId();
0874: }
0875: return sessionId;
0876: }
0877:
0878: /**
0879: * Checks whether this request is associated with a session that
0880: * is valid in the current session context. If it is not valid,
0881: * the requested session will never be returned from the
0882: * <code>getSession</code> method.
0883: *
0884: * @return true if this request is assocated with a session that is
0885: * valid in the current session context.
0886: *
0887: */
0888: public boolean isRequestedSessionIdValid() {
0889: HSession sess = null;
0890: String sessionId = getRequestedSessionId();
0891: if (sessionId != null) {
0892: sess = rd.getContext().getSession(sessionId);
0893: }
0894: return (sess != null && sess.isValid());
0895: }
0896:
0897: /**
0898: * Checks whether the session id specified by this request came in
0899: * as a cookie. (The requested session may not be one returned by
0900: * the <code>getSession</code> method.)
0901: *
0902: * @return true if the session id specified by this request came in
0903: * as a cookie; false otherwise
0904: *
0905: */
0906: public boolean isRequestedSessionIdFromCookie() {
0907: return true;
0908: }
0909:
0910: /**
0911: * Checks whether the session id specified by this request came in
0912: * as part of the URL. (The requested session may not be the one
0913: * returned by the <code>getSession</code> method.)
0914: *
0915: * @return true if the session id specified by the request for this
0916: * session came in as part of the URL; false otherwise
0917: *
0918: */
0919: public boolean isRequestedSessionIdFromURL() {
0920: return false;
0921: }
0922:
0923: public boolean isRequestedSessionIdFromUrl() {
0924: return false;
0925: }
0926:
0927: /**
0928: * ---- private parsing helpers
0929: */
0930:
0931: /**
0932: * Parsing helper: Get the next bytes all of which are in the
0933: * specified map
0934: *
0935: * @param map the octet map specifying the bytes we want
0936: */
0937: String getToken(OctetMap map) throws IOException {
0938: scanner.skipWhile(OctetMap.wsChars);
0939: return scanner.parseWhile(map);
0940: }
0941:
0942: static OctetMap versionMap = new OctetMap("HhTtPp/1.0");
0943:
0944: /**
0945: * Parse the http version field from the request line
0946: *
0947: * @return the http protocol and version
0948: *
0949: * @exception IOException if the version field is incorrect
0950: */
0951: String parseHttpVersion() throws IOException {
0952: scanner.skipWhile(OctetMap.wsChars);
0953: return scanner.parseWhile(versionMap);
0954: }
0955:
0956: static final String urlDecode(String s) {
0957: StringBuffer sb = null;
0958: for (int i = 0; i < s.length(); i++) {
0959: char c = s.charAt(i);
0960: switch (c) {
0961: case '+':
0962: if (sb == null)
0963: sb = new StringBuffer(s.substring(0, i));
0964: sb.append(' ');
0965: break;
0966: case '%':
0967: if (sb == null)
0968: sb = new StringBuffer(s.substring(0, i));
0969: try {
0970: sb.append((char) Integer.parseInt(s.substring(
0971: i + 1, i + 3), 16));
0972: i += 2;
0973: } catch (NumberFormatException e) {
0974: throw new IllegalArgumentException();
0975: } catch (StringIndexOutOfBoundsException e) {
0976: String rest = s.substring(i);
0977: sb.append(rest);
0978: if (rest.length() == 2)
0979: i++;
0980: }
0981: break;
0982: default:
0983: if (sb != null)
0984: sb.append(c);
0985: }
0986: }
0987: return sb == null ? s : sb.toString();
0988: }
0989:
0990: /**
0991: * Parse a set of parameters from the specified input stream.
0992: *
0993: * @param is the input stream
0994: * @return a table containing the parameters as String -> String[]
0995: * entries.
0996: */
0997: public static void parseParameters(Hashtable params, InputStream is) {
0998: parseParameters(params, new Scanner(is));
0999: }
1000:
1001: public static void parseParameters(Hashtable params, Scanner s) {
1002: try {
1003: do {
1004: String name = urlDecode(s.parseUntil(mapE));
1005: s.matchChar('=');
1006: String val = urlDecode(s.parseUntil(mapM));
1007: String[] vals = (String[]) params.get(name);
1008: if (vals == null) {
1009: vals = new String[1];
1010: } else {
1011: String[] oldvals = vals;
1012: vals = new String[vals.length + 1];
1013: for (int i = 0; i < oldvals.length; i++) {
1014: vals[i] = oldvals[i];
1015: }
1016: }
1017: vals[vals.length - 1] = val;
1018: params.put(name, vals);
1019: s.matchChar('&');
1020: } while (s.peek() >= 0);
1021: } catch (IOException e) {
1022: }
1023: }
1024:
1025: /**
1026: * Associate this request with its matching response. We need this
1027: * to facilitate session management via cookies
1028: *
1029: * @param res the HttpResponse that goes with this request
1030: */
1031: void setResponse(HttpResponse res) {
1032: this .res = res;
1033: }
1034:
1035: /**
1036: * Return the portion of the request URI that specifies the context
1037: * for this request
1038: */
1039: public String getContextPath() {
1040: return rd.getContextPath();
1041: }
1042:
1043: /**
1044: * Returns a boolean indicating whether the authenticated user is
1045: * included in the indicated 'role'.
1046: */
1047: public boolean isUserInRole(String role) {
1048: return false; // XXX implement me
1049: }
1050:
1051: /**
1052: * Return a Principal object indicating the identity of the user
1053: * associated with this request.
1054: */
1055: public Principal getUserPrincipal() {
1056: return null; // XXX implement me
1057: }
1058:
1059: /**
1060: * Return the preferred Locale that the client will accept content
1061: * from based on the <code>Accept-Language</code> header, or the
1062: * server default Locale
1063: */
1064: public Locale getLocale() {
1065: Locale locale = null;
1066: String accept = getHeader("Accept-Language");
1067: if (accept != null) {
1068: int idx = accept.indexOf(',');
1069: if (idx > 0) {
1070: accept = accept.substring(0, idx).trim();
1071: }
1072: locale = makeLocale(accept);
1073: } else {
1074: locale = Locale.getDefault();
1075: }
1076: return locale;
1077: }
1078:
1079: final Locale makeLocale(String s) {
1080: int idx = s.indexOf(';');
1081: if (idx > 0) {
1082: s = s.substring(0, idx).trim();
1083: }
1084: idx = s.indexOf('-');
1085: String lang = null;
1086: String country = "";
1087: if (idx > 0) {
1088: lang = s.substring(0, idx);
1089: country = s.substring(idx + 1);
1090: } else {
1091: lang = s;
1092: }
1093: return new Locale(lang, country);
1094: }
1095:
1096: final float getLangQual(String s) {
1097: float q = 1.0f;
1098: int idx = s.indexOf(';');
1099: if (idx > 0) {
1100: s = s.substring(idx + 1).trim();
1101: idx = s.indexOf("q=");
1102: if (idx >= 0) {
1103: q = Float.parseFloat(s.substring(idx + 2));
1104: }
1105: }
1106: return q;
1107: }
1108:
1109: final int compareAccept(String a, String b) {
1110: float aq = getLangQual(a);
1111: float bq = getLangQual(b);
1112: if (aq < bq)
1113: return -1;
1114: if (aq > bq)
1115: return 1;
1116: return 0;
1117: }
1118:
1119: public Enumeration getLocales() {
1120: String accept = getHeader("Accept-Language");
1121: if (accept == null) {
1122: Vector v = new Vector();
1123: v.addElement(Locale.getDefault());
1124: return v.elements();
1125: }
1126: Vector v = Util.split(accept, ',');
1127: Comparator c = new Comparator() {
1128: public int compare(Object a, Object b) {
1129: return compareAccept(a.toString(), b.toString());
1130: }
1131: };
1132: Collections.sort(v, c);
1133: final Enumeration e = v.elements();
1134: return new Enumeration() {
1135: public boolean hasMoreElements() {
1136: return e.hasMoreElements();
1137: }
1138:
1139: public Object nextElement() {
1140: return makeLocale(e.nextElement().toString());
1141: }
1142: };
1143: }
1144:
1145: public RequestDispatcher getRequestDispatcher(String path) {
1146: return rd.getContext().getRelativeRequestDispatcher(path, this );
1147: }
1148:
1149: public boolean isSecure() {
1150: return false;
1151: }
1152:
1153: final void setRequestDispatcher(HttpDispatcher rd) {
1154: this.rd = rd;
1155: }
1156:
1157: }
|