0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.server.connection;
0031:
0032: import com.caucho.i18n.CharacterEncoding;
0033: import com.caucho.security.SecurityContext;
0034: import com.caucho.security.SecurityContextProvider;
0035: import com.caucho.server.dispatch.DispatchServer;
0036: import com.caucho.server.dispatch.Invocation;
0037: import com.caucho.server.port.Port;
0038: import com.caucho.server.port.TcpConnection;
0039: import com.caucho.server.security.AbstractAuthenticator;
0040: import com.caucho.server.security.AbstractLogin;
0041: import com.caucho.server.session.SessionImpl;
0042: import com.caucho.server.session.SessionManager;
0043: import com.caucho.server.webapp.WebApp;
0044: import com.caucho.util.*;
0045: import com.caucho.vfs.BufferedReaderAdapter;
0046: import com.caucho.vfs.Encoding;
0047: import com.caucho.vfs.Path;
0048: import com.caucho.vfs.ReadStream;
0049:
0050: import javax.servlet.RequestDispatcher;
0051: import javax.servlet.ServletContext;
0052: import javax.servlet.ServletException;
0053: import javax.servlet.ServletInputStream;
0054: import javax.servlet.ServletRequestAttributeEvent;
0055: import javax.servlet.ServletRequestAttributeListener;
0056: import javax.servlet.http.Cookie;
0057: import javax.servlet.http.HttpServletResponse;
0058: import javax.servlet.http.HttpSession;
0059: import java.io.BufferedReader;
0060: import java.io.IOException;
0061: import java.io.UnsupportedEncodingException;
0062: import java.net.InetAddress;
0063: import java.security.Principal;
0064: import java.security.cert.X509Certificate;
0065: import java.util.ArrayList;
0066: import java.util.Collections;
0067: import java.util.Enumeration;
0068: import java.util.HashMap;
0069: import java.util.Locale;
0070: import java.util.Map;
0071: import java.util.logging.Level;
0072: import java.util.logging.Logger;
0073:
0074: /**
0075: * Abstract request implementing methods common to the different
0076: * request implementations.
0077: */
0078: public abstract class AbstractHttpRequest implements CauchoRequest,
0079: SecurityContextProvider {
0080: protected static final Logger log = Logger
0081: .getLogger(AbstractHttpRequest.class.getName());
0082:
0083: static final L10N L = new L10N(AbstractHttpRequest.class);
0084:
0085: protected static final CaseInsensitiveIntMap _headerCodes;
0086:
0087: public static final String REQUEST_URI = "javax.servlet.include.request_uri";
0088: public static final String CONTEXT_PATH = "javax.servlet.include.context_path";
0089: public static final String SERVLET_PATH = "javax.servlet.include.servlet_path";
0090: public static final String PATH_INFO = "javax.servlet.include.path_info";
0091: public static final String QUERY_STRING = "javax.servlet.include.query_string";
0092:
0093: public static final String STATUS_CODE = "javax.servlet.error.status_code";
0094: public static final String EXCEPTION_TYPE = "javax.servlet.error.exception_type";
0095: public static final String MESSAGE = "javax.servlet.error.message";
0096: public static final String EXCEPTION = "javax.servlet.error.exception";
0097: public static final String ERROR_URI = "javax.servlet.error.request_uri";
0098: public static final String SERVLET_NAME = "javax.servlet.error.servlet_name";
0099:
0100: public static final String JSP_EXCEPTION = "javax.servlet.jsp.jspException";
0101:
0102: public static final String SHUTDOWN = "com.caucho.shutdown";
0103:
0104: private static final String CHAR_ENCODING = "resin.form.character.encoding";
0105: private static final String FORM_LOCALE = "resin.form.local";
0106: private static final String CAUCHO_CHAR_ENCODING = "caucho.form.character.encoding";
0107:
0108: private static final char[] CONNECTION = "connection".toCharArray();
0109: private static final char[] COOKIE = "cookie".toCharArray();
0110: private static final char[] EXPECT = "expect".toCharArray();
0111: private static final char[] HOST = "host".toCharArray();
0112:
0113: private static final char[] CONTINUE_100 = "100-continue"
0114: .toCharArray();
0115: private static final char[] CLOSE = "close".toCharArray();
0116:
0117: private static final boolean[] TOKEN;
0118: private static final boolean[] VALUE;
0119:
0120: private static final ServletRequestAttributeListener[] NULL_LISTENERS = new ServletRequestAttributeListener[0];
0121:
0122: private static final Cookie[] NULL_COOKIES = new Cookie[0];
0123:
0124: protected final DispatchServer _server;
0125:
0126: protected final Connection _conn;
0127: protected final TcpConnection _tcpConn;
0128:
0129: protected AbstractHttpResponse _response;
0130:
0131: protected Invocation _invocation;
0132:
0133: private SecurityContextProvider _oldProvider;
0134: private String _runAs;
0135:
0136: private boolean _keepalive;
0137:
0138: protected CharSegment _hostHeader;
0139: protected boolean _expect100Continue;
0140:
0141: private Cookie[] _cookiesIn;
0142: private ArrayList<Cookie> _cookies = new ArrayList<Cookie>();
0143:
0144: // True if the page depends on cookies
0145: private boolean _varyCookies;
0146: // The cookie the page depends on
0147: private String _varyCookie;
0148: private boolean _hasCookie;
0149: private boolean _isSessionIdFromCookie;
0150:
0151: protected int _sessionGroup;
0152:
0153: private boolean _sessionIsLoaded;
0154: private SessionImpl _session;
0155:
0156: // Connection stream
0157: protected final ReadStream _rawRead;
0158: // Stream for reading post contents
0159: protected final ReadStream _readStream;
0160:
0161: // True if the post stream has been initialized
0162: protected boolean _hasReadStream;
0163:
0164: // Servlet input stream for post contents
0165: private final ServletInputStreamImpl _is = new ServletInputStreamImpl();
0166:
0167: // character incoding for a Post
0168: private String _readEncoding;
0169: // Reader for post contents
0170: private final BufferedReaderAdapter _bufferedReader;
0171:
0172: private boolean _hasReader;
0173: private boolean _hasInputStream;
0174:
0175: // HttpServletRequest stuff
0176: private final Form _formParser = new Form();
0177: private final HashMapImpl<String, String[]> _form = new HashMapImpl<String, String[]>();
0178: private HashMapImpl<String, String[]> _filledForm;
0179:
0180: private final HashMapImpl<String, Object> _attributes = new HashMapImpl<String, Object>();
0181:
0182: private ArrayList<Locale> _locales = new ArrayList<Locale>();
0183:
0184: private long _startTime;
0185: private ArrayList<Path> _closeOnExit = new ArrayList<Path>();
0186:
0187: // Efficient date class for printing date headers
0188: protected final QDate _calendar = new QDate();
0189: private final CharBuffer _cbName = new CharBuffer();
0190: private final CharBuffer _cbValue = new CharBuffer();
0191: protected final CharBuffer _cb = new CharBuffer();
0192: // private final ArrayList<CharSegment> _arrayList = new ArrayList<CharSegment>();
0193:
0194: private final byte[] _address = new byte[256];
0195:
0196: private final byte[] _logBuffer = new byte[8 * 1024];
0197:
0198: private ServletRequestAttributeListener[] _attributeListeners;
0199:
0200: /**
0201: * Create a new Request. Because the actual initialization occurs with
0202: * the start() method, this just allocates statics.
0203: *
0204: * @param server the parent server
0205: */
0206: protected AbstractHttpRequest(DispatchServer server, Connection conn) {
0207: _server = server;
0208:
0209: _conn = conn;
0210: if (conn != null)
0211: _rawRead = conn.getReadStream();
0212: else
0213: _rawRead = null;
0214:
0215: if (conn instanceof TcpConnection)
0216: _tcpConn = (TcpConnection) conn;
0217: else
0218: _tcpConn = null;
0219:
0220: _readStream = new ReadStream();
0221: _readStream.setReuseBuffer(true);
0222:
0223: _bufferedReader = new BufferedReaderAdapter(_readStream);
0224: }
0225:
0226: /**
0227: * Initialization.
0228: */
0229: public void init() {
0230: }
0231:
0232: /**
0233: * Returns the connection.
0234: */
0235: public final Connection getConnection() {
0236: return _conn;
0237: }
0238:
0239: /**
0240: * returns the dispatch server.
0241: */
0242: public final DispatchServer getDispatchServer() {
0243: return _server;
0244: }
0245:
0246: /**
0247: * Prepare the Request object for a new request.
0248: *
0249: * @param s the raw connection stream
0250: */
0251: protected void start() throws IOException {
0252: resume();
0253:
0254: _invocation = null;
0255:
0256: _varyCookies = false;
0257: _varyCookie = null;
0258: _hasCookie = false;
0259:
0260: _hostHeader = null;
0261: _expect100Continue = false;
0262:
0263: _cookiesIn = null;
0264: _cookies.clear();
0265:
0266: _sessionGroup = -1;
0267: _session = null;
0268: _sessionIsLoaded = false;
0269:
0270: _hasReadStream = false;
0271: _hasReader = false;
0272: _hasInputStream = false;
0273:
0274: _filledForm = null;
0275: _locales.clear();
0276:
0277: _readEncoding = null;
0278:
0279: _keepalive = true;
0280: _isSessionIdFromCookie = false;
0281:
0282: _oldProvider = null;
0283: _runAs = null;
0284:
0285: _attributeListeners = NULL_LISTENERS;
0286:
0287: if (_attributes.size() > 0)
0288: _attributes.clear();
0289: }
0290:
0291: /**
0292: * Prepare the Request object for a new request.
0293: *
0294: * @param s the raw connection stream
0295: */
0296: protected void resume() throws IOException {
0297: _oldProvider = SecurityContext.setProvider(this );
0298: _startTime = Alarm.getCurrentTime();
0299:
0300: if (_tcpConn != null)
0301: _tcpConn.beginActive();
0302: }
0303:
0304: /**
0305: * Returns true if client disconnects should be ignored.
0306: */
0307: public boolean isIgnoreClientDisconnect() {
0308: // server/183c
0309:
0310: Invocation invocation = _invocation;
0311:
0312: if (invocation == null)
0313: return true;
0314: else {
0315: WebApp webApp = invocation.getWebApp();
0316:
0317: if (webApp != null)
0318: return webApp.isIgnoreClientDisconnect();
0319: else
0320: return true;
0321: }
0322: }
0323:
0324: /**
0325: * Returns the response for this request.
0326: */
0327: public CauchoResponse getResponse() {
0328: return _response;
0329: }
0330:
0331: /**
0332: * Returns the local server name.
0333: */
0334: public String getServerName() {
0335: String host = _conn.getVirtualHost();
0336:
0337: /*
0338: if (host == null && _invocation != null)
0339: host = _invocation.getHostName();
0340: */
0341:
0342: CharSequence rawHost;
0343: if (host == null && (rawHost = getHost()) != null) {
0344: if (rawHost instanceof CharSegment) {
0345: CharSegment cb = (CharSegment) rawHost;
0346:
0347: char[] buffer = cb.getBuffer();
0348: int offset = cb.getOffset();
0349: int length = cb.getLength();
0350:
0351: for (int i = length - 1; i >= 0; i--) {
0352: char ch = buffer[i + offset];
0353:
0354: if ('A' <= ch && ch <= 'Z')
0355: buffer[i + offset] = (char) (ch + 'a' - 'A');
0356: }
0357:
0358: host = new String(buffer, offset, length);
0359: } else
0360: return rawHost.toString().toLowerCase();
0361: }
0362:
0363: if (host == null) {
0364: InetAddress addr = _conn.getLocalAddress();
0365: return addr.getHostName();
0366: }
0367:
0368: int p1 = host.lastIndexOf('/');
0369: if (p1 < 0)
0370: p1 = 0;
0371:
0372: int p = host.lastIndexOf(':');
0373: if (p >= 0 && p1 < p)
0374: return host.substring(p1, p);
0375: else
0376: return host;
0377: }
0378:
0379: protected CharSequence getHost() {
0380: return null;
0381: }
0382:
0383: /**
0384: * Returns the server's port.
0385: */
0386: public int getServerPort() {
0387: String host = _conn.getVirtualHost();
0388:
0389: CharSequence rawHost;
0390: if (host == null && (rawHost = getHost()) != null) {
0391: int length = rawHost.length();
0392: int i;
0393:
0394: for (i = length - 1; i >= 0; i--) {
0395: if (rawHost.charAt(i) == ':') {
0396: int port = 0;
0397:
0398: for (i++; i < length; i++) {
0399: char ch = rawHost.charAt(i);
0400:
0401: if ('0' <= ch && ch <= '9')
0402: port = 10 * port + ch - '0';
0403: }
0404:
0405: return port;
0406: }
0407: }
0408:
0409: return isSecure() ? 443 : 80;
0410: }
0411:
0412: if (host == null)
0413: return _conn.getLocalPort();
0414:
0415: int p1 = host.lastIndexOf(':');
0416: if (p1 < 0)
0417: return isSecure() ? 443 : 80;
0418: else {
0419: int length = host.length();
0420: int port = 0;
0421:
0422: for (int i = p1 + 1; i < length; i++) {
0423: char ch = host.charAt(i);
0424:
0425: if ('0' <= ch && ch <= '9')
0426: port = 10 * port + ch - '0';
0427: }
0428:
0429: return port;
0430: }
0431: }
0432:
0433: /**
0434: * Returns the local port.
0435: */
0436: public int getLocalPort() {
0437: return _conn.getLocalPort();
0438: }
0439:
0440: /**
0441: * Returns the server's address.
0442: */
0443: public String getLocalAddr() {
0444: return _conn.getLocalAddress().getHostAddress();
0445: }
0446:
0447: /**
0448: * Returns the server's address.
0449: */
0450: public String getLocalName() {
0451: return _conn.getLocalAddress().getHostAddress();
0452: }
0453:
0454: public String getRemoteAddr() {
0455: return _conn.getRemoteHost();
0456: }
0457:
0458: public int printRemoteAddr(byte[] buffer, int offset)
0459: throws IOException {
0460: int len = _conn.getRemoteAddress(buffer, offset, buffer.length
0461: - offset);
0462:
0463: return offset + len;
0464: }
0465:
0466: public String getRemoteHost() {
0467: return _conn.getRemoteHost();
0468: }
0469:
0470: /**
0471: * Returns the local port.
0472: */
0473: public int getRemotePort() {
0474: return _conn.getRemotePort();
0475: }
0476:
0477: /**
0478: * Returns the request's scheme.
0479: */
0480: public String getScheme() {
0481: return isSecure() ? "https" : "http";
0482: }
0483:
0484: abstract public String getProtocol();
0485:
0486: abstract public String getMethod();
0487:
0488: /**
0489: * Returns the URI for the request
0490: */
0491: public String getRequestURI() {
0492: if (_invocation != null)
0493: return _invocation.getRawURI();
0494: else
0495: return "";
0496: }
0497:
0498: /**
0499: * Returns the URI for the page. getPageURI and getRequestURI differ
0500: * for included files. getPageURI gets the URI for the included page.
0501: * getRequestURI returns the original URI.
0502: */
0503: public String getPageURI() {
0504: return _invocation.getRawURI();
0505: }
0506:
0507: public abstract byte[] getUriBuffer();
0508:
0509: public abstract int getUriLength();
0510:
0511: /**
0512: * Returns the context part of the uri. The context part is the part
0513: * that maps to an webApp.
0514: */
0515: public String getContextPath() {
0516: return _invocation.getContextPath();
0517: }
0518:
0519: /**
0520: * Returns the context part of the uri. For included files, this will
0521: * return the included context-path.
0522: */
0523: public String getPageContextPath() {
0524: return getContextPath();
0525: }
0526:
0527: /**
0528: * Returns the portion of the uri mapped to the servlet for the original
0529: * request.
0530: */
0531: public String getServletPath() {
0532: return _invocation.getServletPath();
0533: }
0534:
0535: /**
0536: * Returns the portion of the uri mapped to the servlet for the current
0537: * page.
0538: */
0539: public String getPageServletPath() {
0540: return _invocation.getServletPath();
0541: }
0542:
0543: /**
0544: * Returns the portion of the uri after the servlet path for the original
0545: * request.
0546: */
0547: public String getPathInfo() {
0548: return _invocation.getPathInfo();
0549: }
0550:
0551: /**
0552: * Returns the portion of the uri after the servlet path for the current
0553: * page.
0554: */
0555: public String getPagePathInfo() {
0556: return _invocation.getPathInfo();
0557: }
0558:
0559: /**
0560: * Returns the URL for the request
0561: */
0562: public StringBuffer getRequestURL() {
0563: StringBuffer sb = new StringBuffer();
0564:
0565: sb.append(getScheme());
0566: sb.append("://");
0567:
0568: sb.append(getServerName());
0569: int port = getServerPort();
0570:
0571: if (port > 0 && port != 80 && port != 443) {
0572: sb.append(":");
0573: sb.append(port);
0574: }
0575:
0576: sb.append(getRequestURI());
0577:
0578: return sb;
0579: }
0580:
0581: /**
0582: * @deprecated As of JSDK 2.1
0583: */
0584: public String getRealPath(String path) {
0585: if (path == null)
0586: return null;
0587: if (path.length() > 0 && path.charAt(0) == '/')
0588: return _invocation.getWebApp().getRealPath(path);
0589:
0590: String uri = getPageURI();
0591: String context = getPageContextPath();
0592: if (context != null)
0593: uri = uri.substring(context.length());
0594:
0595: int p = uri.lastIndexOf('/');
0596: if (p >= 0)
0597: path = uri.substring(0, p + 1) + path;
0598:
0599: return _invocation.getWebApp().getRealPath(path);
0600: }
0601:
0602: /**
0603: * Returns the real path of pathInfo.
0604: */
0605: public String getPathTranslated() {
0606: String pathInfo = getPathInfo();
0607:
0608: if (pathInfo == null)
0609: return null;
0610: else
0611: return getRealPath(pathInfo);
0612: }
0613:
0614: /**
0615: * Returns the current page's query string.
0616: */
0617: public String getQueryString() {
0618: if (_invocation != null)
0619: return _invocation.getQueryString();
0620: else
0621: return null;
0622: }
0623:
0624: /**
0625: * Returns the current page's query string.
0626: */
0627: public String getPageQueryString() {
0628: return getQueryString();
0629: }
0630:
0631: /**
0632: * Returns the named header.
0633: *
0634: * @param key the header key
0635: */
0636: abstract public String getHeader(String key);
0637:
0638: /**
0639: * Returns the number of headers.
0640: */
0641: public int getHeaderSize() {
0642: return -1;
0643: }
0644:
0645: /**
0646: * Returns the header key
0647: */
0648: public CharSegment getHeaderKey(int index) {
0649: throw new UnsupportedOperationException();
0650: }
0651:
0652: /**
0653: * Returns the header value
0654: */
0655: public CharSegment getHeaderValue(int index) {
0656: throw new UnsupportedOperationException();
0657: }
0658:
0659: /**
0660: * Fills the result with the header values as
0661: * CharSegment values. Most implementations will
0662: * implement this directly.
0663: *
0664: * @param name the header name
0665: */
0666: public CharSegment getHeaderBuffer(String name) {
0667: String value = getHeader(name);
0668:
0669: if (value != null)
0670: return new CharBuffer(value);
0671: else
0672: return null;
0673: }
0674:
0675: /**
0676: * Enumerates the header keys
0677: */
0678: abstract public Enumeration getHeaderNames();
0679:
0680: /**
0681: * Sets the header. setHeader is used for
0682: * Resin's caching to simulate If-None-Match.
0683: */
0684: public void setHeader(String key, String value) {
0685: }
0686:
0687: /**
0688: * Adds the header, checking for known values.
0689: */
0690: protected boolean addHeaderInt(char[] keyBuf, int keyOff,
0691: int keyLen, CharSegment value) {
0692: if (keyLen < 4)
0693: return true;
0694:
0695: int key1 = keyBuf[keyOff];
0696: switch (key1) {
0697: case 'c':
0698: case 'C':
0699: if (keyLen == CONNECTION.length
0700: && match(keyBuf, keyOff, keyLen, CONNECTION)) {
0701: if (match(value.getBuffer(), value.getOffset(), value
0702: .length(), CLOSE)) {
0703: connectionClose();
0704: }
0705: } else if (keyLen == COOKIE.length
0706: && match(keyBuf, keyOff, keyLen, COOKIE)) {
0707: fillCookie(_cookies, value);
0708: }
0709: return true;
0710:
0711: case 'e':
0712: case 'E':
0713: if (match(keyBuf, keyOff, keyLen, EXPECT)) {
0714: if (match(value.getBuffer(), value.getOffset(), value
0715: .length(), CONTINUE_100)) {
0716: _expect100Continue = true;
0717: return false;
0718: }
0719: }
0720:
0721: return true;
0722:
0723: case 'h':
0724: case 'H':
0725: if (match(keyBuf, keyOff, keyLen, HOST)) {
0726: _hostHeader = value;
0727: }
0728: return true;
0729:
0730: default:
0731: return true;
0732: }
0733: }
0734:
0735: /**
0736: * Called for a connection: close
0737: */
0738: protected void connectionClose() {
0739: killKeepalive();
0740: }
0741:
0742: /**
0743: * Matches case insensitively, with the second normalized to lower case.
0744: */
0745: private boolean match(char[] a, int aOff, int aLength, char[] b) {
0746: int bLength = b.length;
0747:
0748: if (aLength != bLength)
0749: return false;
0750:
0751: for (int i = aLength - 1; i >= 0; i--) {
0752: char chA = a[aOff + i];
0753: char chB = b[i];
0754:
0755: if (chA != chB && chA + 'a' - 'A' != chB) {
0756: return false;
0757: }
0758: }
0759:
0760: return true;
0761: }
0762:
0763: /**
0764: * Returns an enumeration of the headers for the named attribute.
0765: *
0766: * @param name the header name
0767: */
0768: public Enumeration getHeaders(String name) {
0769: String value = getHeader(name);
0770: if (value == null)
0771: return NullEnumeration.create();
0772:
0773: ArrayList<String> list = new ArrayList<String>();
0774: list.add(value);
0775:
0776: return Collections.enumeration(list);
0777: }
0778:
0779: /**
0780: * Fills the result with a list of the header values as
0781: * CharSegment values. Most implementations will
0782: * implement this directly.
0783: *
0784: * @param name the header name
0785: * @param resultList the resulting buffer
0786: */
0787: public void getHeaderBuffers(String name,
0788: ArrayList<CharSegment> resultList) {
0789: String value = getHeader(name);
0790:
0791: if (value != null)
0792: resultList.add(new CharBuffer(value));
0793: }
0794:
0795: /**
0796: * Returns the named header, converted to an integer.
0797: *
0798: * @param key the header key.
0799: *
0800: * @return the value of the header as an integer.
0801: */
0802: public int getIntHeader(String key) {
0803: CharSegment value = getHeaderBuffer(key);
0804:
0805: if (value == null)
0806: return -1;
0807:
0808: int len = value.length();
0809: if (len == 0)
0810: throw new NumberFormatException(value.toString());
0811:
0812: int iValue = 0;
0813: int i = 0;
0814: int ch = value.charAt(i);
0815: int sign = 1;
0816: if (ch == '+') {
0817: if (i + 1 < len)
0818: ch = value.charAt(++i);
0819: else
0820: throw new NumberFormatException(value.toString());
0821: } else if (ch == '-') {
0822: sign = -1;
0823: if (i + 1 < len)
0824: ch = value.charAt(++i);
0825: else
0826: throw new NumberFormatException(value.toString());
0827: }
0828:
0829: for (; i < len && (ch = value.charAt(i)) >= '0' && ch <= '9'; i++)
0830: iValue = 10 * iValue + ch - '0';
0831:
0832: if (i < len)
0833: throw new NumberFormatException(value.toString());
0834:
0835: return sign * iValue;
0836: }
0837:
0838: /**
0839: * Returns a header interpreted as a date.
0840: *
0841: * @param key the header key.
0842: *
0843: * @return the value of the header as an integer.
0844: */
0845: public long getDateHeader(String key) {
0846: String value = getHeader(key);
0847: if (value == null)
0848: return -1;
0849:
0850: long date = -1;
0851: try {
0852: date = _calendar.parseDate(value);
0853:
0854: if (date == Long.MAX_VALUE)
0855: throw new IllegalArgumentException("getDateHeader("
0856: + value + ")");
0857:
0858: return date;
0859: } catch (RuntimeException e) {
0860: throw e;
0861: } catch (Exception e) {
0862: throw new IllegalArgumentException(e);
0863: }
0864: }
0865:
0866: /**
0867: * Returns the content length of a post.
0868: */
0869: public int getContentLength() {
0870: CharSegment cl = getHeaderBuffer("Content-Length");
0871:
0872: if (cl == null)
0873: return -1;
0874:
0875: int value = 0;
0876: int i = 0;
0877: int ch;
0878:
0879: int length = cl.length();
0880: for (; i < length && (ch = cl.charAt(i)) >= '0' && ch <= '9'; i++)
0881: value = 10 * value + ch - '0';
0882:
0883: return i == 0 ? -1 : value;
0884: }
0885:
0886: /**
0887: * Returns the content length of a post.
0888: */
0889: public long getLongContentLength() {
0890: CharSegment cl = getHeaderBuffer("Content-Length");
0891:
0892: if (cl == null)
0893: return -1;
0894:
0895: long value = 0;
0896: int i = 0;
0897: int ch;
0898:
0899: int length = cl.length();
0900: for (; i < length && (ch = cl.charAt(i)) >= '0' && ch <= '9'; i++)
0901: value = 10 * value + ch - '0';
0902:
0903: return i == 0 ? -1 : value;
0904: }
0905:
0906: /**
0907: * Returns the content-length of a post.
0908: */
0909: public String getContentType() {
0910: return getHeader("Content-Type");
0911: }
0912:
0913: /**
0914: * Returns the content-length of a post.
0915: */
0916: public CharSegment getContentTypeBuffer() {
0917: return getHeaderBuffer("Content-Type");
0918: }
0919:
0920: /**
0921: * Returns the character encoding of a post.
0922: */
0923: public String getCharacterEncoding() {
0924: if (_readEncoding != null)
0925: return _readEncoding;
0926:
0927: CharSegment value = getHeaderBuffer("Content-Type");
0928:
0929: if (value == null)
0930: return null;
0931:
0932: int i = value.indexOf("charset");
0933: if (i < 0)
0934: return null;
0935:
0936: int len = value.length();
0937: for (i += 7; i < len && Character.isWhitespace(value.charAt(i)); i++) {
0938: }
0939:
0940: if (i >= len || value.charAt(i) != '=')
0941: return null;
0942:
0943: for (i++; i < len && Character.isWhitespace(value.charAt(i)); i++) {
0944: }
0945:
0946: if (i >= len)
0947: return null;
0948:
0949: char end = value.charAt(i);
0950: if (end == '"') {
0951: int tail;
0952: for (tail = ++i; tail < len; tail++) {
0953: if (value.charAt(tail) == end)
0954: break;
0955: }
0956:
0957: _readEncoding = Encoding.getMimeName(value.substring(i,
0958: tail));
0959:
0960: return _readEncoding;
0961: }
0962:
0963: int tail;
0964: for (tail = i; tail < len; tail++) {
0965: if (Character.isWhitespace(value.charAt(tail))
0966: || value.charAt(tail) == ';')
0967: break;
0968: }
0969:
0970: _readEncoding = Encoding.getMimeName(value.substring(i, tail));
0971:
0972: return _readEncoding;
0973: }
0974:
0975: /**
0976: * Sets the character encoding of a post.
0977: */
0978: public void setCharacterEncoding(String encoding)
0979: throws UnsupportedEncodingException {
0980: _readEncoding = encoding;
0981:
0982: try {
0983: getStream(true).setEncoding(_readEncoding);
0984: } catch (UnsupportedEncodingException e) {
0985: throw e;
0986: } catch (java.nio.charset.UnsupportedCharsetException e) {
0987: throw new UnsupportedEncodingException(e.getMessage());
0988: } catch (IOException e) {
0989: log.log(Level.FINE, e.toString(), e);
0990: }
0991: }
0992:
0993: /**
0994: * Returns the cookies from the browser
0995: */
0996: public Cookie[] getCookies() {
0997: // The page varies depending on the presense of any cookies
0998: setVaryCookie(null);
0999:
1000: if (_cookiesIn == null)
1001: fillCookies();
1002:
1003: // If any cookies actually exist, the page is not anonymous
1004: if (_cookiesIn != null && _cookiesIn.length > 0)
1005: setHasCookie();
1006:
1007: if (_cookiesIn == null || _cookiesIn.length == 0)
1008: return null;
1009: else
1010: return _cookiesIn;
1011: }
1012:
1013: /**
1014: * Returns the named cookie from the browser
1015: */
1016: public Cookie getCookie(String name) {
1017: // The page varies depending on the presense of any cookies
1018: setVaryCookie(name);
1019:
1020: return findCookie(name);
1021: }
1022:
1023: private Cookie findCookie(String name) {
1024: if (_cookiesIn == null)
1025: fillCookies();
1026:
1027: if (_cookiesIn == null)
1028: return null;
1029:
1030: int length = _cookiesIn.length;
1031: for (int i = 0; i < length; i++) {
1032: Cookie cookie = _cookiesIn[i];
1033:
1034: if (cookie.getName().equals(name)) {
1035: setHasCookie();
1036: return cookie;
1037: }
1038: }
1039:
1040: return null;
1041: }
1042:
1043: /**
1044: * Parses cookie information from the cookie headers.
1045: */
1046: private void fillCookies() {
1047: int size = _cookies.size();
1048:
1049: if (size > 0) {
1050: _cookiesIn = new Cookie[_cookies.size()];
1051: _cookies.toArray(_cookiesIn);
1052: } else
1053: _cookiesIn = NULL_COOKIES;
1054: }
1055:
1056: /**
1057: * Parses a single cookie
1058: *
1059: * @param cookies the array of cookies read
1060: * @param rawCook the input for the cookie
1061: */
1062: private void fillCookie(ArrayList cookies, CharSegment rawCookie) {
1063: char[] buf = rawCookie.getBuffer();
1064: int j = rawCookie.getOffset();
1065: int end = j + rawCookie.length();
1066: int version = 0;
1067: Cookie cookie = null;
1068:
1069: while (j < end) {
1070: char ch = 0;
1071:
1072: CharBuffer cbName = _cbName;
1073: CharBuffer cbValue = _cbValue;
1074:
1075: cbName.clear();
1076: cbValue.clear();
1077:
1078: for (; j < end
1079: && ((ch = buf[j]) == ' ' || ch == ';' || ch == ','); j++) {
1080: }
1081:
1082: if (end <= j)
1083: break;
1084:
1085: boolean isSpecial = false;
1086: if (buf[j] == '$') {
1087: isSpecial = true;
1088: j++;
1089: }
1090:
1091: for (; j < end; j++) {
1092: ch = buf[j];
1093: if (ch < 128 && TOKEN[ch])
1094: cbName.append(ch);
1095: else
1096: break;
1097: }
1098:
1099: for (; j < end && (ch = buf[j]) == ' '; j++) {
1100: }
1101:
1102: if (end <= j)
1103: break;
1104: else if (ch == ';' || ch == ',') {
1105: try {
1106: cookie = new Cookie(cbName.toString(), "");
1107: cookie.setVersion(version);
1108: _cookies.add(cookie);
1109: // some clients can send bogus cookies
1110: } catch (Exception e) {
1111: log.log(Level.FINE, e.toString(), e);
1112: }
1113: continue;
1114: } else if (ch != '=') {
1115: for (; j < end && (ch = buf[j]) != ';'; j++) {
1116: }
1117: continue;
1118: }
1119:
1120: j++;
1121:
1122: for (; j < end && (ch = buf[j]) == ' '; j++) {
1123: }
1124:
1125: if (ch == '"') {
1126: for (j++; j < end; j++) {
1127: ch = buf[j];
1128: if (ch == '"')
1129: break;
1130: cbValue.append(ch);
1131: }
1132: j++;
1133: } else {
1134: for (; j < end; j++) {
1135: ch = buf[j];
1136: if (ch < 128 && VALUE[ch])
1137: cbValue.append(ch);
1138: else
1139: break;
1140: }
1141: }
1142:
1143: if (!isSpecial) {
1144: if (cbName.length() == 0)
1145: log.warning("bad cookie: " + rawCookie);
1146: else {
1147: cookie = new Cookie(cbName.toString(), cbValue
1148: .toString());
1149: cookie.setVersion(version);
1150: _cookies.add(cookie);
1151: }
1152: } else if (cookie == null) {
1153: if (cbName.matchesIgnoreCase("Version"))
1154: version = cbValue.charAt(0) - '0';
1155: } else if (cbName.matchesIgnoreCase("Version"))
1156: cookie.setVersion(cbValue.charAt(0) - '0');
1157: else if (cbName.matchesIgnoreCase("Domain"))
1158: cookie.setDomain(cbValue.toString());
1159: else if (cbName.matchesIgnoreCase("Path"))
1160: cookie.setPath(cbValue.toString());
1161: }
1162: }
1163:
1164: /**
1165: * Called if the page depends on a cookie. If the cookie is null, then
1166: * the page depends on all cookies.
1167: *
1168: * @param cookie the cookie the page depends on.
1169: */
1170: public void setVaryCookie(String cookie) {
1171: if (_varyCookies == false)
1172: _varyCookie = cookie;
1173: else if (_varyCookie != null && !_varyCookie.equals(cookie))
1174: _varyCookie = null;
1175:
1176: _varyCookies = true;
1177:
1178: // XXX: server/1315 vs 2671
1179: // _response.setPrivateOrResinCache(true);
1180: }
1181:
1182: /**
1183: * Returns true if the page depends on cookies.
1184: */
1185: public boolean getVaryCookies() {
1186: return _varyCookies;
1187: }
1188:
1189: /**
1190: * Returns the cookie the page depends on, or null if the page
1191: * depends on several cookies.
1192: */
1193: public String getVaryCookie() {
1194: return _varyCookie;
1195: }
1196:
1197: /**
1198: * Set when the page actually has a cookie.
1199: */
1200: public void setHasCookie() {
1201: _hasCookie = true;
1202:
1203: // XXX: 1171 vs 1240
1204: // _response.setPrivateOrResinCache(true);
1205: }
1206:
1207: /**
1208: * True if this page uses cookies.
1209: */
1210: public boolean getHasCookie() {
1211: if (_hasCookie)
1212: return true;
1213: else if (_invocation != null)
1214: return _invocation.getSessionId() != null;
1215: else
1216: return false;
1217: }
1218:
1219: /**
1220: * Returns the memory session.
1221: */
1222: public HttpSession getMemorySession() {
1223: if (_session != null && _session.isValid())
1224: return _session;
1225: else
1226: return null;
1227: }
1228:
1229: /**
1230: * Returns the current session, creating one if necessary.
1231: */
1232: public HttpSession getSession() {
1233: return getSession(true);
1234: }
1235:
1236: /**
1237: * Returns the current session.
1238: *
1239: * @param create true if a new session should be created
1240: *
1241: * @return the current session
1242: */
1243: public HttpSession getSession(boolean create) {
1244: if (_session != null) {
1245: if (_session.isValid())
1246: return _session;
1247: } else if (!create && _sessionIsLoaded)
1248: return null;
1249:
1250: _sessionIsLoaded = true;
1251:
1252: boolean hasOldSession = _session != null;
1253: _session = createSession(create, hasOldSession);
1254:
1255: return _session;
1256: }
1257:
1258: /**
1259: * Returns the current session.
1260: *
1261: * @param create true if a new session should be created
1262: *
1263: * @return the current session
1264: */
1265: public HttpSession getLoadedSession() {
1266: if (_session != null && _session.isValid())
1267: return _session;
1268: else
1269: return null;
1270: }
1271:
1272: /**
1273: * Returns true if the HTTP request's session id refers to a valid
1274: * session.
1275: */
1276: public boolean isRequestedSessionIdValid() {
1277: String id = getRequestedSessionId();
1278:
1279: if (id == null)
1280: return false;
1281:
1282: SessionImpl session = (SessionImpl) getSession(false);
1283:
1284: return session != null && session.isValid()
1285: && session.getId().equals(id);
1286: }
1287:
1288: /**
1289: * Returns true if the current sessionId came from a cookie.
1290: */
1291: public boolean isRequestedSessionIdFromCookie() {
1292: return findSessionIdFromCookie() != null;
1293: }
1294:
1295: /**
1296: * Returns true if the current sessionId came from the url.
1297: */
1298: public boolean isRequestedSessionIdFromURL() {
1299: return findSessionIdFromUrl() != null;
1300: }
1301:
1302: /**
1303: * @deprecated
1304: */
1305: public boolean isRequestedSessionIdFromUrl() {
1306: return isRequestedSessionIdFromURL();
1307: }
1308:
1309: /**
1310: * Returns the session id in the HTTP request. The cookie has
1311: * priority over the URL. Because the webApp might be using
1312: * the cookie to change the page contents, the caching sets
1313: * vary: JSESSIONID.
1314: */
1315: public String getRequestedSessionIdNoVary() {
1316: boolean varyCookies = _varyCookies;
1317: String varyCookie = _varyCookie;
1318: boolean hasCookie = _hasCookie;
1319: boolean privateCache = _response.getPrivateCache();
1320:
1321: String id = getRequestedSessionId();
1322:
1323: _varyCookies = varyCookies;
1324: _varyCookie = varyCookie;
1325: _hasCookie = hasCookie;
1326: _response.setPrivateOrResinCache(privateCache);
1327:
1328: return id;
1329: }
1330:
1331: /**
1332: * Returns the session id in the HTTP request. The cookie has
1333: * priority over the URL. Because the webApp might be using
1334: * the cookie to change the page contents, the caching sets
1335: * vary: JSESSIONID.
1336: */
1337: public String getRequestedSessionId() {
1338: SessionManager manager = getSessionManager();
1339:
1340: if (manager != null && manager.enableSessionCookies()) {
1341: setVaryCookie(getSessionCookie(manager));
1342:
1343: String id = findSessionIdFromCookie();
1344: if (id != null) {
1345: _isSessionIdFromCookie = true;
1346: setHasCookie();
1347: return id;
1348: }
1349: }
1350:
1351: String id = findSessionIdFromUrl();
1352: if (id != null) {
1353: return id;
1354: }
1355:
1356: if (manager != null && manager.enableSessionCookies())
1357: return null;
1358: else
1359: return findSessionIdFromConnection();
1360: }
1361:
1362: /**
1363: * For SSL connections, use the SSL identifier.
1364: */
1365: public String findSessionIdFromConnection() {
1366: return null;
1367: }
1368:
1369: /**
1370: * Returns the session id in the HTTP request cookies.
1371: * Because the webApp might use the cookie to change
1372: * the page contents, the caching sets vary: JSESSIONID.
1373: */
1374: private String findSessionIdFromCookie() {
1375: SessionManager manager = getSessionManager();
1376:
1377: if (manager == null || !manager.enableSessionCookies())
1378: return null;
1379:
1380: Cookie cookie = findCookie(getSessionCookie(manager));
1381:
1382: if (cookie != null) {
1383: _isSessionIdFromCookie = true;
1384: return cookie.getValue();
1385: } else
1386: return null;
1387: }
1388:
1389: /**
1390: * Returns the session id in the HTTP request from the url.
1391: */
1392: private String findSessionIdFromUrl() {
1393: // server/1319
1394: // setVaryCookie(getSessionCookie(manager));
1395:
1396: String id = _invocation != null ? _invocation.getSessionId()
1397: : null;
1398: if (id != null)
1399: setHasCookie();
1400:
1401: return id;
1402: }
1403:
1404: public int getSessionGroup() {
1405: return _sessionGroup;
1406: }
1407:
1408: /**
1409: * Returns the current session.
1410: *
1411: * XXX: duplicated in RequestAdapter
1412: *
1413: * @param create true if a new session should be created
1414: *
1415: * @return the current session
1416: */
1417: private SessionImpl createSession(boolean create,
1418: boolean hasOldSession) {
1419: SessionManager manager = getSessionManager();
1420:
1421: if (manager == null)
1422: return null;
1423:
1424: String id = getRequestedSessionId();
1425:
1426: long now = Alarm.getCurrentTime();
1427:
1428: SessionImpl session;
1429:
1430: if (id != null && id.length() > 6) {
1431: session = manager.getSession(id, now, create,
1432: _isSessionIdFromCookie);
1433:
1434: if (session == null) {
1435: } else if (session.isValid()) {
1436: if (session != null) {
1437: setVaryCookie(getSessionCookie(manager));
1438: setHasCookie();
1439: }
1440:
1441: if (!session.getId().equals(id)
1442: && manager.enableSessionCookies())
1443: getResponse().setSessionId(session.getId());
1444:
1445: return session;
1446: }
1447: } else
1448: id = null;
1449:
1450: if (!create)
1451: return null;
1452:
1453: // Must accept old ids because different webApps in the same
1454: // server must share the same cookie
1455: //
1456: // But, if the session group doesn't match, then create a new
1457: // session.
1458:
1459: session = manager.createSession(id, now, this ,
1460: _isSessionIdFromCookie);
1461:
1462: if (session != null)
1463: setHasCookie();
1464:
1465: if (session.getId().equals(id))
1466: return session;
1467:
1468: if (manager.enableSessionCookies())
1469: getResponse().setSessionId(session.getId());
1470:
1471: return session;
1472: }
1473:
1474: /**
1475: * Returns the session manager.
1476: */
1477: protected final SessionManager getSessionManager() {
1478: WebApp webApp = getWebApp();
1479:
1480: if (webApp != null)
1481: return webApp.getSessionManager();
1482: else
1483: return null;
1484: }
1485:
1486: /**
1487: * Returns the session cookie.
1488: */
1489: protected final String getSessionCookie(SessionManager manager) {
1490: if (isSecure())
1491: return manager.getSSLCookieName();
1492: else
1493: return manager.getCookieName();
1494: }
1495:
1496: /**
1497: * Gets the authorization type
1498: */
1499: public String getAuthType() {
1500: Object login = getAttribute(com.caucho.server.security.AbstractAuthenticator.LOGIN_NAME);
1501:
1502: if (login instanceof X509Certificate)
1503: return CLIENT_CERT_AUTH;
1504:
1505: WebApp app = getWebApp();
1506:
1507: if (app != null && app.getLogin() != null
1508: && getUserPrincipal() != null)
1509: return app.getLogin().getAuthType();
1510: else
1511: return null;
1512: }
1513:
1514: /**
1515: * Internal logging return to get the remote user. If the request already
1516: * knows the user, get it, otherwise just return null.
1517: */
1518: public String getRemoteUser(boolean create) {
1519: if (_session == null)
1520: return null;
1521:
1522: Principal user = _session.getUser();
1523:
1524: if (user == null) {
1525: if (!create)
1526: return null;
1527:
1528: user = getUserPrincipal();
1529: }
1530:
1531: if (user != null)
1532: return user.getName();
1533: else
1534: return null;
1535: }
1536:
1537: /**
1538: * Authenticate the user.
1539: */
1540: public boolean authenticate() throws ServletException, IOException {
1541: Principal user = null;
1542:
1543: if (_session == null)
1544: getSession(false);
1545:
1546: // If the user object is already an attribute, return it.
1547: if (_session != null) {
1548: user = _session.getUser();
1549: if (user != null)
1550: return true;
1551: }
1552:
1553: WebApp app = getWebApp();
1554: if (app == null) {
1555: _response.sendError(HttpServletResponse.SC_FORBIDDEN);
1556: return false;
1557: }
1558:
1559: // If the authenticator can find the user, return it.
1560: AbstractLogin login = app.getLogin();
1561:
1562: if (login != null) {
1563: user = login.authenticate(this , getResponse(), app);
1564: if (user == null)
1565: return false;
1566:
1567: if (_session == null)
1568: getSession(true);
1569:
1570: _session.setUser(user);
1571: }
1572:
1573: if (user != null)
1574: return true;
1575: else {
1576: _response.sendError(HttpServletResponse.SC_FORBIDDEN);
1577: return false;
1578: }
1579: }
1580:
1581: /**
1582: * Gets the remote user from the authorization type
1583: */
1584: public String getRemoteUser() {
1585: Principal principal = getUserPrincipal();
1586:
1587: if (principal != null)
1588: return principal.getName();
1589: else
1590: return null;
1591: }
1592:
1593: /**
1594: * Returns the Principal representing the logged in user.
1595: */
1596: public Principal getUserPrincipal() {
1597: try {
1598: Principal user;
1599: user = (Principal) getAttribute(AbstractAuthenticator.LOGIN_NAME);
1600:
1601: if (user != null)
1602: return user;
1603:
1604: if (_session == null)
1605: getSession(false);
1606:
1607: // If the user object is already an attribute, return it.
1608: if (_session != null) {
1609: user = _session.getUser();
1610: if (user != null)
1611: return user;
1612: }
1613:
1614: WebApp app = getWebApp();
1615: if (app == null)
1616: return null;
1617:
1618: // If the authenticator can find the user, return it.
1619: AbstractLogin login = app.getLogin();
1620:
1621: if (login != null) {
1622: user = login.getUserPrincipal(this , getResponse(), app);
1623:
1624: if (user != null) {
1625: getSession(true);
1626:
1627: _session.setUser(user);
1628:
1629: _response.setPrivateCache(true);
1630: } else {
1631: // server/123h, server/1920
1632: // distinguishes between setPrivateCache and setPrivateOrResinCache
1633: // _response.setPrivateOrResinCache(true);
1634: }
1635: }
1636:
1637: return user;
1638: } catch (ServletException e) {
1639: log.log(Level.WARNING, e.toString(), e);
1640:
1641: return null;
1642: }
1643: }
1644:
1645: /**
1646: * Logs out the principal.
1647: */
1648: public void logout() {
1649: if (_session != null)
1650: _session.logout();
1651: }
1652:
1653: /**
1654: * Clear the principal from the request object.
1655: */
1656: public void logoutUserPrincipal() {
1657: if (_session != null)
1658: _session.logout();
1659: }
1660:
1661: /**
1662: * Sets the overriding role.
1663: */
1664: public String runAs(String role) {
1665: String oldRunAs = _runAs;
1666:
1667: _runAs = role;
1668:
1669: return oldRunAs;
1670: }
1671:
1672: /**
1673: * Returns true if the user represented by the current request
1674: * plays the named role.
1675: *
1676: * @param role the named role to test.
1677: * @return true if the user plays the role.
1678: */
1679: public boolean isUserInRole(String role) {
1680: HashMap<String, String> roleMap = _invocation
1681: .getSecurityRoleMap();
1682:
1683: if (roleMap != null) {
1684: String linkRole = roleMap.get(role);
1685:
1686: if (linkRole != null)
1687: role = linkRole;
1688: }
1689:
1690: if (_runAs != null)
1691: return _runAs.equals(role);
1692:
1693: WebApp app = getWebApp();
1694: AbstractLogin login = app == null ? null : app.getLogin();
1695:
1696: if (login == null)
1697: return false;
1698:
1699: boolean inRole = false;
1700:
1701: Principal user = getUserPrincipal();
1702:
1703: try {
1704: inRole = login.isUserInRole(this , getResponse(), app, user,
1705: role);
1706: } catch (ServletException e) {
1707: if (app != null)
1708: app.log(String.valueOf(e), e);
1709:
1710: log.log(Level.FINE, e.toString(), e);
1711: }
1712:
1713: if (log.isLoggable(Level.FINE)) {
1714: if (user == null)
1715: log.fine("no user for isUserInRole");
1716: else if (inRole)
1717: log.fine(user + " is in role: " + role);
1718: else
1719: log.fine("failed " + user + " in role: " + role);
1720: }
1721:
1722: return inRole;
1723: }
1724:
1725: /**
1726: * Returns true if the transport is secure.
1727: */
1728: public boolean isTransportSecure() {
1729: return _conn.isSecure();
1730: }
1731:
1732: /**
1733: * Returns the requests underlying read stream, e.g. the post stream.
1734: */
1735: public ReadStream getStream() throws IOException {
1736: return getStream(true);
1737: }
1738:
1739: /**
1740: * Returns the requests underlying read stream, e.g. the post stream.
1741: */
1742: public ReadStream getStream(boolean isReader) throws IOException {
1743: if (!_hasReadStream) {
1744: _hasReadStream = true;
1745:
1746: initStream(_readStream, _rawRead);
1747:
1748: if (isReader) {
1749: // Encoding is based on getCharacterEncoding.
1750: // getReader needs the encoding.
1751: String charEncoding = getCharacterEncoding();
1752: String javaEncoding = Encoding
1753: .getJavaName(charEncoding);
1754: _readStream.setEncoding(javaEncoding);
1755: }
1756:
1757: if (_expect100Continue) {
1758: _expect100Continue = false;
1759: _response.writeContinue();
1760: }
1761: }
1762:
1763: return _readStream;
1764: }
1765:
1766: /**
1767: * Returns the raw read buffer.
1768: */
1769: public byte[] getRawReadBuffer() {
1770: return _rawRead.getBuffer();
1771: }
1772:
1773: protected void skip() throws IOException {
1774: if (!_hasReadStream) {
1775: if (!initStream(_readStream, _rawRead))
1776: return;
1777:
1778: _hasReadStream = true;
1779: }
1780:
1781: while ((_readStream.skip(8192) > 0)) {
1782: }
1783: }
1784:
1785: /**
1786: * Initialize the read stream from the raw stream.
1787: */
1788: abstract protected boolean initStream(ReadStream readStream,
1789: ReadStream rawStream) throws IOException;
1790:
1791: /**
1792: * Returns the raw input stream.
1793: */
1794: public ReadStream getRawInput() {
1795: throw new UnsupportedOperationException(L
1796: .l("raw mode is not supported in this configuration"));
1797: }
1798:
1799: /**
1800: * Returns a stream for reading POST data.
1801: */
1802: public ServletInputStream getInputStream() throws IOException {
1803: if (_hasReader)
1804: throw new IllegalStateException(
1805: L
1806: .l("getInputStream() can't be called after getReader()"));
1807:
1808: _hasInputStream = true;
1809:
1810: ReadStream stream = getStream(false);
1811:
1812: _is.init(stream);
1813:
1814: return _is;
1815: }
1816:
1817: /**
1818: * Returns a Reader for the POST contents
1819: */
1820: public BufferedReader getReader() throws IOException {
1821: if (_hasInputStream)
1822: throw new IllegalStateException(
1823: L
1824: .l("getReader() can't be called after getInputStream()"));
1825:
1826: _hasReader = true;
1827:
1828: try {
1829: // bufferedReader is just an adapter to get the signature right.
1830: _bufferedReader.init(getStream(true));
1831:
1832: return _bufferedReader;
1833: } catch (java.nio.charset.UnsupportedCharsetException e) {
1834: throw new UnsupportedEncodingException(e.getMessage());
1835: }
1836: }
1837:
1838: /**
1839: * Returns an enumeration of the form names.
1840: */
1841: public Enumeration<String> getParameterNames() {
1842: if (_filledForm == null)
1843: _filledForm = parseQuery();
1844:
1845: return Collections.enumeration(_filledForm.keySet());
1846: }
1847:
1848: /**
1849: * Returns a map of the form.
1850: */
1851: public Map<String, String[]> getParameterMap() {
1852: if (_filledForm == null)
1853: _filledForm = parseQuery();
1854:
1855: return Collections.unmodifiableMap(_filledForm);
1856: }
1857:
1858: /**
1859: * Returns the form's values for the given name.
1860: *
1861: * @param name key in the form
1862: * @return value matching the key
1863: */
1864: public String[] getParameterValues(String name) {
1865: if (_filledForm == null)
1866: _filledForm = parseQuery();
1867:
1868: return (String[]) _filledForm.get(name);
1869: }
1870:
1871: /**
1872: * Returns the form primary value for the given name.
1873: */
1874: public String getParameter(String name) {
1875: String[] values = getParameterValues(name);
1876: if (values == null || values.length == 0)
1877: return null;
1878:
1879: return values[0];
1880: }
1881:
1882: /**
1883: * Parses the query, either from the GET or the post.
1884: *
1885: * <p/>The character encoding is somewhat tricky. If it's a post, then
1886: * assume the encoded form uses the same encoding as
1887: * getCharacterEncoding().
1888: *
1889: * <p/>If the request doesn't provide the encoding, use the
1890: * character-encoding parameter from the webApp.
1891: *
1892: * <p/>Otherwise use the default system encoding.
1893: */
1894: private HashMapImpl<String, String[]> parseQuery() {
1895: try {
1896: _form.clear();
1897:
1898: String query = getQueryString();
1899: CharSegment contentType = getContentTypeBuffer();
1900:
1901: if (query == null && contentType == null)
1902: return _form;
1903:
1904: String charEncoding = getCharacterEncoding();
1905: if (charEncoding == null)
1906: charEncoding = (String) getAttribute(CAUCHO_CHAR_ENCODING);
1907: if (charEncoding == null)
1908: charEncoding = (String) getAttribute(CHAR_ENCODING);
1909: if (charEncoding == null) {
1910: Locale locale = (Locale) getAttribute(FORM_LOCALE);
1911: if (locale != null)
1912: charEncoding = Encoding.getMimeName(locale);
1913: }
1914:
1915: if (query != null) {
1916: String queryEncoding = charEncoding;
1917:
1918: if (queryEncoding == null && _server != null)
1919: queryEncoding = _server.getURLCharacterEncoding();
1920:
1921: if (queryEncoding == null)
1922: queryEncoding = CharacterEncoding
1923: .getLocalEncoding();
1924:
1925: String javaEncoding = Encoding
1926: .getJavaName(queryEncoding);
1927:
1928: _formParser.parseQueryString(_form, query,
1929: javaEncoding, true);
1930: }
1931:
1932: if (charEncoding == null)
1933: charEncoding = CharacterEncoding.getLocalEncoding();
1934:
1935: String javaEncoding = Encoding.getJavaName(charEncoding);
1936:
1937: if (contentType == null
1938: || !"POST".equalsIgnoreCase(getMethod())) {
1939: }
1940:
1941: else if (contentType
1942: .startsWith("application/x-www-form-urlencoded")) {
1943: _formParser.parsePostData(_form, getInputStream(),
1944: javaEncoding);
1945: }
1946:
1947: else if (getWebApp().doMultipartForm()
1948: && contentType.startsWith("multipart/form-data")) {
1949: int length = contentType.length();
1950: int i = contentType.indexOf("boundary=");
1951:
1952: if (i < 0)
1953: return _form;
1954:
1955: long formUploadMax = getWebApp().getFormUploadMax();
1956:
1957: Object uploadMax = getAttribute("caucho.multipart.form.upload-max");
1958: if (uploadMax instanceof Number)
1959: formUploadMax = ((Number) uploadMax).longValue();
1960:
1961: // XXX: should this be an error?
1962: if (formUploadMax >= 0
1963: && formUploadMax < getLongContentLength()) {
1964: setAttribute(
1965: "caucho.multipart.form.error",
1966: L
1967: .l(
1968: "Multipart form upload of '{0}' bytes was too large.",
1969: String
1970: .valueOf(getLongContentLength())));
1971: setAttribute("caucho.multipart.form.error.size",
1972: new Long(getLongContentLength()));
1973:
1974: return _form;
1975: }
1976:
1977: i += "boundary=".length();
1978: char ch = contentType.charAt(i);
1979: CharBuffer boundary = new CharBuffer();
1980: if (ch == '\'') {
1981: for (i++; i < length
1982: && contentType.charAt(i) != '\''; i++)
1983: boundary.append(contentType.charAt(i));
1984: } else if (ch == '\"') {
1985: for (i++; i < length
1986: && contentType.charAt(i) != '\"'; i++)
1987: boundary.append(contentType.charAt(i));
1988: } else {
1989: for (; i < length
1990: && (ch = contentType.charAt(i)) != ' '
1991: && ch != ';'; i++) {
1992: boundary.append(ch);
1993: }
1994: }
1995:
1996: try {
1997: MultipartForm.parsePostData(_form,
1998: getStream(false), boundary.toString(),
1999: this , javaEncoding, formUploadMax);
2000: } catch (IOException e) {
2001: log.log(Level.FINE, e.toString(), e);
2002: setAttribute("caucho.multipart.form.error", e
2003: .getMessage());
2004: }
2005: }
2006: } catch (IOException e) {
2007: log.log(Level.FINE, e.toString(), e);
2008: }
2009:
2010: return _form;
2011: }
2012:
2013: // request attributes
2014:
2015: /**
2016: * Returns an enumeration of the request attribute names.
2017: */
2018: public Enumeration<String> getAttributeNames() {
2019: return Collections.enumeration(_attributes.keySet());
2020: }
2021:
2022: /**
2023: * Returns the value of the named request attribute.
2024: *
2025: * @param name the attribute name.
2026: *
2027: * @return the attribute value.
2028: */
2029: public Object getAttribute(String name) {
2030: return _attributes.get(name);
2031: }
2032:
2033: /**
2034: * Sets the value of the named request attribute.
2035: *
2036: * @param name the attribute name.
2037: * @param value the new attribute value.
2038: */
2039: public void setAttribute(String name, Object value) {
2040: if (value != null) {
2041: Object oldValue = _attributes.put(name, value);
2042:
2043: for (int i = 0; i < _attributeListeners.length; i++) {
2044: ServletRequestAttributeEvent event;
2045:
2046: if (oldValue != null) {
2047: event = new ServletRequestAttributeEvent(
2048: getWebApp(), this , name, oldValue);
2049:
2050: _attributeListeners[i].attributeReplaced(event);
2051: } else {
2052: event = new ServletRequestAttributeEvent(
2053: getWebApp(), this , name, value);
2054:
2055: _attributeListeners[i].attributeAdded(event);
2056: }
2057: }
2058: } else
2059: removeAttribute(name);
2060: }
2061:
2062: /**
2063: * Removes the value of the named request attribute.
2064: *
2065: * @param name the attribute name.
2066: */
2067: public void removeAttribute(String name) {
2068: Object oldValue = _attributes.remove(name);
2069:
2070: for (int i = 0; i < _attributeListeners.length; i++) {
2071: ServletRequestAttributeEvent event;
2072:
2073: event = new ServletRequestAttributeEvent(getWebApp(), this ,
2074: name, oldValue);
2075:
2076: _attributeListeners[i].attributeRemoved(event);
2077: }
2078:
2079: if (oldValue instanceof ScopeRemoveListener) {
2080: ((ScopeRemoveListener) oldValue).removeEvent(this , name);
2081: }
2082: }
2083:
2084: /**
2085: * Returns a request dispatcher relative to the current request.
2086: *
2087: * @param path the relative uri to the new servlet.
2088: */
2089: public RequestDispatcher getRequestDispatcher(String path) {
2090: if (path == null || path.length() == 0)
2091: return null;
2092: else if (path.charAt(0) == '/')
2093: return getWebApp().getRequestDispatcher(path);
2094: else {
2095: CharBuffer cb = new CharBuffer();
2096:
2097: ServletContext app = getWebApp();
2098:
2099: String servletPath = getPageServletPath();
2100: if (servletPath != null)
2101: cb.append(servletPath);
2102: String pathInfo = getPagePathInfo();
2103: if (pathInfo != null)
2104: cb.append(pathInfo);
2105:
2106: int p = cb.lastIndexOf('/');
2107: if (p >= 0)
2108: cb.setLength(p);
2109: cb.append('/');
2110: cb.append(path);
2111:
2112: if (app != null)
2113: return app.getRequestDispatcher(cb.toString());
2114:
2115: return app.getRequestDispatcher(cb.toString());
2116: }
2117: }
2118:
2119: /*
2120: * jsdk 2.2
2121: */
2122:
2123: public Locale getLocale() {
2124: fillLocales();
2125:
2126: return _locales.get(0);
2127: }
2128:
2129: public Enumeration<Locale> getLocales() {
2130: fillLocales();
2131:
2132: return Collections.enumeration(_locales);
2133: }
2134:
2135: /**
2136: * Fill the locale array from the request's headers.
2137: */
2138: private void fillLocales() {
2139: if (_locales.size() > 0)
2140: return;
2141:
2142: Enumeration headers = getHeaders("Accept-Language");
2143: if (headers == null) {
2144: _locales.add(Locale.getDefault());
2145: return;
2146: }
2147:
2148: CharBuffer cb = _cb;
2149: while (headers.hasMoreElements()) {
2150: String header = (String) headers.nextElement();
2151: StringCharCursor cursor = new StringCharCursor(header);
2152:
2153: while (cursor.current() != cursor.DONE) {
2154: char ch;
2155: for (; Character.isWhitespace(cursor.current()); cursor
2156: .next()) {
2157: }
2158:
2159: cb.clear();
2160: for (; (ch = cursor.current()) >= 'a' && ch <= 'z'
2161: || ch >= 'A' && ch <= 'Z' || ch >= '0'
2162: && ch <= '0'; cursor.next()) {
2163: cb.append(cursor.current());
2164: }
2165:
2166: String language = cb.toString();
2167: String country = "";
2168: String var = "";
2169:
2170: if (cursor.current() == '_' || cursor.current() == '-') {
2171: cb.clear();
2172: for (cursor.next(); (ch = cursor.current()) >= 'a'
2173: && ch <= 'z' || ch >= 'A' && ch <= 'Z'
2174: || ch >= '0' && ch <= '9'; cursor.next()) {
2175: cb.append(cursor.current());
2176: }
2177: country = cb.toString();
2178: }
2179:
2180: if (language.length() > 0) {
2181: Locale locale = new Locale(language, country);
2182: _locales.add(locale);
2183: }
2184:
2185: for (; cursor.current() != cursor.DONE
2186: && cursor.current() != ','; cursor.next()) {
2187: }
2188: cursor.next();
2189: }
2190: }
2191:
2192: if (_locales.size() == 0)
2193: _locales.add(Locale.getDefault());
2194: }
2195:
2196: /**
2197: * Returns true if the request is secure.
2198: */
2199: public boolean isSecure() {
2200: return _conn.isSecure();
2201: }
2202:
2203: // internal goodies
2204:
2205: /**
2206: * Returns the request's invocation.
2207: */
2208: public final Invocation getInvocation() {
2209: return _invocation;
2210: }
2211:
2212: /**
2213: * Sets the request's invocation.
2214: */
2215: public final void setInvocation(Invocation invocation) {
2216: _invocation = invocation;
2217:
2218: WebApp app = invocation.getWebApp();
2219: if (app != null)
2220: _attributeListeners = app.getRequestAttributeListeners();
2221: }
2222:
2223: /**
2224: * Sets the start time to the current time.
2225: */
2226: protected final void setStartTime() {
2227: _startTime = Alarm.getExactTime();
2228: }
2229:
2230: /**
2231: * Returns the date for the current request.
2232: */
2233: public final long getStartTime() {
2234: return _startTime;
2235: }
2236:
2237: /**
2238: * Returns the servlet name.
2239: */
2240: public String getServletName() {
2241: return _invocation.getServletName();
2242: }
2243:
2244: /**
2245: * Returns the invocation's webApp.
2246: */
2247: public final WebApp getWebApp() {
2248: if (_invocation != null)
2249: return _invocation.getWebApp();
2250: else
2251: return null;
2252: }
2253:
2254: /**
2255: * Returns the log buffer.
2256: */
2257: public final byte[] getLogBuffer() {
2258: return _logBuffer;
2259: }
2260:
2261: /**
2262: * Returns true for the top-level request, but false for any include()
2263: * or forward()
2264: */
2265: public boolean isTop() {
2266: return false;
2267: }
2268:
2269: /**
2270: * Adds a file to be removed at the end.
2271: */
2272: public void addCloseOnExit(Path path) {
2273: _closeOnExit.add(path);
2274: }
2275:
2276: /**
2277: * Returns the depth of the request calls.
2278: */
2279: public int getRequestDepth(int depth) {
2280: return depth + 1;
2281: }
2282:
2283: public int getRequestDepth() {
2284: return 0;
2285: }
2286:
2287: /**
2288: * Handles a comet-style resume.
2289: *
2290: * @return true if the connection should stay open (keepalive)
2291: */
2292: public boolean handleResume() throws IOException {
2293: return false;
2294: }
2295:
2296: /**
2297: * Kills the keepalive.
2298: */
2299: public void killKeepalive() {
2300: _keepalive = false;
2301:
2302: /*
2303: ConnectionController controller = _conn.getController();
2304: if (controller != null)
2305: controller.close();
2306: */
2307: }
2308:
2309: /**
2310: * Returns true if the keepalive is active.
2311: */
2312: protected boolean isKeepalive() {
2313: return _keepalive;
2314: }
2315:
2316: /**
2317: * Returns true if keepalives are allowed.
2318: *
2319: * This method should only be called once, when the response is
2320: * deciding whether to send the Connection: close (or 'Q' vs 'X'),
2321: * after that, the calling routines should call isKeepalive() to
2322: * see what the decision was.
2323: *
2324: * Otherwise, the browser might see a keepalive when the final decision
2325: * is to close the connection.
2326: */
2327: public boolean allowKeepalive() {
2328: if (!_keepalive)
2329: return false;
2330:
2331: TcpConnection tcpConn = _tcpConn;
2332:
2333: if (tcpConn == null)
2334: return true;
2335:
2336: if (!tcpConn.allowKeepalive())
2337: _keepalive = false;
2338:
2339: return _keepalive;
2340: }
2341:
2342: /**
2343: * Restarts the server.
2344: */
2345: protected void restartServer() throws IOException, ServletException {
2346: HttpServletResponse res = (HttpServletResponse) getResponse();
2347:
2348: res.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
2349:
2350: _server.update();
2351: }
2352:
2353: void saveSession() {
2354: SessionImpl session = _session;
2355: if (session != null)
2356: session.save();
2357: }
2358:
2359: /**
2360: * Cleans up at the end of the request
2361: */
2362: public void finish() throws IOException {
2363: try {
2364: SecurityContextProvider oldProvider = _oldProvider;
2365: _oldProvider = null;
2366:
2367: SecurityContext.setProvider(oldProvider);
2368:
2369: SessionImpl session = _session;
2370:
2371: // server/0219
2372: // _invocation = null;
2373:
2374: if (session != null)
2375: session.finish();
2376:
2377: cleanup();
2378: } finally {
2379: for (int i = _closeOnExit.size() - 1; i >= 0; i--) {
2380: Path path = _closeOnExit.get(i);
2381:
2382: try {
2383: path.remove();
2384: } catch (Throwable e) {
2385: log.log(Level.FINE, e.toString(), e);
2386: }
2387: }
2388: _closeOnExit.clear();
2389:
2390: if (_tcpConn != null)
2391: _tcpConn.endActive();
2392: }
2393: }
2394:
2395: public void cleanup() {
2396: ConnectionController comet = getConnection().getController();
2397:
2398: if (comet == null) {
2399: _session = null;
2400:
2401: if (_attributes.size() > 0) {
2402: for (Map.Entry<String, Object> entry : _attributes
2403: .entrySet()) {
2404: Object value = entry.getValue();
2405:
2406: if (value instanceof ScopeRemoveListener) {
2407: ((ScopeRemoveListener) value).removeEvent(this ,
2408: entry.getKey());
2409: }
2410: }
2411:
2412: _attributes.clear();
2413: }
2414: }
2415: }
2416:
2417: protected String dbgId() {
2418: return "Tcp[" + _conn.getId() + "] ";
2419: }
2420:
2421: static {
2422: _headerCodes = new CaseInsensitiveIntMap();
2423:
2424: TOKEN = new boolean[256];
2425: VALUE = new boolean[256];
2426:
2427: for (int i = 0; i < 256; i++) {
2428: TOKEN[i] = true;
2429: }
2430:
2431: for (int i = 0; i < 32; i++) {
2432: TOKEN[i] = false;
2433: }
2434:
2435: for (int i = 127; i < 256; i++) {
2436: TOKEN[i] = false;
2437: }
2438:
2439: //TOKEN['('] = false;
2440: //TOKEN[')'] = false;
2441: //TOKEN['<'] = false;
2442: //TOKEN['>'] = false;
2443: //TOKEN['@'] = false;
2444: TOKEN[','] = false;
2445: TOKEN[';'] = false;
2446: //TOKEN[':'] = false;
2447: TOKEN['\\'] = false;
2448: TOKEN['"'] = false;
2449: //TOKEN['/'] = false;
2450: //TOKEN['['] = false;
2451: //TOKEN[']'] = false;
2452: //TOKEN['?'] = false;
2453: TOKEN['='] = false;
2454: //TOKEN['{'] = false;
2455: //TOKEN['}'] = false;
2456: TOKEN[' '] = false;
2457:
2458: System.arraycopy(TOKEN, 0, VALUE, 0, TOKEN.length);
2459:
2460: VALUE['='] = true;
2461: }
2462: }
|