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.hmux;
0031:
0032: import com.caucho.log.Log;
0033: import com.caucho.server.cluster.BackingManager;
0034: import com.caucho.server.cluster.Cluster;
0035: import com.caucho.server.connection.AbstractHttpRequest;
0036: import com.caucho.server.connection.Connection;
0037: import com.caucho.server.dispatch.DispatchServer;
0038: import com.caucho.server.dispatch.Invocation;
0039: import com.caucho.server.dispatch.InvocationDecoder;
0040: import com.caucho.server.http.InvocationKey;
0041: import com.caucho.server.port.ServerRequest;
0042: import com.caucho.server.webapp.ErrorPageManager;
0043: import com.caucho.util.ByteBuffer;
0044: import com.caucho.util.CharBuffer;
0045: import com.caucho.util.CharSegment;
0046: import com.caucho.vfs.ClientDisconnectException;
0047: import com.caucho.vfs.ReadStream;
0048: import com.caucho.vfs.StreamImpl;
0049: import com.caucho.vfs.WriteStream;
0050:
0051: import java.io.IOException;
0052: import java.io.InputStream;
0053: import java.io.InterruptedIOException;
0054: import java.net.InetAddress;
0055: import java.security.cert.CertificateFactory;
0056: import java.security.cert.X509Certificate;
0057: import java.util.ArrayList;
0058: import java.util.Collections;
0059: import java.util.Enumeration;
0060: import java.util.HashSet;
0061: import java.util.logging.Level;
0062: import java.util.logging.Logger;
0063:
0064: /**
0065: * Handles requests from a remote dispatcher. For example, mod_caucho
0066: * and the IIS plugin use this protocol to talk to the backend.
0067: *
0068: * <p>Packets are straightforward:
0069: * <pre>code l2 l1 l0 contents</pre>
0070: * Where code is the code of the packet and l2-0 give 12 bits of length.
0071: *
0072: * <p>The protocol is designed to allow pipelining and buffering whenever
0073: * possible. So most commands are not acked. Data from the frontend (POST)
0074: * does need acks to prevent deadlocks at either end while waiting
0075: * for new data.
0076: *
0077: * <p>The overriding protocol is controlled by requests from the
0078: * frontend server.
0079: *
0080: * <p>A ping request:
0081: * <pre>
0082: * Frontend Backend
0083: * CSE_PING
0084: * CSE_END
0085: * CSE_END
0086: * <pre>
0087: *
0088: * <p>A GET request:
0089: * <pre>
0090: * Frontend Backend
0091: * CSE_METHOD
0092: * ...
0093: * CSE_HEADER/CSE_VALUE
0094: * CSE_END
0095: * CSE_DATA
0096: * CSE_DATA
0097: * CSE_END
0098: * <pre>
0099: *
0100: * <p>Short POST:
0101: * <pre>
0102: * Frontend Backend
0103: * CSE_METHOD
0104: * ...
0105: * CSE_HEADER/CSE_VALUE
0106: * CSE_DATA
0107: * CSE_END
0108: * CSE_DATA
0109: * CSE_DATA
0110: * CSE_END
0111: * <pre>
0112: *
0113: * <p>Long POST:
0114: * <pre>
0115: * Frontend Backend
0116: * CSE_METHOD
0117: * ...
0118: * CSE_HEADER/CSE_VALUE
0119: * CSE_DATA
0120: * CSE_DATA (optional)
0121: * CSE_DATA
0122: * CSE_ACK
0123: * CSE_DATA (optional)
0124: * CSE_DATA
0125: * CSE_ACK
0126: * CSE_END
0127: * CSE_DATA
0128: * CSE_END
0129: * <pre>
0130: *
0131: */
0132: public class HmuxRequest extends AbstractHttpRequest implements
0133: ServerRequest {
0134: private static final Logger log = Logger
0135: .getLogger(HmuxRequest.class.getName());
0136:
0137: // HMUX channel control codes
0138: public static final int HMUX_CHANNEL = 'C';
0139: public static final int HMUX_ACK = 'A';
0140: public static final int HMUX_ERROR = 'E';
0141: public static final int HMUX_YIELD = 'Y';
0142: public static final int HMUX_QUIT = 'Q';
0143: public static final int HMUX_EXIT = 'X';
0144:
0145: public static final int HMUX_DATA = 'D';
0146: public static final int HMUX_URI = 'U';
0147: public static final int HMUX_STRING = 'S';
0148: public static final int HMUX_HEADER = 'H';
0149: public static final int HMUX_PROTOCOL = 'P';
0150: public static final int HMUX_META_HEADER = 'M';
0151:
0152: // The following are HTTP codes
0153: public static final int CSE_NULL = '?';
0154: public static final int CSE_PATH_INFO = 'b';
0155: public static final int CSE_PROTOCOL = 'c';
0156: public static final int CSE_REMOTE_USER = 'd';
0157: public static final int CSE_QUERY_STRING = 'e';
0158: public static final int HMUX_FLUSH = 'f';
0159: public static final int CSE_SERVER_PORT = 'g';
0160: public static final int CSE_REMOTE_HOST = 'h';
0161: public static final int CSE_REMOTE_ADDR = 'i';
0162: public static final int CSE_REMOTE_PORT = 'j';
0163: public static final int CSE_REAL_PATH = 'k';
0164: public static final int CSE_SCRIPT_FILENAME = 'l';
0165: public static final int HMUX_METHOD = 'm';
0166: public static final int CSE_AUTH_TYPE = 'n';
0167: public static final int CSE_URI = 'o';
0168: public static final int CSE_CONTENT_LENGTH = 'p';
0169: public static final int CSE_CONTENT_TYPE = 'q';
0170: public static final int CSE_IS_SECURE = 'r';
0171: public static final int HMUX_STATUS = 's';
0172: public static final int CSE_CLIENT_CERT = 't';
0173: public static final int CSE_SERVER_TYPE = 'u';
0174: public static final int HMUX_SERVER_NAME = 'v';
0175:
0176: public static final int CSE_SEND_HEADER = 'G';
0177:
0178: public static final int CSE_DATA = 'D';
0179: public static final int CSE_FLUSH = 'F';
0180: public static final int CSE_KEEPALIVE = 'K';
0181: public static final int CSE_ACK = 'A';
0182: public static final int CSE_END = 'Z';
0183: public static final int CSE_CLOSE = 'X';
0184:
0185: // other, specialized protocols
0186: public static final int CSE_QUERY = 'Q';
0187: public static final int CSE_PING = 'P';
0188:
0189: public static final int HMUX_CLUSTER_PROTOCOL = 0x101;
0190: public static final int HMUX_DISPATCH_PROTOCOL = 0x102;
0191: public static final int HMUX_JMS_PROTOCOL = 0x103;
0192:
0193: public enum ProtocolResult {
0194: QUIT, EXIT, YIELD
0195: };
0196:
0197: static final int HTTP_0_9 = 0x0009;
0198: static final int HTTP_1_0 = 0x0100;
0199: static final int HTTP_1_1 = 0x0101;
0200:
0201: private static final int HEADER_CAPACITY = 256;
0202:
0203: static final CharBuffer _getCb = new CharBuffer("GET");
0204: static final CharBuffer _headCb = new CharBuffer("HEAD");
0205: static final CharBuffer _postCb = new CharBuffer("POST");
0206:
0207: private final CharBuffer _method; // "GET"
0208: private String _methodString; // "GET"
0209: // private CharBuffer scheme; // "http:"
0210: private CharBuffer _host; // www.caucho.com
0211: private int _port; // :80
0212:
0213: private ByteBuffer _uri; // "/path/test.jsp/Junk"
0214: private CharBuffer _protocol; // "HTTP/1.0"
0215: private int _version;
0216:
0217: private CharBuffer _remoteAddr;
0218: private CharBuffer _remoteHost;
0219: private CharBuffer _serverName;
0220: private CharBuffer _serverPort;
0221: private CharBuffer _remotePort;
0222:
0223: private boolean _isSecure;
0224: private ByteBuffer _clientCert;
0225:
0226: private CharBuffer[] _headerKeys;
0227: private CharBuffer[] _headerValues;
0228: private int _headerSize;
0229:
0230: private byte[] _lengthBuf;
0231:
0232: private int _serverType;
0233:
0234: // write stream from the connection
0235: private WriteStream _rawWrite;
0236: // servlet write stream
0237: private WriteStream _writeStream;
0238:
0239: // StreamImpl to break reads and writes to the underlying protocol
0240: private ServletFilter _filter;
0241: private int _pendingData;
0242:
0243: private InvocationKey _invocationKey = new InvocationKey();
0244:
0245: private CharBuffer _cb1;
0246: private CharBuffer _cb2;
0247: private boolean _hasRequest;
0248:
0249: private AbstractClusterRequest _clusterRequest;
0250: private HmuxDispatchRequest _dispatchRequest;
0251: private BackingManager _backingManager;
0252: private Cluster _cluster;
0253:
0254: private HmuxProtocol _hmuxProtocol;
0255: private ErrorPageManager _errorManager = new ErrorPageManager();
0256:
0257: private int _srunIndex;
0258:
0259: public HmuxRequest(DispatchServer server, Connection conn,
0260: HmuxProtocol protocol) {
0261: super (server, conn);
0262:
0263: _hmuxProtocol = protocol;
0264:
0265: _response = new HmuxResponse(this );
0266:
0267: _rawWrite = conn.getWriteStream();
0268: _writeStream = new WriteStream();
0269: _writeStream.setReuseBuffer(true);
0270:
0271: // XXX: response.setIgnoreClientDisconnect(server.getIgnoreClientDisconnect());
0272:
0273: _cluster = Cluster.getLocal();
0274: if (_cluster != null) {
0275: try {
0276: Class cl = Class
0277: .forName("com.caucho.server.hmux.HmuxClusterRequest");
0278:
0279: _clusterRequest = (AbstractClusterRequest) cl
0280: .newInstance();
0281: _clusterRequest.setRequest(this );
0282: _clusterRequest.setCluster(_cluster);
0283: } catch (ClassNotFoundException e) {
0284: log.finer(e.toString());
0285: } catch (Throwable e) {
0286: log.log(Level.FINER, e.toString(), e);
0287: }
0288: }
0289:
0290: _dispatchRequest = new HmuxDispatchRequest(this );
0291:
0292: _uri = new ByteBuffer();
0293:
0294: _method = new CharBuffer();
0295: _host = new CharBuffer();
0296: _protocol = new CharBuffer();
0297:
0298: _headerKeys = new CharBuffer[HEADER_CAPACITY];
0299: _headerValues = new CharBuffer[_headerKeys.length];
0300: for (int i = 0; i < _headerKeys.length; i++) {
0301: _headerKeys[i] = new CharBuffer();
0302: _headerValues[i] = new CharBuffer();
0303: }
0304:
0305: _remoteHost = new CharBuffer();
0306: _remoteAddr = new CharBuffer();
0307: _serverName = new CharBuffer();
0308: _serverPort = new CharBuffer();
0309: _remotePort = new CharBuffer();
0310:
0311: _clientCert = new ByteBuffer();
0312:
0313: _cb1 = new CharBuffer();
0314: _cb2 = new CharBuffer();
0315:
0316: _lengthBuf = new byte[16];
0317:
0318: _filter = new ServletFilter();
0319: }
0320:
0321: public boolean isWaitForRead() {
0322: return true;
0323: }
0324:
0325: /**
0326: * Handles a new request. Initializes the protocol handler and
0327: * the request streams.
0328: *
0329: * <p>Note: ClientDisconnectException must be rethrown to
0330: * the caller.
0331: */
0332: public boolean handleRequest() throws IOException {
0333: // XXX: should be moved to TcpConnection
0334: Thread thread = Thread.currentThread();
0335: thread.setContextClassLoader(_server.getClassLoader());
0336:
0337: if (log.isLoggable(Level.FINE))
0338: log.fine(dbgId() + "start request");
0339:
0340: _filter.init(this , _rawRead, _rawWrite);
0341: _writeStream.init(_filter);
0342: // _writeStream.setWritePrefix(3);
0343:
0344: _response.init(_writeStream);
0345:
0346: _serverType = 0;
0347: _uri.setLength(0);
0348:
0349: boolean hasRequest = false;
0350:
0351: try {
0352: start();
0353: _response.start();
0354:
0355: try {
0356: if (!scanHeaders()) {
0357: killKeepalive();
0358: return false;
0359: } else if (_uri.size() == 0) {
0360: return true;
0361: }
0362: } catch (InterruptedIOException e) {
0363: killKeepalive();
0364: log.fine(dbgId() + "interrupted keepalive");
0365: return false;
0366: }
0367:
0368: if (_isSecure)
0369: getClientCertificate();
0370:
0371: hasRequest = true;
0372: // setStartDate();
0373:
0374: if (_server == null || _server.isDestroyed()) {
0375: log.fine(dbgId() + "server is closed");
0376:
0377: try {
0378: _writeStream.setDisableClose(false);
0379: _writeStream.close();
0380: } catch (Throwable e) {
0381: }
0382:
0383: try {
0384: _readStream.setDisableClose(false);
0385: _readStream.close();
0386: } catch (Throwable e) {
0387: }
0388:
0389: return false;
0390: }
0391:
0392: _filter.setPending(_pendingData);
0393:
0394: try {
0395: if (_method.getLength() == 0)
0396: throw new RuntimeException(
0397: "HTTP protocol exception");
0398:
0399: _invocationKey.init(_isSecure, getHost(),
0400: getServerPort(), _uri.getBuffer(), _uri
0401: .getLength());
0402:
0403: Invocation invocation;
0404:
0405: invocation = _server.getInvocation(_invocationKey);
0406:
0407: if (invocation == null) {
0408: invocation = _server.createInvocation();
0409:
0410: if (_host != null)
0411: invocation.setHost(_host.toString());
0412:
0413: invocation.setPort(getServerPort());
0414:
0415: InvocationDecoder decoder = _server
0416: .getInvocationDecoder();
0417:
0418: decoder.splitQueryAndUnescape(invocation, _uri
0419: .getBuffer(), _uri.getLength());
0420:
0421: invocation = _server.buildInvocation(_invocationKey
0422: .clone(), invocation);
0423: }
0424:
0425: invocation = invocation.getRequestInvocation(this );
0426:
0427: setInvocation(invocation);
0428:
0429: invocation.service(this , _response);
0430: } catch (ClientDisconnectException e) {
0431: throw e;
0432: } catch (Throwable e) {
0433: try {
0434: _errorManager.sendServletError(e, this , _response);
0435: } catch (ClientDisconnectException e1) {
0436: throw e1;
0437: } catch (Exception e1) {
0438: log.log(Level.FINE, e1.toString(), e1);
0439: }
0440:
0441: return false;
0442: }
0443: } finally {
0444: if (!hasRequest)
0445: _response.setHeaderWritten(true);
0446:
0447: try {
0448: finish();
0449: _response.finish();
0450: } catch (ClientDisconnectException e) {
0451: throw e;
0452: } catch (Exception e) {
0453: killKeepalive();
0454: log.log(Level.FINE, dbgId() + e, e);
0455: }
0456:
0457: try {
0458: _writeStream.setDisableClose(false);
0459: _writeStream.close();
0460: } catch (ClientDisconnectException e) {
0461: killKeepalive();
0462: log.log(Level.FINE, dbgId() + e, e);
0463:
0464: throw e;
0465: } catch (Exception e) {
0466: killKeepalive();
0467: log.log(Level.FINE, dbgId() + e, e);
0468: }
0469:
0470: try {
0471: _readStream.setDisableClose(false);
0472: _readStream.close();
0473: } catch (Exception e) {
0474: killKeepalive();
0475: log.log(Level.FINE, dbgId() + e, e);
0476: }
0477: }
0478:
0479: boolean allowKeepalive = isKeepalive();
0480:
0481: if (log.isLoggable(Level.FINE)) {
0482: if (allowKeepalive)
0483: log.fine(dbgId() + "complete request - keepalive");
0484: else
0485: log.fine(dbgId() + "complete request");
0486: }
0487:
0488: return allowKeepalive;
0489: }
0490:
0491: /**
0492: * Initialize the read stream from the raw stream.
0493: */
0494: protected boolean initStream(ReadStream readStream,
0495: ReadStream rawStream) throws IOException {
0496: readStream.init(_filter, null);
0497:
0498: return true;
0499: }
0500:
0501: private void getClientCertificate() {
0502: String cipher = getHeader("SSL_CIPHER");
0503: if (cipher == null)
0504: cipher = getHeader("HTTPS_CIPHER");
0505: if (cipher != null)
0506: setAttribute("javax.servlet.request.cipher_suite", cipher);
0507:
0508: String keySize = getHeader("SSL_CIPHER_USEKEYSIZE");
0509: if (keySize == null)
0510: keySize = getHeader("SSL_SECRETKEYSIZE");
0511: if (keySize != null)
0512: setAttribute("javax.servlet.request.key_size", keySize);
0513:
0514: if (_clientCert.size() == 0)
0515: return;
0516:
0517: try {
0518: CertificateFactory cf = CertificateFactory
0519: .getInstance("X.509");
0520: InputStream is = _clientCert.createInputStream();
0521: Object cert = cf.generateCertificate(is);
0522: is.close();
0523: setAttribute("javax.servlet.request.X509Certificate", cert);
0524: setAttribute(
0525: com.caucho.server.security.AbstractAuthenticator.LOGIN_NAME,
0526: ((X509Certificate) cert).getSubjectDN());
0527: } catch (Throwable e) {
0528: log.log(Level.FINE, e.toString(), e);
0529: }
0530: }
0531:
0532: /**
0533: * Returns true for the top-level request, but false for any include()
0534: * or forward()
0535: */
0536: public boolean isTop() {
0537: return true;
0538: }
0539:
0540: protected boolean checkLogin() {
0541: return true;
0542: }
0543:
0544: /**
0545: * Clears variables at the start of a new request.
0546: */
0547: protected void start() throws IOException {
0548: super .start();
0549:
0550: _method.clear();
0551: _methodString = null;
0552: _protocol.clear();
0553: _version = 0;
0554: _uri.clear();
0555: _host.clear();
0556: _port = 0;
0557:
0558: _headerSize = 0;
0559:
0560: _remoteHost.clear();
0561: _remoteAddr.clear();
0562: _serverName.clear();
0563: _serverPort.clear();
0564: _remotePort.clear();
0565:
0566: _clientCert.clear();
0567:
0568: _pendingData = 0;
0569:
0570: _isSecure = _conn.isSecure();
0571: }
0572:
0573: /**
0574: * Fills request parameters from the stream.
0575: */
0576: private boolean scanHeaders() throws IOException {
0577: boolean hasURI = false;
0578: CharBuffer cb = _cb;
0579: boolean isLoggable = log.isLoggable(Level.FINE);
0580: ReadStream is = _rawRead;
0581: int code;
0582: int len;
0583:
0584: while (true) {
0585: code = is.read();
0586:
0587: switch (code) {
0588: case -1:
0589: if (isLoggable)
0590: log.fine(dbgId() + "end of file");
0591: return false;
0592:
0593: case HMUX_CHANNEL:
0594: int channel = (is.read() << 8) + is.read();
0595:
0596: if (isLoggable)
0597: log.fine(dbgId() + "channel " + channel);
0598: break;
0599:
0600: case HMUX_QUIT:
0601: if (isLoggable)
0602: log
0603: .fine(dbgId() + (char) code
0604: + ": end of request");
0605:
0606: return hasURI;
0607:
0608: case HMUX_EXIT:
0609: if (isLoggable)
0610: log.fine(dbgId() + (char) code + ": end of socket");
0611:
0612: killKeepalive();
0613:
0614: return hasURI;
0615:
0616: case HMUX_PROTOCOL:
0617: len = (is.read() << 8) + is.read();
0618:
0619: if (len != 4) {
0620: log.fine(dbgId() + (char) code
0621: + ": protocol length (" + len
0622: + ") must be 4.");
0623: killKeepalive();
0624: return false;
0625: }
0626:
0627: int value = ((is.read() << 24) + (is.read() << 16)
0628: + (is.read() << 8) + (is.read()));
0629:
0630: int result = HMUX_EXIT;
0631: boolean isKeepalive = false;
0632: if (value == HMUX_CLUSTER_PROTOCOL) {
0633: if (isLoggable)
0634: log.fine(dbgId() + (char) code
0635: + ": cluster protocol");
0636: _filter.setClientClosed(true);
0637:
0638: if (_server == null || _server.isDestroyed()) {
0639: return false;
0640: }
0641:
0642: result = _clusterRequest.handleRequest(is,
0643: _rawWrite);
0644: } else if (value == HMUX_DISPATCH_PROTOCOL) {
0645: if (isLoggable)
0646: log.fine(dbgId() + (char) code
0647: + ": dispatch protocol");
0648: _filter.setClientClosed(true);
0649:
0650: if (_server == null || _server.isDestroyed()) {
0651: return false;
0652: }
0653:
0654: isKeepalive = _dispatchRequest.handleRequest(is,
0655: _rawWrite);
0656:
0657: if (isKeepalive)
0658: result = HMUX_QUIT;
0659: else
0660: result = HMUX_EXIT;
0661: } else {
0662: if (_server == null || _server.isDestroyed()) {
0663: return false;
0664: }
0665:
0666: HmuxExtension ext = _hmuxProtocol
0667: .getExtension(value);
0668:
0669: if (ext != null) {
0670: if (isLoggable)
0671: log.fine(dbgId() + (char) code
0672: + ": extension " + ext);
0673: _filter.setClientClosed(true);
0674:
0675: result = ext.handleRequest(this , is, _rawWrite);
0676: } else {
0677: log.fine(dbgId() + (char) code
0678: + ": unknown protocol (" + value + ")");
0679: result = HMUX_EXIT;
0680: }
0681: }
0682:
0683: if (result == HMUX_YIELD)
0684: break;
0685: else {
0686: if (result == HMUX_QUIT && !allowKeepalive())
0687: result = HMUX_EXIT;
0688:
0689: if (result == HMUX_QUIT) {
0690: _rawWrite.write(HMUX_QUIT);
0691: _rawWrite.flush();
0692: } else {
0693: _rawWrite.write(HMUX_EXIT);
0694: _rawWrite.close();
0695: }
0696:
0697: return result == HMUX_QUIT;
0698: }
0699:
0700: case HMUX_URI:
0701: hasURI = true;
0702: len = (is.read() << 8) + is.read();
0703: _uri.setLength(len);
0704: _rawRead.readAll(_uri.getBuffer(), 0, len);
0705: if (isLoggable)
0706: log.fine(dbgId() + (char) code + ":uri " + _uri);
0707: break;
0708:
0709: case HMUX_METHOD:
0710: len = (is.read() << 8) + is.read();
0711: is.readAll(_method, len);
0712: if (isLoggable)
0713: log.fine(dbgId() + (char) code + ":method "
0714: + _method);
0715: break;
0716:
0717: case CSE_REAL_PATH:
0718: len = (is.read() << 8) + is.read();
0719: _cb1.clear();
0720: _rawRead.readAll(_cb1, len);
0721: code = _rawRead.read();
0722: if (code != HMUX_STRING)
0723: throw new IOException(
0724: "protocol expected HMUX_STRING");
0725: _cb2.clear();
0726: _rawRead.readAll(_cb2, readLength());
0727:
0728: //http.setRealPath(cb1.toString(), cb2.toString());
0729: if (isLoggable)
0730: log.fine(dbgId() + (char) code + " "
0731: + _cb1.toString() + "->" + _cb2.toString());
0732: //throw new RuntimeException();
0733: break;
0734:
0735: case CSE_REMOTE_HOST:
0736: len = (is.read() << 8) + is.read();
0737: _rawRead.readAll(_remoteHost, len);
0738: if (isLoggable)
0739: log.fine(dbgId() + (char) code + " " + _remoteHost);
0740: break;
0741:
0742: case CSE_REMOTE_ADDR:
0743: len = (is.read() << 8) + is.read();
0744: _rawRead.readAll(_remoteAddr, len);
0745: if (isLoggable)
0746: log.fine(dbgId() + (char) code + " " + _remoteAddr);
0747: break;
0748:
0749: case HMUX_SERVER_NAME:
0750: len = (is.read() << 8) + is.read();
0751: _rawRead.readAll(_serverName, len);
0752: if (isLoggable)
0753: log.fine(dbgId() + (char) code + " server-host: "
0754: + _serverName);
0755: break;
0756:
0757: case CSE_REMOTE_PORT:
0758: len = (is.read() << 8) + is.read();
0759: _rawRead.readAll(_remotePort, len);
0760: if (isLoggable)
0761: log.fine(dbgId() + (char) code + " remote-port: "
0762: + _remotePort);
0763: break;
0764:
0765: case CSE_SERVER_PORT:
0766: len = (is.read() << 8) + is.read();
0767: _rawRead.readAll(_serverPort, len);
0768: if (isLoggable)
0769: log.fine(dbgId() + (char) code + " server-port: "
0770: + _serverPort);
0771: break;
0772:
0773: case CSE_QUERY_STRING:
0774: len = (is.read() << 8) + is.read();
0775: if (len > 0) {
0776: _uri.add('?');
0777: _uri.ensureCapacity(_uri.getLength() + len);
0778: _rawRead.readAll(_uri.getBuffer(),
0779: _uri.getLength(), len);
0780: _uri.setLength(_uri.getLength() + len);
0781: }
0782: break;
0783:
0784: case CSE_PROTOCOL:
0785: len = (is.read() << 8) + is.read();
0786: _rawRead.readAll(_protocol, len);
0787: if (isLoggable)
0788: log.fine(dbgId() + (char) code + " protocol: "
0789: + _protocol);
0790: for (int i = 0; i < len; i++) {
0791: char ch = _protocol.charAt(i);
0792: if (ch >= '0' && ch <= '9')
0793: _version = 16 * _version + ch - '0';
0794: else if (ch == '.')
0795: _version = 16 * _version;
0796: }
0797: break;
0798:
0799: case HMUX_HEADER:
0800: len = (is.read() << 8) + is.read();
0801:
0802: int headerSize = _headerSize;
0803:
0804: CharBuffer key = _headerKeys[headerSize];
0805: key.clear();
0806:
0807: CharBuffer valueCb = _headerValues[headerSize];
0808: valueCb.clear();
0809:
0810: _rawRead.readAll(key, len);
0811: code = _rawRead.read();
0812: if (code != HMUX_STRING)
0813: throw new IOException(
0814: "protocol expected HMUX_STRING at "
0815: + (char) code);
0816: _rawRead.readAll(valueCb, readLength());
0817:
0818: if (isLoggable)
0819: log.fine(dbgId() + "H " + key + "=" + valueCb);
0820:
0821: if (addHeaderInt(key.getBuffer(), 0, key.length(),
0822: valueCb)) {
0823: _headerSize++;
0824: }
0825: break;
0826:
0827: case CSE_CONTENT_LENGTH:
0828: len = (is.read() << 8) + is.read();
0829: if (_headerKeys.length <= _headerSize)
0830: resizeHeaders();
0831: _headerKeys[_headerSize].clear();
0832: _headerKeys[_headerSize].append("Content-Length");
0833: _headerValues[_headerSize].clear();
0834: _rawRead.readAll(_headerValues[_headerSize], len);
0835:
0836: if (isLoggable)
0837: log.fine(dbgId() + (char) code + " content-length="
0838: + _headerValues[_headerSize]);
0839: _headerSize++;
0840: break;
0841:
0842: case CSE_CONTENT_TYPE:
0843: len = (is.read() << 8) + is.read();
0844: if (_headerKeys.length <= _headerSize)
0845: resizeHeaders();
0846: _headerKeys[_headerSize].clear();
0847: _headerKeys[_headerSize].append("Content-Type");
0848: _headerValues[_headerSize].clear();
0849: _rawRead.readAll(_headerValues[_headerSize], len);
0850: if (isLoggable)
0851: log.fine(dbgId() + (char) code + " content-type="
0852: + _headerValues[_headerSize]);
0853: _headerSize++;
0854: break;
0855:
0856: case CSE_IS_SECURE:
0857: len = (is.read() << 8) + is.read();
0858: _isSecure = true;
0859: if (isLoggable)
0860: log.fine(dbgId() + "secure");
0861: _rawRead.skip(len);
0862: break;
0863:
0864: case CSE_CLIENT_CERT:
0865: len = (is.read() << 8) + is.read();
0866: _clientCert.clear();
0867: _clientCert.setLength(len);
0868: _rawRead.readAll(_clientCert.getBuffer(), 0, len);
0869: if (isLoggable)
0870: log.fine(dbgId() + (char) code + " cert="
0871: + _clientCert + " len:" + len);
0872: break;
0873:
0874: case CSE_SERVER_TYPE:
0875: len = (is.read() << 8) + is.read();
0876: _cb1.clear();
0877: _rawRead.readAll(_cb1, len);
0878: if (isLoggable)
0879: log.fine(dbgId() + (char) code + " server=" + _cb1);
0880: if (_cb1.length() > 0)
0881: _serverType = _cb1.charAt(0);
0882: break;
0883:
0884: case CSE_REMOTE_USER:
0885: len = (is.read() << 8) + is.read();
0886: _cb.clear();
0887: _rawRead.readAll(_cb, len);
0888: if (isLoggable)
0889: log.fine(dbgId() + (char) code + " " + _cb);
0890: setAttribute(
0891: com.caucho.server.security.AbstractAuthenticator.LOGIN_NAME,
0892: new com.caucho.security.BasicPrincipal(_cb
0893: .toString()));
0894: break;
0895:
0896: case CSE_DATA:
0897: len = (is.read() << 8) + is.read();
0898: _pendingData = len;
0899: if (isLoggable)
0900: log.fine(dbgId() + (char) code + " post-data: "
0901: + len);
0902: return hasURI;
0903:
0904: default:
0905: len = (is.read() << 8) + is.read();
0906:
0907: if (isLoggable)
0908: log.fine(dbgId() + (char) code + " " + len);
0909: is.skip(len);
0910: break;
0911: }
0912: }
0913:
0914: // _filter.setClientClosed(true);
0915:
0916: // return false;
0917: }
0918:
0919: private void resizeHeaders() {
0920: CharBuffer[] newKeys = new CharBuffer[_headerSize * 2];
0921: CharBuffer[] newValues = new CharBuffer[_headerSize * 2];
0922:
0923: for (int i = 0; i < _headerSize; i++) {
0924: newKeys[i] = _headerKeys[i];
0925: newValues[i] = _headerValues[i];
0926: }
0927:
0928: for (int i = _headerSize; i < newKeys.length; i++) {
0929: newKeys[i] = new CharBuffer();
0930: newValues[i] = new CharBuffer();
0931: }
0932:
0933: _headerKeys = newKeys;
0934: _headerValues = newValues;
0935: }
0936:
0937: private int readLength() throws IOException {
0938: return ((_rawRead.read() << 8) + _rawRead.read());
0939: }
0940:
0941: /**
0942: * Returns the header.
0943: */
0944: public String getMethod() {
0945: if (_methodString == null) {
0946: CharSegment cb = getMethodBuffer();
0947: if (cb.length() == 0) {
0948: _methodString = "GET";
0949: return _methodString;
0950: }
0951:
0952: switch (cb.charAt(0)) {
0953: case 'G':
0954: _methodString = cb.equals(_getCb) ? "GET" : cb
0955: .toString();
0956: break;
0957:
0958: case 'H':
0959: _methodString = cb.equals(_headCb) ? "HEAD" : cb
0960: .toString();
0961: break;
0962:
0963: case 'P':
0964: _methodString = cb.equals(_postCb) ? "POST" : cb
0965: .toString();
0966: break;
0967:
0968: default:
0969: _methodString = cb.toString();
0970: }
0971: }
0972:
0973: return _methodString;
0974:
0975: }
0976:
0977: public CharSegment getMethodBuffer() {
0978: return _method;
0979: }
0980:
0981: /**
0982: * Returns a char buffer containing the host.
0983: */
0984: protected CharBuffer getHost() {
0985: if (_host.length() > 0)
0986: return _host;
0987:
0988: _host.append(_serverName);
0989: _host.toLowerCase();
0990:
0991: return _host;
0992: }
0993:
0994: public final byte[] getUriBuffer() {
0995: return _uri.getBuffer();
0996: }
0997:
0998: public final int getUriLength() {
0999: return _uri.getLength();
1000: }
1001:
1002: /**
1003: * Returns the protocol.
1004: */
1005: public String getProtocol() {
1006: return _protocol.toString();
1007: }
1008:
1009: public CharSegment getProtocolBuffer() {
1010: return _protocol;
1011: }
1012:
1013: final int getVersion() {
1014: return _version;
1015: }
1016:
1017: /**
1018: * Returns true if the request is secure.
1019: */
1020: public boolean isSecure() {
1021: return _isSecure;
1022: }
1023:
1024: /**
1025: * Returns the header.
1026: */
1027: public String getHeader(String key) {
1028: CharSegment buf = getHeaderBuffer(key);
1029: if (buf != null)
1030: return buf.toString();
1031: else
1032: return null;
1033: }
1034:
1035: public CharSegment getHeaderBuffer(String key) {
1036: for (int i = 0; i < _headerSize; i++) {
1037: CharBuffer test = _headerKeys[i];
1038:
1039: if (test.equalsIgnoreCase(key))
1040: return _headerValues[i];
1041: }
1042:
1043: return null;
1044: }
1045:
1046: public CharSegment getHeaderBuffer(char[] buf, int length) {
1047: for (int i = 0; i < _headerSize; i++) {
1048: CharBuffer test = _headerKeys[i];
1049:
1050: if (test.length() != length)
1051: continue;
1052:
1053: char[] keyBuf = test.getBuffer();
1054: int j;
1055: for (j = 0; j < length; j++) {
1056: char a = buf[j];
1057: char b = keyBuf[j];
1058: if (a == b)
1059: continue;
1060:
1061: if (a >= 'A' && a <= 'Z')
1062: a += 'a' - 'A';
1063: if (b >= 'A' && b <= 'Z')
1064: b += 'a' - 'A';
1065: if (a != b)
1066: break;
1067: }
1068:
1069: if (j == length)
1070: return _headerValues[i];
1071: }
1072:
1073: return null;
1074: }
1075:
1076: public void setHeader(String key, String value) {
1077: if (_headerKeys.length <= _headerSize)
1078: resizeHeaders();
1079:
1080: _headerKeys[_headerSize].clear();
1081: _headerKeys[_headerSize].append(key);
1082: _headerValues[_headerSize].clear();
1083: _headerValues[_headerSize].append(value);
1084: _headerSize++;
1085: }
1086:
1087: public void getHeaderBuffers(String key,
1088: ArrayList<CharSegment> values) {
1089: CharBuffer cb = _cb;
1090:
1091: cb.clear();
1092: cb.append(key);
1093:
1094: int size = _headerSize;
1095: for (int i = 0; i < size; i++) {
1096: CharBuffer test = _headerKeys[i];
1097: if (test.equalsIgnoreCase(cb))
1098: values.add(_headerValues[i]);
1099: }
1100: }
1101:
1102: public Enumeration getHeaderNames() {
1103: HashSet<String> names = new HashSet<String>();
1104: for (int i = 0; i < _headerSize; i++)
1105: names.add(_headerKeys[i].toString());
1106:
1107: return Collections.enumeration(names);
1108: }
1109:
1110: /**
1111: * Returns the URI for the request, special casing the IIS issues.
1112: * Because IIS already escapes the URI before sending it, the URI
1113: * needs to be re-escaped.
1114: */
1115: public String getRequestURI() {
1116: if (_serverType == 'R')
1117: return super .getRequestURI();
1118:
1119: String _rawURI = super .getRequestURI();
1120: CharBuffer cb = CharBuffer.allocate();
1121:
1122: for (int i = 0; i < _rawURI.length(); i++) {
1123: char ch = _rawURI.charAt(i);
1124:
1125: if (ch <= ' ' || ch >= 0x80 || ch == '%') {
1126: addHex(cb, ch);
1127: } else
1128: cb.append(ch);
1129: }
1130:
1131: return cb.close();
1132: }
1133:
1134: /**
1135: * Adds a hex escape.
1136: *
1137: * @param cb the char buffer containing the escape.
1138: * @param ch the character to be escaped.
1139: */
1140: private void addHex(CharBuffer cb, int ch) {
1141: cb.append('%');
1142:
1143: int d = (ch >> 4) & 0xf;
1144: if (d < 10)
1145: cb.append((char) ('0' + d));
1146: else
1147: cb.append((char) ('a' + d - 10));
1148:
1149: d = ch & 0xf;
1150: if (d < 10)
1151: cb.append((char) ('0' + d));
1152: else
1153: cb.append((char) ('a' + d - 10));
1154: }
1155:
1156: /**
1157: * Returns the server name.
1158: */
1159: public String getServerName() {
1160: CharBuffer host = getHost();
1161: if (host == null) {
1162: InetAddress addr = getConnection().getRemoteAddress();
1163: return addr.getHostName();
1164: }
1165:
1166: int p = host.indexOf(':');
1167: if (p >= 0)
1168: return host.substring(0, p);
1169: else
1170: return host.toString();
1171: }
1172:
1173: public int getServerPort() {
1174: int len = _serverPort.length();
1175: int port = 0;
1176: for (int i = 0; i < len; i++) {
1177: char ch = _serverPort.charAt(i);
1178: port = 10 * port + ch - '0';
1179: }
1180:
1181: return port;
1182: }
1183:
1184: public String getRemoteAddr() {
1185: return _remoteAddr.toString();
1186: }
1187:
1188: public void getRemoteAddr(CharBuffer cb) {
1189: cb.append(_remoteAddr);
1190: }
1191:
1192: public int printRemoteAddr(byte[] buffer, int offset)
1193: throws IOException {
1194: char[] buf = _remoteAddr.getBuffer();
1195: int len = _remoteAddr.getLength();
1196:
1197: for (int i = 0; i < len; i++)
1198: buffer[offset + i] = (byte) buf[i];
1199:
1200: return offset + len;
1201: }
1202:
1203: public String getRemoteHost() {
1204: return _remoteHost.toString();
1205: }
1206:
1207: /**
1208: * Called for a connection: close
1209: */
1210: protected void connectionClose() {
1211: // ignore for hmux
1212: }
1213:
1214: // Response data
1215: void writeStatus(CharBuffer message) throws IOException {
1216: int channel = 2;
1217:
1218: WriteStream os = _rawWrite;
1219:
1220: os.write(HMUX_CHANNEL);
1221: os.write(channel >> 8);
1222: os.write(channel);
1223:
1224: writeString(HMUX_STATUS, message);
1225: }
1226:
1227: /**
1228: * Complete sending of all headers.
1229: */
1230: void sendHeader() throws IOException {
1231: writeString(CSE_SEND_HEADER, "");
1232: }
1233:
1234: /**
1235: * Writes a header to the plugin.
1236: *
1237: * @param key the header's key
1238: * @param value the header's value
1239: */
1240: void writeHeader(String key, String value) throws IOException {
1241: writeString(HMUX_HEADER, key);
1242: writeString(HMUX_STRING, value);
1243: }
1244:
1245: /**
1246: * Writes a header to the plugin.
1247: *
1248: * @param key the header's key
1249: * @param value the header's value
1250: */
1251: void writeHeader(String key, CharBuffer value) throws IOException {
1252: writeString(HMUX_HEADER, key);
1253: writeString(HMUX_STRING, value);
1254: }
1255:
1256: void writeString(int code, String value) throws IOException {
1257: int len = value.length();
1258:
1259: WriteStream os = _rawWrite;
1260:
1261: os.write(code);
1262: os.write(len >> 8);
1263: os.write(len);
1264: os.print(value);
1265:
1266: if (log.isLoggable(Level.FINE))
1267: log.fine(dbgId() + (char) code + " " + value);
1268: }
1269:
1270: void writeString(int code, CharBuffer cb) throws IOException {
1271: int len = cb.length();
1272:
1273: WriteStream os = _rawWrite;
1274:
1275: os.write(code);
1276: os.write(len >> 8);
1277: os.write(len);
1278: os.print(cb.getBuffer(), 0, len);
1279:
1280: if (log.isLoggable(Level.FINE))
1281: log.fine(dbgId() + (char) code + " " + cb);
1282: }
1283:
1284: public void protocolCloseEvent() {
1285: }
1286:
1287: public final String dbgId() {
1288: String id = _server.getServerId();
1289:
1290: if (id.equals(""))
1291: return "Hmux[" + getConnection().getId() + "] ";
1292: else
1293: return "Hmux[" + id + ":" + getConnection().getId() + "] ";
1294: }
1295:
1296: public String toString() {
1297: return "HmuxRequest" + dbgId();
1298: }
1299:
1300: /**
1301: * Implements the protocol for data reads and writes. Data from the
1302: * web server to the JVM must be acked, except for the first data.
1303: * Data back to the web server needs no ack.
1304: */
1305: static class ServletFilter extends StreamImpl {
1306: HmuxRequest _request;
1307: ReadStream _is;
1308: WriteStream _os;
1309: byte[] _buffer = new byte[16];
1310: int _pendingData;
1311: boolean _needsAck;
1312: boolean _isClosed;
1313: boolean _isClientClosed;
1314:
1315: ServletFilter() {
1316: }
1317:
1318: void init(HmuxRequest request, ReadStream nextRead,
1319: WriteStream nextWrite) {
1320: _request = request;
1321: _is = nextRead;
1322: _os = nextWrite;
1323: _pendingData = 0;
1324: _isClosed = false;
1325: _isClientClosed = false;
1326: _needsAck = false;
1327: }
1328:
1329: void setPending(int pendingData) {
1330: _pendingData = pendingData;
1331: }
1332:
1333: void setClientClosed(boolean isClientClosed) {
1334: _isClientClosed = isClientClosed;
1335: }
1336:
1337: public boolean canRead() {
1338: return true;
1339: }
1340:
1341: public int getAvailable() {
1342: return _pendingData;
1343: }
1344:
1345: /**
1346: * Reads available data. If the data needs an ack, then do so.
1347: */
1348: public int read(byte[] buf, int offset, int length)
1349: throws IOException {
1350: int sublen = _pendingData;
1351: ReadStream is = _is;
1352:
1353: if (sublen <= 0)
1354: return -1;
1355:
1356: if (length < sublen)
1357: sublen = length;
1358:
1359: int readLen = is.read(buf, offset, sublen);
1360: _pendingData -= readLen;
1361:
1362: if (log.isLoggable(Level.FINEST))
1363: log.finest(new String(buf, offset, readLen));
1364:
1365: while (_pendingData == 0) {
1366: if (_needsAck) {
1367: int channel = 2;
1368:
1369: _os.write(HMUX_ACK);
1370: _os.write(channel >> 8);
1371: _os.write(channel);
1372:
1373: if (log.isLoggable(Level.FINE))
1374: log.fine(_request.dbgId() + "A:ack channel 2");
1375: }
1376:
1377: _needsAck = false;
1378:
1379: int code = is.read();
1380:
1381: if (code == HMUX_DATA) {
1382: int len = (is.read() << 8) + is.read();
1383:
1384: if (log.isLoggable(Level.FINE))
1385: log.fine(_request.dbgId() + "D:post-data "
1386: + len);
1387:
1388: _pendingData = len;
1389: } else if (code == HMUX_QUIT) {
1390: if (log.isLoggable(Level.FINE))
1391: log.fine(_request.dbgId() + "Q:quit");
1392:
1393: return readLen;
1394: } else if (code == HMUX_EXIT) {
1395: if (log.isLoggable(Level.FINE))
1396: log.fine(_request.dbgId() + "X:exit");
1397:
1398: _request.killKeepalive();
1399: return readLen;
1400: } else if (code == HMUX_YIELD) {
1401: _needsAck = true;
1402: } else if (code == HMUX_CHANNEL) {
1403: int channel = (is.read() << 8) + is.read();
1404:
1405: if (log.isLoggable(Level.FINE))
1406: log.fine(_request.dbgId() + "channel "
1407: + channel);
1408: } else if (code < 0) {
1409: _request.killKeepalive();
1410:
1411: return readLen;
1412: } else {
1413: _request.killKeepalive();
1414:
1415: int len = (is.read() << 8) + is.read();
1416:
1417: if (log.isLoggable(Level.FINE))
1418: log.fine(_request.dbgId() + "unknown `"
1419: + (char) code + "' " + len);
1420:
1421: is.skip(len);
1422: }
1423: }
1424:
1425: return readLen;
1426: }
1427:
1428: public boolean canWrite() {
1429: return true;
1430: }
1431:
1432: /**
1433: * Send data back to the web server
1434: */
1435: public void write(byte[] buf, int offset, int length,
1436: boolean isEnd) throws IOException {
1437: if (log.isLoggable(Level.FINE)) {
1438: log.fine(_request.dbgId() + (char) HMUX_DATA + ":data "
1439: + length);
1440:
1441: if (log.isLoggable(Level.FINEST))
1442: log.finest(_request.dbgId() + "data <"
1443: + new String(buf, offset, length) + ">");
1444: }
1445:
1446: byte[] tempBuf = _buffer;
1447:
1448: while (length > 0) {
1449: int sublen = length;
1450:
1451: if (32 * 1024 < sublen)
1452: sublen = 32 * 1024;
1453:
1454: // The 3 bytes are already allocated by setPrefixWrite
1455: tempBuf[0] = HMUX_DATA;
1456: tempBuf[1] = (byte) (sublen >> 8);
1457: tempBuf[2] = (byte) sublen;
1458:
1459: _os.write(tempBuf, 0, 3);
1460: _os.write(buf, offset, sublen);
1461:
1462: length -= sublen;
1463: offset += sublen;
1464: }
1465: }
1466:
1467: public void flush() throws IOException {
1468: if (log.isLoggable(Level.FINE))
1469: log.fine(_request.dbgId() + (char) HMUX_FLUSH
1470: + ":flush");
1471:
1472: _os.write(HMUX_FLUSH);
1473: _os.write(0);
1474: _os.write(0);
1475: _os.flush();
1476: }
1477:
1478: public void close() throws IOException {
1479: if (_isClosed)
1480: return;
1481:
1482: _isClosed = true;
1483:
1484: if (_pendingData > 0) {
1485: _is.skip(_pendingData);
1486: _pendingData = 0;
1487: }
1488:
1489: boolean keepalive = _request.allowKeepalive();
1490:
1491: if (!_isClientClosed) {
1492: if (log.isLoggable(Level.FINE)) {
1493: if (keepalive)
1494: log.fine(_request.dbgId() + (char) HMUX_QUIT
1495: + ": quit channel");
1496: else
1497: log.fine(_request.dbgId() + (char) HMUX_EXIT
1498: + ": exit socket");
1499: }
1500:
1501: if (keepalive)
1502: _os.write(HMUX_QUIT);
1503: else
1504: _os.write(HMUX_EXIT);
1505: }
1506:
1507: if (keepalive)
1508: _os.flush();
1509: else
1510: _os.close();
1511: //nextRead.close();
1512: }
1513: }
1514: }
|