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.http11;
0019:
0020: import java.io.IOException;
0021: import java.io.InterruptedIOException;
0022: import java.net.InetAddress;
0023: import java.net.Socket;
0024: import java.security.AccessController;
0025: import java.security.PrivilegedAction;
0026: import java.util.StringTokenizer;
0027: import java.util.regex.Pattern;
0028: import java.util.regex.PatternSyntaxException;
0029:
0030: import org.apache.coyote.ActionCode;
0031: import org.apache.coyote.ActionHook;
0032: import org.apache.coyote.Adapter;
0033: import org.apache.coyote.Request;
0034: import org.apache.coyote.RequestInfo;
0035: import org.apache.coyote.Response;
0036: import org.apache.coyote.http11.filters.BufferedInputFilter;
0037: import org.apache.coyote.http11.filters.ChunkedInputFilter;
0038: import org.apache.coyote.http11.filters.ChunkedOutputFilter;
0039: import org.apache.coyote.http11.filters.GzipOutputFilter;
0040: import org.apache.coyote.http11.filters.IdentityInputFilter;
0041: import org.apache.coyote.http11.filters.IdentityOutputFilter;
0042: import org.apache.coyote.http11.filters.SavedRequestInputFilter;
0043: import org.apache.coyote.http11.filters.VoidInputFilter;
0044: import org.apache.coyote.http11.filters.VoidOutputFilter;
0045: import org.apache.tomcat.util.buf.Ascii;
0046: import org.apache.tomcat.util.buf.ByteChunk;
0047: import org.apache.tomcat.util.buf.HexUtils;
0048: import org.apache.tomcat.util.buf.MessageBytes;
0049: import org.apache.tomcat.util.http.FastHttpDateFormat;
0050: import org.apache.tomcat.util.http.MimeHeaders;
0051: import org.apache.tomcat.util.net.JIoEndpoint;
0052: import org.apache.tomcat.util.net.SSLSupport;
0053: import org.apache.tomcat.util.res.StringManager;
0054:
0055: /**
0056: * Processes HTTP requests.
0057: *
0058: * @author Remy Maucherat
0059: */
0060: public class Http11Processor implements ActionHook {
0061:
0062: /**
0063: * Logger.
0064: */
0065: protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
0066: .getLog(Http11Processor.class);
0067:
0068: /**
0069: * The string manager for this package.
0070: */
0071: protected static StringManager sm = StringManager
0072: .getManager(Constants.Package);
0073:
0074: // ------------------------------------------------------------ Constructor
0075:
0076: public Http11Processor(int headerBufferSize, JIoEndpoint endpoint) {
0077:
0078: this .endpoint = endpoint;
0079:
0080: request = new Request();
0081: inputBuffer = new InternalInputBuffer(request, headerBufferSize);
0082: request.setInputBuffer(inputBuffer);
0083:
0084: response = new Response();
0085: response.setHook(this );
0086: outputBuffer = new InternalOutputBuffer(response,
0087: headerBufferSize);
0088: response.setOutputBuffer(outputBuffer);
0089: request.setResponse(response);
0090:
0091: initializeFilters();
0092:
0093: // Cause loading of HexUtils
0094: int foo = HexUtils.DEC[0];
0095:
0096: }
0097:
0098: // ----------------------------------------------------- Instance Variables
0099:
0100: /**
0101: * Associated adapter.
0102: */
0103: protected Adapter adapter = null;
0104:
0105: /**
0106: * Request object.
0107: */
0108: protected Request request = null;
0109:
0110: /**
0111: * Response object.
0112: */
0113: protected Response response = null;
0114:
0115: /**
0116: * Input.
0117: */
0118: protected InternalInputBuffer inputBuffer = null;
0119:
0120: /**
0121: * Output.
0122: */
0123: protected InternalOutputBuffer outputBuffer = null;
0124:
0125: /**
0126: * State flag.
0127: */
0128: protected boolean started = false;
0129:
0130: /**
0131: * Error flag.
0132: */
0133: protected boolean error = false;
0134:
0135: /**
0136: * Keep-alive.
0137: */
0138: protected boolean keepAlive = true;
0139:
0140: /**
0141: * HTTP/1.1 flag.
0142: */
0143: protected boolean http11 = true;
0144:
0145: /**
0146: * HTTP/0.9 flag.
0147: */
0148: protected boolean http09 = false;
0149:
0150: /**
0151: * Content delimitator for the request (if false, the connection will
0152: * be closed at the end of the request).
0153: */
0154: protected boolean contentDelimitation = true;
0155:
0156: /**
0157: * Is there an expectation ?
0158: */
0159: protected boolean expectation = false;
0160:
0161: /**
0162: * List of restricted user agents.
0163: */
0164: protected Pattern[] restrictedUserAgents = null;
0165:
0166: /**
0167: * Maximum number of Keep-Alive requests to honor.
0168: */
0169: protected int maxKeepAliveRequests = -1;
0170:
0171: /**
0172: * The number of seconds Tomcat will wait for a subsequent request
0173: * before closing the connection.
0174: */
0175: protected int keepAliveTimeout = -1;
0176:
0177: /**
0178: * SSL information.
0179: */
0180: protected SSLSupport sslSupport;
0181:
0182: /**
0183: * Socket associated with the current connection.
0184: */
0185: protected Socket socket;
0186:
0187: /**
0188: * Remote Address associated with the current connection.
0189: */
0190: protected String remoteAddr = null;
0191:
0192: /**
0193: * Remote Host associated with the current connection.
0194: */
0195: protected String remoteHost = null;
0196:
0197: /**
0198: * Local Host associated with the current connection.
0199: */
0200: protected String localName = null;
0201:
0202: /**
0203: * Local port to which the socket is connected
0204: */
0205: protected int localPort = -1;
0206:
0207: /**
0208: * Remote port to which the socket is connected
0209: */
0210: protected int remotePort = -1;
0211:
0212: /**
0213: * The local Host address.
0214: */
0215: protected String localAddr = null;
0216:
0217: /**
0218: * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server.
0219: */
0220: protected int timeout = 300000;
0221:
0222: /**
0223: * Flag to disable setting a different time-out on uploads.
0224: */
0225: protected boolean disableUploadTimeout = false;
0226:
0227: /**
0228: * Allowed compression level.
0229: */
0230: protected int compressionLevel = 0;
0231:
0232: /**
0233: * Minimum contentsize to make compression.
0234: */
0235: protected int compressionMinSize = 2048;
0236:
0237: /**
0238: * Socket buffering.
0239: */
0240: protected int socketBuffer = -1;
0241:
0242: /**
0243: * Max saved post size.
0244: */
0245: protected int maxSavePostSize = 4 * 1024;
0246:
0247: /**
0248: * List of user agents to not use gzip with
0249: */
0250: protected Pattern noCompressionUserAgents[] = null;
0251:
0252: /**
0253: * List of MIMES which could be gzipped
0254: */
0255: protected String[] compressableMimeTypes = { "text/html",
0256: "text/xml", "text/plain" };
0257:
0258: /**
0259: * Host name (used to avoid useless B2C conversion on the host name).
0260: */
0261: protected char[] hostNameC = new char[0];
0262:
0263: /**
0264: * Associated endpoint.
0265: */
0266: protected JIoEndpoint endpoint;
0267:
0268: /**
0269: * Allow a customized the server header for the tin-foil hat folks.
0270: */
0271: protected String server = null;
0272:
0273: // ------------------------------------------------------------- Properties
0274:
0275: /**
0276: * Return compression level.
0277: */
0278: public String getCompression() {
0279: switch (compressionLevel) {
0280: case 0:
0281: return "off";
0282: case 1:
0283: return "on";
0284: case 2:
0285: return "force";
0286: }
0287: return "off";
0288: }
0289:
0290: /**
0291: * Set compression level.
0292: */
0293: public void setCompression(String compression) {
0294: if (compression.equals("on")) {
0295: this .compressionLevel = 1;
0296: } else if (compression.equals("force")) {
0297: this .compressionLevel = 2;
0298: } else if (compression.equals("off")) {
0299: this .compressionLevel = 0;
0300: } else {
0301: try {
0302: // Try to parse compression as an int, which would give the
0303: // minimum compression size
0304: compressionMinSize = Integer.parseInt(compression);
0305: this .compressionLevel = 1;
0306: } catch (Exception e) {
0307: this .compressionLevel = 0;
0308: }
0309: }
0310: }
0311:
0312: /**
0313: * Set Minimum size to trigger compression.
0314: */
0315: public void setCompressionMinSize(int compressionMinSize) {
0316: this .compressionMinSize = compressionMinSize;
0317: }
0318:
0319: /**
0320: * Add user-agent for which gzip compression didn't works
0321: * The user agent String given will be exactly matched
0322: * to the user-agent header submitted by the client.
0323: *
0324: * @param userAgent user-agent string
0325: */
0326: public void addNoCompressionUserAgent(String userAgent) {
0327: try {
0328: Pattern nRule = Pattern.compile(userAgent);
0329: noCompressionUserAgents = addREArray(
0330: noCompressionUserAgents, nRule);
0331: } catch (PatternSyntaxException pse) {
0332: log.error(sm.getString("http11processor.regexp.error",
0333: userAgent), pse);
0334: }
0335: }
0336:
0337: /**
0338: * Set no compression user agent list (this method is best when used with
0339: * a large number of connectors, where it would be better to have all of
0340: * them referenced a single array).
0341: */
0342: public void setNoCompressionUserAgents(
0343: Pattern[] noCompressionUserAgents) {
0344: this .noCompressionUserAgents = noCompressionUserAgents;
0345: }
0346:
0347: /**
0348: * Set no compression user agent list.
0349: * List contains users agents separated by ',' :
0350: *
0351: * ie: "gorilla,desesplorer,tigrus"
0352: */
0353: public void setNoCompressionUserAgents(
0354: String noCompressionUserAgents) {
0355: if (noCompressionUserAgents != null) {
0356: StringTokenizer st = new StringTokenizer(
0357: noCompressionUserAgents, ",");
0358:
0359: while (st.hasMoreTokens()) {
0360: addNoCompressionUserAgent(st.nextToken().trim());
0361: }
0362: }
0363: }
0364:
0365: /**
0366: * Add a mime-type which will be compressable
0367: * The mime-type String will be exactly matched
0368: * in the response mime-type header .
0369: *
0370: * @param mimeType mime-type string
0371: */
0372: public void addCompressableMimeType(String mimeType) {
0373: compressableMimeTypes = addStringArray(compressableMimeTypes,
0374: mimeType);
0375: }
0376:
0377: /**
0378: * Set compressable mime-type list (this method is best when used with
0379: * a large number of connectors, where it would be better to have all of
0380: * them referenced a single array).
0381: */
0382: public void setCompressableMimeTypes(String[] compressableMimeTypes) {
0383: this .compressableMimeTypes = compressableMimeTypes;
0384: }
0385:
0386: /**
0387: * Set compressable mime-type list
0388: * List contains users agents separated by ',' :
0389: *
0390: * ie: "text/html,text/xml,text/plain"
0391: */
0392: public void setCompressableMimeTypes(String compressableMimeTypes) {
0393: if (compressableMimeTypes != null) {
0394: StringTokenizer st = new StringTokenizer(
0395: compressableMimeTypes, ",");
0396:
0397: while (st.hasMoreTokens()) {
0398: addCompressableMimeType(st.nextToken().trim());
0399: }
0400: }
0401: }
0402:
0403: /**
0404: * Return the list of restricted user agents.
0405: */
0406: public String[] findCompressableMimeTypes() {
0407: return (compressableMimeTypes);
0408: }
0409:
0410: // --------------------------------------------------------- Public Methods
0411:
0412: /**
0413: * Add input or output filter.
0414: *
0415: * @param className class name of the filter
0416: */
0417: protected void addFilter(String className) {
0418: try {
0419: Class clazz = Class.forName(className);
0420: Object obj = clazz.newInstance();
0421: if (obj instanceof InputFilter) {
0422: inputBuffer.addFilter((InputFilter) obj);
0423: } else if (obj instanceof OutputFilter) {
0424: outputBuffer.addFilter((OutputFilter) obj);
0425: } else {
0426: log.warn(sm.getString("http11processor.filter.unknown",
0427: className));
0428: }
0429: } catch (Exception e) {
0430: log.error(sm.getString("http11processor.filter.error",
0431: className), e);
0432: }
0433: }
0434:
0435: /**
0436: * General use method
0437: *
0438: * @param sArray the StringArray
0439: * @param value string
0440: */
0441: private String[] addStringArray(String sArray[], String value) {
0442: String[] result = null;
0443: if (sArray == null) {
0444: result = new String[1];
0445: result[0] = value;
0446: } else {
0447: result = new String[sArray.length + 1];
0448: for (int i = 0; i < sArray.length; i++)
0449: result[i] = sArray[i];
0450: result[sArray.length] = value;
0451: }
0452: return result;
0453: }
0454:
0455: /**
0456: * General use method
0457: *
0458: * @param rArray the REArray
0459: * @param value Obj
0460: */
0461: private Pattern[] addREArray(Pattern rArray[], Pattern value) {
0462: Pattern[] result = null;
0463: if (rArray == null) {
0464: result = new Pattern[1];
0465: result[0] = value;
0466: } else {
0467: result = new Pattern[rArray.length + 1];
0468: for (int i = 0; i < rArray.length; i++)
0469: result[i] = rArray[i];
0470: result[rArray.length] = value;
0471: }
0472: return result;
0473: }
0474:
0475: /**
0476: * General use method
0477: *
0478: * @param sArray the StringArray
0479: * @param value string
0480: */
0481: private boolean inStringArray(String sArray[], String value) {
0482: for (int i = 0; i < sArray.length; i++) {
0483: if (sArray[i].equals(value)) {
0484: return true;
0485: }
0486: }
0487: return false;
0488: }
0489:
0490: /**
0491: * Checks if any entry in the string array starts with the specified value
0492: *
0493: * @param sArray the StringArray
0494: * @param value string
0495: */
0496: private boolean startsWithStringArray(String sArray[], String value) {
0497: if (value == null)
0498: return false;
0499: for (int i = 0; i < sArray.length; i++) {
0500: if (value.startsWith(sArray[i])) {
0501: return true;
0502: }
0503: }
0504: return false;
0505: }
0506:
0507: /**
0508: * Add restricted user-agent (which will downgrade the connector
0509: * to HTTP/1.0 mode). The user agent String given will be matched
0510: * via regexp to the user-agent header submitted by the client.
0511: *
0512: * @param userAgent user-agent string
0513: */
0514: public void addRestrictedUserAgent(String userAgent) {
0515: try {
0516: Pattern nRule = Pattern.compile(userAgent);
0517: restrictedUserAgents = addREArray(restrictedUserAgents,
0518: nRule);
0519: } catch (PatternSyntaxException pse) {
0520: log.error(sm.getString("http11processor.regexp.error",
0521: userAgent), pse);
0522: }
0523: }
0524:
0525: /**
0526: * Set restricted user agent list (this method is best when used with
0527: * a large number of connectors, where it would be better to have all of
0528: * them referenced a single array).
0529: */
0530: public void setRestrictedUserAgents(Pattern[] restrictedUserAgents) {
0531: this .restrictedUserAgents = restrictedUserAgents;
0532: }
0533:
0534: /**
0535: * Set restricted user agent list (which will downgrade the connector
0536: * to HTTP/1.0 mode). List contains users agents separated by ',' :
0537: *
0538: * ie: "gorilla,desesplorer,tigrus"
0539: */
0540: public void setRestrictedUserAgents(String restrictedUserAgents) {
0541: if (restrictedUserAgents != null) {
0542: StringTokenizer st = new StringTokenizer(
0543: restrictedUserAgents, ",");
0544: while (st.hasMoreTokens()) {
0545: addRestrictedUserAgent(st.nextToken().trim());
0546: }
0547: }
0548: }
0549:
0550: /**
0551: * Return the list of restricted user agents.
0552: */
0553: public String[] findRestrictedUserAgents() {
0554: String[] sarr = new String[restrictedUserAgents.length];
0555:
0556: for (int i = 0; i < restrictedUserAgents.length; i++)
0557: sarr[i] = restrictedUserAgents[i].toString();
0558:
0559: return (sarr);
0560: }
0561:
0562: /**
0563: * Set the maximum number of Keep-Alive requests to honor.
0564: * This is to safeguard from DoS attacks. Setting to a negative
0565: * value disables the check.
0566: */
0567: public void setMaxKeepAliveRequests(int mkar) {
0568: maxKeepAliveRequests = mkar;
0569: }
0570:
0571: /**
0572: * Return the number of Keep-Alive requests that we will honor.
0573: */
0574: public int getMaxKeepAliveRequests() {
0575: return maxKeepAliveRequests;
0576: }
0577:
0578: /**
0579: * Set the Keep-Alive timeout.
0580: */
0581: public void setKeepAliveTimeout(int timeout) {
0582: keepAliveTimeout = timeout;
0583: }
0584:
0585: /**
0586: * Return the number Keep-Alive timeout.
0587: */
0588: public int getKeepAliveTimeout() {
0589: return keepAliveTimeout;
0590: }
0591:
0592: /**
0593: * Set the maximum size of a POST which will be buffered in SSL mode.
0594: */
0595: public void setMaxSavePostSize(int msps) {
0596: maxSavePostSize = msps;
0597: }
0598:
0599: /**
0600: * Return the maximum size of a POST which will be buffered in SSL mode.
0601: */
0602: public int getMaxSavePostSize() {
0603: return maxSavePostSize;
0604: }
0605:
0606: /**
0607: * Set the SSL information for this HTTP connection.
0608: */
0609: public void setSSLSupport(SSLSupport sslSupport) {
0610: this .sslSupport = sslSupport;
0611: }
0612:
0613: /**
0614: * Set the flag to control upload time-outs.
0615: */
0616: public void setDisableUploadTimeout(boolean isDisabled) {
0617: disableUploadTimeout = isDisabled;
0618: }
0619:
0620: /**
0621: * Get the flag that controls upload time-outs.
0622: */
0623: public boolean getDisableUploadTimeout() {
0624: return disableUploadTimeout;
0625: }
0626:
0627: /**
0628: * Set the socket buffer flag.
0629: */
0630: public void setSocketBuffer(int socketBuffer) {
0631: this .socketBuffer = socketBuffer;
0632: outputBuffer.setSocketBuffer(socketBuffer);
0633: }
0634:
0635: /**
0636: * Get the socket buffer flag.
0637: */
0638: public int getSocketBuffer() {
0639: return socketBuffer;
0640: }
0641:
0642: /**
0643: * Set the upload timeout.
0644: */
0645: public void setTimeout(int timeouts) {
0646: timeout = timeouts;
0647: }
0648:
0649: /**
0650: * Get the upload timeout.
0651: */
0652: public int getTimeout() {
0653: return timeout;
0654: }
0655:
0656: /**
0657: * Set the server header name.
0658: */
0659: public void setServer(String server) {
0660: if (server == null || server.equals("")) {
0661: this .server = null;
0662: } else {
0663: this .server = server;
0664: }
0665: }
0666:
0667: /**
0668: * Get the server header name.
0669: */
0670: public String getServer() {
0671: return server;
0672: }
0673:
0674: /** Get the request associated with this processor.
0675: *
0676: * @return The request
0677: */
0678: public Request getRequest() {
0679: return request;
0680: }
0681:
0682: /**
0683: * Process pipelined HTTP requests using the specified input and output
0684: * streams.
0685: *
0686: * @param input stream from which the HTTP requests will be read
0687: * @param output stream which will be used to output the HTTP
0688: * responses
0689: * @throws IOException error during an I/O operation
0690: */
0691: public void process(Socket socket) throws IOException {
0692: RequestInfo rp = request.getRequestProcessor();
0693: rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
0694:
0695: // Set the remote address
0696: remoteAddr = null;
0697: remoteHost = null;
0698: localAddr = null;
0699: localName = null;
0700: remotePort = -1;
0701: localPort = -1;
0702:
0703: // Setting up the I/O
0704: this .socket = socket;
0705: inputBuffer.setInputStream(socket.getInputStream());
0706: outputBuffer.setOutputStream(socket.getOutputStream());
0707:
0708: // Error flag
0709: error = false;
0710: keepAlive = true;
0711:
0712: int keepAliveLeft = maxKeepAliveRequests;
0713: int soTimeout = socket.getSoTimeout();
0714: int oldSoTimeout = soTimeout;
0715:
0716: int threadRatio = (endpoint.getCurrentThreadsBusy() * 100)
0717: / endpoint.getMaxThreads();
0718: if (threadRatio > 75) {
0719: keepAliveLeft = 1;
0720: }
0721:
0722: if (soTimeout != oldSoTimeout) {
0723: try {
0724: socket.setSoTimeout(soTimeout);
0725: } catch (Throwable t) {
0726: log
0727: .debug(
0728: sm
0729: .getString("http11processor.socket.timeout"),
0730: t);
0731: error = true;
0732: }
0733: }
0734:
0735: boolean keptAlive = false;
0736:
0737: while (started && !error && keepAlive) {
0738:
0739: // Parsing the request header
0740: try {
0741: if (!disableUploadTimeout && keptAlive) {
0742: if (keepAliveTimeout > 0) {
0743: socket.setSoTimeout(keepAliveTimeout);
0744: } else if (soTimeout > 0) {
0745: socket.setSoTimeout(soTimeout);
0746: }
0747: }
0748: inputBuffer.parseRequestLine();
0749: request.setStartTime(System.currentTimeMillis());
0750: keptAlive = true;
0751: if (!disableUploadTimeout) {
0752: socket.setSoTimeout(timeout);
0753: }
0754: inputBuffer.parseHeaders();
0755: } catch (IOException e) {
0756: error = true;
0757: break;
0758: } catch (Throwable t) {
0759: if (log.isDebugEnabled()) {
0760: log.debug(sm
0761: .getString("http11processor.header.parse"),
0762: t);
0763: }
0764: // 400 - Bad Request
0765: response.setStatus(400);
0766: error = true;
0767: }
0768:
0769: // Setting up filters, and parse some request headers
0770: rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
0771: try {
0772: prepareRequest();
0773: } catch (Throwable t) {
0774: if (log.isDebugEnabled()) {
0775: log
0776: .debug(
0777: sm
0778: .getString("http11processor.request.prepare"),
0779: t);
0780: }
0781: // 400 - Internal Server Error
0782: response.setStatus(400);
0783: error = true;
0784: }
0785:
0786: if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
0787: keepAlive = false;
0788:
0789: // Process the request in the adapter
0790: if (!error) {
0791: try {
0792: rp
0793: .setStage(org.apache.coyote.Constants.STAGE_SERVICE);
0794: adapter.service(request, response);
0795: // Handle when the response was committed before a serious
0796: // error occurred. Throwing a ServletException should both
0797: // set the status to 500 and set the errorException.
0798: // If we fail here, then the response is likely already
0799: // committed, so we can't try and set headers.
0800: if (keepAlive && !error) { // Avoid checking twice.
0801: error = response.getErrorException() != null
0802: || statusDropsConnection(response
0803: .getStatus());
0804: }
0805:
0806: } catch (InterruptedIOException e) {
0807: error = true;
0808: } catch (Throwable t) {
0809: log
0810: .error(
0811: sm
0812: .getString("http11processor.request.process"),
0813: t);
0814: // 500 - Internal Server Error
0815: response.setStatus(500);
0816: error = true;
0817: }
0818: }
0819:
0820: // Finish the handling of the request
0821: try {
0822: rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
0823: inputBuffer.endRequest();
0824: } catch (IOException e) {
0825: error = true;
0826: } catch (Throwable t) {
0827: log
0828: .error(
0829: sm
0830: .getString("http11processor.request.finish"),
0831: t);
0832: // 500 - Internal Server Error
0833: response.setStatus(500);
0834: error = true;
0835: }
0836: try {
0837: rp
0838: .setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
0839: outputBuffer.endRequest();
0840: } catch (IOException e) {
0841: error = true;
0842: } catch (Throwable t) {
0843: log.error(sm
0844: .getString("http11processor.response.finish"),
0845: t);
0846: error = true;
0847: }
0848:
0849: // If there was an error, make sure the request is counted as
0850: // and error, and update the statistics counter
0851: if (error) {
0852: response.setStatus(500);
0853: }
0854: request.updateCounters();
0855:
0856: rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
0857:
0858: // Don't reset the param - we'll see it as ended. Next request
0859: // will reset it
0860: // thrA.setParam(null);
0861: // Next request
0862: inputBuffer.nextRequest();
0863: outputBuffer.nextRequest();
0864:
0865: }
0866:
0867: rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
0868:
0869: // Recycle
0870: inputBuffer.recycle();
0871: outputBuffer.recycle();
0872:
0873: // Recycle ssl info
0874: sslSupport = null;
0875: }
0876:
0877: // ----------------------------------------------------- ActionHook Methods
0878:
0879: /**
0880: * Send an action to the connector.
0881: *
0882: * @param actionCode Type of the action
0883: * @param param Action parameter
0884: */
0885: public void action(ActionCode actionCode, Object param) {
0886:
0887: if (actionCode == ActionCode.ACTION_COMMIT) {
0888: // Commit current response
0889:
0890: if (response.isCommitted())
0891: return;
0892:
0893: // Validate and write response headers
0894: prepareResponse();
0895: try {
0896: outputBuffer.commit();
0897: } catch (IOException e) {
0898: // Set error flag
0899: error = true;
0900: }
0901:
0902: } else if (actionCode == ActionCode.ACTION_ACK) {
0903:
0904: // Acknowlege request
0905:
0906: // Send a 100 status back if it makes sense (response not committed
0907: // yet, and client specified an expectation for 100-continue)
0908:
0909: if ((response.isCommitted()) || !expectation)
0910: return;
0911:
0912: inputBuffer.setSwallowInput(true);
0913: try {
0914: outputBuffer.sendAck();
0915: } catch (IOException e) {
0916: // Set error flag
0917: error = true;
0918: }
0919:
0920: } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
0921:
0922: try {
0923: outputBuffer.flush();
0924: } catch (IOException e) {
0925: // Set error flag
0926: error = true;
0927: response.setErrorException(e);
0928: }
0929:
0930: } else if (actionCode == ActionCode.ACTION_CLOSE) {
0931: // Close
0932:
0933: // End the processing of the current request, and stop any further
0934: // transactions with the client
0935:
0936: try {
0937: outputBuffer.endRequest();
0938: } catch (IOException e) {
0939: // Set error flag
0940: error = true;
0941: }
0942:
0943: } else if (actionCode == ActionCode.ACTION_RESET) {
0944:
0945: // Reset response
0946:
0947: // Note: This must be called before the response is committed
0948:
0949: outputBuffer.reset();
0950:
0951: } else if (actionCode == ActionCode.ACTION_CUSTOM) {
0952:
0953: // Do nothing
0954:
0955: } else if (actionCode == ActionCode.ACTION_START) {
0956:
0957: started = true;
0958:
0959: } else if (actionCode == ActionCode.ACTION_STOP) {
0960:
0961: started = false;
0962:
0963: } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE) {
0964:
0965: try {
0966: if (sslSupport != null) {
0967: Object sslO = sslSupport.getCipherSuite();
0968: if (sslO != null)
0969: request.setAttribute(
0970: SSLSupport.CIPHER_SUITE_KEY, sslO);
0971: sslO = sslSupport.getPeerCertificateChain(false);
0972: if (sslO != null)
0973: request.setAttribute(
0974: SSLSupport.CERTIFICATE_KEY, sslO);
0975: sslO = sslSupport.getKeySize();
0976: if (sslO != null)
0977: request.setAttribute(SSLSupport.KEY_SIZE_KEY,
0978: sslO);
0979: sslO = sslSupport.getSessionId();
0980: if (sslO != null)
0981: request.setAttribute(SSLSupport.SESSION_ID_KEY,
0982: sslO);
0983: }
0984: } catch (Exception e) {
0985: log.warn(sm.getString("http11processor.socket.ssl"), e);
0986: }
0987:
0988: } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
0989:
0990: if ((remoteAddr == null) && (socket != null)) {
0991: InetAddress inetAddr = socket.getInetAddress();
0992: if (inetAddr != null) {
0993: remoteAddr = inetAddr.getHostAddress();
0994: }
0995: }
0996: request.remoteAddr().setString(remoteAddr);
0997:
0998: } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
0999:
1000: if ((localName == null) && (socket != null)) {
1001: InetAddress inetAddr = socket.getLocalAddress();
1002: if (inetAddr != null) {
1003: localName = inetAddr.getHostName();
1004: }
1005: }
1006: request.localName().setString(localName);
1007:
1008: } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
1009:
1010: if ((remoteHost == null) && (socket != null)) {
1011: InetAddress inetAddr = socket.getInetAddress();
1012: if (inetAddr != null) {
1013: remoteHost = inetAddr.getHostName();
1014: }
1015: if (remoteHost == null) {
1016: if (remoteAddr != null) {
1017: remoteHost = remoteAddr;
1018: } else { // all we can do is punt
1019: request.remoteHost().recycle();
1020: }
1021: }
1022: }
1023: request.remoteHost().setString(remoteHost);
1024:
1025: } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
1026:
1027: if (localAddr == null)
1028: localAddr = socket.getLocalAddress().getHostAddress();
1029:
1030: request.localAddr().setString(localAddr);
1031:
1032: } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
1033:
1034: if ((remotePort == -1) && (socket != null)) {
1035: remotePort = socket.getPort();
1036: }
1037: request.setRemotePort(remotePort);
1038:
1039: } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
1040:
1041: if ((localPort == -1) && (socket != null)) {
1042: localPort = socket.getLocalPort();
1043: }
1044: request.setLocalPort(localPort);
1045:
1046: } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
1047: if (sslSupport != null) {
1048: /*
1049: * Consume and buffer the request body, so that it does not
1050: * interfere with the client's handshake messages
1051: */
1052: InputFilter[] inputFilters = inputBuffer.getFilters();
1053: ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
1054: .setLimit(maxSavePostSize);
1055: inputBuffer
1056: .addActiveFilter(inputFilters[Constants.BUFFERED_FILTER]);
1057: try {
1058: Object sslO = sslSupport
1059: .getPeerCertificateChain(true);
1060: if (sslO != null) {
1061: request.setAttribute(
1062: SSLSupport.CERTIFICATE_KEY, sslO);
1063: }
1064: } catch (Exception e) {
1065: log.warn(
1066: sm.getString("http11processor.socket.ssl"),
1067: e);
1068: }
1069: }
1070: } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
1071: ByteChunk body = (ByteChunk) param;
1072:
1073: InputFilter savedBody = new SavedRequestInputFilter(body);
1074: savedBody.setRequest(request);
1075:
1076: InternalInputBuffer internalBuffer = (InternalInputBuffer) request
1077: .getInputBuffer();
1078: internalBuffer.addActiveFilter(savedBody);
1079: }
1080:
1081: }
1082:
1083: // ------------------------------------------------------ Connector Methods
1084:
1085: /**
1086: * Set the associated adapter.
1087: *
1088: * @param adapter the new adapter
1089: */
1090: public void setAdapter(Adapter adapter) {
1091: this .adapter = adapter;
1092: }
1093:
1094: /**
1095: * Get the associated adapter.
1096: *
1097: * @return the associated adapter
1098: */
1099: public Adapter getAdapter() {
1100: return adapter;
1101: }
1102:
1103: // ------------------------------------------------------ Protected Methods
1104:
1105: /**
1106: * After reading the request headers, we have to setup the request filters.
1107: */
1108: protected void prepareRequest() {
1109:
1110: http11 = true;
1111: http09 = false;
1112: contentDelimitation = false;
1113: expectation = false;
1114: if (sslSupport != null) {
1115: request.scheme().setString("https");
1116: }
1117: MessageBytes protocolMB = request.protocol();
1118: if (protocolMB.equals(Constants.HTTP_11)) {
1119: http11 = true;
1120: protocolMB.setString(Constants.HTTP_11);
1121: } else if (protocolMB.equals(Constants.HTTP_10)) {
1122: http11 = false;
1123: keepAlive = false;
1124: protocolMB.setString(Constants.HTTP_10);
1125: } else if (protocolMB.equals("")) {
1126: // HTTP/0.9
1127: http09 = true;
1128: http11 = false;
1129: keepAlive = false;
1130: } else {
1131: // Unsupported protocol
1132: http11 = false;
1133: error = true;
1134: // Send 505; Unsupported HTTP version
1135: if (log.isDebugEnabled()) {
1136: log.debug(sm
1137: .getString("http11processor.request.prepare")
1138: + " Unsupported HTTP version \""
1139: + protocolMB
1140: + "\"");
1141: }
1142: response.setStatus(505);
1143: }
1144:
1145: MessageBytes methodMB = request.method();
1146: if (methodMB.equals(Constants.GET)) {
1147: methodMB.setString(Constants.GET);
1148: } else if (methodMB.equals(Constants.POST)) {
1149: methodMB.setString(Constants.POST);
1150: }
1151:
1152: MimeHeaders headers = request.getMimeHeaders();
1153:
1154: // Check connection header
1155: MessageBytes connectionValueMB = headers.getValue("connection");
1156: if (connectionValueMB != null) {
1157: ByteChunk connectionValueBC = connectionValueMB
1158: .getByteChunk();
1159: if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
1160: keepAlive = false;
1161: } else if (findBytes(connectionValueBC,
1162: Constants.KEEPALIVE_BYTES) != -1) {
1163: keepAlive = true;
1164: }
1165: }
1166:
1167: MessageBytes expectMB = null;
1168: if (http11)
1169: expectMB = headers.getValue("expect");
1170: if ((expectMB != null)
1171: && (expectMB.indexOfIgnoreCase("100-continue", 0) != -1)) {
1172: inputBuffer.setSwallowInput(false);
1173: expectation = true;
1174: }
1175:
1176: // Check user-agent header
1177: if ((restrictedUserAgents != null) && ((http11) || (keepAlive))) {
1178: MessageBytes userAgentValueMB = headers
1179: .getValue("user-agent");
1180: // Check in the restricted list, and adjust the http11
1181: // and keepAlive flags accordingly
1182: if (userAgentValueMB != null) {
1183: String userAgentValue = userAgentValueMB.toString();
1184: for (int i = 0; i < restrictedUserAgents.length; i++) {
1185: if (restrictedUserAgents[i].matcher(userAgentValue)
1186: .matches()) {
1187: http11 = false;
1188: keepAlive = false;
1189: break;
1190: }
1191: }
1192: }
1193: }
1194:
1195: // Check for a full URI (including protocol://host:port/)
1196: ByteChunk uriBC = request.requestURI().getByteChunk();
1197: if (uriBC.startsWithIgnoreCase("http", 0)) {
1198:
1199: int pos = uriBC.indexOf("://", 0, 3, 4);
1200: int uriBCStart = uriBC.getStart();
1201: int slashPos = -1;
1202: if (pos != -1) {
1203: byte[] uriB = uriBC.getBytes();
1204: slashPos = uriBC.indexOf('/', pos + 3);
1205: if (slashPos == -1) {
1206: slashPos = uriBC.getLength();
1207: // Set URI as "/"
1208: request.requestURI().setBytes(uriB,
1209: uriBCStart + pos + 1, 1);
1210: } else {
1211: request.requestURI().setBytes(uriB,
1212: uriBCStart + slashPos,
1213: uriBC.getLength() - slashPos);
1214: }
1215: MessageBytes hostMB = headers.setValue("host");
1216: hostMB.setBytes(uriB, uriBCStart + pos + 3, slashPos
1217: - pos - 3);
1218: }
1219:
1220: }
1221:
1222: // Input filter setup
1223: InputFilter[] inputFilters = inputBuffer.getFilters();
1224:
1225: // Parse transfer-encoding header
1226: MessageBytes transferEncodingValueMB = null;
1227: if (http11)
1228: transferEncodingValueMB = headers
1229: .getValue("transfer-encoding");
1230: if (transferEncodingValueMB != null) {
1231: String transferEncodingValue = transferEncodingValueMB
1232: .toString();
1233: // Parse the comma separated list. "identity" codings are ignored
1234: int startPos = 0;
1235: int commaPos = transferEncodingValue.indexOf(',');
1236: String encodingName = null;
1237: while (commaPos != -1) {
1238: encodingName = transferEncodingValue.substring(
1239: startPos, commaPos).toLowerCase().trim();
1240: if (!addInputFilter(inputFilters, encodingName)) {
1241: // Unsupported transfer encoding
1242: error = true;
1243: // 501 - Unimplemented
1244: response.setStatus(501);
1245: }
1246: startPos = commaPos + 1;
1247: commaPos = transferEncodingValue.indexOf(',', startPos);
1248: }
1249: encodingName = transferEncodingValue.substring(startPos)
1250: .toLowerCase().trim();
1251: if (!addInputFilter(inputFilters, encodingName)) {
1252: // Unsupported transfer encoding
1253: error = true;
1254: // 501 - Unimplemented
1255: if (log.isDebugEnabled()) {
1256: log
1257: .debug(sm
1258: .getString("http11processor.request.prepare")
1259: + " Unsupported transfer encoding \""
1260: + encodingName + "\"");
1261: }
1262: response.setStatus(501);
1263: }
1264: }
1265:
1266: // Parse content-length header
1267: long contentLength = request.getContentLengthLong();
1268: if (contentLength >= 0 && !contentDelimitation) {
1269: inputBuffer
1270: .addActiveFilter(inputFilters[Constants.IDENTITY_FILTER]);
1271: contentDelimitation = true;
1272: }
1273:
1274: MessageBytes valueMB = headers.getValue("host");
1275:
1276: // Check host header
1277: if (http11 && (valueMB == null)) {
1278: error = true;
1279: // 400 - Bad request
1280: if (log.isDebugEnabled()) {
1281: log.debug(sm
1282: .getString("http11processor.request.prepare")
1283: + " host header missing");
1284: }
1285: response.setStatus(400);
1286: }
1287:
1288: parseHost(valueMB);
1289:
1290: if (!contentDelimitation) {
1291: // If there's no content length
1292: // (broken HTTP/1.0 or HTTP/1.1), assume
1293: // the client is not broken and didn't send a body
1294: inputBuffer
1295: .addActiveFilter(inputFilters[Constants.VOID_FILTER]);
1296: contentDelimitation = true;
1297: }
1298:
1299: }
1300:
1301: /**
1302: * Parse host.
1303: */
1304: public void parseHost(MessageBytes valueMB) {
1305:
1306: if (valueMB == null || valueMB.isNull()) {
1307: // HTTP/1.0
1308: // Default is what the socket tells us. Overriden if a host is
1309: // found/parsed
1310: request.setServerPort(socket.getLocalPort());
1311: InetAddress localAddress = socket.getLocalAddress();
1312: // Setting the socket-related fields. The adapter doesn't know
1313: // about socket.
1314: request.serverName().setString(localAddress.getHostName());
1315: return;
1316: }
1317:
1318: ByteChunk valueBC = valueMB.getByteChunk();
1319: byte[] valueB = valueBC.getBytes();
1320: int valueL = valueBC.getLength();
1321: int valueS = valueBC.getStart();
1322: int colonPos = -1;
1323: if (hostNameC.length < valueL) {
1324: hostNameC = new char[valueL];
1325: }
1326:
1327: boolean ipv6 = (valueB[valueS] == '[');
1328: boolean bracketClosed = false;
1329: for (int i = 0; i < valueL; i++) {
1330: char b = (char) valueB[i + valueS];
1331: hostNameC[i] = b;
1332: if (b == ']') {
1333: bracketClosed = true;
1334: } else if (b == ':') {
1335: if (!ipv6 || bracketClosed) {
1336: colonPos = i;
1337: break;
1338: }
1339: }
1340: }
1341:
1342: if (colonPos < 0) {
1343: if (sslSupport == null) {
1344: // 80 - Default HTTP port
1345: request.setServerPort(80);
1346: } else {
1347: // 443 - Default HTTPS port
1348: request.setServerPort(443);
1349: }
1350: request.serverName().setChars(hostNameC, 0, valueL);
1351: } else {
1352:
1353: request.serverName().setChars(hostNameC, 0, colonPos);
1354:
1355: int port = 0;
1356: int mult = 1;
1357: for (int i = valueL - 1; i > colonPos; i--) {
1358: int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
1359: if (charValue == -1) {
1360: // Invalid character
1361: error = true;
1362: // 400 - Bad request
1363: response.setStatus(400);
1364: break;
1365: }
1366: port = port + (charValue * mult);
1367: mult = 10 * mult;
1368: }
1369: request.setServerPort(port);
1370:
1371: }
1372:
1373: }
1374:
1375: /**
1376: * Check for compression
1377: */
1378: private boolean isCompressable() {
1379:
1380: // Nope Compression could works in HTTP 1.0 also
1381: // cf: mod_deflate
1382:
1383: // Compression only since HTTP 1.1
1384: // if (! http11)
1385: // return false;
1386:
1387: // Check if browser support gzip encoding
1388: MessageBytes acceptEncodingMB = request.getMimeHeaders()
1389: .getValue("accept-encoding");
1390:
1391: if ((acceptEncodingMB == null)
1392: || (acceptEncodingMB.indexOf("gzip") == -1))
1393: return false;
1394:
1395: // Check if content is not allready gzipped
1396: MessageBytes contentEncodingMB = response.getMimeHeaders()
1397: .getValue("Content-Encoding");
1398:
1399: if ((contentEncodingMB != null)
1400: && (contentEncodingMB.indexOf("gzip") != -1))
1401: return false;
1402:
1403: // If force mode, allways compress (test purposes only)
1404: if (compressionLevel == 2)
1405: return true;
1406:
1407: // Check for incompatible Browser
1408: if (noCompressionUserAgents != null) {
1409: MessageBytes userAgentValueMB = request.getMimeHeaders()
1410: .getValue("user-agent");
1411: if (userAgentValueMB != null) {
1412: String userAgentValue = userAgentValueMB.toString();
1413:
1414: // If one Regexp rule match, disable compression
1415: for (int i = 0; i < noCompressionUserAgents.length; i++)
1416: if (noCompressionUserAgents[i].matcher(
1417: userAgentValue).matches())
1418: return false;
1419: }
1420: }
1421:
1422: // Check if suffisant len to trig the compression
1423: long contentLength = response.getContentLengthLong();
1424: if ((contentLength == -1)
1425: || (contentLength > compressionMinSize)) {
1426: // Check for compatible MIME-TYPE
1427: if (compressableMimeTypes != null) {
1428: return (startsWithStringArray(compressableMimeTypes,
1429: response.getContentType()));
1430: }
1431: }
1432:
1433: return false;
1434: }
1435:
1436: /**
1437: * When committing the response, we have to validate the set of headers, as
1438: * well as setup the response filters.
1439: */
1440: protected void prepareResponse() {
1441:
1442: boolean entityBody = true;
1443: contentDelimitation = false;
1444:
1445: OutputFilter[] outputFilters = outputBuffer.getFilters();
1446:
1447: if (http09 == true) {
1448: // HTTP/0.9
1449: outputBuffer
1450: .addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]);
1451: return;
1452: }
1453:
1454: int statusCode = response.getStatus();
1455: if ((statusCode == 204) || (statusCode == 205)
1456: || (statusCode == 304)) {
1457: // No entity body
1458: outputBuffer
1459: .addActiveFilter(outputFilters[Constants.VOID_FILTER]);
1460: entityBody = false;
1461: contentDelimitation = true;
1462: }
1463:
1464: MessageBytes methodMB = request.method();
1465: if (methodMB.equals("HEAD")) {
1466: // No entity body
1467: outputBuffer
1468: .addActiveFilter(outputFilters[Constants.VOID_FILTER]);
1469: contentDelimitation = true;
1470: }
1471:
1472: // Check for compression
1473: boolean useCompression = false;
1474: if (entityBody && (compressionLevel > 0)) {
1475: useCompression = isCompressable();
1476:
1477: // Change content-length to -1 to force chunking
1478: if (useCompression) {
1479: response.setContentLength(-1);
1480: }
1481: }
1482:
1483: MimeHeaders headers = response.getMimeHeaders();
1484: if (!entityBody) {
1485: response.setContentLength(-1);
1486: } else {
1487: String contentType = response.getContentType();
1488: if (contentType != null) {
1489: headers.setValue("Content-Type").setString(contentType);
1490: }
1491: String contentLanguage = response.getContentLanguage();
1492: if (contentLanguage != null) {
1493: headers.setValue("Content-Language").setString(
1494: contentLanguage);
1495: }
1496: }
1497:
1498: long contentLength = response.getContentLengthLong();
1499: if (contentLength != -1) {
1500: headers.setValue("Content-Length").setLong(contentLength);
1501: outputBuffer
1502: .addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]);
1503: contentDelimitation = true;
1504: } else {
1505: if (entityBody && http11 && keepAlive) {
1506: outputBuffer
1507: .addActiveFilter(outputFilters[Constants.CHUNKED_FILTER]);
1508: contentDelimitation = true;
1509: headers.addValue(Constants.TRANSFERENCODING).setString(
1510: Constants.CHUNKED);
1511: } else {
1512: outputBuffer
1513: .addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]);
1514: }
1515: }
1516:
1517: if (useCompression) {
1518: outputBuffer
1519: .addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
1520: headers.setValue("Content-Encoding").setString("gzip");
1521: // Make Proxies happy via Vary (from mod_deflate)
1522: headers.setValue("Vary").setString("Accept-Encoding");
1523: }
1524:
1525: // Add date header
1526: String date = null;
1527: if (org.apache.coyote.Constants.IS_SECURITY_ENABLED) {
1528: date = (String) AccessController
1529: .doPrivileged(new PrivilegedAction() {
1530: public Object run() {
1531: return FastHttpDateFormat.getCurrentDate();
1532: }
1533: });
1534: } else {
1535: date = FastHttpDateFormat.getCurrentDate();
1536: }
1537: headers.setValue("Date").setString(date);
1538:
1539: // FIXME: Add transfer encoding header
1540:
1541: if ((entityBody) && (!contentDelimitation)) {
1542: // Mark as close the connection after the request, and add the
1543: // connection: close header
1544: keepAlive = false;
1545: }
1546:
1547: // If we know that the request is bad this early, add the
1548: // Connection: close header.
1549: keepAlive = keepAlive && !statusDropsConnection(statusCode);
1550: if (!keepAlive) {
1551: headers.addValue(Constants.CONNECTION).setString(
1552: Constants.CLOSE);
1553: } else if (!http11 && !error) {
1554: headers.addValue(Constants.CONNECTION).setString(
1555: Constants.KEEPALIVE);
1556: }
1557:
1558: // Build the response header
1559: outputBuffer.sendStatus();
1560:
1561: // Add server header
1562: if (server != null) {
1563: headers.setValue("Server").setString(server);
1564: } else {
1565: outputBuffer.write(Constants.SERVER_BYTES);
1566: }
1567:
1568: int size = headers.size();
1569: for (int i = 0; i < size; i++) {
1570: outputBuffer.sendHeader(headers.getName(i), headers
1571: .getValue(i));
1572: }
1573: outputBuffer.endHeaders();
1574:
1575: }
1576:
1577: /**
1578: * Initialize standard input and output filters.
1579: */
1580: protected void initializeFilters() {
1581:
1582: // Create and add the identity filters.
1583: inputBuffer.addFilter(new IdentityInputFilter());
1584: outputBuffer.addFilter(new IdentityOutputFilter());
1585:
1586: // Create and add the chunked filters.
1587: inputBuffer.addFilter(new ChunkedInputFilter());
1588: outputBuffer.addFilter(new ChunkedOutputFilter());
1589:
1590: // Create and add the void filters.
1591: inputBuffer.addFilter(new VoidInputFilter());
1592: outputBuffer.addFilter(new VoidOutputFilter());
1593:
1594: // Create and add buffered input filter
1595: inputBuffer.addFilter(new BufferedInputFilter());
1596:
1597: // Create and add the chunked filters.
1598: //inputBuffer.addFilter(new GzipInputFilter());
1599: outputBuffer.addFilter(new GzipOutputFilter());
1600:
1601: }
1602:
1603: /**
1604: * Add an input filter to the current request.
1605: *
1606: * @return false if the encoding was not found (which would mean it is
1607: * unsupported)
1608: */
1609: protected boolean addInputFilter(InputFilter[] inputFilters,
1610: String encodingName) {
1611: if (encodingName.equals("identity")) {
1612: // Skip
1613: } else if (encodingName.equals("chunked")) {
1614: inputBuffer
1615: .addActiveFilter(inputFilters[Constants.CHUNKED_FILTER]);
1616: contentDelimitation = true;
1617: } else {
1618: for (int i = 2; i < inputFilters.length; i++) {
1619: if (inputFilters[i].getEncodingName().toString()
1620: .equals(encodingName)) {
1621: inputBuffer.addActiveFilter(inputFilters[i]);
1622: return true;
1623: }
1624: }
1625: return false;
1626: }
1627: return true;
1628: }
1629:
1630: /**
1631: * Specialized utility method: find a sequence of lower case bytes inside
1632: * a ByteChunk.
1633: */
1634: protected int findBytes(ByteChunk bc, byte[] b) {
1635:
1636: byte first = b[0];
1637: byte[] buff = bc.getBuffer();
1638: int start = bc.getStart();
1639: int end = bc.getEnd();
1640:
1641: // Look for first char
1642: int srcEnd = b.length;
1643:
1644: for (int i = start; i <= (end - srcEnd); i++) {
1645: if (Ascii.toLower(buff[i]) != first)
1646: continue;
1647: // found first char, now look for a match
1648: int myPos = i + 1;
1649: for (int srcPos = 1; srcPos < srcEnd;) {
1650: if (Ascii.toLower(buff[myPos++]) != b[srcPos++])
1651: break;
1652: if (srcPos == srcEnd)
1653: return i - start; // found it
1654: }
1655: }
1656: return -1;
1657:
1658: }
1659:
1660: /**
1661: * Determine if we must drop the connection because of the HTTP status
1662: * code. Use the same list of codes as Apache/httpd.
1663: */
1664: protected boolean statusDropsConnection(int status) {
1665: return status == 400 /* SC_BAD_REQUEST */|| status == 408 /* SC_REQUEST_TIMEOUT */
1666: || status == 411 /* SC_LENGTH_REQUIRED */
1667: || status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */
1668: || status == 414 /* SC_REQUEST_URI_TOO_LARGE */
1669: || status == 500 /* SC_INTERNAL_SERVER_ERROR */
1670: || status == 503 /* SC_SERVICE_UNAVAILABLE */
1671: || status == 501 /* SC_NOT_IMPLEMENTED */;
1672: }
1673:
1674: }
|