Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » coyote » http11 » [javadoc | source]
    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one or more
    3    *  contributor license agreements.  See the NOTICE file distributed with
    4    *  this work for additional information regarding copyright ownership.
    5    *  The ASF licenses this file to You under the Apache License, Version 2.0
    6    *  (the "License"); you may not use this file except in compliance with
    7    *  the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    */
   17   
   18   package org.apache.coyote.http11;
   19   
   20   import java.io.ByteArrayInputStream;
   21   import java.io.IOException;
   22   import java.io.InterruptedIOException;
   23   import java.util.StringTokenizer;
   24   import java.util.regex.Pattern;
   25   import java.util.regex.PatternSyntaxException;
   26   import java.security.cert.CertificateFactory;
   27   import java.security.cert.X509Certificate;
   28   
   29   import org.apache.coyote.ActionCode;
   30   import org.apache.coyote.ActionHook;
   31   import org.apache.coyote.Adapter;
   32   import org.apache.coyote.Request;
   33   import org.apache.coyote.RequestInfo;
   34   import org.apache.coyote.Response;
   35   import org.apache.coyote.http11.filters.ChunkedInputFilter;
   36   import org.apache.coyote.http11.filters.ChunkedOutputFilter;
   37   import org.apache.coyote.http11.filters.GzipOutputFilter;
   38   import org.apache.coyote.http11.filters.IdentityInputFilter;
   39   import org.apache.coyote.http11.filters.IdentityOutputFilter;
   40   import org.apache.coyote.http11.filters.SavedRequestInputFilter;
   41   import org.apache.coyote.http11.filters.VoidInputFilter;
   42   import org.apache.coyote.http11.filters.VoidOutputFilter;
   43   import org.apache.coyote.http11.filters.BufferedInputFilter;
   44   import org.apache.tomcat.jni.Address;
   45   import org.apache.tomcat.jni.SSL;
   46   import org.apache.tomcat.jni.SSLSocket;
   47   import org.apache.tomcat.jni.Sockaddr;
   48   import org.apache.tomcat.jni.Socket;
   49   import org.apache.tomcat.util.buf.Ascii;
   50   import org.apache.tomcat.util.buf.ByteChunk;
   51   import org.apache.tomcat.util.buf.HexUtils;
   52   import org.apache.tomcat.util.buf.MessageBytes;
   53   import org.apache.tomcat.util.http.FastHttpDateFormat;
   54   import org.apache.tomcat.util.http.MimeHeaders;
   55   import org.apache.tomcat.util.net.AprEndpoint;
   56   import org.apache.tomcat.util.net.SocketStatus;
   57   import org.apache.tomcat.util.net.AprEndpoint.Handler.SocketState;
   58   import org.apache.tomcat.util.res.StringManager;
   59   
   60   
   61   /**
   62    * Processes HTTP requests.
   63    *
   64    * @author Remy Maucherat
   65    */
   66   public class Http11AprProcessor implements ActionHook {
   67   
   68   
   69       /**
   70        * Logger.
   71        */
   72       protected static org.apache.juli.logging.Log log
   73           = org.apache.juli.logging.LogFactory.getLog(Http11AprProcessor.class);
   74   
   75       /**
   76        * The string manager for this package.
   77        */
   78       protected static StringManager sm =
   79           StringManager.getManager(Constants.Package);
   80   
   81   
   82       // ----------------------------------------------------------- Constructors
   83   
   84   
   85       public Http11AprProcessor(int headerBufferSize, AprEndpoint endpoint) {
   86   
   87           this.endpoint = endpoint;
   88           
   89           request = new Request();
   90           inputBuffer = new InternalAprInputBuffer(request, headerBufferSize);
   91           request.setInputBuffer(inputBuffer);
   92   
   93           response = new Response();
   94           response.setHook(this);
   95           outputBuffer = new InternalAprOutputBuffer(response, headerBufferSize);
   96           response.setOutputBuffer(outputBuffer);
   97           request.setResponse(response);
   98           
   99           ssl = endpoint.isSSLEnabled();
  100   
  101           initializeFilters();
  102   
  103           // Cause loading of HexUtils
  104           int foo = HexUtils.DEC[0];
  105   
  106           // Cause loading of FastHttpDateFormat
  107           FastHttpDateFormat.getCurrentDate();
  108   
  109       }
  110   
  111   
  112       // ----------------------------------------------------- Instance Variables
  113   
  114   
  115       /**
  116        * Associated adapter.
  117        */
  118       protected Adapter adapter = null;
  119   
  120   
  121       /**
  122        * Request object.
  123        */
  124       protected Request request = null;
  125   
  126   
  127       /**
  128        * Response object.
  129        */
  130       protected Response response = null;
  131   
  132   
  133       /**
  134        * Input.
  135        */
  136       protected InternalAprInputBuffer inputBuffer = null;
  137   
  138   
  139       /**
  140        * Output.
  141        */
  142       protected InternalAprOutputBuffer outputBuffer = null;
  143   
  144   
  145       /**
  146        * Error flag.
  147        */
  148       protected boolean error = false;
  149   
  150   
  151       /**
  152        * Keep-alive.
  153        */
  154       protected boolean keepAlive = true;
  155   
  156   
  157       /**
  158        * HTTP/1.1 flag.
  159        */
  160       protected boolean http11 = true;
  161   
  162   
  163       /**
  164        * HTTP/0.9 flag.
  165        */
  166       protected boolean http09 = false;
  167   
  168   
  169       /**
  170        * Sendfile data.
  171        */
  172       protected AprEndpoint.SendfileData sendfileData = null;
  173   
  174   
  175       /**
  176        * Comet used.
  177        */
  178       protected boolean comet = false;
  179   
  180   
  181       /**
  182        * Content delimitator for the request (if false, the connection will
  183        * be closed at the end of the request).
  184        */
  185       protected boolean contentDelimitation = true;
  186   
  187   
  188       /**
  189        * Is there an expectation ?
  190        */
  191       protected boolean expectation = false;
  192   
  193   
  194       /**
  195        * List of restricted user agents.
  196        */
  197       protected Pattern[] restrictedUserAgents = null;
  198   
  199   
  200       /**
  201        * Maximum number of Keep-Alive requests to honor.
  202        */
  203       protected int maxKeepAliveRequests = -1;
  204   
  205   
  206       /**
  207        * SSL enabled ?
  208        */
  209       protected boolean ssl = false;
  210       
  211   
  212       /**
  213        * Socket associated with the current connection.
  214        */
  215       protected long socket = 0;
  216   
  217   
  218       /**
  219        * Remote Address associated with the current connection.
  220        */
  221       protected String remoteAddr = null;
  222   
  223   
  224       /**
  225        * Remote Host associated with the current connection.
  226        */
  227       protected String remoteHost = null;
  228   
  229   
  230       /**
  231        * Local Host associated with the current connection.
  232        */
  233       protected String localName = null;
  234   
  235   
  236   
  237       /**
  238        * Local port to which the socket is connected
  239        */
  240       protected int localPort = -1;
  241   
  242   
  243       /**
  244        * Remote port to which the socket is connected
  245        */
  246       protected int remotePort = -1;
  247   
  248   
  249       /**
  250        * The local Host address.
  251        */
  252       protected String localAddr = null;
  253   
  254   
  255       /**
  256        * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server.
  257        */
  258       protected int timeout = 300000;
  259   
  260   
  261       /**
  262        * Flag to disable setting a different time-out on uploads.
  263        */
  264       protected boolean disableUploadTimeout = false;
  265   
  266   
  267       /**
  268        * Allowed compression level.
  269        */
  270       protected int compressionLevel = 0;
  271   
  272   
  273       /**
  274        * Minimum contentsize to make compression.
  275        */
  276       protected int compressionMinSize = 2048;
  277   
  278   
  279       /**
  280        * Socket buffering.
  281        */
  282       protected int socketBuffer = -1;
  283   
  284   
  285       /**
  286        * Max save post size.
  287        */
  288       protected int maxSavePostSize = 4 * 1024;
  289   
  290   
  291       /**
  292        * List of user agents to not use gzip with
  293        */
  294       protected Pattern noCompressionUserAgents[] = null;
  295   
  296       /**
  297        * List of MIMES which could be gzipped
  298        */
  299       protected String[] compressableMimeTypes =
  300       { "text/html", "text/xml", "text/plain" };
  301   
  302   
  303       /**
  304        * Host name (used to avoid useless B2C conversion on the host name).
  305        */
  306       protected char[] hostNameC = new char[0];
  307   
  308   
  309       /**
  310        * Associated endpoint.
  311        */
  312       protected AprEndpoint endpoint;
  313   
  314   
  315       /**
  316        * Allow a customized the server header for the tin-foil hat folks.
  317        */
  318       protected String server = null;
  319   
  320       
  321       // ------------------------------------------------------------- Properties
  322   
  323   
  324       /**
  325        * Return compression level.
  326        */
  327       public String getCompression() {
  328           switch (compressionLevel) {
  329           case 0:
  330               return "off";
  331           case 1:
  332               return "on";
  333           case 2:
  334               return "force";
  335           }
  336           return "off";
  337       }
  338   
  339   
  340       /**
  341        * Set compression level.
  342        */
  343       public void setCompression(String compression) {
  344           if (compression.equals("on")) {
  345               this.compressionLevel = 1;
  346           } else if (compression.equals("force")) {
  347               this.compressionLevel = 2;
  348           } else if (compression.equals("off")) {
  349               this.compressionLevel = 0;
  350           } else {
  351               try {
  352                   // Try to parse compression as an int, which would give the
  353                   // minimum compression size
  354                   compressionMinSize = Integer.parseInt(compression);
  355                   this.compressionLevel = 1;
  356               } catch (Exception e) {
  357                   this.compressionLevel = 0;
  358               }
  359           }
  360       }
  361   
  362       /**
  363        * Set Minimum size to trigger compression.
  364        */
  365       public void setCompressionMinSize(int compressionMinSize) {
  366           this.compressionMinSize = compressionMinSize;
  367       }
  368   
  369   
  370       /**
  371        * Add user-agent for which gzip compression didn't works
  372        * The user agent String given will be exactly matched
  373        * to the user-agent header submitted by the client.
  374        *
  375        * @param userAgent user-agent string
  376        */
  377       public void addNoCompressionUserAgent(String userAgent) {
  378           try {
  379               Pattern nRule = Pattern.compile(userAgent);
  380               noCompressionUserAgents =
  381                   addREArray(noCompressionUserAgents, nRule);
  382           } catch (PatternSyntaxException pse) {
  383               log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
  384           }
  385       }
  386   
  387   
  388       /**
  389        * Set no compression user agent list (this method is best when used with
  390        * a large number of connectors, where it would be better to have all of
  391        * them referenced a single array).
  392        */
  393       public void setNoCompressionUserAgents(Pattern[] noCompressionUserAgents) {
  394           this.noCompressionUserAgents = noCompressionUserAgents;
  395       }
  396   
  397   
  398       /**
  399        * Set no compression user agent list.
  400        * List contains users agents separated by ',' :
  401        *
  402        * ie: "gorilla,desesplorer,tigrus"
  403        */
  404       public void setNoCompressionUserAgents(String noCompressionUserAgents) {
  405           if (noCompressionUserAgents != null) {
  406               StringTokenizer st = new StringTokenizer(noCompressionUserAgents, ",");
  407   
  408               while (st.hasMoreTokens()) {
  409                   addNoCompressionUserAgent(st.nextToken().trim());
  410               }
  411           }
  412       }
  413   
  414       /**
  415        * Add a mime-type which will be compressable
  416        * The mime-type String will be exactly matched
  417        * in the response mime-type header .
  418        *
  419        * @param mimeType mime-type string
  420        */
  421       public void addCompressableMimeType(String mimeType) {
  422           compressableMimeTypes =
  423               addStringArray(compressableMimeTypes, mimeType);
  424       }
  425   
  426   
  427       /**
  428        * Set compressable mime-type list (this method is best when used with
  429        * a large number of connectors, where it would be better to have all of
  430        * them referenced a single array).
  431        */
  432       public void setCompressableMimeTypes(String[] compressableMimeTypes) {
  433           this.compressableMimeTypes = compressableMimeTypes;
  434       }
  435   
  436   
  437       /**
  438        * Set compressable mime-type list
  439        * List contains users agents separated by ',' :
  440        *
  441        * ie: "text/html,text/xml,text/plain"
  442        */
  443       public void setCompressableMimeTypes(String compressableMimeTypes) {
  444           if (compressableMimeTypes != null) {
  445               StringTokenizer st = new StringTokenizer(compressableMimeTypes, ",");
  446   
  447               while (st.hasMoreTokens()) {
  448                   addCompressableMimeType(st.nextToken().trim());
  449               }
  450           }
  451       }
  452   
  453   
  454       /**
  455        * Return the list of restricted user agents.
  456        */
  457       public String[] findCompressableMimeTypes() {
  458           return (compressableMimeTypes);
  459       }
  460   
  461   
  462   
  463       // --------------------------------------------------------- Public Methods
  464   
  465   
  466       /**
  467        * Add input or output filter.
  468        *
  469        * @param className class name of the filter
  470        */
  471       protected void addFilter(String className) {
  472           try {
  473               Class clazz = Class.forName(className);
  474               Object obj = clazz.newInstance();
  475               if (obj instanceof InputFilter) {
  476                   inputBuffer.addFilter((InputFilter) obj);
  477               } else if (obj instanceof OutputFilter) {
  478                   outputBuffer.addFilter((OutputFilter) obj);
  479               } else {
  480                   log.warn(sm.getString("http11processor.filter.unknown", className));
  481               }
  482           } catch (Exception e) {
  483               log.error(sm.getString("http11processor.filter.error", className), e);
  484           }
  485       }
  486   
  487   
  488       /**
  489        * General use method
  490        *
  491        * @param sArray the StringArray
  492        * @param value string
  493        */
  494       private String[] addStringArray(String sArray[], String value) {
  495           String[] result = null;
  496           if (sArray == null) {
  497               result = new String[1];
  498               result[0] = value;
  499           }
  500           else {
  501               result = new String[sArray.length + 1];
  502               for (int i = 0; i < sArray.length; i++)
  503                   result[i] = sArray[i];
  504               result[sArray.length] = value;
  505           }
  506           return result;
  507       }
  508   
  509   
  510       /**
  511        * General use method
  512        *
  513        * @param rArray the REArray
  514        * @param value Obj
  515        */
  516       private Pattern[] addREArray(Pattern rArray[], Pattern value) {
  517           Pattern[] result = null;
  518           if (rArray == null) {
  519               result = new Pattern[1];
  520               result[0] = value;
  521           }
  522           else {
  523               result = new Pattern[rArray.length + 1];
  524               for (int i = 0; i < rArray.length; i++)
  525                   result[i] = rArray[i];
  526               result[rArray.length] = value;
  527           }
  528           return result;
  529       }
  530   
  531   
  532       /**
  533        * General use method
  534        *
  535        * @param sArray the StringArray
  536        * @param value string
  537        */
  538       private boolean inStringArray(String sArray[], String value) {
  539           for (int i = 0; i < sArray.length; i++) {
  540               if (sArray[i].equals(value)) {
  541                   return true;
  542               }
  543           }
  544           return false;
  545       }
  546   
  547   
  548       /**
  549        * Checks if any entry in the string array starts with the specified value
  550        *
  551        * @param sArray the StringArray
  552        * @param value string
  553        */
  554       private boolean startsWithStringArray(String sArray[], String value) {
  555           if (value == null)
  556              return false;
  557           for (int i = 0; i < sArray.length; i++) {
  558               if (value.startsWith(sArray[i])) {
  559                   return true;
  560               }
  561           }
  562           return false;
  563       }
  564   
  565   
  566       /**
  567        * Add restricted user-agent (which will downgrade the connector
  568        * to HTTP/1.0 mode). The user agent String given will be matched
  569        * via regexp to the user-agent header submitted by the client.
  570        *
  571        * @param userAgent user-agent string
  572        */
  573       public void addRestrictedUserAgent(String userAgent) {
  574           try {
  575               Pattern nRule = Pattern.compile(userAgent);
  576               restrictedUserAgents = addREArray(restrictedUserAgents, nRule);
  577           } catch (PatternSyntaxException pse) {
  578               log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
  579           }
  580       }
  581   
  582   
  583       /**
  584        * Set restricted user agent list (this method is best when used with
  585        * a large number of connectors, where it would be better to have all of
  586        * them referenced a single array).
  587        */
  588       public void setRestrictedUserAgents(Pattern[] restrictedUserAgents) {
  589           this.restrictedUserAgents = restrictedUserAgents;
  590       }
  591   
  592   
  593       /**
  594        * Set restricted user agent list (which will downgrade the connector
  595        * to HTTP/1.0 mode). List contains users agents separated by ',' :
  596        *
  597        * ie: "gorilla,desesplorer,tigrus"
  598        */
  599       public void setRestrictedUserAgents(String restrictedUserAgents) {
  600           if (restrictedUserAgents != null) {
  601               StringTokenizer st =
  602                   new StringTokenizer(restrictedUserAgents, ",");
  603               while (st.hasMoreTokens()) {
  604                   addRestrictedUserAgent(st.nextToken().trim());
  605               }
  606           }
  607       }
  608   
  609   
  610       /**
  611        * Return the list of restricted user agents.
  612        */
  613       public String[] findRestrictedUserAgents() {
  614           String[] sarr = new String [restrictedUserAgents.length];
  615   
  616           for (int i = 0; i < restrictedUserAgents.length; i++)
  617               sarr[i] = restrictedUserAgents[i].toString();
  618   
  619           return (sarr);
  620       }
  621   
  622   
  623       /**
  624        * Set the maximum number of Keep-Alive requests to honor.
  625        * This is to safeguard from DoS attacks.  Setting to a negative
  626        * value disables the check.
  627        */
  628       public void setMaxKeepAliveRequests(int mkar) {
  629           maxKeepAliveRequests = mkar;
  630       }
  631   
  632   
  633       /**
  634        * Return the number of Keep-Alive requests that we will honor.
  635        */
  636       public int getMaxKeepAliveRequests() {
  637           return maxKeepAliveRequests;
  638       }
  639   
  640   
  641       /**
  642        * Set the maximum size of a POST which will be buffered in SSL mode.
  643        */
  644       public void setMaxSavePostSize(int msps) {
  645           maxSavePostSize = msps;
  646       }
  647   
  648   
  649       /**
  650        * Return the maximum size of a POST which will be buffered in SSL mode.
  651        */
  652       public int getMaxSavePostSize() {
  653           return maxSavePostSize;
  654       }
  655   
  656   
  657       /**
  658        * Set the flag to control upload time-outs.
  659        */
  660       public void setDisableUploadTimeout(boolean isDisabled) {
  661           disableUploadTimeout = isDisabled;
  662       }
  663   
  664       /**
  665        * Get the flag that controls upload time-outs.
  666        */
  667       public boolean getDisableUploadTimeout() {
  668           return disableUploadTimeout;
  669       }
  670   
  671       /**
  672        * Set the socket buffer flag.
  673        */
  674       public void setSocketBuffer(int socketBuffer) {
  675           this.socketBuffer = socketBuffer;
  676           outputBuffer.setSocketBuffer(socketBuffer);
  677       }
  678   
  679       /**
  680        * Get the socket buffer flag.
  681        */
  682       public int getSocketBuffer() {
  683           return socketBuffer;
  684       }
  685   
  686       /**
  687        * Set the upload timeout.
  688        */
  689       public void setTimeout( int timeouts ) {
  690           timeout = timeouts ;
  691       }
  692   
  693       /**
  694        * Get the upload timeout.
  695        */
  696       public int getTimeout() {
  697           return timeout;
  698       }
  699   
  700       /**
  701        * Set the server header name.
  702        */
  703       public void setServer( String server ) {
  704           if (server==null || server.equals("")) {
  705               this.server = null;
  706           } else {
  707               this.server = server;
  708           }
  709       }
  710   
  711       /**
  712        * Get the server header name.
  713        */
  714       public String getServer() {
  715           return server;
  716       }
  717   
  718   
  719       /** Get the request associated with this processor.
  720        *
  721        * @return The request
  722        */
  723       public Request getRequest() {
  724           return request;
  725       }
  726   
  727       /**
  728        * Process pipelined HTTP requests using the specified input and output
  729        * streams.
  730        *
  731        * @throws IOException error during an I/O operation
  732        */
  733       public SocketState event(SocketStatus status)
  734           throws IOException {
  735           
  736           RequestInfo rp = request.getRequestProcessor();
  737           
  738           try {
  739               rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
  740               error = !adapter.event(request, response, status);
  741           } catch (InterruptedIOException e) {
  742               error = true;
  743           } catch (Throwable t) {
  744               log.error(sm.getString("http11processor.request.process"), t);
  745               // 500 - Internal Server Error
  746               response.setStatus(500);
  747               error = true;
  748           }
  749           
  750           rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
  751   
  752           if (error) {
  753               inputBuffer.nextRequest();
  754               outputBuffer.nextRequest();
  755               recycle();
  756               return SocketState.CLOSED;
  757           } else if (!comet) {
  758               inputBuffer.nextRequest();
  759               outputBuffer.nextRequest();
  760               recycle();
  761               return SocketState.OPEN;
  762           } else {
  763               return SocketState.LONG;
  764           }
  765       }
  766       
  767       /**
  768        * Process pipelined HTTP requests using the specified input and output
  769        * streams.
  770        *
  771        * @throws IOException error during an I/O operation
  772        */
  773       public SocketState process(long socket)
  774           throws IOException {
  775           RequestInfo rp = request.getRequestProcessor();
  776           rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
  777   
  778           // Set the remote address
  779           remoteAddr = null;
  780           remoteHost = null;
  781           localAddr = null;
  782           localName = null;
  783           remotePort = -1;
  784           localPort = -1;
  785   
  786           // Setting up the socket
  787           this.socket = socket;
  788           inputBuffer.setSocket(socket);
  789           outputBuffer.setSocket(socket);
  790   
  791           // Error flag
  792           error = false;
  793           comet = false;
  794           keepAlive = true;
  795   
  796           int keepAliveLeft = maxKeepAliveRequests;
  797           long soTimeout = endpoint.getSoTimeout();
  798           
  799           boolean keptAlive = false;
  800           boolean openSocket = false;
  801   
  802           while (!error && keepAlive && !comet) {
  803   
  804               // Parsing the request header
  805               try {
  806                   if( !disableUploadTimeout && keptAlive && soTimeout > 0 ) {
  807                       Socket.timeoutSet(socket, soTimeout * 1000);
  808                   }
  809                   if (!inputBuffer.parseRequestLine(keptAlive)) {
  810                       // This means that no data is available right now
  811                       // (long keepalive), so that the processor should be recycled
  812                       // and the method should return true
  813                       openSocket = true;
  814                       // Add the socket to the poller
  815                       endpoint.getPoller().add(socket);
  816                       break;
  817                   }
  818                   request.setStartTime(System.currentTimeMillis());
  819                   keptAlive = true;
  820                   if (!disableUploadTimeout) {
  821                       Socket.timeoutSet(socket, timeout * 1000);
  822                   }
  823                   inputBuffer.parseHeaders();
  824               } catch (IOException e) {
  825                   error = true;
  826                   break;
  827               } catch (Throwable t) {
  828                   if (log.isDebugEnabled()) {
  829                       log.debug(sm.getString("http11processor.header.parse"), t);
  830                   }
  831                   // 400 - Bad Request
  832                   response.setStatus(400);
  833                   error = true;
  834               }
  835   
  836               // Setting up filters, and parse some request headers
  837               rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
  838               try {
  839                   prepareRequest();
  840               } catch (Throwable t) {
  841                   if (log.isDebugEnabled()) {
  842                       log.debug(sm.getString("http11processor.request.prepare"), t);
  843                   }
  844                   // 400 - Internal Server Error
  845                   response.setStatus(400);
  846                   error = true;
  847               }
  848   
  849               if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
  850                   keepAlive = false;
  851   
  852               // Process the request in the adapter
  853               if (!error) {
  854                   try {
  855                       rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
  856                       adapter.service(request, response);
  857                       // Handle when the response was committed before a serious
  858                       // error occurred.  Throwing a ServletException should both
  859                       // set the status to 500 and set the errorException.
  860                       // If we fail here, then the response is likely already
  861                       // committed, so we can't try and set headers.
  862                       if(keepAlive && !error) { // Avoid checking twice.
  863                           error = response.getErrorException() != null ||
  864                                   statusDropsConnection(response.getStatus());
  865                       }
  866                   } catch (InterruptedIOException e) {
  867                       error = true;
  868                   } catch (Throwable t) {
  869                       log.error(sm.getString("http11processor.request.process"), t);
  870                       // 500 - Internal Server Error
  871                       response.setStatus(500);
  872                       error = true;
  873                   }
  874               }
  875   
  876               // Finish the handling of the request
  877               if (!comet) {
  878                   endRequest();
  879               }
  880   
  881               // If there was an error, make sure the request is counted as
  882               // and error, and update the statistics counter
  883               if (error) {
  884                   response.setStatus(500);
  885               }
  886               request.updateCounters();
  887   
  888               if (!comet) {
  889                   // Next request
  890                   inputBuffer.nextRequest();
  891                   outputBuffer.nextRequest();
  892               }
  893               
  894               // Do sendfile as needed: add socket to sendfile and end
  895               if (sendfileData != null && !error) {
  896                   sendfileData.socket = socket;
  897                   sendfileData.keepAlive = keepAlive;
  898                   if (!endpoint.getSendfile().add(sendfileData)) {
  899                       openSocket = true;
  900                       break;
  901                   }
  902               }
  903               
  904               rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
  905   
  906           }
  907   
  908           rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
  909   
  910           if (comet) {
  911               if (error) {
  912                   inputBuffer.nextRequest();
  913                   outputBuffer.nextRequest();
  914                   recycle();
  915                   return SocketState.CLOSED;
  916               } else {
  917                   return SocketState.LONG;
  918               }
  919           } else {
  920               recycle();
  921               return (openSocket) ? SocketState.OPEN : SocketState.CLOSED;
  922           }
  923           
  924       }
  925   
  926       
  927       public void endRequest() {
  928           
  929           // Finish the handling of the request
  930           try {
  931               inputBuffer.endRequest();
  932           } catch (IOException e) {
  933               error = true;
  934           } catch (Throwable t) {
  935               log.error(sm.getString("http11processor.request.finish"), t);
  936               // 500 - Internal Server Error
  937               response.setStatus(500);
  938               error = true;
  939           }
  940           try {
  941               outputBuffer.endRequest();
  942           } catch (IOException e) {
  943               error = true;
  944           } catch (Throwable t) {
  945               log.error(sm.getString("http11processor.response.finish"), t);
  946               error = true;
  947           }
  948   
  949       }
  950       
  951       
  952       public void recycle() {
  953           inputBuffer.recycle();
  954           outputBuffer.recycle();
  955           this.socket = 0;
  956       }
  957       
  958   
  959       // ----------------------------------------------------- ActionHook Methods
  960   
  961   
  962       /**
  963        * Send an action to the connector.
  964        *
  965        * @param actionCode Type of the action
  966        * @param param Action parameter
  967        */
  968       public void action(ActionCode actionCode, Object param) {
  969   
  970           if (actionCode == ActionCode.ACTION_COMMIT) {
  971               // Commit current response
  972   
  973               if (response.isCommitted())
  974                   return;
  975   
  976               // Validate and write response headers
  977               prepareResponse();
  978               try {
  979                   outputBuffer.commit();
  980               } catch (IOException e) {
  981                   // Set error flag
  982                   error = true;
  983               }
  984   
  985           } else if (actionCode == ActionCode.ACTION_ACK) {
  986   
  987               // Acknowlege request
  988   
  989               // Send a 100 status back if it makes sense (response not committed
  990               // yet, and client specified an expectation for 100-continue)
  991   
  992               if ((response.isCommitted()) || !expectation)
  993                   return;
  994   
  995               inputBuffer.setSwallowInput(true);
  996               try {
  997                   outputBuffer.sendAck();
  998               } catch (IOException e) {
  999                   // Set error flag
 1000                   error = true;
 1001               }
 1002   
 1003           } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
 1004   
 1005               try {
 1006                   outputBuffer.flush();
 1007               } catch (IOException e) {
 1008                   // Set error flag
 1009                   error = true;
 1010                   response.setErrorException(e);
 1011               }
 1012   
 1013           } else if (actionCode == ActionCode.ACTION_CLOSE) {
 1014               // Close
 1015   
 1016               // End the processing of the current request, and stop any further
 1017               // transactions with the client
 1018   
 1019               comet = false;
 1020               try {
 1021                   outputBuffer.endRequest();
 1022               } catch (IOException e) {
 1023                   // Set error flag
 1024                   error = true;
 1025               }
 1026   
 1027           } else if (actionCode == ActionCode.ACTION_RESET) {
 1028   
 1029               // Reset response
 1030   
 1031               // Note: This must be called before the response is committed
 1032   
 1033               outputBuffer.reset();
 1034   
 1035           } else if (actionCode == ActionCode.ACTION_CUSTOM) {
 1036   
 1037               // Do nothing
 1038   
 1039           } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
 1040   
 1041               // Get remote host address
 1042               if (remoteAddr == null && (socket != 0)) {
 1043                   try {
 1044                       long sa = Address.get(Socket.APR_REMOTE, socket);
 1045                       remoteAddr = Address.getip(sa);
 1046                   } catch (Exception e) {
 1047                       log.warn(sm.getString("http11processor.socket.info"), e);
 1048                   }
 1049               }
 1050               request.remoteAddr().setString(remoteAddr);
 1051   
 1052           } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
 1053   
 1054               // Get local host name
 1055               if (localName == null && (socket != 0)) {
 1056                   try {
 1057                       long sa = Address.get(Socket.APR_LOCAL, socket);
 1058                       localName = Address.getnameinfo(sa, 0);
 1059                   } catch (Exception e) {
 1060                       log.warn(sm.getString("http11processor.socket.info"), e);
 1061                   }
 1062               }
 1063               request.localName().setString(localName);
 1064   
 1065           } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
 1066   
 1067               // Get remote host name
 1068               if (remoteHost == null && (socket != 0)) {
 1069                   try {
 1070                       long sa = Address.get(Socket.APR_REMOTE, socket);
 1071                       remoteHost = Address.getnameinfo(sa, 0);
 1072                   } catch (Exception e) {
 1073                       log.warn(sm.getString("http11processor.socket.info"), e);
 1074                   }
 1075               }
 1076               request.remoteHost().setString(remoteHost);
 1077   
 1078           } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
 1079   
 1080               // Get local host address
 1081               if (localAddr == null && (socket != 0)) {
 1082                   try {
 1083                       long sa = Address.get(Socket.APR_LOCAL, socket);
 1084                       localAddr = Address.getip(sa);
 1085                   } catch (Exception e) {
 1086                       log.warn(sm.getString("http11processor.socket.info"), e);
 1087                   }
 1088               }
 1089   
 1090               request.localAddr().setString(localAddr);
 1091   
 1092           } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
 1093   
 1094               // Get remote port
 1095               if (remotePort == -1 && (socket != 0)) {
 1096                   try {
 1097                       long sa = Address.get(Socket.APR_REMOTE, socket);
 1098                       Sockaddr addr = Address.getInfo(sa);
 1099                       remotePort = addr.port;
 1100                   } catch (Exception e) {
 1101                       log.warn(sm.getString("http11processor.socket.info"), e);
 1102                   }
 1103               }
 1104               request.setRemotePort(remotePort);
 1105   
 1106           } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
 1107   
 1108               // Get local port
 1109               if (localPort == -1 && (socket != 0)) {
 1110                   try {
 1111                       long sa = Address.get(Socket.APR_LOCAL, socket);
 1112                       Sockaddr addr = Address.getInfo(sa);
 1113                       localPort = addr.port;
 1114                   } catch (Exception e) {
 1115                       log.warn(sm.getString("http11processor.socket.info"), e);
 1116                   }
 1117               }
 1118               request.setLocalPort(localPort);
 1119   
 1120           } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
 1121   
 1122               if (ssl && (socket != 0)) {
 1123                   try {
 1124                       // Cipher suite
 1125                       Object sslO = SSLSocket.getInfoS(socket, SSL.SSL_INFO_CIPHER);
 1126                       if (sslO != null) {
 1127                           request.setAttribute(AprEndpoint.CIPHER_SUITE_KEY, sslO);
 1128                       }
 1129                       // Get client certificate and the certificate chain if present
 1130                       int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
 1131                       byte[] clientCert = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT);
 1132                       X509Certificate[] certs = null;
 1133                       if (clientCert != null) {
 1134                           certs = new X509Certificate[certLength + 1];
 1135                           CertificateFactory cf = CertificateFactory.getInstance("X.509");
 1136                           certs[0] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(clientCert));
 1137                           for (int i = 0; i < certLength; i++) {
 1138                               byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
 1139                               certs[i+1] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(data));
 1140                           }
 1141                       }
 1142                       if (certs != null) {
 1143                           request.setAttribute(AprEndpoint.CERTIFICATE_KEY, certs);
 1144                       }
 1145                       // User key size
 1146                       sslO = new Integer(SSLSocket.getInfoI(socket, SSL.SSL_INFO_CIPHER_USEKEYSIZE));
 1147                       if (sslO != null) {
 1148                           request.setAttribute(AprEndpoint.KEY_SIZE_KEY, sslO);
 1149                       }
 1150                       // SSL session ID
 1151                       sslO = SSLSocket.getInfoS(socket, SSL.SSL_INFO_SESSION_ID);
 1152                       if (sslO != null) {
 1153                           request.setAttribute(AprEndpoint.SESSION_ID_KEY, sslO);
 1154                       }
 1155                   } catch (Exception e) {
 1156                       log.warn(sm.getString("http11processor.socket.ssl"), e);
 1157                   }
 1158               }
 1159   
 1160           } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
 1161   
 1162               if (ssl && (socket != 0)) {
 1163                    // Consume and buffer the request body, so that it does not
 1164                    // interfere with the client's handshake messages
 1165                   InputFilter[] inputFilters = inputBuffer.getFilters();
 1166                   ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]).setLimit(maxSavePostSize);
 1167                   inputBuffer.addActiveFilter(inputFilters[Constants.BUFFERED_FILTER]);
 1168                   try {
 1169                       // Renegociate certificates
 1170                       SSLSocket.renegotiate(socket);
 1171                       // Get client certificate and the certificate chain if present
 1172                       int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
 1173                       byte[] clientCert = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT);
 1174                       X509Certificate[] certs = null;
 1175                       if (clientCert != null) {
 1176                           certs = new X509Certificate[certLength + 1];
 1177                           CertificateFactory cf = CertificateFactory.getInstance("X.509");
 1178                           certs[0] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(clientCert));
 1179                           for (int i = 0; i < certLength; i++) {
 1180                               byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
 1181                               certs[i+1] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(data));
 1182                           }
 1183                       }
 1184                       if (certs != null) {
 1185                           request.setAttribute(AprEndpoint.CERTIFICATE_KEY, certs);
 1186                       }
 1187                   } catch (Exception e) {
 1188                       log.warn(sm.getString("http11processor.socket.ssl"), e);
 1189                   }
 1190               }
 1191   
 1192           } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
 1193               ByteChunk body = (ByteChunk) param;
 1194               
 1195               InputFilter savedBody = new SavedRequestInputFilter(body);
 1196               savedBody.setRequest(request);
 1197               
 1198               InternalAprInputBuffer internalBuffer = (InternalAprInputBuffer)
 1199                   request.getInputBuffer();
 1200               internalBuffer.addActiveFilter(savedBody);
 1201               
 1202           } else if (actionCode == ActionCode.ACTION_AVAILABLE) {
 1203               request.setAvailable(inputBuffer.available());
 1204           } else if (actionCode == ActionCode.ACTION_COMET_BEGIN) {
 1205               comet = true;
 1206           } else if (actionCode == ActionCode.ACTION_COMET_END) {
 1207               comet = false;
 1208           }
 1209   
 1210       }
 1211   
 1212   
 1213       // ------------------------------------------------------ Connector Methods
 1214   
 1215   
 1216       /**
 1217        * Set the associated adapter.
 1218        *
 1219        * @param adapter the new adapter
 1220        */
 1221       public void setAdapter(Adapter adapter) {
 1222           this.adapter = adapter;
 1223       }
 1224   
 1225   
 1226       /**
 1227        * Get the associated adapter.
 1228        *
 1229        * @return the associated adapter
 1230        */
 1231       public Adapter getAdapter() {
 1232           return adapter;
 1233       }
 1234   
 1235   
 1236       // ------------------------------------------------------ Protected Methods
 1237   
 1238   
 1239       /**
 1240        * After reading the request headers, we have to setup the request filters.
 1241        */
 1242       protected void prepareRequest() {
 1243   
 1244           http11 = true;
 1245           http09 = false;
 1246           contentDelimitation = false;
 1247           expectation = false;
 1248           sendfileData = null;
 1249           if (ssl) {
 1250               request.scheme().setString("https");
 1251           }
 1252           MessageBytes protocolMB = request.protocol();
 1253           if (protocolMB.equals(Constants.HTTP_11)) {
 1254               http11 = true;
 1255               protocolMB.setString(Constants.HTTP_11);
 1256           } else if (protocolMB.equals(Constants.HTTP_10)) {
 1257               http11 = false;
 1258               keepAlive = false;
 1259               protocolMB.setString(Constants.HTTP_10);
 1260           } else if (protocolMB.equals("")) {
 1261               // HTTP/0.9
 1262               http09 = true;
 1263               http11 = false;
 1264               keepAlive = false;
 1265           } else {
 1266               // Unsupported protocol
 1267               http11 = false;
 1268               error = true;
 1269               // Send 505; Unsupported HTTP version
 1270               response.setStatus(505);
 1271           }
 1272   
 1273           MessageBytes methodMB = request.method();
 1274           if (methodMB.equals(Constants.GET)) {
 1275               methodMB.setString(Constants.GET);
 1276           } else if (methodMB.equals(Constants.POST)) {
 1277               methodMB.setString(Constants.POST);
 1278           }
 1279   
 1280           MimeHeaders headers = request.getMimeHeaders();
 1281   
 1282           // Check connection header
 1283           MessageBytes connectionValueMB = headers.getValue("connection");
 1284           if (connectionValueMB != null) {
 1285               ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
 1286               if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
 1287                   keepAlive = false;
 1288               } else if (findBytes(connectionValueBC,
 1289                                    Constants.KEEPALIVE_BYTES) != -1) {
 1290                   keepAlive = true;
 1291               }
 1292           }
 1293   
 1294           MessageBytes expectMB = null;
 1295           if (http11)
 1296               expectMB = headers.getValue("expect");
 1297           if ((expectMB != null)
 1298               && (expectMB.indexOfIgnoreCase("100-continue", 0) != -1)) {
 1299               inputBuffer.setSwallowInput(false);
 1300               expectation = true;
 1301           }
 1302   
 1303           // Check user-agent header
 1304           if ((restrictedUserAgents != null) && ((http11) || (keepAlive))) {
 1305               MessageBytes userAgentValueMB = headers.getValue("user-agent");
 1306               // Check in the restricted list, and adjust the http11
 1307               // and keepAlive flags accordingly
 1308               if(userAgentValueMB != null) {
 1309                   String userAgentValue = userAgentValueMB.toString();
 1310                   for (int i = 0; i < restrictedUserAgents.length; i++) {
 1311                       if (restrictedUserAgents[i].matcher(userAgentValue).matches()) {
 1312                           http11 = false;
 1313                           keepAlive = false;
 1314                           break;
 1315                       }
 1316                   }
 1317               }
 1318           }
 1319   
 1320           // Check for a full URI (including protocol://host:port/)
 1321           ByteChunk uriBC = request.requestURI().getByteChunk();
 1322           if (uriBC.startsWithIgnoreCase("http", 0)) {
 1323   
 1324               int pos = uriBC.indexOf("://", 0, 3, 4);
 1325               int uriBCStart = uriBC.getStart();
 1326               int slashPos = -1;
 1327               if (pos != -1) {
 1328                   byte[] uriB = uriBC.getBytes();
 1329                   slashPos = uriBC.indexOf('/', pos + 3);
 1330                   if (slashPos == -1) {
 1331                       slashPos = uriBC.getLength();
 1332                       // Set URI as "/"
 1333                       request.requestURI().setBytes
 1334                           (uriB, uriBCStart + pos + 1, 1);
 1335                   } else {
 1336                       request.requestURI().setBytes
 1337                           (uriB, uriBCStart + slashPos,
 1338                            uriBC.getLength() - slashPos);
 1339                   }
 1340                   MessageBytes hostMB = headers.setValue("host");
 1341                   hostMB.setBytes(uriB, uriBCStart + pos + 3,
 1342                                   slashPos - pos - 3);
 1343               }
 1344   
 1345           }
 1346   
 1347           // Input filter setup
 1348           InputFilter[] inputFilters = inputBuffer.getFilters();
 1349   
 1350           // Parse transfer-encoding header
 1351           MessageBytes transferEncodingValueMB = null;
 1352           if (http11)
 1353               transferEncodingValueMB = headers.getValue("transfer-encoding");
 1354           if (transferEncodingValueMB != null) {
 1355               String transferEncodingValue = transferEncodingValueMB.toString();
 1356               // Parse the comma separated list. "identity" codings are ignored
 1357               int startPos = 0;
 1358               int commaPos = transferEncodingValue.indexOf(',');
 1359               String encodingName = null;
 1360               while (commaPos != -1) {
 1361                   encodingName = transferEncodingValue.substring
 1362                       (startPos, commaPos).toLowerCase().trim();
 1363                   if (!addInputFilter(inputFilters, encodingName)) {
 1364                       // Unsupported transfer encoding
 1365                       error = true;
 1366                       // 501 - Unimplemented
 1367                       response.setStatus(501);
 1368                   }
 1369                   startPos = commaPos + 1;
 1370                   commaPos = transferEncodingValue.indexOf(',', startPos);
 1371               }
 1372               encodingName = transferEncodingValue.substring(startPos)
 1373                   .toLowerCase().trim();
 1374               if (!addInputFilter(inputFilters, encodingName)) {
 1375                   // Unsupported transfer encoding
 1376                   error = true;
 1377                   // 501 - Unimplemented
 1378                   response.setStatus(501);
 1379               }
 1380           }
 1381   
 1382           // Parse content-length header
 1383           long contentLength = request.getContentLengthLong();
 1384           if (contentLength >= 0 && !contentDelimitation) {
 1385               inputBuffer.addActiveFilter
 1386                   (inputFilters[Constants.IDENTITY_FILTER]);
 1387               contentDelimitation = true;
 1388           }
 1389   
 1390           MessageBytes valueMB = headers.getValue("host");
 1391   
 1392           // Check host header
 1393           if (http11 && (valueMB == null)) {
 1394               error = true;
 1395               // 400 - Bad request
 1396               response.setStatus(400);
 1397           }
 1398   
 1399           parseHost(valueMB);
 1400   
 1401           if (!contentDelimitation) {
 1402               // If there's no content length 
 1403               // (broken HTTP/1.0 or HTTP/1.1), assume
 1404               // the client is not broken and didn't send a body
 1405               inputBuffer.addActiveFilter
 1406                       (inputFilters[Constants.VOID_FILTER]);
 1407               contentDelimitation = true;
 1408           }
 1409   
 1410           // Advertise sendfile support through a request attribute
 1411           if (endpoint.getUseSendfile()) {
 1412               request.setAttribute("org.apache.tomcat.sendfile.support", Boolean.TRUE);
 1413           }
 1414           // Advertise comet support through a request attribute
 1415           request.setAttribute("org.apache.tomcat.comet.support", Boolean.TRUE);
 1416           
 1417       }
 1418   
 1419   
 1420       /**
 1421        * Parse host.
 1422        */
 1423       public void parseHost(MessageBytes valueMB) {
 1424   
 1425           if (valueMB == null || valueMB.isNull()) {
 1426               // HTTP/1.0
 1427               // Default is what the socket tells us. Overriden if a host is
 1428               // found/parsed
 1429               request.setServerPort(endpoint.getPort());
 1430               return;
 1431           }
 1432   
 1433           ByteChunk valueBC = valueMB.getByteChunk();
 1434           byte[] valueB = valueBC.getBytes();
 1435           int valueL = valueBC.getLength();
 1436           int valueS = valueBC.getStart();
 1437           int colonPos = -1;
 1438           if (hostNameC.length < valueL) {
 1439               hostNameC = new char[valueL];
 1440           }
 1441   
 1442           boolean ipv6 = (valueB[valueS] == '[');
 1443           boolean bracketClosed = false;
 1444           for (int i = 0; i < valueL; i++) {
 1445               char b = (char) valueB[i + valueS];
 1446               hostNameC[i] = b;
 1447               if (b == ']') {
 1448                   bracketClosed = true;
 1449               } else if (b == ':') {
 1450                   if (!ipv6 || bracketClosed) {
 1451                       colonPos = i;
 1452                       break;
 1453                   }
 1454               }
 1455           }
 1456   
 1457           if (colonPos < 0) {
 1458               if (!ssl) {
 1459                   // 80 - Default HTTP port
 1460                   request.setServerPort(80);
 1461               } else {
 1462                   // 443 - Default HTTPS port
 1463                   request.setServerPort(443);
 1464               }
 1465               request.serverName().setChars(hostNameC, 0, valueL);
 1466           } else {
 1467   
 1468               request.serverName().setChars(hostNameC, 0, colonPos);
 1469   
 1470               int port = 0;
 1471               int mult = 1;
 1472               for (int i = valueL - 1; i > colonPos; i--) {
 1473                   int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
 1474                   if (charValue == -1) {
 1475                       // Invalid character
 1476                       error = true;
 1477                       // 400 - Bad request
 1478                       response.setStatus(400);
 1479                       break;
 1480                   }
 1481                   port = port + (charValue * mult);
 1482                   mult = 10 * mult;
 1483               }
 1484               request.setServerPort(port);
 1485   
 1486           }
 1487   
 1488       }
 1489   
 1490   
 1491       /**
 1492        * Check for compression
 1493        */
 1494       private boolean isCompressable() {
 1495   
 1496           // Nope Compression could works in HTTP 1.0 also
 1497           // cf: mod_deflate
 1498   
 1499           // Compression only since HTTP 1.1
 1500           // if (! http11)
 1501           //    return false;
 1502   
 1503           // Check if browser support gzip encoding
 1504           MessageBytes acceptEncodingMB =
 1505               request.getMimeHeaders().getValue("accept-encoding");
 1506   
 1507           if ((acceptEncodingMB == null)
 1508               || (acceptEncodingMB.indexOf("gzip") == -1))
 1509               return false;
 1510   
 1511           // Check if content is not allready gzipped
 1512           MessageBytes contentEncodingMB =
 1513               response.getMimeHeaders().getValue("Content-Encoding");
 1514   
 1515           if ((contentEncodingMB != null)
 1516               && (contentEncodingMB.indexOf("gzip") != -1))
 1517               return false;
 1518   
 1519           // If force mode, allways compress (test purposes only)
 1520           if (compressionLevel == 2)
 1521              return true;
 1522   
 1523           // Check for incompatible Browser
 1524           if (noCompressionUserAgents != null) {
 1525               MessageBytes userAgentValueMB =
 1526                   request.getMimeHeaders().getValue("user-agent");
 1527               if(userAgentValueMB != null) {
 1528                   String userAgentValue = userAgentValueMB.toString();
 1529   
 1530                   // If one Regexp rule match, disable compression
 1531                   for (int i = 0; i < noCompressionUserAgents.length; i++)
 1532                       if (noCompressionUserAgents[i].matcher(userAgentValue).matches())
 1533                           return false;
 1534               }
 1535           }
 1536   
 1537           // Check if suffisant len to trig the compression
 1538           long contentLength = response.getContentLengthLong();
 1539           if ((contentLength == -1)
 1540               || (contentLength > compressionMinSize)) {
 1541               // Check for compatible MIME-TYPE
 1542               if (compressableMimeTypes != null) {
 1543                   return (startsWithStringArray(compressableMimeTypes,
 1544                                                 response.getContentType()));
 1545               }
 1546           }
 1547   
 1548           return false;
 1549       }
 1550   
 1551   
 1552       /**
 1553        * When committing the response, we have to validate the set of headers, as
 1554        * well as setup the response filters.
 1555        */
 1556       protected void prepareResponse() {
 1557   
 1558           boolean entityBody = true;
 1559           contentDelimitation = false;
 1560   
 1561           OutputFilter[] outputFilters = outputBuffer.getFilters();
 1562   
 1563           if (http09 == true) {
 1564               // HTTP/0.9
 1565               outputBuffer.addActiveFilter
 1566                   (outputFilters[Constants.IDENTITY_FILTER]);
 1567               return;
 1568           }
 1569   
 1570           int statusCode = response.getStatus();
 1571           if ((statusCode == 204) || (statusCode == 205)
 1572               || (statusCode == 304)) {
 1573               // No entity body
 1574               outputBuffer.addActiveFilter
 1575                   (outputFilters[Constants.VOID_FILTER]);
 1576               entityBody = false;
 1577               contentDelimitation = true;
 1578           }
 1579   
 1580           MessageBytes methodMB = request.method();
 1581           if (methodMB.equals("HEAD")) {
 1582               // No entity body
 1583               outputBuffer.addActiveFilter
 1584                   (outputFilters[Constants.VOID_FILTER]);
 1585               contentDelimitation = true;
 1586           }
 1587   
 1588           // Sendfile support
 1589           if (endpoint.getUseSendfile()) {
 1590               String fileName = (String) request.getAttribute("org.apache.tomcat.sendfile.filename");
 1591               if (fileName != null) {
 1592                   // No entity body sent here
 1593                   outputBuffer.addActiveFilter
 1594                       (outputFilters[Constants.VOID_FILTER]);
 1595                   contentDelimitation = true;
 1596                   sendfileData = new AprEndpoint.SendfileData();
 1597                   sendfileData.fileName = fileName;
 1598                   sendfileData.start = 
 1599                       ((Long) request.getAttribute("org.apache.tomcat.sendfile.start")).longValue();
 1600                   sendfileData.end = 
 1601                       ((Long) request.getAttribute("org.apache.tomcat.sendfile.end")).longValue();
 1602               }
 1603           }
 1604           
 1605           // Check for compression
 1606           boolean useCompression = false;
 1607           if (entityBody && (compressionLevel > 0) && (sendfileData == null)) {
 1608               useCompression = isCompressable();
 1609               // Change content-length to -1 to force chunking
 1610               if (useCompression) {
 1611                   response.setContentLength(-1);
 1612               }
 1613           }
 1614   
 1615           MimeHeaders headers = response.getMimeHeaders();
 1616           if (!entityBody) {
 1617               response.setContentLength(-1);
 1618           } else {
 1619               String contentType = response.getContentType();
 1620               if (contentType != null) {
 1621                   headers.setValue("Content-Type").setString(contentType);
 1622               }
 1623               String contentLanguage = response.getContentLanguage();
 1624               if (contentLanguage != null) {
 1625                   headers.setValue("Content-Language")
 1626                       .setString(contentLanguage);
 1627               }
 1628           }
 1629   
 1630           long contentLength = response.getContentLengthLong();
 1631           if (contentLength != -1) {
 1632               headers.setValue("Content-Length").setLong(contentLength);
 1633               outputBuffer.addActiveFilter
 1634                   (outputFilters[Constants.IDENTITY_FILTER]);
 1635               contentDelimitation = true;
 1636           } else {
 1637               if (entityBody && http11 && keepAlive) {
 1638                   outputBuffer.addActiveFilter
 1639                       (outputFilters[Constants.CHUNKED_FILTER]);
 1640                   contentDelimitation = true;
 1641                   headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
 1642               } else {
 1643                   outputBuffer.addActiveFilter
 1644                       (outputFilters[Constants.IDENTITY_FILTER]);
 1645               }
 1646           }
 1647   
 1648           if (useCompression) {
 1649               outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
 1650               headers.setValue("Content-Encoding").setString("gzip");
 1651               // Make Proxies happy via Vary (from mod_deflate)
 1652               headers.setValue("Vary").setString("Accept-Encoding");
 1653           }
 1654   
 1655           // Add date header
 1656           headers.setValue("Date").setString(FastHttpDateFormat.getCurrentDate());
 1657   
 1658           // FIXME: Add transfer encoding header
 1659   
 1660           if ((entityBody) && (!contentDelimitation)) {
 1661               // Mark as close the connection after the request, and add the
 1662               // connection: close header
 1663               keepAlive = false;
 1664           }
 1665   
 1666           // If we know that the request is bad this early, add the
 1667           // Connection: close header.
 1668           keepAlive = keepAlive && !statusDropsConnection(statusCode);
 1669           if (!keepAlive) {
 1670               headers.addValue(Constants.CONNECTION).setString(Constants.CLOSE);
 1671           } else if (!http11 && !error) {
 1672               headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
 1673           }
 1674   
 1675           // Build the response header
 1676           outputBuffer.sendStatus();
 1677   
 1678           // Add server header
 1679           if (server != null) {
 1680               headers.setValue("Server").setString(server);
 1681           } else {
 1682               outputBuffer.write(Constants.SERVER_BYTES);
 1683           }
 1684   
 1685           int size = headers.size();
 1686           for (int i = 0; i < size; i++) {
 1687               outputBuffer.sendHeader(headers.getName(i), headers.getValue(i));
 1688           }
 1689           outputBuffer.endHeaders();
 1690   
 1691       }
 1692   
 1693   
 1694       /**
 1695        * Initialize standard input and output filters.
 1696        */
 1697       protected void initializeFilters() {
 1698   
 1699           // Create and add the identity filters.
 1700           inputBuffer.addFilter(new IdentityInputFilter());
 1701           outputBuffer.addFilter(new IdentityOutputFilter());
 1702   
 1703           // Create and add the chunked filters.
 1704           inputBuffer.addFilter(new ChunkedInputFilter());
 1705           outputBuffer.addFilter(new ChunkedOutputFilter());
 1706   
 1707           // Create and add the void filters.
 1708           inputBuffer.addFilter(new VoidInputFilter());
 1709           outputBuffer.addFilter(new VoidOutputFilter());
 1710   
 1711           // Create and add buffered input filter
 1712           inputBuffer.addFilter(new BufferedInputFilter());
 1713   
 1714           // Create and add the chunked filters.
 1715           //inputBuffer.addFilter(new GzipInputFilter());
 1716           outputBuffer.addFilter(new GzipOutputFilter());
 1717   
 1718       }
 1719   
 1720   
 1721       /**
 1722        * Add an input filter to the current request.
 1723        *
 1724        * @return false if the encoding was not found (which would mean it is
 1725        * unsupported)
 1726        */
 1727       protected boolean addInputFilter(InputFilter[] inputFilters,
 1728                                        String encodingName) {
 1729           if (encodingName.equals("identity")) {
 1730               // Skip
 1731           } else if (encodingName.equals("chunked")) {
 1732               inputBuffer.addActiveFilter
 1733                   (inputFilters[Constants.CHUNKED_FILTER]);
 1734               contentDelimitation = true;
 1735           } else {
 1736               for (int i = 2; i < inputFilters.length; i++) {
 1737                   if (inputFilters[i].getEncodingName()
 1738                       .toString().equals(encodingName)) {
 1739                       inputBuffer.addActiveFilter(inputFilters[i]);
 1740                       return true;
 1741                   }
 1742               }
 1743               return false;
 1744           }
 1745           return true;
 1746       }
 1747   
 1748   
 1749       /**
 1750        * Specialized utility method: find a sequence of lower case bytes inside
 1751        * a ByteChunk.
 1752        */
 1753       protected int findBytes(ByteChunk bc, byte[] b) {
 1754   
 1755           byte first = b[0];
 1756           byte[] buff = bc.getBuffer();
 1757           int start = bc.getStart();
 1758           int end = bc.getEnd();
 1759   
 1760       // Look for first char
 1761       int srcEnd = b.length;
 1762   
 1763       for (int i = start; i <= (end - srcEnd); i++) {
 1764           if (Ascii.toLower(buff[i]) != first) continue;
 1765           // found first char, now look for a match
 1766               int myPos = i+1;
 1767           for (int srcPos = 1; srcPos < srcEnd; ) {
 1768                   if (Ascii.toLower(buff[myPos++]) != b[srcPos++])
 1769               break;
 1770                   if (srcPos == srcEnd) return i - start; // found it
 1771           }
 1772       }
 1773       return -1;
 1774   
 1775       }
 1776   
 1777       /**
 1778        * Determine if we must drop the connection because of the HTTP status
 1779        * code.  Use the same list of codes as Apache/httpd.
 1780        */
 1781       protected boolean statusDropsConnection(int status) {
 1782           return status == 400 /* SC_BAD_REQUEST */ ||
 1783                  status == 408 /* SC_REQUEST_TIMEOUT */ ||
 1784                  status == 411 /* SC_LENGTH_REQUIRED */ ||
 1785                  status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
 1786                  status == 414 /* SC_REQUEST_URI_TOO_LARGE */ ||
 1787                  status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
 1788                  status == 503 /* SC_SERVICE_UNAVAILABLE */ ||
 1789                  status == 501 /* SC_NOT_IMPLEMENTED */;
 1790       }
 1791   
 1792   }

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » coyote » http11 » [javadoc | source]