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