0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package org.apache.coyote.ajp;
0019:
0020: import java.io.ByteArrayInputStream;
0021: import java.io.IOException;
0022: import java.io.InterruptedIOException;
0023: import java.net.InetAddress;
0024: import java.nio.ByteBuffer;
0025: import java.security.cert.CertificateFactory;
0026: import java.security.cert.X509Certificate;
0027:
0028: import org.apache.coyote.ActionCode;
0029: import org.apache.coyote.ActionHook;
0030: import org.apache.coyote.Adapter;
0031: import org.apache.coyote.InputBuffer;
0032: import org.apache.coyote.OutputBuffer;
0033: import org.apache.coyote.Request;
0034: import org.apache.coyote.RequestInfo;
0035: import org.apache.coyote.Response;
0036: import org.apache.tomcat.jni.Socket;
0037: import org.apache.tomcat.jni.Status;
0038: import org.apache.tomcat.util.buf.ByteChunk;
0039: import org.apache.tomcat.util.buf.HexUtils;
0040: import org.apache.tomcat.util.buf.MessageBytes;
0041: import org.apache.tomcat.util.http.HttpMessages;
0042: import org.apache.tomcat.util.http.MimeHeaders;
0043: import org.apache.tomcat.util.net.AprEndpoint;
0044: import org.apache.tomcat.util.res.StringManager;
0045:
0046: /**
0047: * Processes HTTP requests.
0048: *
0049: * @author Remy Maucherat
0050: * @author Henri Gomez
0051: * @author Dan Milstein
0052: * @author Keith Wannamaker
0053: * @author Kevin Seguin
0054: * @author Costin Manolache
0055: * @author Bill Barker
0056: */
0057: public class AjpAprProcessor implements ActionHook {
0058:
0059: /**
0060: * Logger.
0061: */
0062: protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
0063: .getLog(AjpAprProcessor.class);
0064:
0065: /**
0066: * The string manager for this package.
0067: */
0068: protected static StringManager sm = StringManager
0069: .getManager(Constants.Package);
0070:
0071: // ----------------------------------------------------------- Constructors
0072:
0073: public AjpAprProcessor(int packetSize, AprEndpoint endpoint) {
0074:
0075: this .endpoint = endpoint;
0076:
0077: request = new Request();
0078: request.setInputBuffer(new SocketInputBuffer());
0079:
0080: response = new Response();
0081: response.setHook(this );
0082: response.setOutputBuffer(new SocketOutputBuffer());
0083: request.setResponse(response);
0084:
0085: requestHeaderMessage = new AjpMessage(packetSize);
0086: responseHeaderMessage = new AjpMessage(packetSize);
0087: bodyMessage = new AjpMessage(packetSize);
0088:
0089: // Allocate input and output buffers
0090: inputBuffer = ByteBuffer.allocateDirect(packetSize * 2);
0091: inputBuffer.limit(0);
0092: outputBuffer = ByteBuffer.allocateDirect(packetSize * 2);
0093:
0094: // Cause loading of HexUtils
0095: int foo = HexUtils.DEC[0];
0096:
0097: // Cause loading of HttpMessages
0098: HttpMessages.getMessage(200);
0099:
0100: }
0101:
0102: // ----------------------------------------------------- Instance Variables
0103:
0104: /**
0105: * Associated adapter.
0106: */
0107: protected Adapter adapter = null;
0108:
0109: /**
0110: * Request object.
0111: */
0112: protected Request request = null;
0113:
0114: /**
0115: * Response object.
0116: */
0117: protected Response response = null;
0118:
0119: /**
0120: * Header message. Note that this header is merely the one used during the
0121: * processing of the first message of a "request", so it might not be a request
0122: * header. It will stay unchanged during the processing of the whole request.
0123: */
0124: protected AjpMessage requestHeaderMessage = null;
0125:
0126: /**
0127: * Message used for response header composition.
0128: */
0129: protected AjpMessage responseHeaderMessage = null;
0130:
0131: /**
0132: * Body message.
0133: */
0134: protected AjpMessage bodyMessage = null;
0135:
0136: /**
0137: * Body message.
0138: */
0139: protected MessageBytes bodyBytes = MessageBytes.newInstance();
0140:
0141: /**
0142: * State flag.
0143: */
0144: protected boolean started = false;
0145:
0146: /**
0147: * Error flag.
0148: */
0149: protected boolean error = false;
0150:
0151: /**
0152: * Socket associated with the current connection.
0153: */
0154: protected long socket;
0155:
0156: /**
0157: * Host name (used to avoid useless B2C conversion on the host name).
0158: */
0159: protected char[] hostNameC = new char[0];
0160:
0161: /**
0162: * Associated endpoint.
0163: */
0164: protected AprEndpoint endpoint;
0165:
0166: /**
0167: * Temp message bytes used for processing.
0168: */
0169: protected MessageBytes tmpMB = MessageBytes.newInstance();
0170:
0171: /**
0172: * Byte chunk for certs.
0173: */
0174: protected MessageBytes certificates = MessageBytes.newInstance();
0175:
0176: /**
0177: * End of stream flag.
0178: */
0179: protected boolean endOfStream = false;
0180:
0181: /**
0182: * Body empty flag.
0183: */
0184: protected boolean empty = true;
0185:
0186: /**
0187: * First read.
0188: */
0189: protected boolean first = true;
0190:
0191: /**
0192: * Replay read.
0193: */
0194: protected boolean replay = false;
0195:
0196: /**
0197: * Finished response.
0198: */
0199: protected boolean finished = false;
0200:
0201: /**
0202: * Direct buffer used for output.
0203: */
0204: protected ByteBuffer outputBuffer = null;
0205:
0206: /**
0207: * Direct buffer used for input.
0208: */
0209: protected ByteBuffer inputBuffer = null;
0210:
0211: /**
0212: * Direct buffer used for sending right away a get body message.
0213: */
0214: protected static final ByteBuffer getBodyMessageBuffer;
0215:
0216: /**
0217: * Direct buffer used for sending right away a pong message.
0218: */
0219: protected static final ByteBuffer pongMessageBuffer;
0220:
0221: /**
0222: * End message array.
0223: */
0224: protected static final byte[] endMessageArray;
0225:
0226: /**
0227: * Direct buffer used for sending explicit flush message.
0228: */
0229: protected static final ByteBuffer flushMessageBuffer;
0230:
0231: // ----------------------------------------------------- Static Initializer
0232:
0233: static {
0234:
0235: // Set the get body message buffer
0236: AjpMessage getBodyMessage = new AjpMessage(16);
0237: getBodyMessage.reset();
0238: getBodyMessage.appendByte(Constants.JK_AJP13_GET_BODY_CHUNK);
0239: getBodyMessage.appendInt(Constants.MAX_READ_SIZE);
0240: getBodyMessage.end();
0241: getBodyMessageBuffer = ByteBuffer.allocateDirect(getBodyMessage
0242: .getLen());
0243: getBodyMessageBuffer.put(getBodyMessage.getBuffer(), 0,
0244: getBodyMessage.getLen());
0245:
0246: // Set the read body message buffer
0247: AjpMessage pongMessage = new AjpMessage(16);
0248: pongMessage.reset();
0249: pongMessage.appendByte(Constants.JK_AJP13_CPONG_REPLY);
0250: pongMessage.end();
0251: pongMessageBuffer = ByteBuffer.allocateDirect(pongMessage
0252: .getLen());
0253: pongMessageBuffer.put(pongMessage.getBuffer(), 0, pongMessage
0254: .getLen());
0255:
0256: // Allocate the end message array
0257: AjpMessage endMessage = new AjpMessage(16);
0258: endMessage.reset();
0259: endMessage.appendByte(Constants.JK_AJP13_END_RESPONSE);
0260: endMessage.appendByte(1);
0261: endMessage.end();
0262: endMessageArray = new byte[endMessage.getLen()];
0263: System.arraycopy(endMessage.getBuffer(), 0, endMessageArray, 0,
0264: endMessage.getLen());
0265:
0266: // Set the flush message buffer
0267: AjpMessage flushMessage = new AjpMessage(16);
0268: flushMessage.reset();
0269: flushMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
0270: flushMessage.appendInt(0);
0271: flushMessage.appendByte(0);
0272: flushMessage.end();
0273: flushMessageBuffer = ByteBuffer.allocateDirect(flushMessage
0274: .getLen());
0275: flushMessageBuffer.put(flushMessage.getBuffer(), 0,
0276: flushMessage.getLen());
0277:
0278: }
0279:
0280: // ------------------------------------------------------------- Properties
0281:
0282: /**
0283: * Use Tomcat authentication ?
0284: */
0285: protected boolean tomcatAuthentication = true;
0286:
0287: public boolean getTomcatAuthentication() {
0288: return tomcatAuthentication;
0289: }
0290:
0291: public void setTomcatAuthentication(boolean tomcatAuthentication) {
0292: this .tomcatAuthentication = tomcatAuthentication;
0293: }
0294:
0295: /**
0296: * Required secret.
0297: */
0298: protected String requiredSecret = null;
0299:
0300: public void setRequiredSecret(String requiredSecret) {
0301: this .requiredSecret = requiredSecret;
0302: }
0303:
0304: // --------------------------------------------------------- Public Methods
0305:
0306: /** Get the request associated with this processor.
0307: *
0308: * @return The request
0309: */
0310: public Request getRequest() {
0311: return request;
0312: }
0313:
0314: /**
0315: * Process pipelined HTTP requests using the specified input and output
0316: * streams.
0317: *
0318: * @throws IOException error during an I/O operation
0319: */
0320: public boolean process(long socket) throws IOException {
0321: RequestInfo rp = request.getRequestProcessor();
0322: rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
0323:
0324: // Setting up the socket
0325: this .socket = socket;
0326: Socket.setrbb(this .socket, inputBuffer);
0327: Socket.setsbb(this .socket, outputBuffer);
0328:
0329: // Error flag
0330: error = false;
0331:
0332: boolean openSocket = true;
0333: boolean keptAlive = false;
0334:
0335: while (started && !error) {
0336:
0337: // Parsing the request header
0338: try {
0339: // Get first message of the request
0340: if (!readMessage(requestHeaderMessage, true, keptAlive)) {
0341: // This means that no data is available right now
0342: // (long keepalive), so that the processor should be recycled
0343: // and the method should return true
0344: rp
0345: .setStage(org.apache.coyote.Constants.STAGE_ENDED);
0346: break;
0347: }
0348: // Check message type, process right away and break if
0349: // not regular request processing
0350: int type = requestHeaderMessage.getByte();
0351: if (type == Constants.JK_AJP13_CPING_REQUEST) {
0352: if (Socket.sendb(socket, pongMessageBuffer, 0,
0353: pongMessageBuffer.position()) < 0) {
0354: error = true;
0355: }
0356: continue;
0357: } else if (type != Constants.JK_AJP13_FORWARD_REQUEST) {
0358: // Usually the servlet didn't read the previous request body
0359: if (log.isDebugEnabled()) {
0360: log.debug("Unexpected message: " + type);
0361: }
0362: continue;
0363: }
0364:
0365: keptAlive = true;
0366: request.setStartTime(System.currentTimeMillis());
0367: } catch (IOException e) {
0368: error = true;
0369: break;
0370: } catch (Throwable t) {
0371: log.debug(sm.getString("ajpprocessor.header.error"), t);
0372: // 400 - Bad Request
0373: response.setStatus(400);
0374: error = true;
0375: }
0376:
0377: // Setting up filters, and parse some request headers
0378: rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
0379: try {
0380: prepareRequest();
0381: } catch (Throwable t) {
0382: log.debug(sm.getString("ajpprocessor.request.prepare"),
0383: t);
0384: // 400 - Internal Server Error
0385: response.setStatus(400);
0386: error = true;
0387: }
0388:
0389: // Process the request in the adapter
0390: if (!error) {
0391: try {
0392: rp
0393: .setStage(org.apache.coyote.Constants.STAGE_SERVICE);
0394: adapter.service(request, response);
0395: } catch (InterruptedIOException e) {
0396: error = true;
0397: } catch (Throwable t) {
0398: log.error(sm
0399: .getString("ajpprocessor.request.process"),
0400: t);
0401: // 500 - Internal Server Error
0402: response.setStatus(500);
0403: error = true;
0404: }
0405: }
0406:
0407: // Finish the response if not done yet
0408: if (!finished) {
0409: try {
0410: finish();
0411: } catch (Throwable t) {
0412: error = true;
0413: }
0414: }
0415:
0416: // If there was an error, make sure the request is counted as
0417: // and error, and update the statistics counter
0418: if (error) {
0419: response.setStatus(500);
0420: }
0421: request.updateCounters();
0422:
0423: rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
0424: recycle();
0425:
0426: }
0427:
0428: // Add the socket to the poller
0429: if (!error) {
0430: endpoint.getPoller().add(socket);
0431: } else {
0432: openSocket = false;
0433: }
0434:
0435: rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
0436: recycle();
0437:
0438: return openSocket;
0439:
0440: }
0441:
0442: // ----------------------------------------------------- ActionHook Methods
0443:
0444: /**
0445: * Send an action to the connector.
0446: *
0447: * @param actionCode Type of the action
0448: * @param param Action parameter
0449: */
0450: public void action(ActionCode actionCode, Object param) {
0451:
0452: if (actionCode == ActionCode.ACTION_COMMIT) {
0453:
0454: if (response.isCommitted())
0455: return;
0456:
0457: // Validate and write response headers
0458: try {
0459: prepareResponse();
0460: } catch (IOException e) {
0461: // Set error flag
0462: error = true;
0463: }
0464:
0465: } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
0466:
0467: if (!response.isCommitted()) {
0468: // Validate and write response headers
0469: try {
0470: prepareResponse();
0471: } catch (IOException e) {
0472: // Set error flag
0473: error = true;
0474: return;
0475: }
0476: }
0477:
0478: try {
0479: flush();
0480: // Send explicit flush message
0481: if (Socket.sendb(socket, flushMessageBuffer, 0,
0482: flushMessageBuffer.position()) < 0) {
0483: error = true;
0484: }
0485: } catch (IOException e) {
0486: // Set error flag
0487: error = true;
0488: }
0489:
0490: } else if (actionCode == ActionCode.ACTION_CLOSE) {
0491: // Close
0492:
0493: // End the processing of the current request, and stop any further
0494: // transactions with the client
0495:
0496: try {
0497: finish();
0498: } catch (IOException e) {
0499: // Set error flag
0500: error = true;
0501: }
0502:
0503: } else if (actionCode == ActionCode.ACTION_START) {
0504:
0505: started = true;
0506:
0507: } else if (actionCode == ActionCode.ACTION_STOP) {
0508:
0509: started = false;
0510:
0511: } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE) {
0512:
0513: if (!certificates.isNull()) {
0514: ByteChunk certData = certificates.getByteChunk();
0515: X509Certificate jsseCerts[] = null;
0516: ByteArrayInputStream bais = new ByteArrayInputStream(
0517: certData.getBytes(), certData.getStart(),
0518: certData.getLength());
0519: // Fill the first element.
0520: try {
0521: CertificateFactory cf = CertificateFactory
0522: .getInstance("X.509");
0523: X509Certificate cert = (X509Certificate) cf
0524: .generateCertificate(bais);
0525: jsseCerts = new X509Certificate[1];
0526: jsseCerts[0] = cert;
0527: request.setAttribute(AprEndpoint.CERTIFICATE_KEY,
0528: jsseCerts);
0529: } catch (java.security.cert.CertificateException e) {
0530: log.error(sm.getString("ajpprocessor.certs.fail"),
0531: e);
0532: return;
0533: }
0534: }
0535:
0536: } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
0537:
0538: // Get remote host name using a DNS resolution
0539: if (request.remoteHost().isNull()) {
0540: try {
0541: request.remoteHost().setString(
0542: InetAddress.getByName(
0543: request.remoteAddr().toString())
0544: .getHostName());
0545: } catch (IOException iex) {
0546: // Ignore
0547: }
0548: }
0549:
0550: } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
0551:
0552: // Copy from local name for now, which should simply be an address
0553: request.localAddr().setString(
0554: request.localName().toString());
0555:
0556: } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
0557:
0558: // Set the given bytes as the content
0559: ByteChunk bc = (ByteChunk) param;
0560: int length = bc.getLength();
0561: bodyBytes.setBytes(bc.getBytes(), bc.getStart(), length);
0562: request.setContentLength(length);
0563: first = false;
0564: empty = false;
0565: replay = true;
0566:
0567: }
0568:
0569: }
0570:
0571: // ------------------------------------------------------ Connector Methods
0572:
0573: /**
0574: * Set the associated adapter.
0575: *
0576: * @param adapter the new adapter
0577: */
0578: public void setAdapter(Adapter adapter) {
0579: this .adapter = adapter;
0580: }
0581:
0582: /**
0583: * Get the associated adapter.
0584: *
0585: * @return the associated adapter
0586: */
0587: public Adapter getAdapter() {
0588: return adapter;
0589: }
0590:
0591: // ------------------------------------------------------ Protected Methods
0592:
0593: /**
0594: * After reading the request headers, we have to setup the request filters.
0595: */
0596: protected void prepareRequest() {
0597:
0598: // Translate the HTTP method code to a String.
0599: byte methodCode = requestHeaderMessage.getByte();
0600: if (methodCode != Constants.SC_M_JK_STORED) {
0601: String methodName = Constants.methodTransArray[(int) methodCode - 1];
0602: request.method().setString(methodName);
0603: }
0604:
0605: requestHeaderMessage.getBytes(request.protocol());
0606: requestHeaderMessage.getBytes(request.requestURI());
0607:
0608: requestHeaderMessage.getBytes(request.remoteAddr());
0609: requestHeaderMessage.getBytes(request.remoteHost());
0610: requestHeaderMessage.getBytes(request.localName());
0611: request.setLocalPort(requestHeaderMessage.getInt());
0612:
0613: boolean isSSL = requestHeaderMessage.getByte() != 0;
0614: if (isSSL) {
0615: request.scheme().setString("https");
0616: }
0617:
0618: // Decode headers
0619: MimeHeaders headers = request.getMimeHeaders();
0620:
0621: int hCount = requestHeaderMessage.getInt();
0622: for (int i = 0; i < hCount; i++) {
0623: String hName = null;
0624:
0625: // Header names are encoded as either an integer code starting
0626: // with 0xA0, or as a normal string (in which case the first
0627: // two bytes are the length).
0628: int isc = requestHeaderMessage.peekInt();
0629: int hId = isc & 0xFF;
0630:
0631: MessageBytes vMB = null;
0632: isc &= 0xFF00;
0633: if (0xA000 == isc) {
0634: requestHeaderMessage.getInt(); // To advance the read position
0635: hName = Constants.headerTransArray[hId - 1];
0636: vMB = headers.addValue(hName);
0637: } else {
0638: // reset hId -- if the header currently being read
0639: // happens to be 7 or 8 bytes long, the code below
0640: // will think it's the content-type header or the
0641: // content-length header - SC_REQ_CONTENT_TYPE=7,
0642: // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
0643: // behaviour. see bug 5861 for more information.
0644: hId = -1;
0645: requestHeaderMessage.getBytes(tmpMB);
0646: ByteChunk bc = tmpMB.getByteChunk();
0647: vMB = headers.addValue(bc.getBuffer(), bc.getStart(),
0648: bc.getLength());
0649: }
0650:
0651: requestHeaderMessage.getBytes(vMB);
0652:
0653: if (hId == Constants.SC_REQ_CONTENT_LENGTH
0654: || (hId == -1 && tmpMB
0655: .equalsIgnoreCase("Content-Length"))) {
0656: // just read the content-length header, so set it
0657: long cl = vMB.getLong();
0658: if (cl < Integer.MAX_VALUE)
0659: request.setContentLength((int) cl);
0660: } else if (hId == Constants.SC_REQ_CONTENT_TYPE
0661: || (hId == -1 && tmpMB
0662: .equalsIgnoreCase("Content-Type"))) {
0663: // just read the content-type header, so set it
0664: ByteChunk bchunk = vMB.getByteChunk();
0665: request.contentType().setBytes(bchunk.getBytes(),
0666: bchunk.getOffset(), bchunk.getLength());
0667: }
0668: }
0669:
0670: // Decode extra attributes
0671: boolean secret = false;
0672: byte attributeCode;
0673: while ((attributeCode = requestHeaderMessage.getByte()) != Constants.SC_A_ARE_DONE) {
0674:
0675: switch (attributeCode) {
0676:
0677: case Constants.SC_A_REQ_ATTRIBUTE:
0678: requestHeaderMessage.getBytes(tmpMB);
0679: String n = tmpMB.toString();
0680: requestHeaderMessage.getBytes(tmpMB);
0681: String v = tmpMB.toString();
0682: request.setAttribute(n, v);
0683: break;
0684:
0685: case Constants.SC_A_CONTEXT:
0686: requestHeaderMessage.getBytes(tmpMB);
0687: // nothing
0688: break;
0689:
0690: case Constants.SC_A_SERVLET_PATH:
0691: requestHeaderMessage.getBytes(tmpMB);
0692: // nothing
0693: break;
0694:
0695: case Constants.SC_A_REMOTE_USER:
0696: if (tomcatAuthentication) {
0697: // ignore server
0698: requestHeaderMessage.getBytes(tmpMB);
0699: } else {
0700: requestHeaderMessage.getBytes(request
0701: .getRemoteUser());
0702: }
0703: break;
0704:
0705: case Constants.SC_A_AUTH_TYPE:
0706: if (tomcatAuthentication) {
0707: // ignore server
0708: requestHeaderMessage.getBytes(tmpMB);
0709: } else {
0710: requestHeaderMessage
0711: .getBytes(request.getAuthType());
0712: }
0713: break;
0714:
0715: case Constants.SC_A_QUERY_STRING:
0716: requestHeaderMessage.getBytes(request.queryString());
0717: break;
0718:
0719: case Constants.SC_A_JVM_ROUTE:
0720: requestHeaderMessage.getBytes(request.instanceId());
0721: break;
0722:
0723: case Constants.SC_A_SSL_CERT:
0724: request.scheme().setString("https");
0725: // SSL certificate extraction is lazy, moved to JkCoyoteHandler
0726: requestHeaderMessage.getBytes(certificates);
0727: break;
0728:
0729: case Constants.SC_A_SSL_CIPHER:
0730: request.scheme().setString("https");
0731: requestHeaderMessage.getBytes(tmpMB);
0732: request.setAttribute(AprEndpoint.CIPHER_SUITE_KEY,
0733: tmpMB.toString());
0734: break;
0735:
0736: case Constants.SC_A_SSL_SESSION:
0737: request.scheme().setString("https");
0738: requestHeaderMessage.getBytes(tmpMB);
0739: request.setAttribute(AprEndpoint.SESSION_ID_KEY, tmpMB
0740: .toString());
0741: break;
0742:
0743: case Constants.SC_A_SSL_KEY_SIZE:
0744: request.setAttribute(AprEndpoint.KEY_SIZE_KEY,
0745: new Integer(requestHeaderMessage.getInt()));
0746: break;
0747:
0748: case Constants.SC_A_STORED_METHOD:
0749: requestHeaderMessage.getBytes(request.method());
0750: break;
0751:
0752: case Constants.SC_A_SECRET:
0753: requestHeaderMessage.getBytes(tmpMB);
0754: if (requiredSecret != null) {
0755: secret = true;
0756: if (!tmpMB.equals(requiredSecret)) {
0757: response.setStatus(403);
0758: error = true;
0759: }
0760: }
0761: break;
0762:
0763: default:
0764: // Ignore unknown attribute for backward compatibility
0765: break;
0766:
0767: }
0768:
0769: }
0770:
0771: // Check if secret was submitted if required
0772: if ((requiredSecret != null) && !secret) {
0773: response.setStatus(403);
0774: error = true;
0775: }
0776:
0777: // Check for a full URI (including protocol://host:port/)
0778: ByteChunk uriBC = request.requestURI().getByteChunk();
0779: if (uriBC.startsWithIgnoreCase("http", 0)) {
0780:
0781: int pos = uriBC.indexOf("://", 0, 3, 4);
0782: int uriBCStart = uriBC.getStart();
0783: int slashPos = -1;
0784: if (pos != -1) {
0785: byte[] uriB = uriBC.getBytes();
0786: slashPos = uriBC.indexOf('/', pos + 3);
0787: if (slashPos == -1) {
0788: slashPos = uriBC.getLength();
0789: // Set URI as "/"
0790: request.requestURI().setBytes(uriB,
0791: uriBCStart + pos + 1, 1);
0792: } else {
0793: request.requestURI().setBytes(uriB,
0794: uriBCStart + slashPos,
0795: uriBC.getLength() - slashPos);
0796: }
0797: MessageBytes hostMB = headers.setValue("host");
0798: hostMB.setBytes(uriB, uriBCStart + pos + 3, slashPos
0799: - pos - 3);
0800: }
0801:
0802: }
0803:
0804: MessageBytes valueMB = request.getMimeHeaders()
0805: .getValue("host");
0806: parseHost(valueMB);
0807:
0808: }
0809:
0810: /**
0811: * Parse host.
0812: */
0813: public void parseHost(MessageBytes valueMB) {
0814:
0815: if (valueMB == null || (valueMB != null && valueMB.isNull())) {
0816: // HTTP/1.0
0817: // Default is what the socket tells us. Overriden if a host is
0818: // found/parsed
0819: request.setServerPort(endpoint.getPort());
0820: return;
0821: }
0822:
0823: ByteChunk valueBC = valueMB.getByteChunk();
0824: byte[] valueB = valueBC.getBytes();
0825: int valueL = valueBC.getLength();
0826: int valueS = valueBC.getStart();
0827: int colonPos = -1;
0828: if (hostNameC.length < valueL) {
0829: hostNameC = new char[valueL];
0830: }
0831:
0832: boolean ipv6 = (valueB[valueS] == '[');
0833: boolean bracketClosed = false;
0834: for (int i = 0; i < valueL; i++) {
0835: char b = (char) valueB[i + valueS];
0836: hostNameC[i] = b;
0837: if (b == ']') {
0838: bracketClosed = true;
0839: } else if (b == ':') {
0840: if (!ipv6 || bracketClosed) {
0841: colonPos = i;
0842: break;
0843: }
0844: }
0845: }
0846:
0847: if (colonPos < 0) {
0848: if (request.scheme().equalsIgnoreCase("https")) {
0849: // 443 - Default HTTPS port
0850: request.setServerPort(443);
0851: } else {
0852: // 80 - Default HTTTP port
0853: request.setServerPort(80);
0854: }
0855: request.serverName().setChars(hostNameC, 0, valueL);
0856: } else {
0857:
0858: request.serverName().setChars(hostNameC, 0, colonPos);
0859:
0860: int port = 0;
0861: int mult = 1;
0862: for (int i = valueL - 1; i > colonPos; i--) {
0863: int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
0864: if (charValue == -1) {
0865: // Invalid character
0866: error = true;
0867: // 400 - Bad request
0868: response.setStatus(400);
0869: break;
0870: }
0871: port = port + (charValue * mult);
0872: mult = 10 * mult;
0873: }
0874: request.setServerPort(port);
0875:
0876: }
0877:
0878: }
0879:
0880: /**
0881: * When committing the response, we have to validate the set of headers, as
0882: * well as setup the response filters.
0883: */
0884: protected void prepareResponse() throws IOException {
0885:
0886: response.setCommitted(true);
0887:
0888: responseHeaderMessage.reset();
0889: responseHeaderMessage
0890: .appendByte(Constants.JK_AJP13_SEND_HEADERS);
0891:
0892: // HTTP header contents
0893: responseHeaderMessage.appendInt(response.getStatus());
0894: String message = response.getMessage();
0895: if (message == null) {
0896: message = HttpMessages.getMessage(response.getStatus());
0897: } else {
0898: message = message.replace('\n', ' ').replace('\r', ' ');
0899: }
0900: tmpMB.setString(message);
0901: responseHeaderMessage.appendBytes(tmpMB);
0902:
0903: // Special headers
0904: MimeHeaders headers = response.getMimeHeaders();
0905: String contentType = response.getContentType();
0906: if (contentType != null) {
0907: headers.setValue("Content-Type").setString(contentType);
0908: }
0909: String contentLanguage = response.getContentLanguage();
0910: if (contentLanguage != null) {
0911: headers.setValue("Content-Language").setString(
0912: contentLanguage);
0913: }
0914: long contentLength = response.getContentLengthLong();
0915: if (contentLength >= 0) {
0916: headers.setValue("Content-Length").setLong(contentLength);
0917: }
0918:
0919: // Other headers
0920: int numHeaders = headers.size();
0921: responseHeaderMessage.appendInt(numHeaders);
0922: for (int i = 0; i < numHeaders; i++) {
0923: MessageBytes hN = headers.getName(i);
0924: int hC = Constants.getResponseAjpIndex(hN.toString());
0925: if (hC > 0) {
0926: responseHeaderMessage.appendInt(hC);
0927: } else {
0928: responseHeaderMessage.appendBytes(hN);
0929: }
0930: MessageBytes hV = headers.getValue(i);
0931: responseHeaderMessage.appendBytes(hV);
0932: }
0933:
0934: // Write to buffer
0935: responseHeaderMessage.end();
0936: outputBuffer.put(responseHeaderMessage.getBuffer(), 0,
0937: responseHeaderMessage.getLen());
0938:
0939: }
0940:
0941: /**
0942: * Finish AJP response.
0943: */
0944: protected void finish() throws IOException {
0945:
0946: if (!response.isCommitted()) {
0947: // Validate and write response headers
0948: try {
0949: prepareResponse();
0950: } catch (IOException e) {
0951: // Set error flag
0952: error = true;
0953: }
0954: }
0955:
0956: if (finished)
0957: return;
0958:
0959: finished = true;
0960:
0961: // Add the end message
0962: if (outputBuffer.position() + endMessageArray.length > outputBuffer
0963: .capacity()) {
0964: flush();
0965: }
0966: outputBuffer.put(endMessageArray);
0967: flush();
0968:
0969: }
0970:
0971: /**
0972: * Read at least the specified amount of bytes, and place them
0973: * in the input buffer.
0974: */
0975: protected boolean read(int n) throws IOException {
0976:
0977: if (inputBuffer.capacity() - inputBuffer.limit() <= n
0978: - inputBuffer.remaining()) {
0979: inputBuffer.compact();
0980: inputBuffer.limit(inputBuffer.position());
0981: inputBuffer.position(0);
0982: }
0983: int nRead;
0984: while (inputBuffer.remaining() < n) {
0985: nRead = Socket.recvbb(socket, inputBuffer.limit(),
0986: inputBuffer.capacity() - inputBuffer.limit());
0987: if (nRead > 0) {
0988: inputBuffer.limit(inputBuffer.limit() + nRead);
0989: } else {
0990: throw new IOException(sm
0991: .getString("ajpprotocol.failedread"));
0992: }
0993: }
0994:
0995: return true;
0996:
0997: }
0998:
0999: /**
1000: * Read at least the specified amount of bytes, and place them
1001: * in the input buffer.
1002: */
1003: protected boolean readt(int n, boolean useAvailableData)
1004: throws IOException {
1005:
1006: if (useAvailableData && inputBuffer.remaining() == 0) {
1007: return false;
1008: }
1009: if (inputBuffer.capacity() - inputBuffer.limit() <= n
1010: - inputBuffer.remaining()) {
1011: inputBuffer.compact();
1012: inputBuffer.limit(inputBuffer.position());
1013: inputBuffer.position(0);
1014: }
1015: int nRead;
1016: while (inputBuffer.remaining() < n) {
1017: nRead = Socket.recvbb(socket, inputBuffer.limit(),
1018: inputBuffer.capacity() - inputBuffer.limit());
1019: if (nRead > 0) {
1020: inputBuffer.limit(inputBuffer.limit() + nRead);
1021: } else {
1022: if ((-nRead) == Status.ETIMEDOUT
1023: || (-nRead) == Status.TIMEUP) {
1024: return false;
1025: } else {
1026: throw new IOException(sm
1027: .getString("ajpprotocol.failedread"));
1028: }
1029: }
1030: }
1031:
1032: return true;
1033:
1034: }
1035:
1036: /** Receive a chunk of data. Called to implement the
1037: * 'special' packet in ajp13 and to receive the data
1038: * after we send a GET_BODY packet
1039: */
1040: public boolean receive() throws IOException {
1041:
1042: first = false;
1043: bodyMessage.reset();
1044: readMessage(bodyMessage, false, false);
1045:
1046: // No data received.
1047: if (bodyMessage.getLen() == 0) {
1048: // just the header
1049: // Don't mark 'end of stream' for the first chunk.
1050: return false;
1051: }
1052: int blen = bodyMessage.peekInt();
1053: if (blen == 0) {
1054: return false;
1055: }
1056:
1057: bodyMessage.getBytes(bodyBytes);
1058: empty = false;
1059: return true;
1060: }
1061:
1062: /**
1063: * Get more request body data from the web server and store it in the
1064: * internal buffer.
1065: *
1066: * @return true if there is more data, false if not.
1067: */
1068: private boolean refillReadBuffer() throws IOException {
1069: // If the server returns an empty packet, assume that that end of
1070: // the stream has been reached (yuck -- fix protocol??).
1071: // FORM support
1072: if (replay) {
1073: endOfStream = true; // we've read everything there is
1074: }
1075: if (endOfStream) {
1076: return false;
1077: }
1078:
1079: // Request more data immediately
1080: Socket.sendb(socket, getBodyMessageBuffer, 0,
1081: getBodyMessageBuffer.position());
1082:
1083: boolean moreData = receive();
1084: if (!moreData) {
1085: endOfStream = true;
1086: }
1087: return moreData;
1088: }
1089:
1090: /**
1091: * Read an AJP message.
1092: *
1093: * @param first is true if the message is the first in the request, which
1094: * will cause a short duration blocking read
1095: * @return true if the message has been read, false if the short read
1096: * didn't return anything
1097: * @throws IOException any other failure, including incomplete reads
1098: */
1099: protected boolean readMessage(AjpMessage message, boolean first,
1100: boolean useAvailableData) throws IOException {
1101:
1102: byte[] buf = message.getBuffer();
1103: int headerLength = message.getHeaderLength();
1104:
1105: if (first) {
1106: if (!readt(headerLength, useAvailableData)) {
1107: return false;
1108: }
1109: } else {
1110: read(headerLength);
1111: }
1112: inputBuffer.get(message.getBuffer(), 0, headerLength);
1113: message.processHeader();
1114: read(message.getLen());
1115: inputBuffer.get(message.getBuffer(), headerLength, message
1116: .getLen());
1117:
1118: return true;
1119:
1120: }
1121:
1122: /**
1123: * Recycle the processor.
1124: */
1125: public void recycle() {
1126:
1127: // Recycle Request object
1128: first = true;
1129: endOfStream = false;
1130: empty = true;
1131: replay = false;
1132: finished = false;
1133: request.recycle();
1134: response.recycle();
1135: certificates.recycle();
1136:
1137: inputBuffer.clear();
1138: inputBuffer.limit(0);
1139: outputBuffer.clear();
1140:
1141: }
1142:
1143: /**
1144: * Callback to write data from the buffer.
1145: */
1146: protected void flush() throws IOException {
1147: if (outputBuffer.position() > 0) {
1148: if (Socket.sendbb(socket, 0, outputBuffer.position()) < 0) {
1149: throw new IOException(sm
1150: .getString("ajpprocessor.failedsend"));
1151: }
1152: outputBuffer.clear();
1153: }
1154: }
1155:
1156: // ------------------------------------- InputStreamInputBuffer Inner Class
1157:
1158: /**
1159: * This class is an input buffer which will read its data from an input
1160: * stream.
1161: */
1162: protected class SocketInputBuffer implements InputBuffer {
1163:
1164: /**
1165: * Read bytes into the specified chunk.
1166: */
1167: public int doRead(ByteChunk chunk, Request req)
1168: throws IOException {
1169:
1170: if (endOfStream) {
1171: return -1;
1172: }
1173: if (first && req.getContentLengthLong() > 0) {
1174: // Handle special first-body-chunk
1175: if (!receive()) {
1176: return 0;
1177: }
1178: } else if (empty) {
1179: if (!refillReadBuffer()) {
1180: return -1;
1181: }
1182: }
1183: ByteChunk bc = bodyBytes.getByteChunk();
1184: chunk.setBytes(bc.getBuffer(), bc.getStart(), bc
1185: .getLength());
1186: empty = true;
1187: return chunk.getLength();
1188:
1189: }
1190:
1191: }
1192:
1193: // ----------------------------------- OutputStreamOutputBuffer Inner Class
1194:
1195: /**
1196: * This class is an output buffer which will write data to an output
1197: * stream.
1198: */
1199: protected class SocketOutputBuffer implements OutputBuffer {
1200:
1201: /**
1202: * Write chunk.
1203: */
1204: public int doWrite(ByteChunk chunk, Response res)
1205: throws IOException {
1206:
1207: if (!response.isCommitted()) {
1208: // Validate and write response headers
1209: try {
1210: prepareResponse();
1211: } catch (IOException e) {
1212: // Set error flag
1213: error = true;
1214: }
1215: }
1216:
1217: int len = chunk.getLength();
1218: // 4 - hardcoded, byte[] marshalling overhead
1219: int chunkSize = Constants.MAX_SEND_SIZE;
1220: int off = 0;
1221: while (len > 0) {
1222: int this Time = len;
1223: if (this Time > chunkSize) {
1224: this Time = chunkSize;
1225: }
1226: len -= this Time;
1227: if (outputBuffer.position() + this Time
1228: + Constants.H_SIZE + 4 > outputBuffer
1229: .capacity()) {
1230: flush();
1231: }
1232: outputBuffer.put((byte) 0x41);
1233: outputBuffer.put((byte) 0x42);
1234: outputBuffer.putShort((short) (this Time + 4));
1235: outputBuffer.put(Constants.JK_AJP13_SEND_BODY_CHUNK);
1236: outputBuffer.putShort((short) this Time);
1237: outputBuffer.put(chunk.getBytes(), chunk.getOffset()
1238: + off, this Time);
1239: outputBuffer.put((byte) 0x00);
1240: off += thisTime;
1241: }
1242:
1243: return chunk.getLength();
1244:
1245: }
1246:
1247: }
1248:
1249: }
|