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.http;
0031:
0032: import com.caucho.log.Log;
0033: import com.caucho.server.cluster.Server;
0034: import com.caucho.server.connection.AbstractHttpRequest;
0035: import com.caucho.server.connection.Connection;
0036: import com.caucho.server.connection.ConnectionController;
0037: import com.caucho.server.dispatch.BadRequestException;
0038: import com.caucho.server.dispatch.DispatchServer;
0039: import com.caucho.server.dispatch.Invocation;
0040: import com.caucho.server.dispatch.InvocationDecoder;
0041: import com.caucho.server.port.ServerRequest;
0042: import com.caucho.server.port.TcpConnection;
0043: import com.caucho.server.cluster.*;
0044: import com.caucho.server.webapp.*;
0045: import com.caucho.util.CharBuffer;
0046: import com.caucho.util.CharSegment;
0047: import com.caucho.vfs.ClientDisconnectException;
0048: import com.caucho.vfs.QSocket;
0049: import com.caucho.vfs.ReadStream;
0050:
0051: import java.io.IOException;
0052: import java.io.InterruptedIOException;
0053: import java.security.cert.X509Certificate;
0054: import java.util.ArrayList;
0055: import java.util.Collections;
0056: import java.util.Enumeration;
0057: import java.util.logging.Level;
0058: import java.util.logging.Logger;
0059:
0060: /**
0061: * Handles a new request from an HTTP connection.
0062: */
0063: public class HttpRequest extends AbstractHttpRequest implements
0064: ServerRequest {
0065: static final Logger log = Logger.getLogger(HttpRequest.class
0066: .getName());
0067:
0068: static final int HTTP_0_9 = 0x0009;
0069: static final int HTTP_1_0 = 0x0100;
0070: static final int HTTP_1_1 = 0x0101;
0071:
0072: static final CharBuffer _getCb = new CharBuffer("GET");
0073: static final CharBuffer _headCb = new CharBuffer("HEAD");
0074: static final CharBuffer _postCb = new CharBuffer("POST");
0075:
0076: static final char[] _hostCb = "Host".toCharArray();
0077: static final char[] _userAgentCb = "User-Agent".toCharArray();
0078:
0079: static final CharBuffer _http11Cb = new CharBuffer("HTTP/1.1");
0080: static final CharBuffer _http10Cb = new CharBuffer("HTTP/1.0");
0081:
0082: private String _scheme; // "http:" or "https:"
0083: private boolean _isSecure;
0084:
0085: private CharBuffer _method; // "GET"
0086: private String _methodString;
0087:
0088: private CharBuffer _uriHost; // www.caucho.com:8080
0089: private CharSequence _host;
0090: private CharBuffer _hostBuffer = new CharBuffer();
0091:
0092: private final byte[] _uri; // "/path/test.jsp/Junk?query=7"
0093: private int _uriLength;
0094:
0095: private int _urlLengthMax = 8192;
0096:
0097: private CharBuffer _protocol; // "HTTP/1.0"
0098: private int _version;
0099:
0100: private final InvocationKey _invocationKey = new InvocationKey();
0101:
0102: private final char[] _headerBuffer = new char[16 * 1024];
0103:
0104: private CharSegment[] _headerKeys;
0105: private CharSegment[] _headerValues;
0106: private int _headerCapacity = 256;
0107: private int _headerSize;
0108:
0109: private ChunkedInputStream _chunkedInputStream = new ChunkedInputStream();
0110: private ContentLengthStream _contentLengthStream = new ContentLengthStream();
0111:
0112: private ErrorPageManager _errorManager = new ErrorPageManager();
0113:
0114: private boolean _initAttributes;
0115:
0116: /**
0117: * Creates a new HttpRequest. New connections reuse the request.
0118: *
0119: * @param server the owning server.
0120: */
0121: public HttpRequest(DispatchServer server, Connection conn) {
0122: super (server, conn);
0123:
0124: _response = new HttpResponse(this );
0125: _response.init(conn.getWriteStream());
0126:
0127: // _urlLengthMax = server.getURLLengthMax();
0128:
0129: // XXX: response.setIgnoreClientDisconnect(server.getIgnoreClientDisconnect());
0130:
0131: _uri = new byte[_urlLengthMax];
0132:
0133: _method = new CharBuffer();
0134: _uriHost = new CharBuffer();
0135: _protocol = new CharBuffer();
0136:
0137: _headerCapacity = 256;
0138: _headerSize = 0;
0139: _headerKeys = new CharSegment[_headerCapacity];
0140: _headerValues = new CharSegment[_headerCapacity];
0141: for (int i = 0; i < _headerCapacity; i++) {
0142: _headerKeys[i] = new CharSegment();
0143: _headerValues[i] = new CharSegment();
0144: }
0145: }
0146:
0147: /**
0148: * Return true if the request waits for a read before beginning.
0149: */
0150: public final boolean isWaitForRead() {
0151: return true;
0152: }
0153:
0154: /**
0155: * Handles a new HTTP request.
0156: *
0157: * <p>Note: ClientDisconnectException must be rethrown to
0158: * the caller.
0159: *
0160: * @return true if the connection should stay open (keepalive)
0161: */
0162: public boolean handleRequest() throws IOException {
0163: boolean hasRequest = false;
0164: try {
0165: start();
0166: _response.start();
0167:
0168: try {
0169: try {
0170: if (!readRequest(_rawRead)) {
0171: if (log.isLoggable(Level.FINE))
0172: log.fine(dbgId() + "read timeout");
0173:
0174: return false;
0175: }
0176:
0177: setStartTime();
0178:
0179: hasRequest = true;
0180:
0181: _isSecure = _conn.isSecure()
0182: || _conn.getLocalPort() == 443;
0183:
0184: if (_protocol.length() == 0)
0185: _protocol.append("HTTP/0.9");
0186:
0187: if (log.isLoggable(Level.FINE)) {
0188: log.fine(dbgId() + _method + " "
0189: + new String(_uri, 0, _uriLength) + " "
0190: + _protocol);
0191: log.fine(dbgId() + "Remote-IP: "
0192: + _conn.getRemoteHost() + ":"
0193: + _conn.getRemotePort());
0194: }
0195:
0196: parseHeaders(_rawRead);
0197:
0198: if (getVersion() >= HTTP_1_1 && isForce10()) {
0199: _protocol.clear();
0200: _protocol.append("HTTP/1.0");
0201: _version = HTTP_1_0;
0202: }
0203: } catch (ClientDisconnectException e) {
0204: throw e;
0205: } catch (Throwable e) {
0206: log.log(Level.FINER, e.toString(), e);
0207:
0208: throw new BadRequestException(String.valueOf(e));
0209: }
0210:
0211: CharSequence host = getHost();
0212: if (host == null && getVersion() >= HTTP_1_1)
0213: throw new BadRequestException(
0214: "HTTP/1.1 requires host");
0215:
0216: String ipHost = _conn.getVirtualHost();
0217: if (ipHost != null)
0218: host = ipHost;
0219:
0220: _invocationKey.init(_isSecure, host, _conn
0221: .getLocalPort(), _uri, _uriLength);
0222:
0223: Invocation invocation;
0224:
0225: invocation = _server.getInvocation(_invocationKey);
0226:
0227: if (invocation == null) {
0228: invocation = _server.createInvocation();
0229: invocation.setSecure(_isSecure);
0230:
0231: if (host != null) {
0232: String hostName = host.toString().toLowerCase();
0233:
0234: invocation.setHost(hostName);
0235: invocation.setPort(_conn.getLocalPort());
0236:
0237: // Default host name if the host doesn't have a canonical
0238: // name
0239: int p = hostName.indexOf(':');
0240: if (p > 0)
0241: invocation.setHostName(hostName.substring(
0242: 0, p));
0243: else
0244: invocation.setHostName(hostName);
0245: }
0246:
0247: InvocationDecoder decoder = _server
0248: .getInvocationDecoder();
0249:
0250: decoder.splitQueryAndUnescape(invocation, _uri,
0251: _uriLength);
0252:
0253: if (_server.isModified()) {
0254: _server.logModified(log);
0255:
0256: _invocation = invocation;
0257: if (_server instanceof Server)
0258: _invocation.setWebApp(((Server) _server)
0259: .getDefaultWebApp());
0260:
0261: restartServer();
0262: return false;
0263: }
0264:
0265: invocation = _server.buildInvocation(_invocationKey
0266: .clone(), invocation);
0267: }
0268:
0269: invocation = invocation.getRequestInvocation(this );
0270:
0271: setInvocation(invocation);
0272:
0273: invocation.service(this , _response);
0274: } finally {
0275: finish();
0276: }
0277: } catch (ClientDisconnectException e) {
0278: _response.killCache();
0279:
0280: throw e;
0281: } catch (Throwable e) {
0282: log.log(Level.FINE, e.toString(), e);
0283:
0284: _response.killCache();
0285: killKeepalive();
0286:
0287: try {
0288: _errorManager.sendServletError(e, this , _response);
0289: } catch (ClientDisconnectException e1) {
0290: throw e1;
0291: } catch (Throwable e1) {
0292: log.log(Level.FINE, e1.toString(), e1);
0293: }
0294:
0295: if (_server instanceof Server) {
0296: WebApp webApp = ((Server) _server).getDefaultWebApp();
0297: if (webApp != null)
0298: webApp.accessLog(this , _response);
0299: }
0300:
0301: return false;
0302: } finally {
0303: if (hasRequest)
0304: _response.finish();
0305: else
0306: super .finish();
0307: }
0308:
0309: if (log.isLoggable(Level.FINE)) {
0310: log.fine(dbgId()
0311: + (isKeepalive() ? "keepalive" : "no-keepalive"));
0312: }
0313:
0314: return isKeepalive();
0315: }
0316:
0317: /**
0318: * Handles a comet-style resume.
0319: *
0320: * @return true if the connection should stay open (keepalive)
0321: */
0322: @Override
0323: public boolean handleResume() throws IOException {
0324: boolean isResume = false;
0325: ConnectionController controller = null;
0326:
0327: try {
0328: try {
0329: setStartTime();
0330:
0331: Connection conn = getConnection();
0332: controller = conn.getController();
0333:
0334: if (controller == null) {
0335: killKeepalive();
0336: } else if (_invocation.doResume(this , _response)) {
0337: controller = null;
0338: isResume = true;
0339: } else
0340: killKeepalive();
0341: } finally {
0342: finish();
0343:
0344: if (controller != null)
0345: controller.close();
0346: }
0347: } catch (ClientDisconnectException e) {
0348: _response.killCache();
0349: isResume = false;
0350:
0351: throw e;
0352: } catch (Throwable e) {
0353: log.log(Level.FINE, e.toString(), e);
0354:
0355: isResume = false;
0356: _response.killCache();
0357: killKeepalive();
0358:
0359: return false;
0360: } finally {
0361: _response.finish();
0362: }
0363:
0364: if (log.isLoggable(Level.FINE)) {
0365: log.fine(dbgId()
0366: + (isKeepalive() ? "keepalive" : "no-keepalive"));
0367: }
0368:
0369: return isResume && controller == null;
0370: }
0371:
0372: /**
0373: * There are some bogus clients that can't deal with HTTP/1.1 even
0374: * though they advertise it.
0375: */
0376: private boolean isForce10() {
0377: return false;
0378: }
0379:
0380: /**
0381: * Returns true for the top-level request, but false for any include()
0382: * or forward()
0383: */
0384: public boolean isTop() {
0385: return true;
0386: }
0387:
0388: protected boolean checkLogin() {
0389: return true;
0390: }
0391:
0392: /**
0393: * Clear the request variables in preparation for a new request.
0394: *
0395: * @param s the read stream for the request
0396: */
0397: protected void start() throws IOException {
0398: super .start();
0399:
0400: _method.clear();
0401: _methodString = null;
0402: _protocol.clear();
0403: _uriLength = 0;
0404: _uriHost.clear();
0405: _host = null;
0406:
0407: _headerSize = 0;
0408: _initAttributes = false;
0409: }
0410:
0411: /**
0412: * Returns true for a secure connection.
0413: */
0414: public boolean isSecure() {
0415: return _isSecure;
0416: }
0417:
0418: /**
0419: * Read the first line of a request:
0420: *
0421: * GET [http://www.caucho.com[:80]]/path [HTTP/1.x]
0422: *
0423: * @return true if the request is valid
0424: */
0425: private boolean readRequest(ReadStream s) throws IOException {
0426: int i = 0;
0427:
0428: byte[] readBuffer = s.getBuffer();
0429: int readOffset = s.getOffset();
0430: int readLength = s.getLength();
0431: int ch;
0432:
0433: if (readOffset >= readLength) {
0434: try {
0435: if ((readLength = s.fillBuffer()) < 0)
0436: return false;
0437: } catch (InterruptedIOException e) {
0438: log.fine(dbgId() + "keepalive timeout");
0439: return false;
0440: }
0441: readOffset = 0;
0442: }
0443: ch = readBuffer[readOffset++];
0444:
0445: // conn.setAccessTime(getDate());
0446:
0447: // skip leading whitespace
0448: while (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
0449: if (readOffset >= readLength) {
0450: if ((readLength = s.fillBuffer()) < 0)
0451: return false;
0452:
0453: readOffset = 0;
0454: }
0455: ch = readBuffer[readOffset++];
0456: }
0457:
0458: char[] buffer = _method.getBuffer();
0459: int length = buffer.length;
0460: int offset = 0;
0461:
0462: // scan method
0463: while (true) {
0464: if (length <= offset) {
0465: } else if (ch >= 'a' && ch <= 'z')
0466: buffer[offset++] = ((char) (ch + 'A' - 'a'));
0467: else if (ch > ' ')
0468: buffer[offset++] = (char) ch;
0469: else
0470: break;
0471:
0472: if (readLength <= readOffset) {
0473: if ((readLength = s.fillBuffer()) < 0)
0474: return false;
0475:
0476: readOffset = 0;
0477: }
0478: ch = readBuffer[readOffset++];
0479: }
0480:
0481: _method.setLength(offset);
0482:
0483: // skip whitespace
0484: while (ch == ' ' || ch == '\t') {
0485: if (readOffset >= readLength) {
0486: if ((readLength = s.fillBuffer()) < 0)
0487: return false;
0488:
0489: readOffset = 0;
0490: }
0491:
0492: ch = readBuffer[readOffset++];
0493: }
0494:
0495: byte[] uriBuffer = _uri;
0496: int uriLength = 0;
0497:
0498: // skip 'http:'
0499: if (ch != '/') {
0500: while (ch > ' ' && ch != '/') {
0501: if (readOffset >= readLength) {
0502: if ((readLength = s.fillBuffer()) < 0)
0503: return false;
0504: readOffset = 0;
0505: }
0506: ch = readBuffer[readOffset++];
0507: }
0508:
0509: if (readOffset >= readLength) {
0510: if ((readLength = s.fillBuffer()) < 0) {
0511: if (ch == '/') {
0512: uriBuffer[uriLength++] = (byte) ch;
0513: _uriLength = uriLength;
0514: }
0515:
0516: return true;
0517: }
0518: readOffset = 0;
0519: }
0520:
0521: int ch1 = readBuffer[readOffset++];
0522:
0523: if (ch1 != '/') {
0524: uriBuffer[uriLength++] = (byte) ch;
0525: ch = ch1;
0526: } else {
0527: // read host
0528: host: while (true) {
0529: if (readOffset >= readLength) {
0530: if ((readLength = s.fillBuffer()) < 0) {
0531: return true;
0532: }
0533: readOffset = 0;
0534: }
0535: ch = readBuffer[readOffset++];
0536:
0537: switch (ch) {
0538: case ' ':
0539: case '\t':
0540: case '\n':
0541: case '\r':
0542: break host;
0543:
0544: case '?':
0545: break host;
0546:
0547: case '/':
0548: break host;
0549:
0550: default:
0551: _uriHost.append((char) ch);
0552: break;
0553: }
0554: }
0555: }
0556: }
0557:
0558: // read URI
0559: uri: while (true) {
0560: switch (ch) {
0561: case ' ':
0562: case '\t':
0563: case '\n':
0564: case '\r':
0565: break uri;
0566:
0567: default:
0568: // There's no check for overrunning the length because
0569: // allowing resizing would allow a DOS memory attack and
0570: // also lets us save a bit of efficiency.
0571: uriBuffer[uriLength++] = (byte) ch;
0572: break;
0573: }
0574:
0575: if (readOffset >= readLength) {
0576: readOffset = 0;
0577: if ((readLength = s.fillBuffer()) < 0) {
0578: _uriLength = uriLength;
0579: return true;
0580: }
0581: }
0582: ch = readBuffer[readOffset++];
0583: }
0584:
0585: _uriLength = uriLength;
0586:
0587: // skip whitespace
0588: while (ch == ' ' || ch == '\t') {
0589: if (readOffset >= readLength) {
0590: readOffset = 0;
0591: if ((readLength = s.fillBuffer()) < 0)
0592: return true;
0593: }
0594: ch = readBuffer[readOffset++];
0595: }
0596:
0597: buffer = _protocol.getBuffer();
0598: length = buffer.length;
0599: offset = 0;
0600: // scan protocol
0601: while (ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n') {
0602: if (offset >= length) {
0603: } else if (ch >= 'a' && ch <= 'z')
0604: buffer[offset++] = ((char) (ch + 'A' - 'a'));
0605: else
0606: buffer[offset++] = (char) ch;
0607:
0608: if (readOffset >= readLength) {
0609: readOffset = 0;
0610: if ((readLength = s.fillBuffer()) < 0) {
0611: _protocol.setLength(offset);
0612: return true;
0613: }
0614: }
0615: ch = readBuffer[readOffset++];
0616: }
0617: _protocol.setLength(offset);
0618:
0619: if (offset != 8) {
0620: _protocol.append("HTTP/0.9");
0621: _version = HTTP_0_9;
0622: } else if (buffer[7] == '1') // && _protocol.equals(_http11Cb))
0623: _version = HTTP_1_1;
0624: else if (buffer[7] == '0') // && _protocol.equals(_http10Cb))
0625: _version = HTTP_1_0;
0626: else
0627: _version = HTTP_0_9;
0628:
0629: // skip to end of line
0630: while (ch != '\n') {
0631: if (readOffset >= readLength) {
0632: if ((readLength = s.fillBuffer()) < 0)
0633: return true;
0634: readOffset = 0;
0635: }
0636: ch = readBuffer[readOffset++];
0637: }
0638:
0639: s.setOffset(readOffset);
0640:
0641: return true;
0642: }
0643:
0644: /**
0645: * Parses headers from the read stream.
0646: *
0647: * @param s the input read stream
0648: */
0649: private void parseHeaders(ReadStream s) throws IOException {
0650: // This is still slowest part of the web server. I don't see how
0651: // to improve it much more, but there must be a way.
0652: int version = getVersion();
0653:
0654: if (version < HTTP_1_0) {
0655: return;
0656: }
0657:
0658: if (version < HTTP_1_1)
0659: killKeepalive();
0660:
0661: byte[] readBuffer = s.getBuffer();
0662: int readOffset = s.getOffset();
0663: int readLength = s.getLength();
0664:
0665: char[] headerBuffer = _headerBuffer;
0666: int headerOffset = 1;
0667: int headerBufferSize = headerBuffer.length;
0668: headerBuffer[0] = 'z';
0669: int headerSize = 0;
0670: _headerSize = 0;
0671:
0672: CharSegment[] headerKeys = _headerKeys;
0673: CharSegment[] headerValues = _headerValues;
0674:
0675: boolean debug = log.isLoggable(Level.FINE);
0676:
0677: while (true) {
0678: int ch;
0679:
0680: int keyOffset = headerOffset;
0681:
0682: // scan the key
0683: while (true) {
0684: if (readLength <= readOffset) {
0685: readOffset = 0;
0686: if ((readLength = s.fillBuffer()) <= 0)
0687: return;
0688: }
0689: ch = readBuffer[readOffset++];
0690:
0691: if (ch == '\n') {
0692: s.setOffset(readOffset);
0693: return;
0694: } else if (ch == ':')
0695: break;
0696:
0697: headerBuffer[headerOffset++] = (char) ch;
0698: }
0699:
0700: while (headerBuffer[headerOffset - 1] == ' ')
0701: headerOffset--;
0702:
0703: int keyLength = headerOffset - keyOffset;
0704: headerKeys[headerSize].init(headerBuffer, keyOffset,
0705: keyLength);
0706:
0707: do {
0708: if (readLength <= readOffset) {
0709: readOffset = 0;
0710: if ((readLength = s.fillBuffer()) <= 0)
0711: return;
0712: }
0713: ch = readBuffer[readOffset++];
0714: } while (ch == ' ' || ch == '\t');
0715:
0716: int valueOffset = headerOffset;
0717:
0718: // scan the value
0719: while (true) {
0720: if (readLength <= readOffset) {
0721: readOffset = 0;
0722: if ((readLength = s.fillBuffer()) <= 0)
0723: break;
0724: }
0725:
0726: if (ch == '\n') {
0727: int ch1 = readBuffer[readOffset];
0728:
0729: if (ch1 == ' ' || ch1 == '\t') {
0730: ch = ' ';
0731: readOffset++;
0732:
0733: if (headerBuffer[headerOffset - 1] == '\r')
0734: headerOffset--;
0735: } else
0736: break;
0737: }
0738:
0739: headerBuffer[headerOffset++] = (char) ch;
0740:
0741: ch = readBuffer[readOffset++];
0742: }
0743:
0744: while (headerBuffer[headerOffset - 1] <= ' ')
0745: headerOffset--;
0746:
0747: int valueLength = headerOffset - valueOffset;
0748: headerValues[headerSize].init(headerBuffer, valueOffset,
0749: valueLength);
0750:
0751: if (debug) {
0752: log.fine(dbgId() + headerKeys[headerSize] + ": "
0753: + headerValues[headerSize]);
0754: }
0755:
0756: if (addHeaderInt(headerBuffer, keyOffset, keyLength,
0757: headerValues[headerSize])) {
0758: headerSize++;
0759: }
0760:
0761: _headerSize = headerSize;
0762: }
0763: }
0764:
0765: /**
0766: * Returns the HTTP version of the request based on getProtocol().
0767: */
0768: int getVersion() {
0769: if (_version > 0)
0770: return _version;
0771:
0772: CharSegment protocol = getProtocolBuffer();
0773: if (protocol.length() < 8) {
0774: _version = HTTP_0_9;
0775: return _version;
0776: }
0777:
0778: if (protocol.equals("HTTP/1.0")) {
0779: _version = HTTP_1_0;
0780: return _version;
0781: } else if (protocol.equals("HTTP/1.1")) {
0782: _version = HTTP_1_1;
0783: return HTTP_1_1;
0784: } else if (protocol.equals("HTTP/0.9")) {
0785: _version = HTTP_0_9;
0786: return HTTP_0_9;
0787: }
0788:
0789: int i = protocol.indexOf('/');
0790: int len = protocol.length();
0791: int major = 0;
0792: for (i++; i < len; i++) {
0793: char ch = protocol.charAt(i);
0794:
0795: if (ch >= '0' && ch <= '9')
0796: major = 10 * major + ch - '0';
0797: else if (ch == '.')
0798: break;
0799: else {
0800: _version = HTTP_1_0;
0801: return _version;
0802: }
0803: }
0804:
0805: int minor = 0;
0806: for (i++; i < len; i++) {
0807: char ch = protocol.charAt(i);
0808:
0809: if (ch >= '0' && ch <= '9')
0810: minor = 10 * minor + ch - '0';
0811: else
0812: break;
0813: }
0814:
0815: _version = 256 * major + minor;
0816:
0817: return _version;
0818: }
0819:
0820: /**
0821: * Returns the header.
0822: */
0823: public String getMethod() {
0824: if (_methodString == null) {
0825: CharSegment cb = getMethodBuffer();
0826: if (cb.length() == 0) {
0827: _methodString = "GET";
0828: return _methodString;
0829: }
0830:
0831: switch (cb.charAt(0)) {
0832: case 'G':
0833: _methodString = cb.equals(_getCb) ? "GET" : cb
0834: .toString();
0835: break;
0836:
0837: case 'H':
0838: _methodString = cb.equals(_headCb) ? "HEAD" : cb
0839: .toString();
0840: break;
0841:
0842: case 'P':
0843: _methodString = cb.equals(_postCb) ? "POST" : cb
0844: .toString();
0845: break;
0846:
0847: default:
0848: _methodString = cb.toString();
0849: }
0850: }
0851:
0852: return _methodString;
0853:
0854: }
0855:
0856: /**
0857: * Returns a buffer containing the request method.
0858: */
0859: public CharSegment getMethodBuffer() {
0860: return _method;
0861: }
0862:
0863: /**
0864: * Returns the virtual host of the request
0865: */
0866: protected CharSequence getHost() {
0867: if (_host != null)
0868: return _host;
0869:
0870: String virtualHost = _conn.getVirtualHost();
0871: if (virtualHost != null)
0872: _host = virtualHost;
0873: else if (_uriHost.length() > 0)
0874: _host = _uriHost;
0875: else
0876: _host = _hostHeader;
0877:
0878: return _host;
0879: }
0880:
0881: /**
0882: * Returns the byte buffer containing the request URI
0883: */
0884: public byte[] getUriBuffer() {
0885: return _uri;
0886: }
0887:
0888: /**
0889: * Returns the length of the request URI
0890: */
0891: public int getUriLength() {
0892: return _uriLength;
0893: }
0894:
0895: /**
0896: * Returns the protocol.
0897: */
0898: public String getProtocol() {
0899: switch (_version) {
0900: case HTTP_1_1:
0901: return "HTTP/1.1";
0902: case HTTP_1_0:
0903: return "HTTP/1.0";
0904: case HTTP_0_9:
0905: default:
0906: return "HTTP/0.9";
0907: }
0908: }
0909:
0910: /**
0911: * Returns a char segment containing the protocol.
0912: */
0913: public CharSegment getProtocolBuffer() {
0914: return _protocol;
0915: }
0916:
0917: /**
0918: * Adds a new header. Used only by the caching to simulate
0919: * If-Modified-Since.
0920: *
0921: * @param key the key of the new header
0922: * @param value the value for the new header
0923: */
0924: public void setHeader(String key, String value) {
0925: int tail;
0926:
0927: if (_headerSize > 0) {
0928: tail = (_headerValues[_headerSize - 1].getOffset() + _headerValues[_headerSize - 1]
0929: .getLength());
0930: } else
0931: tail = 0;
0932:
0933: char[] headerBuffer = _headerBuffer;
0934: for (int i = key.length() - 1; i >= 0; i--)
0935: headerBuffer[tail + i] = key.charAt(i);
0936:
0937: _headerKeys[_headerSize].init(headerBuffer, tail, key.length());
0938:
0939: tail += key.length();
0940:
0941: for (int i = value.length() - 1; i >= 0; i--)
0942: headerBuffer[tail + i] = value.charAt(i);
0943:
0944: _headerValues[_headerSize].init(headerBuffer, tail, value
0945: .length());
0946: _headerSize++;
0947: }
0948:
0949: /**
0950: * Returns the number of headers.
0951: */
0952: @Override
0953: public int getHeaderSize() {
0954: return _headerSize;
0955: }
0956:
0957: /**
0958: * Returns the header key
0959: */
0960: @Override
0961: public CharSegment getHeaderKey(int index) {
0962: return _headerKeys[index];
0963: }
0964:
0965: /**
0966: * Returns the header value
0967: */
0968: @Override
0969: public CharSegment getHeaderValue(int index) {
0970: return _headerValues[index];
0971: }
0972:
0973: /**
0974: * Returns the header.
0975: */
0976: public String getHeader(String key) {
0977: CharSegment buf = getHeaderBuffer(key);
0978: if (buf != null)
0979: return buf.toString();
0980: else
0981: return null;
0982: }
0983:
0984: /**
0985: * Returns the matching header.
0986: *
0987: * @param testBuf header key
0988: * @param length length of the key.
0989: */
0990: public CharSegment getHeaderBuffer(char[] testBuf, int length) {
0991: char[] keyBuf = _headerBuffer;
0992: CharSegment[] headerKeys = _headerKeys;
0993:
0994: for (int i = _headerSize - 1; i >= 0; i--) {
0995: CharSegment key = headerKeys[i];
0996:
0997: if (key.length() != length)
0998: continue;
0999:
1000: int offset = key.getOffset();
1001: int j;
1002: for (j = length - 1; j >= 0; j--) {
1003: char a = testBuf[j];
1004: char b = keyBuf[offset + j];
1005: if (a == b)
1006: continue;
1007:
1008: if (a >= 'A' && a <= 'Z')
1009: a += 'a' - 'A';
1010: if (b >= 'A' && b <= 'Z')
1011: b += 'a' - 'A';
1012: if (a != b)
1013: break;
1014: }
1015:
1016: if (j < 0)
1017: return _headerValues[i];
1018: }
1019:
1020: return null;
1021: }
1022:
1023: /**
1024: * Returns the header value for the key, returned as a CharSegment.
1025: */
1026: public CharSegment getHeaderBuffer(String key) {
1027: int i = matchNextHeader(0, key);
1028:
1029: if (i >= 0)
1030: return _headerValues[i];
1031: else
1032: return null;
1033: }
1034:
1035: /**
1036: * Fills an ArrayList with the header values matching the key.
1037: *
1038: * @param values ArrayList which will contain the maching values.
1039: * @param key the header key to select.
1040: */
1041: public void getHeaderBuffers(String key,
1042: ArrayList<CharSegment> values) {
1043: int i = -1;
1044: while ((i = matchNextHeader(i + 1, key)) >= 0)
1045: values.add(_headerValues[i]);
1046: }
1047:
1048: /**
1049: * Return an enumeration of headers matching a key.
1050: *
1051: * @param key the header key to match.
1052: * @return the enumeration of the headers.
1053: */
1054: public Enumeration getHeaders(String key) {
1055: ArrayList<String> values = new ArrayList<String>();
1056: int i = -1;
1057: while ((i = matchNextHeader(i + 1, key)) >= 0)
1058: values.add(_headerValues[i].toString());
1059:
1060: return Collections.enumeration(values);
1061: }
1062:
1063: /**
1064: * Returns the index of the next header matching the key.
1065: *
1066: * @param i header index to start search
1067: * @param key header key to match
1068: *
1069: * @return the index of the next header matching, or -1.
1070: */
1071: private int matchNextHeader(int i, String key) {
1072: int size = _headerSize;
1073: int length = key.length();
1074:
1075: char[] keyBuf = _headerBuffer;
1076:
1077: for (; i < size; i++) {
1078: CharSegment header = _headerKeys[i];
1079:
1080: if (header.length() != length)
1081: continue;
1082:
1083: int offset = header.getOffset();
1084:
1085: int j;
1086: for (j = 0; j < length; j++) {
1087: char a = key.charAt(j);
1088: char b = keyBuf[offset + j];
1089: if (a == b)
1090: continue;
1091:
1092: if (a >= 'A' && a <= 'Z')
1093: a += 'a' - 'A';
1094: if (b >= 'A' && b <= 'Z')
1095: b += 'a' - 'A';
1096: if (a != b)
1097: break;
1098: }
1099:
1100: if (j == length)
1101: return i;
1102: }
1103:
1104: return -1;
1105: }
1106:
1107: /**
1108: * Returns an enumeration of all the header keys.
1109: */
1110: public Enumeration getHeaderNames() {
1111: ArrayList<String> names = new ArrayList<String>();
1112:
1113: for (int i = 0; i < _headerSize; i++) {
1114: CharSegment name = _headerKeys[i];
1115:
1116: int j;
1117: for (j = 0; j < names.size(); j++) {
1118: String oldName = names.get(j);
1119: if (name.matches(oldName))
1120: break;
1121: }
1122: if (j == names.size())
1123: names.add(j, name.toString());
1124: }
1125:
1126: return Collections.enumeration(names);
1127: }
1128:
1129: /**
1130: * Returns a stream for reading POST data.
1131: */
1132: public boolean initStream(ReadStream readStream, ReadStream rawRead)
1133: throws IOException {
1134: long contentLength = getLongContentLength();
1135:
1136: // needed to avoid auto-flush on read conflicting with partially
1137: // generated response
1138: rawRead.setSibling(null);
1139:
1140: String te;
1141: if (contentLength < 0 && HTTP_1_1 <= getVersion()
1142: && (te = getHeader("Transfer-Encoding")) != null) {
1143: _chunkedInputStream.init(rawRead);
1144: readStream.init(_chunkedInputStream, null);
1145: return true;
1146: }
1147: // Otherwise use content-length
1148: else if (contentLength >= 0) {
1149: _contentLengthStream.init(rawRead, contentLength);
1150: _readStream.init(_contentLengthStream, null);
1151:
1152: return true;
1153: } else if (getMethod().equals("POST")) {
1154: _contentLengthStream.init(rawRead, 0);
1155: _readStream.init(_contentLengthStream, null);
1156:
1157: throw new com.caucho.server.dispatch.BadRequestException(
1158: "POST requires content-length");
1159: }
1160:
1161: else {
1162: _contentLengthStream.init(rawRead, 0);
1163: _readStream.init(_contentLengthStream, null);
1164:
1165: return false;
1166: }
1167: }
1168:
1169: protected void skip() throws IOException {
1170: if (getMethod() == "GET")
1171: return;
1172:
1173: super .skip();
1174: }
1175:
1176: /**
1177: * Prints the remote address into a buffer.
1178: *
1179: * @param buffer the buffer which will contain the address.
1180: * @param offset the initial offset into the buffer.
1181: *
1182: * @return the final offset into the buffer.
1183: */
1184: /*
1185: public int printRemoteAddr(byte []buffer, int offset)
1186: throws IOException
1187: {
1188: Connection conn = getConnection();
1189:
1190: if (! (conn instanceof TcpConnection))
1191: return super.printRemoteAddr(buffer, offset);
1192:
1193: QSocket socket = ((TcpConnection) conn).getSocket();
1194:
1195: if (socket instanceof QJniSocket) {
1196: QJniSocket jniSocket = (QJniSocket) socket;
1197: long ip = jniSocket.getRemoteIP();
1198:
1199: for (int i = 24; i >= 0; i -= 8) {
1200: int value = (int) (ip >> i) & 0xff;
1201:
1202: if (value < 10)
1203: buffer[offset++] = (byte) (value + '0');
1204: else if (value < 100) {
1205: buffer[offset++] = (byte) (value / 10 + '0');
1206: buffer[offset++] = (byte) (value % 10 + '0');
1207: }
1208: else {
1209: buffer[offset++] = (byte) (value / 100 + '0');
1210: buffer[offset++] = (byte) (value / 10 % 10 + '0');
1211: buffer[offset++] = (byte) (value % 10 + '0');
1212: }
1213:
1214: if (i != 0)
1215: buffer[offset++] = (byte) '.';
1216: }
1217: }
1218: else {
1219: InetAddress addr = conn.getRemoteAddress();
1220:
1221: if (addr == null) {
1222: buffer[offset++] = (byte) '0';
1223: buffer[offset++] = (byte) '.';
1224: buffer[offset++] = (byte) '0';
1225: buffer[offset++] = (byte) '.';
1226: buffer[offset++] = (byte) '0';
1227: buffer[offset++] = (byte) '.';
1228: buffer[offset++] = (byte) '0';
1229:
1230: return offset;
1231: }
1232:
1233: byte []bytes = addr.getAddress();
1234: for (int i = 0; i < bytes.length; i++) {
1235: if (i != 0)
1236: buffer[offset++] = (byte) '.';
1237:
1238: int value = bytes[i] & 0xff;
1239:
1240: if (value < 10)
1241: buffer[offset++] = (byte) (value + '0');
1242: else if (value < 100) {
1243: buffer[offset++] = (byte) (value / 10 + '0');
1244: buffer[offset++] = (byte) (value % 10 + '0');
1245: }
1246: else {
1247: buffer[offset++] = (byte) (value / 100 + '0');
1248: buffer[offset++] = (byte) (value / 10 % 10 + '0');
1249: buffer[offset++] = (byte) (value % 10 + '0');
1250: }
1251: }
1252: }
1253:
1254: return offset;
1255: }
1256: */
1257:
1258: /**
1259: * Returns the client's remote host name.
1260: */
1261: /*
1262: public String getRemoteHost()
1263: {
1264: Connection conn = getConnection();
1265:
1266: if (conn instanceof TcpConnection) {
1267: QSocket socket = ((TcpConnection) conn).getSocket();
1268:
1269: if (socket instanceof QJniSocket) {
1270: QJniSocket jniSocket = (QJniSocket) socket;
1271: long ip = jniSocket.getRemoteIP();
1272:
1273: CharBuffer cb = _cb;
1274: cb.clear();
1275:
1276: for (int i = 24; i >= 0; i -= 8) {
1277: int value = (int) (ip >> i) & 0xff;
1278:
1279: if (value < 10)
1280: cb.append((char) (value + '0'));
1281: else if (value < 100) {
1282: cb.append((char) (value / 10 + '0'));
1283: cb.append((char) (value % 10 + '0'));
1284: }
1285: else {
1286: cb.append((char) (value / 100 + '0'));
1287: cb.append((char) (value / 10 % 10 + '0'));
1288: cb.append((char) (value % 10 + '0'));
1289: }
1290:
1291: if (i != 0)
1292: cb.append('.');
1293: }
1294:
1295: return cb.toString();
1296: }
1297: }
1298:
1299: InetAddress addr = conn.getRemoteAddress();
1300:
1301: byte []bytes = addr.getAddress();
1302: CharBuffer cb = _cb;
1303: cb.clear();
1304: for (int i = 0; i < bytes.length; i++) {
1305: int value = bytes[i] & 0xff;
1306:
1307: if (i != 0)
1308: cb.append('.');
1309:
1310: if (value < 10)
1311: cb.append((char) (value + '0'));
1312: else if (value < 100) {
1313: cb.append((char) (value / 10 + '0'));
1314: cb.append((char) (value % 10 + '0'));
1315: }
1316: else {
1317: cb.append((char) (value / 100 + '0'));
1318: cb.append((char) (value / 10 % 10 + '0'));
1319: cb.append((char) (value % 10 + '0'));
1320: }
1321: }
1322:
1323: return cb.toString();
1324: }
1325: */
1326:
1327: /**
1328: * Returns the named attribute.
1329: */
1330: public Object getAttribute(String name) {
1331: if (!_initAttributes)
1332: initAttributes();
1333:
1334: return super .getAttribute(name);
1335: }
1336:
1337: /**
1338: * Returns an enumeration of the attribute names.
1339: */
1340: public Enumeration<String> getAttributeNames() {
1341: if (!_initAttributes)
1342: initAttributes();
1343:
1344: return super .getAttributeNames();
1345: }
1346:
1347: /**
1348: * For SSL connections, use the SSL identifier.
1349: */
1350: public String findSessionIdFromConnection() {
1351: TcpConnection tcpConn = _tcpConn;
1352:
1353: if (!_isSecure || tcpConn == null)
1354: return null;
1355:
1356: QSocket socket = tcpConn.getSocket(); // XXX:
1357: /*
1358: if (! (socket instanceof SSLSocket))
1359: return null;
1360:
1361: SSLSession sslSession = ((SSLSocket) socket).getSession();
1362: if (sslSession == null)
1363: return null;
1364:
1365: byte []sessionId = sslSession.getId();
1366: if (sessionId == null)
1367: return null;
1368:
1369: CharBuffer cb = CharBuffer.allocate();
1370: Base64.encode(cb, sessionId, 0, sessionId.length);
1371: for (int i = cb.length() - 1; i >= 0; i--) {
1372: char ch = cb.charAt(i);
1373: if (ch == '/')
1374: cb.setCharAt(i, '-');
1375: }
1376:
1377: return cb.close();
1378: */
1379: return null;
1380: }
1381:
1382: /**
1383: * Returns the raw input stream.
1384: */
1385: public ReadStream getRawInput() {
1386: return _rawRead;
1387: }
1388:
1389: /**
1390: * Initialize any special attributes.
1391: */
1392: private void initAttributes() {
1393: _initAttributes = true;
1394:
1395: TcpConnection tcpConn = _tcpConn;
1396:
1397: if (!_isSecure || tcpConn == null)
1398: return;
1399:
1400: QSocket socket = tcpConn.getSocket();
1401:
1402: String cipherSuite = socket.getCipherSuite();
1403: super .setAttribute("javax.servlet.request.cipher_suite",
1404: cipherSuite);
1405:
1406: int keySize = socket.getCipherBits();
1407: if (keySize != 0)
1408: super .setAttribute("javax.servlet.request.key_size",
1409: new Integer(keySize));
1410:
1411: try {
1412: X509Certificate[] certs = socket.getClientCertificates();
1413: if (certs != null && certs.length > 0) {
1414: super .setAttribute(
1415: "javax.servlet.request.X509Certificate",
1416: certs[0]);
1417: super
1418: .setAttribute(
1419: com.caucho.server.security.AbstractAuthenticator.LOGIN_NAME,
1420: certs[0].getSubjectDN());
1421: }
1422: } catch (Exception e) {
1423: log.log(Level.FINER, e.toString(), e);
1424: }
1425: }
1426:
1427: public void protocolCloseEvent() {
1428: }
1429:
1430: /**
1431: * Cleans up at the end of the request
1432: */
1433: public void finish() throws IOException {
1434: super .finish();
1435:
1436: skip();
1437: }
1438:
1439: protected String dbgId() {
1440: if ("".equals(_server.getServerId()))
1441: return "Http[" + _conn.getId() + "] ";
1442: else
1443: return "Http[" + _server.getServerId() + ", "
1444: + _conn.getId() + "] ";
1445: }
1446:
1447: public String toString() {
1448: if ("".equals(_server.getServerId()))
1449: return "HttpRequest[" + _conn.getId() + "]";
1450: else {
1451: return ("HttpRequest[" + _server.getServerId() + ", "
1452: + _conn.getId() + "]");
1453: }
1454: }
1455: }
|