code:     protected void serveResource(HttpServletRequest request,
                                   HttpServletResponse response,
                                   boolean content,
                                   String encoding)
          throws IOException, ServletException {
  
          boolean serveContent = content;
  
          // Identify the requested resource path
          String path = getRelativePath(request);
          if (debug > 0) {
              if (serveContent)
                  log("DefaultServlet.serveResource:  Serving resource '" +
                      path + "' headers and data");
              else
                  log("DefaultServlet.serveResource:  Serving resource '" +
                      path + "' headers only");
          }
  
          WebResource resource = resources.getResource(path);
  
          if (!resource.exists()) {
              // Check if we're included so we can return the appropriate
              // missing resource name in the error
              String requestUri = (String) request.getAttribute(
                      RequestDispatcher.INCLUDE_REQUEST_URI);
              if (requestUri == null) {
                  requestUri = request.getRequestURI();
              } else {
                  // We're included
                  // SRV.9.3 says we must throw a FNFE
                  throw new FileNotFoundException(sm.getString(
                          "defaultServlet.missingResource", requestUri));
              }
  
              response.sendError(HttpServletResponse.SC_NOT_FOUND, requestUri);
              return;
          }
  
          if (!resource.canRead()) {
              // Check if we're included so we can return the appropriate
              // missing resource name in the error
              String requestUri = (String) request.getAttribute(
                      RequestDispatcher.INCLUDE_REQUEST_URI);
              if (requestUri == null) {
                  requestUri = request.getRequestURI();
              } else {
                  // We're included
                  // Spec doesn't say what to do in this case but a FNFE seems
                  // reasonable
                  throw new FileNotFoundException(sm.getString(
                          "defaultServlet.missingResource", requestUri));
              }
  
              response.sendError(HttpServletResponse.SC_FORBIDDEN, requestUri);
              return;
          }
  
          // If the resource is not a collection, and the resource path
          // ends with "/" or "\", return NOT FOUND
          if (resource.isFile() && (path.endsWith("/") || path.endsWith("\\"))) {
              // Check if we're included so we can return the appropriate
              // missing resource name in the error
              String requestUri = (String) request.getAttribute(
                      RequestDispatcher.INCLUDE_REQUEST_URI);
              if (requestUri == null) {
                  requestUri = request.getRequestURI();
              }
              response.sendError(HttpServletResponse.SC_NOT_FOUND, requestUri);
              return;
          }
  
          boolean isError = response.getStatus() >= HttpServletResponse.SC_BAD_REQUEST;
  
          boolean included = false;
          // Check if the conditions specified in the optional If headers are
          // satisfied.
          if (resource.isFile()) {
              // Checking If headers
              included = (request.getAttribute(
                      RequestDispatcher.INCLUDE_CONTEXT_PATH) != null);
              if (!included && !isError && !checkIfHeaders(request, response, resource)) {
                  return;
              }
          }
  
          // Find content type.
          String contentType = resource.getMimeType();
          if (contentType == null) {
              contentType = getServletContext().getMimeType(resource.getName());
              resource.setMimeType(contentType);
          }
  
          // These need to reflect the original resource, not the potentially
          // gzip'd version of the resource so get them now if they are going to
          // be needed later
          String eTag = null;
          String lastModifiedHttp = null;
          if (resource.isFile() && !isError) {
              eTag = resource.getETag();
              lastModifiedHttp = resource.getLastModifiedHttp();
          }
  
  
          // Serve a gzipped version of the file if present
          boolean usingGzippedVersion = false;
          if (gzip && !included && resource.isFile() && !path.endsWith(".gz")) {
              WebResource gzipResource = resources.getResource(path + ".gz");
              if (gzipResource.exists() && gzipResource.isFile()) {
                  Collection<String> varyHeaders = response.getHeaders("Vary");
                  boolean addRequired = true;
                  for (String varyHeader : varyHeaders) {
                      if ("*".equals(varyHeader) ||
                              "accept-encoding".equalsIgnoreCase(varyHeader)) {
                          addRequired = false;
                          break;
                      }
                  }
                  if (addRequired) {
                      response.addHeader("Vary", "accept-encoding");
                  }
                  if (checkIfGzip(request)) {
                      response.addHeader("Content-Encoding", "gzip");
                      resource = gzipResource;
                      usingGzippedVersion = true;
                  }
              }
          }
  
          ArrayList<Range> ranges = null;
          long contentLength = -1L;
  
          if (resource.isDirectory()) {
              // Skip directory listings if we have been configured to
              // suppress them
              if (!listings) {
                  response.sendError(HttpServletResponse.SC_NOT_FOUND,
                                     request.getRequestURI());
                  return;
              }
              contentType = "text/html;charset=UTF-8";
          } else {
              if (!isError) {
                  if (useAcceptRanges) {
                      // Accept ranges header
                      response.setHeader("Accept-Ranges", "bytes");
                  }
  
                  // Parse range specifier
                  ranges = parseRange(request, response, resource);
  
                  // ETag header
                  response.setHeader("ETag", eTag);
  
                  // Last-Modified header
                  response.setHeader("Last-Modified", lastModifiedHttp);
              }
  
              // Get content length
              contentLength = resource.getContentLength();
              // Special case for zero length files, which would cause a
              // (silent) ISE when setting the output buffer size
              if (contentLength == 0L) {
                  serveContent = false;
              }
          }
  
          ServletOutputStream ostream = null;
          PrintWriter writer = null;
  
          if (serveContent) {
              // Trying to retrieve the servlet output stream
              try {
                  ostream = response.getOutputStream();
              } catch (IllegalStateException e) {
                  // If it fails, we try to get a Writer instead if we're
                  // trying to serve a text file
                  if (!usingGzippedVersion &&
                          ((contentType == null) ||
                                  (contentType.startsWith("text")) ||
                                  (contentType.endsWith("xml")) ||
                                  (contentType.contains("/javascript")))
                          ) {
                      writer = response.getWriter();
                      // Cannot reliably serve partial content with a Writer
                      ranges = FULL;
                  } else {
                      throw e;
                  }
              }
          }
  
          // Check to see if a Filter, Valve of wrapper has written some content.
          // If it has, disable range requests and setting of a content length
          // since neither can be done reliably.
          ServletResponse r = response;
          long contentWritten = 0;
          while (r instanceof ServletResponseWrapper) {
              r = ((ServletResponseWrapper) r).getResponse();
          }
          if (r instanceof ResponseFacade) {
              contentWritten = ((ResponseFacade) r).getContentWritten();
          }
          if (contentWritten > 0) {
              ranges = FULL;
          }
  
          if (resource.isDirectory() ||
                  isError ||
                  ( (ranges == null || ranges.isEmpty())
                          && request.getHeader("Range") == null ) ||
                  ranges == FULL ) {
  
              // Set the appropriate output headers
              if (contentType != null) {
                  if (debug > 0)
                      log("DefaultServlet.serveFile:  contentType='" +
                          contentType + "'");
                  response.setContentType(contentType);
              }
              if (resource.isFile() && contentLength >= 0 &&
                      (!serveContent || ostream != null)) {
                  if (debug > 0)
                      log("DefaultServlet.serveFile:  contentLength=" +
                          contentLength);
                  // Don't set a content length if something else has already
                  // written to the response.
                  if (contentWritten == 0) {
                      response.setContentLengthLong(contentLength);
                  }
              }
  
              if (serveContent) {
                  try {
                      response.setBufferSize(output);
                  } catch (IllegalStateException e) {
                      // Silent catch
                  }
                  InputStream renderResult = null;
                  if (ostream == null) {
                      // Output via a writer so can't use sendfile or write
                      // content directly.
                      if (resource.isDirectory()) {
                          renderResult = render(getPathPrefix(request), resource);
                      } else {
                          renderResult = resource.getInputStream();
                      }
                      copy(resource, renderResult, writer, encoding);
                  } else {
                      // Output is via an InputStream
                      if (resource.isDirectory()) {
                          renderResult = render(getPathPrefix(request), resource);
                      } else {
                          // Output is content of resource
                          if (!checkSendfile(request, response, resource,
                                  contentLength, null)) {
                              // sendfile not possible so check if resource
                              // content is available directly
                              byte[] resourceBody = resource.getContent();
                              if (resourceBody == null) {
                                  // Resource content not available, use
                                  // inputstream
                                  renderResult = resource.getInputStream();
                              } else {
                                  // Use the resource content directly
                                  ostream.write(resourceBody);
                              }
                          }
                      }
                      // If a stream was configured, it needs to be copied to
                      // the output (this method closes the stream)
                      if (renderResult != null) {
                          copy(resource, renderResult, ostream);
                      }
                  }
              }
  
          } else {
  
              if ((ranges == null) || (ranges.isEmpty()))
                  return;
  
              // Partial content response.
  
              response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
  
              if (ranges.size() == 1) {
  
                  Range range = ranges.get(0);
                  response.addHeader("Content-Range", "bytes "
                                     + range.start
                                     + "-" + range.end + "/"
                                     + range.length);
                  long length = range.end - range.start + 1;
                  response.setContentLengthLong(length);
  
                  if (contentType != null) {
                      if (debug > 0)
                          log("DefaultServlet.serveFile:  contentType='" +
                              contentType + "'");
                      response.setContentType(contentType);
                  }
  
                  if (serveContent) {
                      try {
                          response.setBufferSize(output);
                      } catch (IllegalStateException e) {
                          // Silent catch
                      }
                      if (ostream != null) {
                          if (!checkSendfile(request, response, resource,
                                  range.end - range.start + 1, range))
                              copy(resource, ostream, range);
                      } else {
                          // we should not get here
                          throw new IllegalStateException();
                      }
                  }
              } else {
                  response.setContentType("multipart/byteranges; boundary="
                                          + mimeSeparation);
                  if (serveContent) {
                      try {
                          response.setBufferSize(output);
                      } catch (IllegalStateException e) {
                          // Silent catch
                      }
                      if (ostream != null) {
                          copy(resource, ostream, ranges.iterator(), contentType);
                      } else {
                          // we should not get here
                          throw new IllegalStateException();
                      }
                  }
              }
          }
      }
  
  
      /**
       * Parse the content-range header.
       *
       * @param request The servlet request we a)re processing
       * @param response The servlet response we are creating
       * @return Range
       */
  