Save This Page
Home » apache-tomcat-6.0.26-src » org.apache » catalina » servlets » [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   
   19   package org.apache.catalina.servlets;
   20   
   21   
   22   import java.io.BufferedInputStream;
   23   import java.io.ByteArrayInputStream;
   24   import java.io.ByteArrayOutputStream;
   25   import java.io.File;
   26   import java.io.FileInputStream;
   27   import java.io.IOException;
   28   import java.io.InputStream;
   29   import java.io.InputStreamReader;
   30   import java.io.OutputStreamWriter;
   31   import java.io.PrintWriter;
   32   import java.io.RandomAccessFile;
   33   import java.io.Reader;
   34   import java.io.StringReader;
   35   import java.io.StringWriter;
   36   import java.util.ArrayList;
   37   import java.util.Iterator;
   38   import java.util.StringTokenizer;
   39   
   40   import javax.naming.InitialContext;
   41   import javax.naming.NameClassPair;
   42   import javax.naming.NamingEnumeration;
   43   import javax.naming.NamingException;
   44   import javax.naming.directory.DirContext;
   45   import javax.servlet.ServletException;
   46   import javax.servlet.ServletOutputStream;
   47   import javax.servlet.UnavailableException;
   48   import javax.servlet.http.HttpServlet;
   49   import javax.servlet.http.HttpServletRequest;
   50   import javax.servlet.http.HttpServletResponse;
   51   import javax.xml.transform.Source;
   52   import javax.xml.transform.Transformer;
   53   import javax.xml.transform.TransformerException;
   54   import javax.xml.transform.TransformerFactory;
   55   import javax.xml.transform.stream.StreamResult;
   56   import javax.xml.transform.stream.StreamSource;
   57   
   58   import org.apache.catalina.Globals;
   59   import org.apache.catalina.connector.RequestFacade;
   60   import org.apache.catalina.util.RequestUtil;
   61   import org.apache.catalina.util.ServerInfo;
   62   import org.apache.catalina.util.StringManager;
   63   import org.apache.catalina.util.URLEncoder;
   64   import org.apache.naming.resources.CacheEntry;
   65   import org.apache.naming.resources.ProxyDirContext;
   66   import org.apache.naming.resources.Resource;
   67   import org.apache.naming.resources.ResourceAttributes;
   68   
   69   
   70   /**
   71    * The default resource-serving servlet for most web applications,
   72    * used to serve static resources such as HTML pages and images.
   73    *
   74    * @author Craig R. McClanahan
   75    * @author Remy Maucherat
   76    * @version $Revision: 915605 $ $Date: 2010-02-24 01:10:45 +0100 (Wed, 24 Feb 2010) $
   77    */
   78   
   79   public class DefaultServlet
   80       extends HttpServlet {
   81       
   82       // ----------------------------------------------------- Instance Variables
   83   
   84   
   85       /**
   86        * The debugging detail level for this servlet.
   87        */
   88       protected int debug = 0;
   89   
   90   
   91       /**
   92        * The input buffer size to use when serving resources.
   93        */
   94       protected int input = 2048;
   95   
   96   
   97       /**
   98        * Should we generate directory listings?
   99        */
  100       protected boolean listings = false;
  101   
  102   
  103       /**
  104        * Read only flag. By default, it's set to true.
  105        */
  106       protected boolean readOnly = true;
  107   
  108   
  109       /**
  110        * The output buffer size to use when serving resources.
  111        */
  112       protected int output = 2048;
  113   
  114   
  115       /**
  116        * Array containing the safe characters set.
  117        */
  118       protected static URLEncoder urlEncoder;
  119   
  120   
  121       /**
  122        * Allow customized directory listing per directory.
  123        */
  124       protected String localXsltFile = null;
  125   
  126   
  127       /**
  128        * Allow customized directory listing per context.
  129        */
  130       protected String contextXsltFile = null;
  131       
  132       
  133       /**
  134        * Allow customized directory listing per instance.
  135        */
  136       protected String globalXsltFile = null;
  137   
  138   
  139       /**
  140        * Allow a readme file to be included.
  141        */
  142       protected String readmeFile = null;
  143   
  144   
  145       /**
  146        * Proxy directory context.
  147        */
  148       protected ProxyDirContext resources = null;
  149   
  150   
  151       /**
  152        * File encoding to be used when reading static files. If none is specified
  153        * the platform default is used.
  154        */
  155       protected String fileEncoding = null;
  156       
  157       
  158       /**
  159        * Minimum size for sendfile usage in bytes.
  160        */
  161       protected int sendfileSize = 48 * 1024;
  162       
  163       /**
  164        * Should the Accept-Ranges: bytes header be send with static resources?
  165        */
  166       protected boolean useAcceptRanges = true;
  167   
  168       /**
  169        * Full range marker.
  170        */
  171       protected static ArrayList FULL = new ArrayList();
  172       
  173       
  174       // ----------------------------------------------------- Static Initializer
  175   
  176   
  177       /**
  178        * GMT timezone - all HTTP dates are on GMT
  179        */
  180       static {
  181           urlEncoder = new URLEncoder();
  182           urlEncoder.addSafeCharacter('-');
  183           urlEncoder.addSafeCharacter('_');
  184           urlEncoder.addSafeCharacter('.');
  185           urlEncoder.addSafeCharacter('*');
  186           urlEncoder.addSafeCharacter('/');
  187       }
  188   
  189   
  190       /**
  191        * MIME multipart separation string
  192        */
  193       protected static final String mimeSeparation = "CATALINA_MIME_BOUNDARY";
  194   
  195   
  196       /**
  197        * JNDI resources name.
  198        */
  199       protected static final String RESOURCES_JNDI_NAME = "java:/comp/Resources";
  200   
  201   
  202       /**
  203        * The string manager for this package.
  204        */
  205       protected static StringManager sm =
  206           StringManager.getManager(Constants.Package);
  207   
  208   
  209       /**
  210        * Size of file transfer buffer in bytes.
  211        */
  212       protected static final int BUFFER_SIZE = 4096;
  213   
  214   
  215       // --------------------------------------------------------- Public Methods
  216   
  217   
  218       /**
  219        * Finalize this servlet.
  220        */
  221       public void destroy() {
  222       }
  223   
  224   
  225       /**
  226        * Initialize this servlet.
  227        */
  228       public void init() throws ServletException {
  229   
  230           if (getServletConfig().getInitParameter("debug") != null)
  231               debug = Integer.parseInt(getServletConfig().getInitParameter("debug"));
  232   
  233           if (getServletConfig().getInitParameter("input") != null)
  234               input = Integer.parseInt(getServletConfig().getInitParameter("input"));
  235   
  236           if (getServletConfig().getInitParameter("output") != null)
  237               output = Integer.parseInt(getServletConfig().getInitParameter("output"));
  238   
  239           listings = Boolean.parseBoolean(getServletConfig().getInitParameter("listings"));
  240   
  241           if (getServletConfig().getInitParameter("readonly") != null)
  242               readOnly = Boolean.parseBoolean(getServletConfig().getInitParameter("readonly"));
  243   
  244           if (getServletConfig().getInitParameter("sendfileSize") != null)
  245               sendfileSize = 
  246                   Integer.parseInt(getServletConfig().getInitParameter("sendfileSize")) * 1024;
  247   
  248           fileEncoding = getServletConfig().getInitParameter("fileEncoding");
  249   
  250           globalXsltFile = getServletConfig().getInitParameter("globalXsltFile");
  251           contextXsltFile = getServletConfig().getInitParameter("contextXsltFile");
  252           localXsltFile = getServletConfig().getInitParameter("localXsltFile");
  253           readmeFile = getServletConfig().getInitParameter("readmeFile");
  254   
  255           if (getServletConfig().getInitParameter("useAcceptRanges") != null)
  256               useAcceptRanges = Boolean.parseBoolean(getServletConfig().getInitParameter("useAcceptRanges"));
  257   
  258           // Sanity check on the specified buffer sizes
  259           if (input < 256)
  260               input = 256;
  261           if (output < 256)
  262               output = 256;
  263   
  264           if (debug > 0) {
  265               log("DefaultServlet.init:  input buffer size=" + input +
  266                   ", output buffer size=" + output);
  267           }
  268   
  269           // Load the proxy dir context.
  270           resources = (ProxyDirContext) getServletContext()
  271               .getAttribute(Globals.RESOURCES_ATTR);
  272           if (resources == null) {
  273               try {
  274                   resources =
  275                       (ProxyDirContext) new InitialContext()
  276                       .lookup(RESOURCES_JNDI_NAME);
  277               } catch (NamingException e) {
  278                   // Failed
  279                   throw new ServletException("No resources", e);
  280               }
  281           }
  282   
  283           if (resources == null) {
  284               throw new UnavailableException("No resources");
  285           }
  286   
  287       }
  288   
  289   
  290       // ------------------------------------------------------ Protected Methods
  291   
  292   
  293       /**
  294        * Return the relative path associated with this servlet.
  295        *
  296        * @param request The servlet request we are processing
  297        */
  298       protected String getRelativePath(HttpServletRequest request) {
  299   
  300           // Are we being processed by a RequestDispatcher.include()?
  301           if (request.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR) != null) {
  302               String result = (String) request.getAttribute(
  303                                               Globals.INCLUDE_PATH_INFO_ATTR);
  304               if (result == null)
  305                   result = (String) request.getAttribute(
  306                                               Globals.INCLUDE_SERVLET_PATH_ATTR);
  307               if ((result == null) || (result.equals("")))
  308                   result = "/";
  309               return (result);
  310           }
  311   
  312           // No, extract the desired path directly from the request
  313           String result = request.getPathInfo();
  314           if (result == null) {
  315               result = request.getServletPath();
  316           }
  317           if ((result == null) || (result.equals(""))) {
  318               result = "/";
  319           }
  320           return (result);
  321   
  322       }
  323   
  324   
  325       /**
  326        * Process a GET request for the specified resource.
  327        *
  328        * @param request The servlet request we are processing
  329        * @param response The servlet response we are creating
  330        *
  331        * @exception IOException if an input/output error occurs
  332        * @exception ServletException if a servlet-specified error occurs
  333        */
  334       protected void doGet(HttpServletRequest request,
  335                            HttpServletResponse response)
  336           throws IOException, ServletException {
  337   
  338           // Serve the requested resource, including the data content
  339           serveResource(request, response, true);
  340   
  341       }
  342   
  343   
  344       /**
  345        * Process a HEAD request for the specified resource.
  346        *
  347        * @param request The servlet request we are processing
  348        * @param response The servlet response we are creating
  349        *
  350        * @exception IOException if an input/output error occurs
  351        * @exception ServletException if a servlet-specified error occurs
  352        */
  353       protected void doHead(HttpServletRequest request,
  354                             HttpServletResponse response)
  355           throws IOException, ServletException {
  356   
  357           // Serve the requested resource, without the data content
  358           serveResource(request, response, false);
  359   
  360       }
  361   
  362   
  363       /**
  364        * Override default implementation to ensure that TRACE is correctly
  365        * handled.
  366        *
  367        * @param req   the {@link HttpServletRequest} object that
  368        *                  contains the request the client made of
  369        *                  the servlet
  370        *
  371        * @param resp  the {@link HttpServletResponse} object that
  372        *                  contains the response the servlet returns
  373        *                  to the client                                
  374        *
  375        * @exception IOException   if an input or output error occurs
  376        *                              while the servlet is handling the
  377        *                              OPTIONS request
  378        *
  379        * @exception ServletException  if the request for the
  380        *                                  OPTIONS cannot be handled
  381        */
  382       protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
  383           throws ServletException, IOException {
  384   
  385           StringBuffer allow = new StringBuffer();
  386           // There is a doGet method
  387           allow.append("GET, HEAD");
  388           // There is a doPost
  389           allow.append(", POST");
  390           // There is a doPut
  391           allow.append(", PUT");
  392           // There is a doDelete
  393           allow.append(", DELETE");
  394           // Trace - assume disabled unless we can prove otherwise
  395           if (req instanceof RequestFacade &&
  396                   ((RequestFacade) req).getAllowTrace()) {
  397               allow.append(", TRACE");
  398           }
  399           // Always allow options
  400           allow.append(", OPTIONS");
  401           
  402           resp.setHeader("Allow", allow.toString());
  403       }
  404       
  405       
  406       /**
  407        * Process a POST request for the specified resource.
  408        *
  409        * @param request The servlet request we are processing
  410        * @param response The servlet response we are creating
  411        *
  412        * @exception IOException if an input/output error occurs
  413        * @exception ServletException if a servlet-specified error occurs
  414        */
  415       protected void doPost(HttpServletRequest request,
  416                             HttpServletResponse response)
  417           throws IOException, ServletException {
  418           doGet(request, response);
  419       }
  420   
  421   
  422       /**
  423        * Process a POST request for the specified resource.
  424        *
  425        * @param req The servlet request we are processing
  426        * @param resp The servlet response we are creating
  427        *
  428        * @exception IOException if an input/output error occurs
  429        * @exception ServletException if a servlet-specified error occurs
  430        */
  431       protected void doPut(HttpServletRequest req, HttpServletResponse resp)
  432           throws ServletException, IOException {
  433   
  434           if (readOnly) {
  435               resp.sendError(HttpServletResponse.SC_FORBIDDEN);
  436               return;
  437           }
  438   
  439           String path = getRelativePath(req);
  440   
  441           boolean exists = true;
  442           try {
  443               resources.lookup(path);
  444           } catch (NamingException e) {
  445               exists = false;
  446           }
  447   
  448           boolean result = true;
  449   
  450           // Temp. content file used to support partial PUT
  451           File contentFile = null;
  452   
  453           Range range = parseContentRange(req, resp);
  454   
  455           InputStream resourceInputStream = null;
  456   
  457           // Append data specified in ranges to existing content for this
  458           // resource - create a temp. file on the local filesystem to
  459           // perform this operation
  460           // Assume just one range is specified for now
  461           if (range != null) {
  462               contentFile = executePartialPut(req, range, path);
  463               resourceInputStream = new FileInputStream(contentFile);
  464           } else {
  465               resourceInputStream = req.getInputStream();
  466           }
  467   
  468           try {
  469               Resource newResource = new Resource(resourceInputStream);
  470               // FIXME: Add attributes
  471               if (exists) {
  472                   resources.rebind(path, newResource);
  473               } else {
  474                   resources.bind(path, newResource);
  475               }
  476           } catch(NamingException e) {
  477               result = false;
  478           }
  479   
  480           if (result) {
  481               if (exists) {
  482                   resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
  483               } else {
  484                   resp.setStatus(HttpServletResponse.SC_CREATED);
  485               }
  486           } else {
  487               resp.sendError(HttpServletResponse.SC_CONFLICT);
  488           }
  489   
  490       }
  491   
  492   
  493       /**
  494        * Handle a partial PUT.  New content specified in request is appended to
  495        * existing content in oldRevisionContent (if present). This code does
  496        * not support simultaneous partial updates to the same resource.
  497        */
  498       protected File executePartialPut(HttpServletRequest req, Range range,
  499                                        String path)
  500           throws IOException {
  501   
  502           // Append data specified in ranges to existing content for this
  503           // resource - create a temp. file on the local filesystem to
  504           // perform this operation
  505           File tempDir = (File) getServletContext().getAttribute
  506               ("javax.servlet.context.tempdir");
  507           // Convert all '/' characters to '.' in resourcePath
  508           String convertedResourcePath = path.replace('/', '.');
  509           File contentFile = new File(tempDir, convertedResourcePath);
  510           if (contentFile.createNewFile()) {
  511               // Clean up contentFile when Tomcat is terminated
  512               contentFile.deleteOnExit();
  513           }
  514   
  515           RandomAccessFile randAccessContentFile =
  516               new RandomAccessFile(contentFile, "rw");
  517   
  518           Resource oldResource = null;
  519           try {
  520               Object obj = resources.lookup(path);
  521               if (obj instanceof Resource)
  522                   oldResource = (Resource) obj;
  523           } catch (NamingException e) {
  524               ;
  525           }
  526   
  527           // Copy data in oldRevisionContent to contentFile
  528           if (oldResource != null) {
  529               BufferedInputStream bufOldRevStream =
  530                   new BufferedInputStream(oldResource.streamContent(),
  531                                           BUFFER_SIZE);
  532   
  533               int numBytesRead;
  534               byte[] copyBuffer = new byte[BUFFER_SIZE];
  535               while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) {
  536                   randAccessContentFile.write(copyBuffer, 0, numBytesRead);
  537               }
  538   
  539               bufOldRevStream.close();
  540           }
  541   
  542           randAccessContentFile.setLength(range.length);
  543   
  544           // Append data in request input stream to contentFile
  545           randAccessContentFile.seek(range.start);
  546           int numBytesRead;
  547           byte[] transferBuffer = new byte[BUFFER_SIZE];
  548           BufferedInputStream requestBufInStream =
  549               new BufferedInputStream(req.getInputStream(), BUFFER_SIZE);
  550           while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) {
  551               randAccessContentFile.write(transferBuffer, 0, numBytesRead);
  552           }
  553           randAccessContentFile.close();
  554           requestBufInStream.close();
  555   
  556           return contentFile;
  557   
  558       }
  559   
  560   
  561       /**
  562        * Process a POST request for the specified resource.
  563        *
  564        * @param req The servlet request we are processing
  565        * @param resp The servlet response we are creating
  566        *
  567        * @exception IOException if an input/output error occurs
  568        * @exception ServletException if a servlet-specified error occurs
  569        */
  570       protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
  571           throws ServletException, IOException {
  572   
  573           if (readOnly) {
  574               resp.sendError(HttpServletResponse.SC_FORBIDDEN);
  575               return;
  576           }
  577   
  578           String path = getRelativePath(req);
  579   
  580           boolean exists = true;
  581           try {
  582               resources.lookup(path);
  583           } catch (NamingException e) {
  584               exists = false;
  585           }
  586   
  587           if (exists) {
  588               boolean result = true;
  589               try {
  590                   resources.unbind(path);
  591               } catch (NamingException e) {
  592                   result = false;
  593               }
  594               if (result) {
  595                   resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
  596               } else {
  597                   resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
  598               }
  599           } else {
  600               resp.sendError(HttpServletResponse.SC_NOT_FOUND);
  601           }
  602   
  603       }
  604   
  605   
  606       /**
  607        * Check if the conditions specified in the optional If headers are
  608        * satisfied.
  609        *
  610        * @param request The servlet request we are processing
  611        * @param response The servlet response we are creating
  612        * @param resourceAttributes The resource information
  613        * @return boolean true if the resource meets all the specified conditions,
  614        * and false if any of the conditions is not satisfied, in which case
  615        * request processing is stopped
  616        */
  617       protected boolean checkIfHeaders(HttpServletRequest request,
  618                                        HttpServletResponse response,
  619                                        ResourceAttributes resourceAttributes)
  620           throws IOException {
  621   
  622           return checkIfMatch(request, response, resourceAttributes)
  623               && checkIfModifiedSince(request, response, resourceAttributes)
  624               && checkIfNoneMatch(request, response, resourceAttributes)
  625               && checkIfUnmodifiedSince(request, response, resourceAttributes);
  626   
  627       }
  628   
  629   
  630       /**
  631        * URL rewriter.
  632        *
  633        * @param path Path which has to be rewiten
  634        */
  635       protected String rewriteUrl(String path) {
  636           return urlEncoder.encode( path );
  637       }
  638   
  639   
  640       /**
  641        * Display the size of a file.
  642        */
  643       protected void displaySize(StringBuffer buf, int filesize) {
  644   
  645           int leftside = filesize / 1024;
  646           int rightside = (filesize % 1024) / 103;  // makes 1 digit
  647           // To avoid 0.0 for non-zero file, we bump to 0.1
  648           if (leftside == 0 && rightside == 0 && filesize != 0)
  649               rightside = 1;
  650           buf.append(leftside).append(".").append(rightside);
  651           buf.append(" KB");
  652   
  653       }
  654   
  655   
  656       /**
  657        * Serve the specified resource, optionally including the data content.
  658        *
  659        * @param request The servlet request we are processing
  660        * @param response The servlet response we are creating
  661        * @param content Should the content be included?
  662        *
  663        * @exception IOException if an input/output error occurs
  664        * @exception ServletException if a servlet-specified error occurs
  665        */
  666       protected void serveResource(HttpServletRequest request,
  667                                    HttpServletResponse response,
  668                                    boolean content)
  669           throws IOException, ServletException {
  670   
  671           // Identify the requested resource path
  672           String path = getRelativePath(request);
  673           if (debug > 0) {
  674               if (content)
  675                   log("DefaultServlet.serveResource:  Serving resource '" +
  676                       path + "' headers and data");
  677               else
  678                   log("DefaultServlet.serveResource:  Serving resource '" +
  679                       path + "' headers only");
  680           }
  681   
  682           CacheEntry cacheEntry = resources.lookupCache(path);
  683   
  684           if (!cacheEntry.exists) {
  685               // Check if we're included so we can return the appropriate 
  686               // missing resource name in the error
  687               String requestUri = (String) request.getAttribute(
  688                                               Globals.INCLUDE_REQUEST_URI_ATTR);
  689               if (requestUri == null) {
  690                   requestUri = request.getRequestURI();
  691               } else {
  692                   // We're included, and the response.sendError() below is going
  693                   // to be ignored by the resource that is including us.
  694                   // Therefore, the only way we can let the including resource
  695                   // know is by including warning message in response
  696                   response.getWriter().write(RequestUtil.filter(
  697                       sm.getString("defaultServlet.missingResource",
  698                       requestUri)));
  699               }
  700   
  701               response.sendError(HttpServletResponse.SC_NOT_FOUND,
  702                                  requestUri);
  703               return;
  704           }
  705   
  706           // If the resource is not a collection, and the resource path
  707           // ends with "/" or "\", return NOT FOUND
  708           if (cacheEntry.context == null) {
  709               if (path.endsWith("/") || (path.endsWith("\\"))) {
  710                   // Check if we're included so we can return the appropriate 
  711                   // missing resource name in the error
  712                   String requestUri = (String) request.getAttribute(
  713                                               Globals.INCLUDE_REQUEST_URI_ATTR);
  714                   if (requestUri == null) {
  715                       requestUri = request.getRequestURI();
  716                   }
  717                   response.sendError(HttpServletResponse.SC_NOT_FOUND,
  718                                      requestUri);
  719                   return;
  720               }
  721           }
  722   
  723           // Check if the conditions specified in the optional If headers are
  724           // satisfied.
  725           if (cacheEntry.context == null) {
  726   
  727               // Checking If headers
  728               boolean included =
  729                   (request.getAttribute(Globals.INCLUDE_CONTEXT_PATH_ATTR) != null);
  730               if (!included
  731                   && !checkIfHeaders(request, response, cacheEntry.attributes)) {
  732                   return;
  733               }
  734   
  735           }
  736   
  737           // Find content type.
  738           String contentType = cacheEntry.attributes.getMimeType();
  739           if (contentType == null) {
  740               contentType = getServletContext().getMimeType(cacheEntry.name);
  741               cacheEntry.attributes.setMimeType(contentType);
  742           }
  743   
  744           ArrayList ranges = null;
  745           long contentLength = -1L;
  746   
  747           if (cacheEntry.context != null) {
  748   
  749               // Skip directory listings if we have been configured to
  750               // suppress them
  751               if (!listings) {
  752                   response.sendError(HttpServletResponse.SC_NOT_FOUND,
  753                                      request.getRequestURI());
  754                   return;
  755               }
  756               contentType = "text/html;charset=UTF-8";
  757   
  758           } else {
  759               if (useAcceptRanges) {
  760                   // Accept ranges header
  761                   response.setHeader("Accept-Ranges", "bytes");
  762               }
  763   
  764               // Parse range specifier
  765               ranges = parseRange(request, response, cacheEntry.attributes);
  766   
  767               // ETag header
  768               response.setHeader("ETag", cacheEntry.attributes.getETag());
  769   
  770               // Last-Modified header
  771               response.setHeader("Last-Modified",
  772                       cacheEntry.attributes.getLastModifiedHttp());
  773   
  774               // Get content length
  775               contentLength = cacheEntry.attributes.getContentLength();
  776               // Special case for zero length files, which would cause a
  777               // (silent) ISE when setting the output buffer size
  778               if (contentLength == 0L) {
  779                   content = false;
  780               }
  781   
  782           }
  783   
  784           ServletOutputStream ostream = null;
  785           PrintWriter writer = null;
  786   
  787           if (content) {
  788   
  789               // Trying to retrieve the servlet output stream
  790   
  791               try {
  792                   ostream = response.getOutputStream();
  793               } catch (IllegalStateException e) {
  794                   // If it fails, we try to get a Writer instead if we're
  795                   // trying to serve a text file
  796                   if ( (contentType == null)
  797                           || (contentType.startsWith("text"))
  798                           || (contentType.endsWith("xml")) ) {
  799                       writer = response.getWriter();
  800                   } else {
  801                       throw e;
  802                   }
  803               }
  804   
  805           }
  806   
  807           if ( (cacheEntry.context != null) 
  808                   || ( ((ranges == null) || (ranges.isEmpty()))
  809                           && (request.getHeader("Range") == null) )
  810                   || (ranges == FULL) ) {
  811   
  812               // Set the appropriate output headers
  813               if (contentType != null) {
  814                   if (debug > 0)
  815                       log("DefaultServlet.serveFile:  contentType='" +
  816                           contentType + "'");
  817                   response.setContentType(contentType);
  818               }
  819               if ((cacheEntry.resource != null) && (contentLength >= 0)) {
  820                   if (debug > 0)
  821                       log("DefaultServlet.serveFile:  contentLength=" +
  822                           contentLength);
  823                   if (contentLength < Integer.MAX_VALUE) {
  824                       response.setContentLength((int) contentLength);
  825                   } else {
  826                       // Set the content-length as String to be able to use a long
  827                       response.setHeader("content-length", "" + contentLength);
  828                   }
  829               }
  830   
  831               InputStream renderResult = null;
  832               if (cacheEntry.context != null) {
  833   
  834                   if (content) {
  835                       // Serve the directory browser
  836                       renderResult =
  837                           render(request.getContextPath(), cacheEntry);
  838                   }
  839   
  840               }
  841   
  842               // Copy the input stream to our output stream (if requested)
  843               if (content) {
  844                   try {
  845                       response.setBufferSize(output);
  846                   } catch (IllegalStateException e) {
  847                       // Silent catch
  848                   }
  849                   if (ostream != null) {
  850                       if (!checkSendfile(request, response, cacheEntry, contentLength, null))
  851                           copy(cacheEntry, renderResult, ostream);
  852                   } else {
  853                       copy(cacheEntry, renderResult, writer);
  854                   }
  855               }
  856   
  857           } else {
  858   
  859               if ((ranges == null) || (ranges.isEmpty()))
  860                   return;
  861   
  862               // Partial content response.
  863   
  864               response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
  865   
  866               if (ranges.size() == 1) {
  867   
  868                   Range range = (Range) ranges.get(0);
  869                   response.addHeader("Content-Range", "bytes "
  870                                      + range.start
  871                                      + "-" + range.end + "/"
  872                                      + range.length);
  873                   long length = range.end - range.start + 1;
  874                   if (length < Integer.MAX_VALUE) {
  875                       response.setContentLength((int) length);
  876                   } else {
  877                       // Set the content-length as String to be able to use a long
  878                       response.setHeader("content-length", "" + length);
  879                   }
  880   
  881                   if (contentType != null) {
  882                       if (debug > 0)
  883                           log("DefaultServlet.serveFile:  contentType='" +
  884                               contentType + "'");
  885                       response.setContentType(contentType);
  886                   }
  887   
  888                   if (content) {
  889                       try {
  890                           response.setBufferSize(output);
  891                       } catch (IllegalStateException e) {
  892                           // Silent catch
  893                       }
  894                       if (ostream != null) {
  895                           if (!checkSendfile(request, response, cacheEntry, range.end - range.start + 1, range))
  896                               copy(cacheEntry, ostream, range);
  897                       } else {
  898                           copy(cacheEntry, writer, range);
  899                       }
  900                   }
  901   
  902               } else {
  903   
  904                   response.setContentType("multipart/byteranges; boundary="
  905                                           + mimeSeparation);
  906   
  907                   if (content) {
  908                       try {
  909                           response.setBufferSize(output);
  910                       } catch (IllegalStateException e) {
  911                           // Silent catch
  912                       }
  913                       if (ostream != null) {
  914                           copy(cacheEntry, ostream, ranges.iterator(),
  915                                contentType);
  916                       } else {
  917                           copy(cacheEntry, writer, ranges.iterator(),
  918                                contentType);
  919                       }
  920                   }
  921   
  922               }
  923   
  924           }
  925   
  926       }
  927   
  928   
  929       /**
  930        * Parse the content-range header.
  931        *
  932        * @param request The servlet request we are processing
  933        * @param response The servlet response we are creating
  934        * @return Range
  935        */
  936       protected Range parseContentRange(HttpServletRequest request,
  937                                         HttpServletResponse response)
  938           throws IOException {
  939   
  940           // Retrieving the content-range header (if any is specified
  941           String rangeHeader = request.getHeader("Content-Range");
  942   
  943           if (rangeHeader == null)
  944               return null;
  945   
  946           // bytes is the only range unit supported
  947           if (!rangeHeader.startsWith("bytes")) {
  948               response.sendError(HttpServletResponse.SC_BAD_REQUEST);
  949               return null;
  950           }
  951   
  952           rangeHeader = rangeHeader.substring(6).trim();
  953   
  954           int dashPos = rangeHeader.indexOf('-');
  955           int slashPos = rangeHeader.indexOf('/');
  956   
  957           if (dashPos == -1) {
  958               response.sendError(HttpServletResponse.SC_BAD_REQUEST);
  959               return null;
  960           }
  961   
  962           if (slashPos == -1) {
  963               response.sendError(HttpServletResponse.SC_BAD_REQUEST);
  964               return null;
  965           }
  966   
  967           Range range = new Range();
  968   
  969           try {
  970               range.start = Long.parseLong(rangeHeader.substring(0, dashPos));
  971               range.end =
  972                   Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos));
  973               range.length = Long.parseLong
  974                   (rangeHeader.substring(slashPos + 1, rangeHeader.length()));
  975           } catch (NumberFormatException e) {
  976               response.sendError(HttpServletResponse.SC_BAD_REQUEST);
  977               return null;
  978           }
  979   
  980           if (!range.validate()) {
  981               response.sendError(HttpServletResponse.SC_BAD_REQUEST);
  982               return null;
  983           }
  984   
  985           return range;
  986   
  987       }
  988   
  989   
  990       /**
  991        * Parse the range header.
  992        *
  993        * @param request The servlet request we are processing
  994        * @param response The servlet response we are creating
  995        * @return Vector of ranges
  996        */
  997       protected ArrayList parseRange(HttpServletRequest request,
  998                                   HttpServletResponse response,
  999                                   ResourceAttributes resourceAttributes)
 1000           throws IOException {
 1001   
 1002           // Checking If-Range
 1003           String headerValue = request.getHeader("If-Range");
 1004   
 1005           if (headerValue != null) {
 1006   
 1007               long headerValueTime = (-1L);
 1008               try {
 1009                   headerValueTime = request.getDateHeader("If-Range");
 1010               } catch (IllegalArgumentException e) {
 1011                   ;
 1012               }
 1013   
 1014               String eTag = resourceAttributes.getETag();
 1015               long lastModified = resourceAttributes.getLastModified();
 1016   
 1017               if (headerValueTime == (-1L)) {
 1018   
 1019                   // If the ETag the client gave does not match the entity
 1020                   // etag, then the entire entity is returned.
 1021                   if (!eTag.equals(headerValue.trim()))
 1022                       return FULL;
 1023   
 1024               } else {
 1025   
 1026                   // If the timestamp of the entity the client got is older than
 1027                   // the last modification date of the entity, the entire entity
 1028                   // is returned.
 1029                   if (lastModified > (headerValueTime + 1000))
 1030                       return FULL;
 1031   
 1032               }
 1033   
 1034           }
 1035   
 1036           long fileLength = resourceAttributes.getContentLength();
 1037   
 1038           if (fileLength == 0)
 1039               return null;
 1040   
 1041           // Retrieving the range header (if any is specified
 1042           String rangeHeader = request.getHeader("Range");
 1043   
 1044           if (rangeHeader == null)
 1045               return null;
 1046           // bytes is the only range unit supported (and I don't see the point
 1047           // of adding new ones).
 1048           if (!rangeHeader.startsWith("bytes")) {
 1049               response.addHeader("Content-Range", "bytes */" + fileLength);
 1050               response.sendError
 1051                   (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
 1052               return null;
 1053           }
 1054   
 1055           rangeHeader = rangeHeader.substring(6);
 1056   
 1057           // Vector which will contain all the ranges which are successfully
 1058           // parsed.
 1059           ArrayList<Range> result = new ArrayList<Range>();
 1060           StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
 1061   
 1062           // Parsing the range list
 1063           while (commaTokenizer.hasMoreTokens()) {
 1064               String rangeDefinition = commaTokenizer.nextToken().trim();
 1065   
 1066               Range currentRange = new Range();
 1067               currentRange.length = fileLength;
 1068   
 1069               int dashPos = rangeDefinition.indexOf('-');
 1070   
 1071               if (dashPos == -1) {
 1072                   response.addHeader("Content-Range", "bytes */" + fileLength);
 1073                   response.sendError
 1074                       (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
 1075                   return null;
 1076               }
 1077   
 1078               if (dashPos == 0) {
 1079   
 1080                   try {
 1081                       long offset = Long.parseLong(rangeDefinition);
 1082                       currentRange.start = fileLength + offset;
 1083                       currentRange.end = fileLength - 1;
 1084                   } catch (NumberFormatException e) {
 1085                       response.addHeader("Content-Range",
 1086                                          "bytes */" + fileLength);
 1087                       response.sendError
 1088                           (HttpServletResponse
 1089                            .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
 1090                       return null;
 1091                   }
 1092   
 1093               } else {
 1094   
 1095                   try {
 1096                       currentRange.start = Long.parseLong
 1097                           (rangeDefinition.substring(0, dashPos));
 1098                       if (dashPos < rangeDefinition.length() - 1)
 1099                           currentRange.end = Long.parseLong
 1100                               (rangeDefinition.substring
 1101                                (dashPos + 1, rangeDefinition.length()));
 1102                       else
 1103                           currentRange.end = fileLength - 1;
 1104                   } catch (NumberFormatException e) {
 1105                       response.addHeader("Content-Range",
 1106                                          "bytes */" + fileLength);
 1107                       response.sendError
 1108                           (HttpServletResponse
 1109                            .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
 1110                       return null;
 1111                   }
 1112   
 1113               }
 1114   
 1115               if (!currentRange.validate()) {
 1116                   response.addHeader("Content-Range", "bytes */" + fileLength);
 1117                   response.sendError
 1118                       (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
 1119                   return null;
 1120               }
 1121   
 1122               result.add(currentRange);
 1123           }
 1124   
 1125           return result;
 1126       }
 1127   
 1128   
 1129   
 1130       /**
 1131        *  Decide which way to render. HTML or XML.
 1132        */
 1133       protected InputStream render(String contextPath, CacheEntry cacheEntry)
 1134           throws IOException, ServletException {
 1135   
 1136           InputStream xsltInputStream =
 1137               findXsltInputStream(cacheEntry.context);
 1138   
 1139           if (xsltInputStream==null) {
 1140               return renderHtml(contextPath, cacheEntry);
 1141           } else {
 1142               return renderXml(contextPath, cacheEntry, xsltInputStream);
 1143           }
 1144   
 1145       }
 1146   
 1147       /**
 1148        * Return an InputStream to an HTML representation of the contents
 1149        * of this directory.
 1150        *
 1151        * @param contextPath Context path to which our internal paths are
 1152        *  relative
 1153        */
 1154       protected InputStream renderXml(String contextPath,
 1155                                       CacheEntry cacheEntry,
 1156                                       InputStream xsltInputStream)
 1157           throws IOException, ServletException {
 1158   
 1159           StringBuffer sb = new StringBuffer();
 1160   
 1161           sb.append("<?xml version=\"1.0\"?>");
 1162           sb.append("<listing ");
 1163           sb.append(" contextPath='");
 1164           sb.append(contextPath);
 1165           sb.append("'");
 1166           sb.append(" directory='");
 1167           sb.append(cacheEntry.name);
 1168           sb.append("' ");
 1169           sb.append(" hasParent='").append(!cacheEntry.name.equals("/"));
 1170           sb.append("'>");
 1171   
 1172           sb.append("<entries>");
 1173   
 1174           try {
 1175   
 1176               // Render the directory entries within this directory
 1177               NamingEnumeration enumeration = resources.list(cacheEntry.name);
 1178               
 1179               // rewriteUrl(contextPath) is expensive. cache result for later reuse
 1180               String rewrittenContextPath =  rewriteUrl(contextPath);
 1181   
 1182               while (enumeration.hasMoreElements()) {
 1183   
 1184                   NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
 1185                   String resourceName = ncPair.getName();
 1186                   String trimmed = resourceName/*.substring(trim)*/;
 1187                   if (trimmed.equalsIgnoreCase("WEB-INF") ||
 1188                       trimmed.equalsIgnoreCase("META-INF") ||
 1189                       trimmed.equalsIgnoreCase(localXsltFile))
 1190                       continue;
 1191   
 1192                   if ((cacheEntry.name + trimmed).equals(contextXsltFile))
 1193                       continue;
 1194   
 1195                   CacheEntry childCacheEntry =
 1196                       resources.lookupCache(cacheEntry.name + resourceName);
 1197                   if (!childCacheEntry.exists) {
 1198                       continue;
 1199                   }
 1200   
 1201                   sb.append("<entry");
 1202                   sb.append(" type='")
 1203                     .append((childCacheEntry.context != null)?"dir":"file")
 1204                     .append("'");
 1205                   sb.append(" urlPath='")
 1206                     .append(rewrittenContextPath)
 1207                     .append(rewriteUrl(cacheEntry.name + resourceName))
 1208                     .append((childCacheEntry.context != null)?"/":"")
 1209                     .append("'");
 1210                   if (childCacheEntry.resource != null) {
 1211                       sb.append(" size='")
 1212                         .append(renderSize(childCacheEntry.attributes.getContentLength()))
 1213                         .append("'");
 1214                   }
 1215                   sb.append(" date='")
 1216                     .append(childCacheEntry.attributes.getLastModifiedHttp())
 1217                     .append("'");
 1218   
 1219                   sb.append(">");
 1220                   sb.append(RequestUtil.filter(trimmed));
 1221                   if (childCacheEntry.context != null)
 1222                       sb.append("/");
 1223                   sb.append("</entry>");
 1224   
 1225               }
 1226   
 1227           } catch (NamingException e) {
 1228               // Something went wrong
 1229               throw new ServletException("Error accessing resource", e);
 1230           }
 1231   
 1232           sb.append("</entries>");
 1233   
 1234           String readme = getReadme(cacheEntry.context);
 1235   
 1236           if (readme!=null) {
 1237               sb.append("<readme><![CDATA[");
 1238               sb.append(readme);
 1239               sb.append("]]></readme>");
 1240           }
 1241   
 1242   
 1243           sb.append("</listing>");
 1244   
 1245   
 1246           try {
 1247               TransformerFactory tFactory = TransformerFactory.newInstance();
 1248               Source xmlSource = new StreamSource(new StringReader(sb.toString()));
 1249               Source xslSource = new StreamSource(xsltInputStream);
 1250               Transformer transformer = tFactory.newTransformer(xslSource);
 1251   
 1252               ByteArrayOutputStream stream = new ByteArrayOutputStream();
 1253               OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8");
 1254               StreamResult out = new StreamResult(osWriter);
 1255               transformer.transform(xmlSource, out);
 1256               osWriter.flush();
 1257               return (new ByteArrayInputStream(stream.toByteArray()));
 1258           } catch (TransformerException e) {
 1259               throw new ServletException("XSL transformer error", e);
 1260           }
 1261       }
 1262   
 1263       /**
 1264        * Return an InputStream to an HTML representation of the contents
 1265        * of this directory.
 1266        *
 1267        * @param contextPath Context path to which our internal paths are
 1268        *  relative
 1269        */
 1270       protected InputStream renderHtml(String contextPath, CacheEntry cacheEntry)
 1271           throws IOException, ServletException {
 1272   
 1273           String name = cacheEntry.name;
 1274   
 1275           // Number of characters to trim from the beginnings of filenames
 1276           int trim = name.length();
 1277           if (!name.endsWith("/"))
 1278               trim += 1;
 1279           if (name.equals("/"))
 1280               trim = 1;
 1281   
 1282           // Prepare a writer to a buffered area
 1283           ByteArrayOutputStream stream = new ByteArrayOutputStream();
 1284           OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8");
 1285           PrintWriter writer = new PrintWriter(osWriter);
 1286   
 1287           StringBuffer sb = new StringBuffer();
 1288           
 1289           // rewriteUrl(contextPath) is expensive. cache result for later reuse
 1290           String rewrittenContextPath =  rewriteUrl(contextPath);
 1291   
 1292           // Render the page header
 1293           sb.append("<html>\r\n");
 1294           sb.append("<head>\r\n");
 1295           sb.append("<title>");
 1296           sb.append(sm.getString("directory.title", name));
 1297           sb.append("</title>\r\n");
 1298           sb.append("<STYLE><!--");
 1299           sb.append(org.apache.catalina.util.TomcatCSS.TOMCAT_CSS);
 1300           sb.append("--></STYLE> ");
 1301           sb.append("</head>\r\n");
 1302           sb.append("<body>");
 1303           sb.append("<h1>");
 1304           sb.append(sm.getString("directory.title", name));
 1305   
 1306           // Render the link to our parent (if required)
 1307           String parentDirectory = name;
 1308           if (parentDirectory.endsWith("/")) {
 1309               parentDirectory =
 1310                   parentDirectory.substring(0, parentDirectory.length() - 1);
 1311           }
 1312           int slash = parentDirectory.lastIndexOf('/');
 1313           if (slash >= 0) {
 1314               String parent = name.substring(0, slash);
 1315               sb.append(" - <a href=\"");
 1316               sb.append(rewrittenContextPath);
 1317               if (parent.equals(""))
 1318                   parent = "/";
 1319               sb.append(rewriteUrl(parent));
 1320               if (!parent.endsWith("/"))
 1321                   sb.append("/");
 1322               sb.append("\">");
 1323               sb.append("<b>");
 1324               sb.append(sm.getString("directory.parent", parent));
 1325               sb.append("</b>");
 1326               sb.append("</a>");
 1327           }
 1328   
 1329           sb.append("</h1>");
 1330           sb.append("<HR size=\"1\" noshade=\"noshade\">");
 1331   
 1332           sb.append("<table width=\"100%\" cellspacing=\"0\"" +
 1333                        " cellpadding=\"5\" align=\"center\">\r\n");
 1334   
 1335           // Render the column headings
 1336           sb.append("<tr>\r\n");
 1337           sb.append("<td align=\"left\"><font size=\"+1\"><strong>");
 1338           sb.append(sm.getString("directory.filename"));
 1339           sb.append("</strong></font></td>\r\n");
 1340           sb.append("<td align=\"center\"><font size=\"+1\"><strong>");
 1341           sb.append(sm.getString("directory.size"));
 1342           sb.append("</strong></font></td>\r\n");
 1343           sb.append("<td align=\"right\"><font size=\"+1\"><strong>");
 1344           sb.append(sm.getString("directory.lastModified"));
 1345           sb.append("</strong></font></td>\r\n");
 1346           sb.append("</tr>");
 1347   
 1348           try {
 1349   
 1350               // Render the directory entries within this directory
 1351               NamingEnumeration enumeration = resources.list(cacheEntry.name);
 1352               boolean shade = false;
 1353               while (enumeration.hasMoreElements()) {
 1354   
 1355                   NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
 1356                   String resourceName = ncPair.getName();
 1357                   String trimmed = resourceName/*.substring(trim)*/;
 1358                   if (trimmed.equalsIgnoreCase("WEB-INF") ||
 1359                       trimmed.equalsIgnoreCase("META-INF"))
 1360                       continue;
 1361   
 1362                   CacheEntry childCacheEntry =
 1363                       resources.lookupCache(cacheEntry.name + resourceName);
 1364                   if (!childCacheEntry.exists) {
 1365                       continue;
 1366                   }
 1367   
 1368                   sb.append("<tr");
 1369                   if (shade)
 1370                       sb.append(" bgcolor=\"#eeeeee\"");
 1371                   sb.append(">\r\n");
 1372                   shade = !shade;
 1373   
 1374                   sb.append("<td align=\"left\">&nbsp;&nbsp;\r\n");
 1375                   sb.append("<a href=\"");
 1376                   sb.append(rewrittenContextPath);
 1377                   resourceName = rewriteUrl(name + resourceName);
 1378                   sb.append(resourceName);
 1379                   if (childCacheEntry.context != null)
 1380                       sb.append("/");
 1381                   sb.append("\"><tt>");
 1382                   sb.append(RequestUtil.filter(trimmed));
 1383                   if (childCacheEntry.context != null)
 1384                       sb.append("/");
 1385                   sb.append("</tt></a></td>\r\n");
 1386   
 1387                   sb.append("<td align=\"right\"><tt>");
 1388                   if (childCacheEntry.context != null)
 1389                       sb.append("&nbsp;");
 1390                   else
 1391                       sb.append(renderSize(childCacheEntry.attributes.getContentLength()));
 1392                   sb.append("</tt></td>\r\n");
 1393   
 1394                   sb.append("<td align=\"right\"><tt>");
 1395                   sb.append(childCacheEntry.attributes.getLastModifiedHttp());
 1396                   sb.append("</tt></td>\r\n");
 1397   
 1398                   sb.append("</tr>\r\n");
 1399               }
 1400   
 1401           } catch (NamingException e) {
 1402               // Something went wrong
 1403               throw new ServletException("Error accessing resource", e);
 1404           }
 1405   
 1406           // Render the page footer
 1407           sb.append("</table>\r\n");
 1408   
 1409           sb.append("<HR size=\"1\" noshade=\"noshade\">");
 1410   
 1411           String readme = getReadme(cacheEntry.context);
 1412           if (readme!=null) {
 1413               sb.append(readme);
 1414               sb.append("<HR size=\"1\" noshade=\"noshade\">");
 1415           }
 1416   
 1417           sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>");
 1418           sb.append("</body>\r\n");
 1419           sb.append("</html>\r\n");
 1420   
 1421           // Return an input stream to the underlying bytes
 1422           writer.write(sb.toString());
 1423           writer.flush();
 1424           return (new ByteArrayInputStream(stream.toByteArray()));
 1425   
 1426       }
 1427   
 1428   
 1429       /**
 1430        * Render the specified file size (in bytes).
 1431        *
 1432        * @param size File size (in bytes)
 1433        */
 1434       protected String renderSize(long size) {
 1435   
 1436           long leftSide = size / 1024;
 1437           long rightSide = (size % 1024) / 103;   // Makes 1 digit
 1438           if ((leftSide == 0) && (rightSide == 0) && (size > 0))
 1439               rightSide = 1;
 1440   
 1441           return ("" + leftSide + "." + rightSide + " kb");
 1442   
 1443       }
 1444   
 1445   
 1446       /**
 1447        * Get the readme file as a string.
 1448        */
 1449       protected String getReadme(DirContext directory)
 1450           throws IOException, ServletException {
 1451   
 1452           if (readmeFile != null) {
 1453               try {
 1454                   Object obj = directory.lookup(readmeFile);
 1455                   if ((obj != null) && (obj instanceof Resource)) {
 1456                       StringWriter buffer = new StringWriter();
 1457                       InputStream is = ((Resource) obj).streamContent();
 1458                       copyRange(new InputStreamReader(is),
 1459                               new PrintWriter(buffer));
 1460                       return buffer.toString();
 1461                   }
 1462               } catch (NamingException e) {
 1463                   if (debug > 10)
 1464                       log("readme '" + readmeFile + "' not found", e);
 1465   
 1466                   return null;
 1467               }
 1468           }
 1469   
 1470           return null;
 1471       }
 1472   
 1473   
 1474       /**
 1475        * Return the xsl template inputstream (if possible)
 1476        */
 1477       protected InputStream findXsltInputStream(DirContext directory)
 1478           throws IOException, ServletException {
 1479   
 1480           if (localXsltFile != null) {
 1481               try {
 1482                   Object obj = directory.lookup(localXsltFile);
 1483                   if ((obj != null) && (obj instanceof Resource)) {
 1484                       InputStream is = ((Resource) obj).streamContent();
 1485                       if (is != null)
 1486                           return is;
 1487                   }
 1488               } catch (NamingException e) {
 1489                   if (debug > 10)
 1490                       log("localXsltFile '" + localXsltFile + "' not found", e);
 1491               }
 1492           }
 1493   
 1494           if (contextXsltFile != null) {
 1495               InputStream is =
 1496                   getServletContext().getResourceAsStream(contextXsltFile);
 1497               if (is != null)
 1498                   return is;
 1499   
 1500               if (debug > 10)
 1501                   log("contextXsltFile '" + contextXsltFile + "' not found");
 1502           }
 1503   
 1504           /*  Open and read in file in one fell swoop to reduce chance
 1505            *  chance of leaving handle open.
 1506            */
 1507           if (globalXsltFile!=null) {
 1508               FileInputStream fis = null;
 1509   
 1510               try {
 1511                   File f = new File(globalXsltFile);
 1512                   if (f.exists()){
 1513                       fis =new FileInputStream(f);
 1514                       byte b[] = new byte[(int)f.length()]; /* danger! */
 1515                       fis.read(b);
 1516                       return new ByteArrayInputStream(b);
 1517                   }
 1518               } finally {
 1519                   if (fis!=null)
 1520                       fis.close();
 1521               }
 1522           }
 1523   
 1524           return null;
 1525   
 1526       }
 1527   
 1528   
 1529       // -------------------------------------------------------- protected Methods
 1530   
 1531   
 1532       /**
 1533        * Check if sendfile can be used.
 1534        */
 1535       protected boolean checkSendfile(HttpServletRequest request,
 1536                                     HttpServletResponse response,
 1537                                     CacheEntry entry,
 1538                                     long length, Range range) {
 1539           if ((sendfileSize > 0)
 1540               && (entry.resource != null)
 1541               && ((length > sendfileSize) || (entry.resource.getContent() == null))
 1542               && (entry.attributes.getCanonicalPath() != null)
 1543               && (Boolean.TRUE == request.getAttribute("org.apache.tomcat.sendfile.support"))
 1544               && (request.getClass().getName().equals("org.apache.catalina.connector.RequestFacade"))
 1545               && (response.getClass().getName().equals("org.apache.catalina.connector.ResponseFacade"))) {
 1546               request.setAttribute("org.apache.tomcat.sendfile.filename", entry.attributes.getCanonicalPath());
 1547               if (range == null) {
 1548                   request.setAttribute("org.apache.tomcat.sendfile.start", new Long(0L));
 1549                   request.setAttribute("org.apache.tomcat.sendfile.end", new Long(length));
 1550               } else {
 1551                   request.setAttribute("org.apache.tomcat.sendfile.start", new Long(range.start));
 1552                   request.setAttribute("org.apache.tomcat.sendfile.end", new Long(range.end + 1));
 1553               }
 1554               request.setAttribute("org.apache.tomcat.sendfile.token", this);
 1555               return true;
 1556           } else {
 1557               return false;
 1558           }
 1559       }
 1560       
 1561       
 1562       /**
 1563        * Check if the if-match condition is satisfied.
 1564        *
 1565        * @param request The servlet request we are processing
 1566        * @param response The servlet response we are creating
 1567        * @param resourceInfo File object
 1568        * @return boolean true if the resource meets the specified condition,
 1569        * and false if the condition is not satisfied, in which case request
 1570        * processing is stopped
 1571        */
 1572       protected boolean checkIfMatch(HttpServletRequest request,
 1573                                    HttpServletResponse response,
 1574                                    ResourceAttributes resourceAttributes)
 1575           throws IOException {
 1576   
 1577           String eTag = resourceAttributes.getETag();
 1578           String headerValue = request.getHeader("If-Match");
 1579           if (headerValue != null) {
 1580               if (headerValue.indexOf('*') == -1) {
 1581   
 1582                   StringTokenizer commaTokenizer = new StringTokenizer
 1583                       (headerValue, ",");
 1584                   boolean conditionSatisfied = false;
 1585   
 1586                   while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
 1587                       String currentToken = commaTokenizer.nextToken();
 1588                       if (currentToken.trim().equals(eTag))
 1589                           conditionSatisfied = true;
 1590                   }
 1591   
 1592                   // If none of the given ETags match, 412 Precodition failed is
 1593                   // sent back
 1594                   if (!conditionSatisfied) {
 1595                       response.sendError
 1596                           (HttpServletResponse.SC_PRECONDITION_FAILED);
 1597                       return false;
 1598                   }
 1599   
 1600               }
 1601           }
 1602           return true;
 1603   
 1604       }
 1605   
 1606   
 1607       /**
 1608        * Check if the if-modified-since condition is satisfied.
 1609        *
 1610        * @param request The servlet request we are processing
 1611        * @param response The servlet response we are creating
 1612        * @param resourceInfo File object
 1613        * @return boolean true if the resource meets the specified condition,
 1614        * and false if the condition is not satisfied, in which case request
 1615        * processing is stopped
 1616        */
 1617       protected boolean checkIfModifiedSince(HttpServletRequest request,
 1618                                            HttpServletResponse response,
 1619                                            ResourceAttributes resourceAttributes)
 1620           throws IOException {
 1621           try {
 1622               long headerValue = request.getDateHeader("If-Modified-Since");
 1623               long lastModified = resourceAttributes.getLastModified();
 1624               if (headerValue != -1) {
 1625   
 1626                   // If an If-None-Match header has been specified, if modified since
 1627                   // is ignored.
 1628                   if ((request.getHeader("If-None-Match") == null)
 1629                       && (lastModified < headerValue + 1000)) {
 1630                       // The entity has not been modified since the date
 1631                       // specified by the client. This is not an error case.
 1632                       response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
 1633                       response.setHeader("ETag", resourceAttributes.getETag());
 1634   
 1635                       return false;
 1636                   }
 1637               }
 1638           } catch (IllegalArgumentException illegalArgument) {
 1639               return true;
 1640           }
 1641           return true;
 1642   
 1643       }
 1644   
 1645   
 1646       /**
 1647        * Check if the if-none-match condition is satisfied.
 1648        *
 1649        * @param request The servlet request we are processing
 1650        * @param response The servlet response we are creating
 1651        * @param resourceInfo File object
 1652        * @return boolean true if the resource meets the specified condition,
 1653        * and false if the condition is not satisfied, in which case request
 1654        * processing is stopped
 1655        */
 1656       protected boolean checkIfNoneMatch(HttpServletRequest request,
 1657                                        HttpServletResponse response,
 1658                                        ResourceAttributes resourceAttributes)
 1659           throws IOException {
 1660   
 1661           String eTag = resourceAttributes.getETag();
 1662           String headerValue = request.getHeader("If-None-Match");
 1663           if (headerValue != null) {
 1664   
 1665               boolean conditionSatisfied = false;
 1666   
 1667               if (!headerValue.equals("*")) {
 1668   
 1669                   StringTokenizer commaTokenizer =
 1670                       new StringTokenizer(headerValue, ",");
 1671   
 1672                   while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
 1673                       String currentToken = commaTokenizer.nextToken();
 1674                       if (currentToken.trim().equals(eTag))
 1675                           conditionSatisfied = true;
 1676                   }
 1677   
 1678               } else {
 1679                   conditionSatisfied = true;
 1680               }
 1681   
 1682               if (conditionSatisfied) {
 1683   
 1684                   // For GET and HEAD, we should respond with
 1685                   // 304 Not Modified.
 1686                   // For every other method, 412 Precondition Failed is sent
 1687                   // back.
 1688                   if ( ("GET".equals(request.getMethod()))
 1689                        || ("HEAD".equals(request.getMethod())) ) {
 1690                       response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
 1691                       response.setHeader("ETag", eTag);
 1692   
 1693                       return false;
 1694                   } else {
 1695                       response.sendError
 1696                           (HttpServletResponse.SC_PRECONDITION_FAILED);
 1697                       return false;
 1698                   }
 1699               }
 1700           }
 1701           return true;
 1702   
 1703       }
 1704   
 1705   
 1706       /**
 1707        * Check if the if-unmodified-since condition is satisfied.
 1708        *
 1709        * @param request The servlet request we are processing
 1710        * @param response The servlet response we are creating
 1711        * @param resourceInfo File object
 1712        * @return boolean true if the resource meets the specified condition,
 1713        * and false if the condition is not satisfied, in which case request
 1714        * processing is stopped
 1715        */
 1716       protected boolean checkIfUnmodifiedSince(HttpServletRequest request,
 1717                                              HttpServletResponse response,
 1718                                              ResourceAttributes resourceAttributes)
 1719           throws IOException {
 1720           try {
 1721               long lastModified = resourceAttributes.getLastModified();
 1722               long headerValue = request.getDateHeader("If-Unmodified-Since");
 1723               if (headerValue != -1) {
 1724                   if ( lastModified >= (headerValue + 1000)) {
 1725                       // The entity has not been modified since the date
 1726                       // specified by the client. This is not an error case.
 1727                       response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
 1728                       return false;
 1729                   }
 1730               }
 1731           } catch(IllegalArgumentException illegalArgument) {
 1732               return true;
 1733           }
 1734           return true;
 1735   
 1736       }
 1737   
 1738   
 1739       /**
 1740        * Copy the contents of the specified input stream to the specified
 1741        * output stream, and ensure that both streams are closed before returning
 1742        * (even in the face of an exception).
 1743        *
 1744        * @param resourceInfo The resource information
 1745        * @param ostream The output stream to write to
 1746        *
 1747        * @exception IOException if an input/output error occurs
 1748        */
 1749       protected void copy(CacheEntry cacheEntry, InputStream is,
 1750                         ServletOutputStream ostream)
 1751           throws IOException {
 1752   
 1753           IOException exception = null;
 1754           InputStream resourceInputStream = null;
 1755   
 1756           // Optimization: If the binary content has already been loaded, send
 1757           // it directly
 1758           if (cacheEntry.resource != null) {
 1759               byte buffer[] = cacheEntry.resource.getContent();
 1760               if (buffer != null) {
 1761                   ostream.write(buffer, 0, buffer.length);
 1762                   return;
 1763               }
 1764               resourceInputStream = cacheEntry.resource.streamContent();
 1765           } else {
 1766               resourceInputStream = is;
 1767           }
 1768   
 1769           InputStream istream = new BufferedInputStream
 1770               (resourceInputStream, input);
 1771   
 1772           // Copy the input stream to the output stream
 1773           exception = copyRange(istream, ostream);
 1774   
 1775           // Clean up the input stream
 1776           istream.close();
 1777   
 1778           // Rethrow any exception that has occurred
 1779           if (exception != null)
 1780               throw exception;
 1781   
 1782       }
 1783   
 1784   
 1785       /**
 1786        * Copy the contents of the specified input stream to the specified
 1787        * output stream, and ensure that both streams are closed before returning
 1788        * (even in the face of an exception).
 1789        *
 1790        * @param resourceInfo The resource info
 1791        * @param writer The writer to write to
 1792        *
 1793        * @exception IOException if an input/output error occurs
 1794        */
 1795       protected void copy(CacheEntry cacheEntry, InputStream is, PrintWriter writer)
 1796           throws IOException {
 1797   
 1798           IOException exception = null;
 1799   
 1800           InputStream resourceInputStream = null;
 1801           if (cacheEntry.resource != null) {
 1802               resourceInputStream = cacheEntry.resource.streamContent();
 1803           } else {
 1804               resourceInputStream = is;
 1805           }
 1806   
 1807           Reader reader;
 1808           if (fileEncoding == null) {
 1809               reader = new InputStreamReader(resourceInputStream);
 1810           } else {
 1811               reader = new InputStreamReader(resourceInputStream,
 1812                                              fileEncoding);
 1813           }
 1814   
 1815           // Copy the input stream to the output stream
 1816           exception = copyRange(reader, writer);
 1817   
 1818           // Clean up the reader
 1819           reader.close();
 1820   
 1821           // Rethrow any exception that has occurred
 1822           if (exception != null)
 1823               throw exception;
 1824   
 1825       }
 1826   
 1827   
 1828       /**
 1829        * Copy the contents of the specified input stream to the specified
 1830        * output stream, and ensure that both streams are closed before returning
 1831        * (even in the face of an exception).
 1832        *
 1833        * @param resourceInfo The ResourceInfo object
 1834        * @param ostream The output stream to write to
 1835        * @param range Range the client wanted to retrieve
 1836        * @exception IOException if an input/output error occurs
 1837        */
 1838       protected void copy(CacheEntry cacheEntry, ServletOutputStream ostream,
 1839                         Range range)
 1840           throws IOException {
 1841   
 1842           IOException exception = null;
 1843   
 1844           InputStream resourceInputStream = cacheEntry.resource.streamContent();
 1845           InputStream istream =
 1846               new BufferedInputStream(resourceInputStream, input);
 1847           exception = copyRange(istream, ostream, range.start, range.end);
 1848   
 1849           // Clean up the input stream
 1850           istream.close();
 1851   
 1852           // Rethrow any exception that has occurred
 1853           if (exception != null)
 1854               throw exception;
 1855   
 1856       }
 1857   
 1858   
 1859       /**
 1860        * Copy the contents of the specified input stream to the specified
 1861        * output stream, and ensure that both streams are closed before returning
 1862        * (even in the face of an exception).
 1863        *
 1864        * @param resourceInfo The ResourceInfo object
 1865        * @param writer The writer to write to
 1866        * @param range Range the client wanted to retrieve
 1867        * @exception IOException if an input/output error occurs
 1868        */
 1869       protected void copy(CacheEntry cacheEntry, PrintWriter writer,
 1870                         Range range)
 1871           throws IOException {
 1872   
 1873           IOException exception = null;
 1874   
 1875           InputStream resourceInputStream = cacheEntry.resource.streamContent();
 1876   
 1877           Reader reader;
 1878           if (fileEncoding == null) {
 1879               reader = new InputStreamReader(resourceInputStream);
 1880           } else {
 1881               reader = new InputStreamReader(resourceInputStream,
 1882                                              fileEncoding);
 1883           }
 1884   
 1885           exception = copyRange(reader, writer, range.start, range.end);
 1886   
 1887           // Clean up the input stream
 1888           reader.close();
 1889   
 1890           // Rethrow any exception that has occurred
 1891           if (exception != null)
 1892               throw exception;
 1893   
 1894       }
 1895   
 1896   
 1897       /**
 1898        * Copy the contents of the specified input stream to the specified
 1899        * output stream, and ensure that both streams are closed before returning
 1900        * (even in the face of an exception).
 1901        *
 1902        * @param resourceInfo The ResourceInfo object
 1903        * @param ostream The output stream to write to
 1904        * @param ranges Enumeration of the ranges the client wanted to retrieve
 1905        * @param contentType Content type of the resource
 1906        * @exception IOException if an input/output error occurs
 1907        */
 1908       protected void copy(CacheEntry cacheEntry, ServletOutputStream ostream,
 1909                         Iterator ranges, String contentType)
 1910           throws IOException {
 1911   
 1912           IOException exception = null;
 1913   
 1914           while ( (exception == null) && (ranges.hasNext()) ) {
 1915   
 1916               InputStream resourceInputStream = cacheEntry.resource.streamContent();
 1917               InputStream istream =
 1918                   new BufferedInputStream(resourceInputStream, input);
 1919   
 1920               Range currentRange = (Range) ranges.next();
 1921   
 1922               // Writing MIME header.
 1923               ostream.println();
 1924               ostream.println("--" + mimeSeparation);
 1925               if (contentType != null)
 1926                   ostream.println("Content-Type: " + contentType);
 1927               ostream.println("Content-Range: bytes " + currentRange.start
 1928                              + "-" + currentRange.end + "/"
 1929                              + currentRange.length);
 1930               ostream.println();
 1931   
 1932               // Printing content
 1933               exception = copyRange(istream, ostream, currentRange.start,
 1934                                     currentRange.end);
 1935   
 1936               istream.close();
 1937   
 1938           }
 1939   
 1940           ostream.println();
 1941           ostream.print("--" + mimeSeparation + "--");
 1942   
 1943           // Rethrow any exception that has occurred
 1944           if (exception != null)
 1945               throw exception;
 1946   
 1947       }
 1948   
 1949   
 1950       /**
 1951        * Copy the contents of the specified input stream to the specified
 1952        * output stream, and ensure that both streams are closed before returning
 1953        * (even in the face of an exception).
 1954        *
 1955        * @param resourceInfo The ResourceInfo object
 1956        * @param writer The writer to write to
 1957        * @param ranges Enumeration of the ranges the client wanted to retrieve
 1958        * @param contentType Content type of the resource
 1959        * @exception IOException if an input/output error occurs
 1960        */
 1961       protected void copy(CacheEntry cacheEntry, PrintWriter writer,
 1962                         Iterator ranges, String contentType)
 1963           throws IOException {
 1964   
 1965           IOException exception = null;
 1966   
 1967           while ( (exception == null) && (ranges.hasNext()) ) {
 1968   
 1969               InputStream resourceInputStream = cacheEntry.resource.streamContent();
 1970               
 1971               Reader reader;
 1972               if (fileEncoding == null) {
 1973                   reader = new InputStreamReader(resourceInputStream);
 1974               } else {
 1975                   reader = new InputStreamReader(resourceInputStream,
 1976                                                  fileEncoding);
 1977               }
 1978   
 1979               Range currentRange = (Range) ranges.next();
 1980   
 1981               // Writing MIME header.
 1982               writer.println();
 1983               writer.println("--" + mimeSeparation);
 1984               if (contentType != null)
 1985                   writer.println("Content-Type: " + contentType);
 1986               writer.println("Content-Range: bytes " + currentRange.start
 1987                              + "-" + currentRange.end + "/"
 1988                              + currentRange.length);
 1989               writer.println();
 1990   
 1991               // Printing content
 1992               exception = copyRange(reader, writer, currentRange.start,
 1993                                     currentRange.end);
 1994   
 1995               reader.close();
 1996   
 1997           }
 1998   
 1999           writer.println();
 2000           writer.print("--" + mimeSeparation + "--");
 2001   
 2002           // Rethrow any exception that has occurred
 2003           if (exception != null)
 2004               throw exception;
 2005   
 2006       }
 2007   
 2008   
 2009       /**
 2010        * Copy the contents of the specified input stream to the specified
 2011        * output stream, and ensure that both streams are closed before returning
 2012        * (even in the face of an exception).
 2013        *
 2014        * @param istream The input stream to read from
 2015        * @param ostream The output stream to write to
 2016        * @return Exception which occurred during processing
 2017        */
 2018       protected IOException copyRange(InputStream istream,
 2019                                     ServletOutputStream ostream) {
 2020   
 2021           // Copy the input stream to the output stream
 2022           IOException exception = null;
 2023           byte buffer[] = new byte[input];
 2024           int len = buffer.length;
 2025           while (true) {
 2026               try {
 2027                   len = istream.read(buffer);
 2028                   if (len == -1)
 2029                       break;
 2030                   ostream.write(buffer, 0, len);
 2031               } catch (IOException e) {
 2032                   exception = e;
 2033                   len = -1;
 2034                   break;
 2035               }
 2036           }
 2037           return exception;
 2038   
 2039       }
 2040   
 2041   
 2042       /**
 2043        * Copy the contents of the specified input stream to the specified
 2044        * output stream, and ensure that both streams are closed before returning
 2045        * (even in the face of an exception).
 2046        *
 2047        * @param reader The reader to read from
 2048        * @param writer The writer to write to
 2049        * @return Exception which occurred during processing
 2050        */
 2051       protected IOException copyRange(Reader reader, PrintWriter writer) {
 2052   
 2053           // Copy the input stream to the output stream
 2054           IOException exception = null;
 2055           char buffer[] = new char[input];
 2056           int len = buffer.length;
 2057           while (true) {
 2058               try {
 2059                   len = reader.read(buffer);
 2060                   if (len == -1)
 2061                       break;
 2062                   writer.write(buffer, 0, len);
 2063               } catch (IOException e) {
 2064                   exception = e;
 2065                   len = -1;
 2066                   break;
 2067               }
 2068           }
 2069           return exception;
 2070   
 2071       }
 2072   
 2073   
 2074       /**
 2075        * Copy the contents of the specified input stream to the specified
 2076        * output stream, and ensure that both streams are closed before returning
 2077        * (even in the face of an exception).
 2078        *
 2079        * @param istream The input stream to read from
 2080        * @param ostream The output stream to write to
 2081        * @param start Start of the range which will be copied
 2082        * @param end End of the range which will be copied
 2083        * @return Exception which occurred during processing
 2084        */
 2085       protected IOException copyRange(InputStream istream,
 2086                                     ServletOutputStream ostream,
 2087                                     long start, long end) {
 2088   
 2089           if (debug > 10)
 2090               log("Serving bytes:" + start + "-" + end);
 2091   
 2092           try {
 2093               istream.skip(start);
 2094           } catch (IOException e) {
 2095               return e;
 2096           }
 2097   
 2098           IOException exception = null;
 2099           long bytesToRead = end - start + 1;
 2100   
 2101           byte buffer[] = new byte[input];
 2102           int len = buffer.length;
 2103           while ( (bytesToRead > 0) && (len >= buffer.length)) {
 2104               try {
 2105                   len = istream.read(buffer);
 2106                   if (bytesToRead >= len) {
 2107                       ostream.write(buffer, 0, len);
 2108                       bytesToRead -= len;
 2109                   } else {
 2110                       ostream.write(buffer, 0, (int) bytesToRead);
 2111                       bytesToRead = 0;
 2112                   }
 2113               } catch (IOException e) {
 2114                   exception = e;
 2115                   len = -1;
 2116               }
 2117               if (len < buffer.length)
 2118                   break;
 2119           }
 2120   
 2121           return exception;
 2122   
 2123       }
 2124   
 2125   
 2126       /**
 2127        * Copy the contents of the specified input stream to the specified
 2128        * output stream, and ensure that both streams are closed before returning
 2129        * (even in the face of an exception).
 2130        *
 2131        * @param reader The reader to read from
 2132        * @param writer The writer to write to
 2133        * @param start Start of the range which will be copied
 2134        * @param end End of the range which will be copied
 2135        * @return Exception which occurred during processing
 2136        */
 2137       protected IOException copyRange(Reader reader, PrintWriter writer,
 2138                                     long start, long end) {
 2139   
 2140           try {
 2141               reader.skip(start);
 2142           } catch (IOException e) {
 2143               return e;
 2144           }
 2145   
 2146           IOException exception = null;
 2147           long bytesToRead = end - start + 1;
 2148   
 2149           char buffer[] = new char[input];
 2150           int len = buffer.length;
 2151           while ( (bytesToRead > 0) && (len >= buffer.length)) {
 2152               try {
 2153                   len = reader.read(buffer);
 2154                   if (bytesToRead >= len) {
 2155                       writer.write(buffer, 0, len);
 2156                       bytesToRead -= len;
 2157                   } else {
 2158                       writer.write(buffer, 0, (int) bytesToRead);
 2159                       bytesToRead = 0;
 2160                   }
 2161               } catch (IOException e) {
 2162                   exception = e;
 2163                   len = -1;
 2164               }
 2165               if (len < buffer.length)
 2166                   break;
 2167           }
 2168   
 2169           return exception;
 2170   
 2171       }
 2172   
 2173   
 2174   
 2175       // ------------------------------------------------------ Range Inner Class
 2176   
 2177   
 2178       protected class Range {
 2179   
 2180           public long start;
 2181           public long end;
 2182           public long length;
 2183   
 2184           /**
 2185            * Validate range.
 2186            */
 2187           public boolean validate() {
 2188               if (end >= length)
 2189                   end = length - 1;
 2190               return ( (start >= 0) && (end >= 0) && (start <= end)
 2191                        && (length > 0) );
 2192           }
 2193   
 2194           public void recycle() {
 2195               start = 0;
 2196               end = 0;
 2197               length = 0;
 2198           }
 2199   
 2200       }
 2201   
 2202   
 2203   }

Save This Page
Home » apache-tomcat-6.0.26-src » org.apache » catalina » servlets » [javadoc | source]