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

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