remm 01/03/17 22:36:14
Modified: catalina/src/share/org/apache/catalina/servlets
DefaultServlet.java
Log:
- Changes most of the methods in DefaultServlet from private to protected,
so that it's easier to extend its functionality.
The patch is big, but there is actually no real changes (since the method are
sorted by access type, I had to move around big chunks of code).
Revision Changes Path
1.30 +904 -903
jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/DefaultServlet.java
Index: DefaultServlet.java
===================================================================
RCS file:
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/DefaultServlet.java,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -r1.29 -r1.30
--- DefaultServlet.java 2001/03/17 05:24:28 1.29
+++ DefaultServlet.java 2001/03/18 06:36:13 1.30
@@ -1,7 +1,7 @@
/*
- * $Header:
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/DefaultServlet.java,v
1.29 2001/03/17 05:24:28 remm Exp $
- * $Revision: 1.29 $
- * $Date: 2001/03/17 05:24:28 $
+ * $Header:
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/DefaultServlet.java,v
1.30 2001/03/18 06:36:13 remm Exp $
+ * $Revision: 1.30 $
+ * $Date: 2001/03/18 06:36:13 $
*
* ====================================================================
*
@@ -122,7 +122,7 @@
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 1.29 $ $Date: 2001/03/17 05:24:28 $
+ * @version $Revision: 1.30 $ $Date: 2001/03/18 06:36:13 $
*/
public class DefaultServlet
@@ -973,1040 +973,1041 @@
}
- // -------------------------------------------------------- Private Methods
-
-
/**
- * Copy the contents of the specified input stream to the specified
- * output stream, and ensure that both streams are closed before returning
- * (even in the face of an exception).
- *
- * @param istream The input stream to read from
- * @param ostream The output stream to write to
- *
- * @exception IOException if an input/output error occurs
+ * Display the size of a file.
*/
- private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream)
- throws IOException {
-
- IOException exception = null;
-
- // FIXME : i18n ?
- InputStream resourceInputStream = resourceInfo.getStream();
- InputStream istream = new BufferedInputStream
- (resourceInputStream, input);
-
- // Copy the input stream to the output stream
- exception = copyRange(istream, ostream);
+ protected void displaySize(StringBuffer buf, int filesize) {
- // Clean up the input stream
- try {
- istream.close();
- } catch (Throwable t) {
- ;
- }
+ int leftside = filesize / 1024;
+ int rightside = (filesize % 1024) / 103; // makes 1 digit
+ // To avoid 0.0 for non-zero file, we bump to 0.1
+ if (leftside == 0 && rightside == 0 && filesize != 0)
+ rightside = 1;
+ buf.append(leftside).append(".").append(rightside);
+ buf.append(" KB");
- // Rethrow any exception that has occurred
- if (exception != null)
- throw exception;
-
}
-
-
+
+
/**
- * Copy the contents of the specified input stream to the specified
- * output stream, and ensure that both streams are closed before returning
- * (even in the face of an exception).
+ * Serve the specified resource, optionally including the data content.
*
- * @param istream The input stream to read from
- * @param writer The writer to write to
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ * @param content Should the content be included?
*
* @exception IOException if an input/output error occurs
+ * @exception ServletException if a servlet-specified error occurs
*/
- private void copy(ResourceInfo resourceInfo, PrintWriter writer)
- throws IOException {
-
- IOException exception = null;
-
- InputStream resourceInputStream = resourceInfo.getStream();
- // FIXME : i18n ?
- Reader reader = new InputStreamReader(resourceInputStream);
-
- // Copy the input stream to the output stream
- exception = copyRange(reader, writer);
-
- // Clean up the reader
- try {
- reader.close();
- } catch (Throwable t) {
- ;
- }
-
- // Rethrow any exception that has occurred
- if (exception != null)
- throw exception;
-
- }
+ protected void serveResource(HttpServletRequest request,
+ HttpServletResponse response,
+ boolean content)
+ throws IOException, ServletException {
+ // Identify the requested resource path
+ String path = getRelativePath(request);
+ if (debug > 0) {
+ if (content)
+ log("DefaultServlet.serveResource: Serving resource '" +
+ path + "' headers and data");
+ else
+ log("DefaultServlet.serveResource: Serving resource '" +
+ path + "' headers only");
+ }
- /**
- * Copy the contents of the specified input stream to the specified
- * output stream, and ensure that both streams are closed before returning
- * (even in the face of an exception).
- *
- * @param resourceInfo The ResourceInfo object
- * @param ostream The output stream to write to
- * @param range Range the client wanted to retrieve
- * @exception IOException if an input/output error occurs
- */
- private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream,
- Range range)
- throws IOException {
-
- IOException exception = null;
-
- InputStream resourceInputStream = resourceInfo.getStream();
- InputStream istream =
- new BufferedInputStream(resourceInputStream, input);
- exception = copyRange(istream, ostream, range.start, range.end);
-
- // Clean up the input stream
- try {
- istream.close();
- } catch (Throwable t) {
- ;
+ // Exclude any resource in the /WEB-INF and /META-INF subdirectories
+ // (the "toUpperCase()" avoids problems on Windows systems)
+ if ((path == null) ||
+ path.toUpperCase().startsWith("/WEB-INF") ||
+ path.toUpperCase().startsWith("/META-INF")) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
+ return;
}
- // Rethrow any exception that has occurred
- if (exception != null)
- throw exception;
-
- }
+ // Retrieve the Catalina context and Resources implementation
+ DirContext resources = getResources();
+ ResourceInfo resourceInfo = new ResourceInfo(path, resources);
+ if (!resourceInfo.exists) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
+ return;
+ }
- /**
- * Copy the contents of the specified input stream to the specified
- * output stream, and ensure that both streams are closed before returning
- * (even in the face of an exception).
- *
- * @param resourceInfo The ResourceInfo object
- * @param writer The writer to write to
- * @param range Range the client wanted to retrieve
- * @exception IOException if an input/output error occurs
- */
- private void copy(ResourceInfo resourceInfo, PrintWriter writer,
- Range range)
- throws IOException {
-
- IOException exception = null;
-
- InputStream resourceInputStream = resourceInfo.getStream();
- Reader reader = new InputStreamReader(resourceInputStream);
- exception = copyRange(reader, writer, range.start, range.end);
-
- // Clean up the input stream
- try {
- reader.close();
- } catch (Throwable t) {
- ;
- }
+ // If the resource is not a collection, and the resource path
+ // ends with "/" or "\", return NOT FOUND
+ if (!resourceInfo.collection) {
+ if (path.endsWith("/") || (path.endsWith("\\"))) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
+ return;
+ }
+ }
- // Rethrow any exception that has occurred
- if (exception != null)
- throw exception;
-
- }
+ // If the resource is a collection (aka a directory), we check
+ // the welcome files list.
+ if (resourceInfo.collection) {
+ if (!request.getRequestURI().endsWith("/")) {
+ response.sendRedirect(request.getRequestURI() + "/");
+ return;
+ }
- /**
- * Copy the contents of the specified input stream to the specified
- * output stream, and ensure that both streams are closed before returning
- * (even in the face of an exception).
- *
- * @param resourceInfo The ResourceInfo object
- * @param ostream The output stream to write to
- * @param ranges Enumeration of the ranges the client wanted to retrieve
- * @param contentType Content type of the resource
- * @exception IOException if an input/output error occurs
- */
- private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream,
- Enumeration ranges, String contentType)
- throws IOException {
-
- IOException exception = null;
-
- while ( (exception == null) && (ranges.hasMoreElements()) ) {
-
- InputStream resourceInputStream = resourceInfo.getStream();
- InputStream istream = // FIXME: internationalization???????
- new BufferedInputStream(resourceInputStream, input);
-
- Range currentRange = (Range) ranges.nextElement();
-
- // Writing MIME header.
- ostream.println("--" + mimeSeparation);
- if (contentType != null)
- ostream.println("Content-Type: " + contentType);
- ostream.println("Content-Range: bytes " + currentRange.start
- + "-" + currentRange.end + "/"
- + currentRange.length);
- ostream.println();
-
- // Printing content
- exception = copyRange(istream, ostream, currentRange.start,
- currentRange.end);
-
- try {
- istream.close();
- } catch (Throwable t) {
- ;
+ ResourceInfo welcomeFileInfo = checkWelcomeFiles(path, resources);
+ if (welcomeFileInfo != null) {
+ String redirectPath = welcomeFileInfo.path;
+ String contextPath = request.getContextPath();
+ if ((contextPath != null) && (!contextPath.equals("/"))) {
+ redirectPath = contextPath + redirectPath;
+ }
+ response.sendRedirect(rewriteUrl(redirectPath));
+ return;
}
}
- ostream.print("--" + mimeSeparation + "--");
+ // Checking If headers
+ if ( !checkIfHeaders(request, response, resourceInfo) ) {
+ return;
+ }
- // Rethrow any exception that has occurred
- if (exception != null)
- throw exception;
+ // Find content type.
+ String contentType =
+ getServletContext().getMimeType(resourceInfo.path);
- }
+ if (resourceInfo.collection) {
+ // Skip directory listings if we have been configured to
+ // suppress them
+ if (!listings) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND,
+ resourceInfo.path);
+ return;
+ }
+ contentType = "text/html;charset=UTF-8";
+ }
- /**
- * Copy the contents of the specified input stream to the specified
- * output stream, and ensure that both streams are closed before returning
- * (even in the face of an exception).
- *
- * @param resourceInfo The ResourceInfo object
- * @param writer The writer to write to
- * @param ranges Enumeration of the ranges the client wanted to retrieve
- * @param contentType Content type of the resource
- * @exception IOException if an input/output error occurs
- */
- private void copy(ResourceInfo resourceInfo, PrintWriter writer,
- Enumeration ranges, String contentType)
- throws IOException {
+ // Parse range specifier
+ Vector ranges = null;
+ if (!resourceInfo.collection) {
+
+ ranges = parseRange(request, response, resourceInfo);
+
+ // ETag header
+ response.setHeader("ETag", getETag(resourceInfo, true));
+
+ }
- IOException exception = null;
+ // Last-Modified header
+ if (debug > 0)
+ log("DefaultServlet.serveFile: lastModified='" +
+ (new Timestamp(resourceInfo.date)).toString() + "'");
+ response.setDateHeader("Last-Modified", resourceInfo.date);
- while ( (exception == null) && (ranges.hasMoreElements()) ) {
+ ServletOutputStream ostream = null;
+ PrintWriter writer = null;
+
+ if (content) {
+
+ // Trying to retrieve the servlet output stream
- InputStream resourceInputStream = resourceInfo.getStream();
- Reader reader = new InputStreamReader(resourceInputStream);
+ 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 ( (contentType != null)
+ && (contentType.startsWith("text")) ) {
+ writer = response.getWriter();
+ } else {
+ throw e;
+ }
+ }
+
+ }
- Range currentRange = (Range) ranges.nextElement();
+ if ( (resourceInfo.collection) ||
+ ( ((ranges == null) || (ranges.isEmpty()))
+ && (request.getHeader("Range") == null) ) ) {
- // Writing MIME header.
- writer.println("--" + mimeSeparation);
- if (contentType != null)
- writer.println("Content-Type: " + contentType);
- writer.println("Content-Range: bytes " + currentRange.start
- + "-" + currentRange.end + "/"
- + currentRange.length);
- writer.println();
+ // Set the appropriate output headers
+ if (contentType != null) {
+ if (debug > 0)
+ log("DefaultServlet.serveFile: contentType='" +
+ contentType + "'");
+ response.setContentType(contentType);
+ }
+ long contentLength = resourceInfo.length;
+ if ((!resourceInfo.collection) && (contentLength >= 0)) {
+ if (debug > 0)
+ log("DefaultServlet.serveFile: contentLength=" +
+ contentLength);
+ response.setContentLength((int) contentLength);
+ }
- // Printing content
- exception = copyRange(reader, writer, currentRange.start,
- currentRange.end);
+ if (resourceInfo.collection) {
+
+ if (content) {
+ // Serve the directory browser
+ resourceInfo.setStream
+ (render(request.getContextPath(), resourceInfo));
+ }
+
+ }
- try {
- reader.close();
- } catch (Throwable t) {
- ;
+ // Copy the input stream to our output stream (if requested)
+ if (content) {
+ try {
+ response.setBufferSize(output);
+ } catch (IllegalStateException e) {
+ // Silent catch
+ }
+ if (ostream != null) {
+ copy(resourceInfo, ostream);
+ } else {
+ copy(resourceInfo, writer);
+ }
+ }
+
+ } else {
+
+ if ((ranges == null) || (ranges.isEmpty()))
+ return;
+
+ // Partial content response.
+
+ response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
+
+ if (ranges.size() == 1) {
+
+ Range range = (Range) ranges.elementAt(0);
+ response.addHeader("Content-Range", "bytes "
+ + range.start
+ + "-" + range.end + "/"
+ + range.length);
+ response.setContentLength((int) range.length);
+
+ if (contentType != null) {
+ if (debug > 0)
+ log("DefaultServlet.serveFile: contentType='" +
+ contentType + "'");
+ response.setContentType(contentType);
+ }
+
+ if (content) {
+ try {
+ response.setBufferSize(output);
+ } catch (IllegalStateException e) {
+ // Silent catch
+ }
+ if (ostream != null) {
+ copy(resourceInfo, ostream, range);
+ } else {
+ copy(resourceInfo, writer, range);
+ }
+ }
+
+ } else {
+
+ response.setContentType("multipart/byteranges; boundary="
+ + mimeSeparation);
+
+ if (content) {
+ try {
+ response.setBufferSize(output);
+ } catch (IllegalStateException e) {
+ // Silent catch
+ }
+ if (ostream != null) {
+ copy(resourceInfo, ostream, ranges.elements(),
+ contentType);
+ } else {
+ copy(resourceInfo, writer, ranges.elements(),
+ contentType);
+ }
+ }
+
}
}
- writer.print("--" + mimeSeparation + "--");
-
- // Rethrow any exception that has occurred
- if (exception != null)
- throw exception;
-
}
/**
- * Copy the contents of the specified input stream to the specified
- * output stream, and ensure that both streams are closed before returning
- * (even in the face of an exception).
- *
- * @param istream The input stream to read from
- * @param ostream The output stream to write to
- * @return Exception which occured during processing
+ * Parse the range header.
+ *
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ * @return Vector of ranges
*/
- private IOException copyRange(InputStream istream,
- ServletOutputStream ostream) {
+ protected Vector parseRange(HttpServletRequest request,
+ HttpServletResponse response,
+ ResourceInfo resourceInfo)
+ throws IOException {
- // Copy the input stream to the output stream
- IOException exception = null;
- byte buffer[] = new byte[input];
- int len = buffer.length;
- while (true) {
- try {
- len = istream.read(buffer);
- if (len == -1)
- break;
- ostream.write(buffer, 0, len);
- } catch (IOException e) {
- exception = e;
- len = -1;
- break;
- }
- }
- return exception;
+ // Checking If-Range
+ String headerValue = request.getHeader("If-Range");
+ if (headerValue != null) {
+
+ String eTag = getETag(resourceInfo, true);
+ long lastModified = resourceInfo.date;
+
+ Date date = null;
+
+ // Parsing the HTTP Date
+ for (int i = 0; (date == null) && (i < formats.length); i++) {
+ try {
+ date = formats[i].parse(headerValue);
+ } catch (ParseException e) {
+ ;
+ }
+ }
+
+ if (date == null) {
+
+ // If the ETag the client gave does not match the entity
+ // etag, then the entire entity is returned.
+ if (!eTag.equals(headerValue.trim()))
+ return null;
+
+ } else {
+
+ // If the timestamp of the entity the client got is older than
+ // the last modification date of the entity, the entire entity
+ // is returned.
+ if (lastModified > (date.getTime() + 1000))
+ return null;
+
+ }
+
+ }
- }
-
-
- /**
- * Copy the contents of the specified input stream to the specified
- * output stream, and ensure that both streams are closed before returning
- * (even in the face of an exception).
- *
- * @param reader The reader to read from
- * @param writer The writer to write to
- * @return Exception which occured during processing
- */
- private IOException copyRange(Reader reader, PrintWriter writer) {
+ long fileLength = resourceInfo.length;
- // Copy the input stream to the output stream
- IOException exception = null;
- char buffer[] = new char[input];
- int len = buffer.length;
- while (true) {
- try {
- len = reader.read(buffer);
- if (len == -1)
- break;
- writer.write(buffer, 0, len);
- } catch (IOException e) {
- exception = e;
- len = -1;
- break;
- }
- }
- return exception;
-
- }
-
-
- /**
- * Copy the contents of the specified input stream to the specified
- * output stream, and ensure that both streams are closed before returning
- * (even in the face of an exception).
- *
- * @param istream The input stream to read from
- * @param ostream The output stream to write to
- * @param start Start of the range which will be copied
- * @param end End of the range which will be copied
- * @return Exception which occured during processing
- */
- private IOException copyRange(InputStream istream,
- ServletOutputStream ostream,
- long start, long end) {
-
- if (debug > 10)
- System.out.println("Serving bytes:" + start + "-" + end);
-
- try {
- istream.skip(start);
- } catch (IOException e) {
- return e;
- }
-
- IOException exception = null;
- long bytesToRead = end - start + 1;
-
- byte buffer[] = new byte[input];
- int len = buffer.length;
- while ( (bytesToRead > 0) && (len >= buffer.length)) {
- try {
- len = istream.read(buffer);
- if (bytesToRead >= len) {
- ostream.write(buffer, 0, len);
- bytesToRead -= len;
- } else {
- ostream.write(buffer, 0, (int) bytesToRead);
- bytesToRead = 0;
- }
- } catch (IOException e) {
- exception = e;
- len = -1;
- }
- if (len < buffer.length)
- break;
- }
-
- return exception;
+ if (fileLength == 0)
+ return null;
- }
-
-
- /**
- * Copy the contents of the specified input stream to the specified
- * output stream, and ensure that both streams are closed before returning
- * (even in the face of an exception).
- *
- * @param reader The reader to read from
- * @param writer The writer to write to
- * @param start Start of the range which will be copied
- * @param end End of the range which will be copied
- * @return Exception which occured during processing
- */
- private IOException copyRange(Reader reader, PrintWriter writer,
- long start, long end) {
+ // Retrieving the range header (if any is specified
+ String rangeHeader = request.getHeader("Range");
- try {
- reader.skip(start);
- } catch (IOException e) {
- return e;
+ if (rangeHeader == null)
+ return null;
+ // bytes is the only range unit supported (and I don't see the point
+ // of adding new ones).
+ if (!rangeHeader.startsWith("bytes")) {
+ response.sendError
+ (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+ return null;
}
-
- IOException exception = null;
- long bytesToRead = end - start + 1;
-
- char buffer[] = new char[input];
- int len = buffer.length;
- while ( (bytesToRead > 0) && (len >= buffer.length)) {
- try {
- len = reader.read(buffer);
- if (bytesToRead >= len) {
- writer.write(buffer, 0, len);
- bytesToRead -= len;
- } else {
- writer.write(buffer, 0, (int) bytesToRead);
- bytesToRead = 0;
- }
- } catch (IOException e) {
- exception = e;
- len = -1;
- }
- if (len < buffer.length)
- break;
- }
-
- return exception;
-
- }
-
-
- /**
- * Display the size of a file.
- */
- private void displaySize(StringBuffer buf, int filesize) {
- int leftside = filesize / 1024;
- int rightside = (filesize % 1024) / 103; // makes 1 digit
- // To avoid 0.0 for non-zero file, we bump to 0.1
- if (leftside == 0 && rightside == 0 && filesize != 0)
- rightside = 1;
- buf.append(leftside).append(".").append(rightside);
- buf.append(" KB");
+ rangeHeader = rangeHeader.substring(6);
- }
-
-
- /**
- * Check to see if a default page exists.
- *
- * @param pathname Pathname of the file to be served
- */
- private ResourceInfo checkWelcomeFiles(String pathname,
- DirContext resources) {
+ // Vector which will contain all the ranges which are successfully
+ // parsed.
+ Vector result = new Vector();
+ StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
- String collectionName = pathname;
- if (!pathname.endsWith("/")) {
- collectionName += "/";
- }
+ // Parsing the range list
+ while (commaTokenizer.hasMoreTokens()) {
+ String rangeDefinition = commaTokenizer.nextToken();
- // Refresh our currently defined set of welcome files
- synchronized (welcomes) {
- welcomes = (String[]) getServletContext().getAttribute
- (Globals.WELCOME_FILES_ATTR);
- if (welcomes == null)
- welcomes = new String[0];
- }
-
- // Serve a welcome resource or file if one exists
- for (int i = 0; i < welcomes.length; i++) {
+ Range currentRange = new Range();
+ currentRange.length = fileLength;
- // Does the specified resource exist?
- String resourceName = collectionName + welcomes[i];
- ResourceInfo resourceInfo =
- new ResourceInfo(resourceName, resources);
- if (resourceInfo.exists()) {
- return resourceInfo;
+ int dashPos = rangeDefinition.indexOf('-');
+
+ if (dashPos == -1) {
+ response.sendError
+ (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+ return null;
+ }
+
+ if (dashPos == 0) {
+
+ try {
+ long offset = Long.parseLong(rangeDefinition);
+ currentRange.start = fileLength + offset;
+ currentRange.end = fileLength - 1;
+ } catch (NumberFormatException e) {
+ response.sendError
+ (HttpServletResponse
+ .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+ return null;
+ }
+
+ } else {
+
+ try {
+ currentRange.start = Long.parseLong
+ (rangeDefinition.substring(0, dashPos));
+ if (dashPos < rangeDefinition.length() - 1)
+ currentRange.end = Long.parseLong
+ (rangeDefinition.substring
+ (dashPos + 1, rangeDefinition.length()));
+ else
+ currentRange.end = fileLength - 1;
+ } catch (NumberFormatException e) {
+ response.sendError
+ (HttpServletResponse
+ .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+ return null;
+ }
+
}
+ if (!currentRange.validate()) {
+ response.sendError
+ (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+ return null;
+ }
+
+ result.addElement(currentRange);
}
-
- return null;
+ return result;
}
/**
- * Serve the specified resource, optionally including the data content.
- *
- * @param request The servlet request we are processing
- * @param response The servlet response we are creating
- * @param content Should the content be included?
+ * Return an InputStream to an HTML representation of the contents
+ * of this directory.
*
- * @exception IOException if an input/output error occurs
- * @exception ServletException if a servlet-specified error occurs
+ * @param contextPath Context path to which our internal paths are
+ * relative
*/
- private void serveResource(HttpServletRequest request,
- HttpServletResponse response,
- boolean content)
- throws IOException, ServletException {
+ protected InputStream render
+ (String contextPath, ResourceInfo resourceInfo) {
- // Identify the requested resource path
- String path = getRelativePath(request);
- if (debug > 0) {
- if (content)
- log("DefaultServlet.serveResource: Serving resource '" +
- path + "' headers and data");
- else
- log("DefaultServlet.serveResource: Serving resource '" +
- path + "' headers only");
- }
+ String name = resourceInfo.path;
- // Exclude any resource in the /WEB-INF and /META-INF subdirectories
- // (the "toUpperCase()" avoids problems on Windows systems)
- if ((path == null) ||
- path.toUpperCase().startsWith("/WEB-INF") ||
- path.toUpperCase().startsWith("/META-INF")) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
- return;
- }
-
- // Retrieve the Catalina context and Resources implementation
- DirContext resources = getResources();
- ResourceInfo resourceInfo = new ResourceInfo(path, resources);
+ // Number of characters to trim from the beginnings of filenames
+ int trim = name.length();
+ if (!name.endsWith("/"))
+ trim += 1;
+ if (name.equals("/"))
+ trim = 1;
- if (!resourceInfo.exists) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
- return;
+ // Prepare a writer to a buffered area
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ OutputStreamWriter osWriter = null;
+ try {
+ osWriter = new OutputStreamWriter(stream, "UTF8");
+ } catch (Exception e) {
+ // Should never happen
+ osWriter = new OutputStreamWriter(stream);
}
+ PrintWriter writer = new PrintWriter(osWriter);
- // If the resource is not a collection, and the resource path
- // ends with "/" or "\", return NOT FOUND
- if (!resourceInfo.collection) {
- if (path.endsWith("/") || (path.endsWith("\\"))) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
- return;
- }
+ // Render the page header
+ writer.print("<html>\r\n");
+ writer.print("<head>\r\n");
+ writer.print("<title>");
+ writer.print(sm.getString("directory.title", name));
+ writer.print("</title>\r\n</head>\r\n");
+ writer.print("<body bgcolor=\"white\">\r\n");
+ writer.print("<table width=\"90%\" cellspacing=\"0\"" +
+ " cellpadding=\"5\" align=\"center\">\r\n");
+
+ // Render the in-page title
+ writer.print("<tr><td colspan=\"3\"><font size=\"+2\">\r\n<strong>");
+ writer.print(sm.getString("directory.title", name));
+ writer.print("</strong>\r\n</font></td></tr>\r\n");
+
+ // Render the link to our parent (if required)
+ String parentDirectory = name;
+ if (parentDirectory.endsWith("/")) {
+ parentDirectory =
+ parentDirectory.substring(0, parentDirectory.length() - 1);
}
+ int slash = parentDirectory.lastIndexOf("/");
+ if (slash >= 0) {
+ String parent = name.substring(0, slash);
+ writer.print("<tr><td colspan=\"3\" bgcolor=\"#ffffff\">\r\n");
+ writer.print("<a href=\"");
+ writer.print(rewriteUrl(contextPath));
+ if (parent.equals(""))
+ parent = "/";
+ writer.print(rewriteUrl(parent));
+ if (!parent.endsWith("/"))
+ writer.print("/");
+ writer.print("\">");
+ writer.print(sm.getString("directory.parent", parent));
+ writer.print("</a>\r\n");
+ writer.print("</td></tr>\r\n");
+ }
- // If the resource is a collection (aka a directory), we check
- // the welcome files list.
- if (resourceInfo.collection) {
+ // Render the column headings
+ writer.print("<tr bgcolor=\"#cccccc\">\r\n");
+ writer.print("<td align=\"left\"><font size=\"+1\"><strong>");
+ writer.print(sm.getString("directory.filename"));
+ writer.print("</strong></font></td>\r\n");
+ writer.print("<td align=\"center\"><font size=\"+1\"><strong>");
+ writer.print(sm.getString("directory.size"));
+ writer.print("</strong></font></td>\r\n");
+ writer.print("<td align=\"right\"><font size=\"+1\"><strong>");
+ writer.print(sm.getString("directory.lastModified"));
+ writer.print("</strong></font></td>\r\n");
+ writer.print("</tr>\r\n");
- if (!request.getRequestURI().endsWith("/")) {
- response.sendRedirect(request.getRequestURI() + "/");
- return;
- }
+ try {
- ResourceInfo welcomeFileInfo = checkWelcomeFiles(path, resources);
- if (welcomeFileInfo != null) {
- String redirectPath = welcomeFileInfo.path;
- String contextPath = request.getContextPath();
- if ((contextPath != null) && (!contextPath.equals("/"))) {
- redirectPath = contextPath + redirectPath;
- }
- response.sendRedirect(rewriteUrl(redirectPath));
- return;
- }
-
- }
-
- // Checking If headers
- if ( !checkIfHeaders(request, response, resourceInfo) ) {
- return;
- }
-
- // Find content type.
- String contentType =
- getServletContext().getMimeType(resourceInfo.path);
-
- if (resourceInfo.collection) {
- // Skip directory listings if we have been configured to
- // suppress them
- if (!listings) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND,
- resourceInfo.path);
- return;
- }
- contentType = "text/html;charset=UTF-8";
- }
+ // Render the directory entries within this directory
+ DirContext directory = resourceInfo.directory;
+ NamingEnumeration enum =
+ resourceInfo.resources.list(resourceInfo.path);
+ boolean shade = false;
+ while (enum.hasMoreElements()) {
+ NameClassPair ncPair = (NameClassPair) enum.nextElement();
+ String resourceName = ncPair.getName();
+ ResourceInfo childResourceInfo =
+ new ResourceInfo(resourceName, directory);
- // Parse range specifier
- Vector ranges = null;
- if (!resourceInfo.collection) {
-
- ranges = parseRange(request, response, resourceInfo);
-
- // ETag header
- response.setHeader("ETag", getETag(resourceInfo, true));
-
- }
-
- // Last-Modified header
- if (debug > 0)
- log("DefaultServlet.serveFile: lastModified='" +
- (new Timestamp(resourceInfo.date)).toString() + "'");
- response.setDateHeader("Last-Modified", resourceInfo.date);
-
- ServletOutputStream ostream = null;
- PrintWriter writer = null;
-
- if (content) {
+ String trimmed = resourceName/*.substring(trim)*/;
+ if (trimmed.equalsIgnoreCase("WEB-INF") ||
+ trimmed.equalsIgnoreCase("META-INF"))
+ continue;
- // 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 ( (contentType != null)
- && (contentType.startsWith("text")) ) {
- writer = response.getWriter();
- } else {
- throw e;
- }
- }
+ writer.print("<tr");
+ if (shade)
+ writer.print(" bgcolor=\"eeeeee\"");
+ writer.print(">\r\n");
+ shade = !shade;
- }
-
- if ( (resourceInfo.collection) ||
- ( ((ranges == null) || (ranges.isEmpty()))
- && (request.getHeader("Range") == null) ) ) {
-
- // Set the appropriate output headers
- if (contentType != null) {
- if (debug > 0)
- log("DefaultServlet.serveFile: contentType='" +
- contentType + "'");
- response.setContentType(contentType);
- }
- long contentLength = resourceInfo.length;
- if ((!resourceInfo.collection) && (contentLength >= 0)) {
- if (debug > 0)
- log("DefaultServlet.serveFile: contentLength=" +
- contentLength);
- response.setContentLength((int) contentLength);
- }
-
- if (resourceInfo.collection) {
-
- if (content) {
- // Serve the directory browser
- resourceInfo.setStream
- (render(request.getContextPath(), resourceInfo));
- }
-
- }
-
- // Copy the input stream to our output stream (if requested)
- if (content) {
- try {
- response.setBufferSize(output);
- } catch (IllegalStateException e) {
- // Silent catch
- }
- if (ostream != null) {
- copy(resourceInfo, ostream);
- } else {
- copy(resourceInfo, writer);
- }
- }
-
- } else {
-
- if ((ranges == null) || (ranges.isEmpty()))
- return;
-
- // Partial content response.
-
- response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
-
- if (ranges.size() == 1) {
-
- Range range = (Range) ranges.elementAt(0);
- response.addHeader("Content-Range", "bytes "
- + range.start
- + "-" + range.end + "/"
- + range.length);
- response.setContentLength((int) range.length);
-
- if (contentType != null) {
- if (debug > 0)
- log("DefaultServlet.serveFile: contentType='" +
- contentType + "'");
- response.setContentType(contentType);
- }
-
- if (content) {
- try {
- response.setBufferSize(output);
- } catch (IllegalStateException e) {
- // Silent catch
- }
- if (ostream != null) {
- copy(resourceInfo, ostream, range);
- } else {
- copy(resourceInfo, writer, range);
- }
- }
-
- } else {
-
- response.setContentType("multipart/byteranges; boundary="
- + mimeSeparation);
-
- if (content) {
- try {
- response.setBufferSize(output);
- } catch (IllegalStateException e) {
- // Silent catch
- }
- if (ostream != null) {
- copy(resourceInfo, ostream, ranges.elements(),
- contentType);
- } else {
- copy(resourceInfo, writer, ranges.elements(),
- contentType);
- }
- }
-
+ writer.print("<td align=\"left\"> \r\n");
+ writer.print("<a href=\"");
+ writer.print(rewriteUrl(contextPath));
+ resourceName = rewriteUrl(name + resourceName);
+ writer.print(resourceName);
+ if (childResourceInfo.collection)
+ writer.print("/");
+ writer.print("\"><tt>");
+ writer.print(trimmed);
+
+ if (childResourceInfo.collection)
+ writer.print("/");
+ writer.print("</tt></a></td>\r\n");
+
+ writer.print("<td align=\"right\"><tt>");
+ if (childResourceInfo.collection)
+ writer.print(" ");
+ else
+ writer.print(renderSize(childResourceInfo.length));
+ writer.print("</tt></td>\r\n");
+
+ writer.print("<td align=\"right\"><tt>");
+ writer.print(renderLastModified(childResourceInfo.date));
+ writer.print("</tt></td>\r\n");
+
+ writer.print("</tr>\r\n");
}
-
+
+ } catch (NamingException e) {
+ // Something went wrong
+ e.printStackTrace();
}
-
+
+ // Render the page footer
+ writer.print("<tr><td colspan=\"3\"> </td></tr>\r\n");
+ writer.print("<tr><td colspan=\"3\" bgcolor=\"#cccccc\">");
+ writer.print("<font size=\"-1\">");
+ writer.print(Globals.SERVER_INFO);
+ writer.print("</font></td></tr>\r\n");
+ writer.print("</table>\r\n");
+ writer.print("</body>\r\n");
+ writer.print("</html>\r\n");
+
+ // Return an input stream to the underlying bytes
+ writer.flush();
+ return (new ByteArrayInputStream(stream.toByteArray()));
+
}
/**
- * Parse the range header.
- *
- * @param request The servlet request we are processing
- * @param response The servlet response we are creating
- * @return Vector of ranges
+ * Render the last modified date and time for the specified timestamp.
+ *
+ * @param lastModified Last modified date and time, in milliseconds since
+ * the epoch
*/
- private Vector parseRange(HttpServletRequest request,
- HttpServletResponse response,
- ResourceInfo resourceInfo)
- throws IOException {
-
- // Checking If-Range
- String headerValue = request.getHeader("If-Range");
- if (headerValue != null) {
-
- String eTag = getETag(resourceInfo, true);
- long lastModified = resourceInfo.date;
-
- Date date = null;
-
- // Parsing the HTTP Date
- for (int i = 0; (date == null) && (i < formats.length); i++) {
- try {
- date = formats[i].parse(headerValue);
- } catch (ParseException e) {
- ;
- }
- }
-
- if (date == null) {
-
- // If the ETag the client gave does not match the entity
- // etag, then the entire entity is returned.
- if (!eTag.equals(headerValue.trim()))
- return null;
-
- } else {
-
- // If the timestamp of the entity the client got is older than
- // the last modification date of the entity, the entire entity
- // is returned.
- if (lastModified > (date.getTime() + 1000))
- return null;
-
- }
+ protected String renderLastModified(long lastModified) {
+
+ return (formats[0].format(new Date(lastModified)));
+
+ }
+
+
+ /**
+ * Render the specified file size (in bytes).
+ *
+ * @param size File size (in bytes)
+ */
+ protected String renderSize(long size) {
+
+ long leftSide = size / 1024;
+ long rightSide = (size % 1024) / 103; // Makes 1 digit
+ if ((leftSide == 0) && (rightSide == 0) && (size > 0))
+ rightSide = 1;
+
+ return ("" + leftSide + "." + rightSide + " kb");
+
+ }
+
+
+ // -------------------------------------------------------- Private Methods
+
+
+ /**
+ * Copy the contents of the specified input stream to the specified
+ * output stream, and ensure that both streams are closed before returning
+ * (even in the face of an exception).
+ *
+ * @param istream The input stream to read from
+ * @param ostream The output stream to write to
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream)
+ throws IOException {
+
+ IOException exception = null;
- }
-
- long fileLength = resourceInfo.length;
-
- if (fileLength == 0)
- return null;
+ // FIXME : i18n ?
+ InputStream resourceInputStream = resourceInfo.getStream();
+ InputStream istream = new BufferedInputStream
+ (resourceInputStream, input);
- // Retrieving the range header (if any is specified
- String rangeHeader = request.getHeader("Range");
+ // Copy the input stream to the output stream
+ exception = copyRange(istream, ostream);
- if (rangeHeader == null)
- return null;
- // bytes is the only range unit supported (and I don't see the point
- // of adding new ones).
- if (!rangeHeader.startsWith("bytes")) {
- response.sendError
- (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
- return null;
+ // Clean up the input stream
+ try {
+ istream.close();
+ } catch (Throwable t) {
+ ;
}
-
- rangeHeader = rangeHeader.substring(6);
-
- // Vector which will contain all the ranges which are successfully
- // parsed.
- Vector result = new Vector();
- StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
- // Parsing the range list
- while (commaTokenizer.hasMoreTokens()) {
- String rangeDefinition = commaTokenizer.nextToken();
-
- Range currentRange = new Range();
- currentRange.length = fileLength;
-
- int dashPos = rangeDefinition.indexOf('-');
-
- if (dashPos == -1) {
- response.sendError
- (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
- return null;
- }
-
- if (dashPos == 0) {
-
- try {
- long offset = Long.parseLong(rangeDefinition);
- currentRange.start = fileLength + offset;
- currentRange.end = fileLength - 1;
- } catch (NumberFormatException e) {
- response.sendError
- (HttpServletResponse
- .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
- return null;
- }
-
- } else {
-
- try {
- currentRange.start = Long.parseLong
- (rangeDefinition.substring(0, dashPos));
- if (dashPos < rangeDefinition.length() - 1)
- currentRange.end = Long.parseLong
- (rangeDefinition.substring
- (dashPos + 1, rangeDefinition.length()));
- else
- currentRange.end = fileLength - 1;
- } catch (NumberFormatException e) {
- response.sendError
- (HttpServletResponse
- .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
- return null;
- }
-
- }
-
- if (!currentRange.validate()) {
- response.sendError
- (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
- return null;
- }
+ // Rethrow any exception that has occurred
+ if (exception != null)
+ throw exception;
+
+ }
+
+
+ /**
+ * Copy the contents of the specified input stream to the specified
+ * output stream, and ensure that both streams are closed before returning
+ * (even in the face of an exception).
+ *
+ * @param istream The input stream to read from
+ * @param writer The writer to write to
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ private void copy(ResourceInfo resourceInfo, PrintWriter writer)
+ throws IOException {
+
+ IOException exception = null;
- result.addElement(currentRange);
+ InputStream resourceInputStream = resourceInfo.getStream();
+ // FIXME : i18n ?
+ Reader reader = new InputStreamReader(resourceInputStream);
+
+ // Copy the input stream to the output stream
+ exception = copyRange(reader, writer);
+
+ // Clean up the reader
+ try {
+ reader.close();
+ } catch (Throwable t) {
+ ;
}
- return result;
+ // Rethrow any exception that has occurred
+ if (exception != null)
+ throw exception;
+
}
/**
- * Return an InputStream to an HTML representation of the contents
- * of this directory.
+ * Copy the contents of the specified input stream to the specified
+ * output stream, and ensure that both streams are closed before returning
+ * (even in the face of an exception).
*
- * @param contextPath Context path to which our internal paths are
- * relative
+ * @param resourceInfo The ResourceInfo object
+ * @param ostream The output stream to write to
+ * @param range Range the client wanted to retrieve
+ * @exception IOException if an input/output error occurs
*/
- private InputStream render(String contextPath, ResourceInfo resourceInfo) {
-
- String name = resourceInfo.path;
-
- // Number of characters to trim from the beginnings of filenames
- int trim = name.length();
- if (!name.endsWith("/"))
- trim += 1;
- if (name.equals("/"))
- trim = 1;
-
- // Prepare a writer to a buffered area
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- OutputStreamWriter osWriter = null;
- try {
- osWriter = new OutputStreamWriter(stream, "UTF8");
- } catch (Exception e) {
- // Should never happen
- osWriter = new OutputStreamWriter(stream);
- }
- PrintWriter writer = new PrintWriter(osWriter);
-
- // Render the page header
- writer.print("<html>\r\n");
- writer.print("<head>\r\n");
- writer.print("<title>");
- writer.print(sm.getString("directory.title", name));
- writer.print("</title>\r\n</head>\r\n");
- writer.print("<body bgcolor=\"white\">\r\n");
- writer.print("<table width=\"90%\" cellspacing=\"0\"" +
- " cellpadding=\"5\" align=\"center\">\r\n");
-
- // Render the in-page title
- writer.print("<tr><td colspan=\"3\"><font size=\"+2\">\r\n<strong>");
- writer.print(sm.getString("directory.title", name));
- writer.print("</strong>\r\n</font></td></tr>\r\n");
-
- // Render the link to our parent (if required)
- String parentDirectory = name;
- if (parentDirectory.endsWith("/")) {
- parentDirectory =
- parentDirectory.substring(0, parentDirectory.length() - 1);
- }
- int slash = parentDirectory.lastIndexOf("/");
- if (slash >= 0) {
- String parent = name.substring(0, slash);
- writer.print("<tr><td colspan=\"3\" bgcolor=\"#ffffff\">\r\n");
- writer.print("<a href=\"");
- writer.print(rewriteUrl(contextPath));
- if (parent.equals(""))
- parent = "/";
- writer.print(rewriteUrl(parent));
- if (!parent.endsWith("/"))
- writer.print("/");
- writer.print("\">");
- writer.print(sm.getString("directory.parent", parent));
- writer.print("</a>\r\n");
- writer.print("</td></tr>\r\n");
+ private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream,
+ Range range)
+ throws IOException {
+
+ IOException exception = null;
+
+ InputStream resourceInputStream = resourceInfo.getStream();
+ InputStream istream =
+ new BufferedInputStream(resourceInputStream, input);
+ exception = copyRange(istream, ostream, range.start, range.end);
+
+ // Clean up the input stream
+ try {
+ istream.close();
+ } catch (Throwable t) {
+ ;
}
-
- // Render the column headings
- writer.print("<tr bgcolor=\"#cccccc\">\r\n");
- writer.print("<td align=\"left\"><font size=\"+1\"><strong>");
- writer.print(sm.getString("directory.filename"));
- writer.print("</strong></font></td>\r\n");
- writer.print("<td align=\"center\"><font size=\"+1\"><strong>");
- writer.print(sm.getString("directory.size"));
- writer.print("</strong></font></td>\r\n");
- writer.print("<td align=\"right\"><font size=\"+1\"><strong>");
- writer.print(sm.getString("directory.lastModified"));
- writer.print("</strong></font></td>\r\n");
- writer.print("</tr>\r\n");
-
- try {
-
- // Render the directory entries within this directory
- DirContext directory = resourceInfo.directory;
- NamingEnumeration enum =
- resourceInfo.resources.list(resourceInfo.path);
- boolean shade = false;
- while (enum.hasMoreElements()) {
- NameClassPair ncPair = (NameClassPair) enum.nextElement();
- String resourceName = ncPair.getName();
- ResourceInfo childResourceInfo =
- new ResourceInfo(resourceName, directory);
+ // Rethrow any exception that has occurred
+ if (exception != null)
+ throw exception;
+
+ }
- String trimmed = resourceName/*.substring(trim)*/;
- if (trimmed.equalsIgnoreCase("WEB-INF") ||
- trimmed.equalsIgnoreCase("META-INF"))
- continue;
- writer.print("<tr");
- if (shade)
- writer.print(" bgcolor=\"eeeeee\"");
- writer.print(">\r\n");
- shade = !shade;
+ /**
+ * Copy the contents of the specified input stream to the specified
+ * output stream, and ensure that both streams are closed before returning
+ * (even in the face of an exception).
+ *
+ * @param resourceInfo The ResourceInfo object
+ * @param writer The writer to write to
+ * @param range Range the client wanted to retrieve
+ * @exception IOException if an input/output error occurs
+ */
+ private void copy(ResourceInfo resourceInfo, PrintWriter writer,
+ Range range)
+ throws IOException {
+
+ IOException exception = null;
+
+ InputStream resourceInputStream = resourceInfo.getStream();
+ Reader reader = new InputStreamReader(resourceInputStream);
+ exception = copyRange(reader, writer, range.start, range.end);
+
+ // Clean up the input stream
+ try {
+ reader.close();
+ } catch (Throwable t) {
+ ;
+ }
- writer.print("<td align=\"left\"> \r\n");
- writer.print("<a href=\"");
- writer.print(rewriteUrl(contextPath));
- resourceName = rewriteUrl(name + resourceName);
- writer.print(resourceName);
- if (childResourceInfo.collection)
- writer.print("/");
- writer.print("\"><tt>");
- writer.print(trimmed);
+ // Rethrow any exception that has occurred
+ if (exception != null)
+ throw exception;
+
+ }
- if (childResourceInfo.collection)
- writer.print("/");
- writer.print("</tt></a></td>\r\n");
- writer.print("<td align=\"right\"><tt>");
- if (childResourceInfo.collection)
- writer.print(" ");
- else
- writer.print(renderSize(childResourceInfo.length));
- writer.print("</tt></td>\r\n");
+ /**
+ * Copy the contents of the specified input stream to the specified
+ * output stream, and ensure that both streams are closed before returning
+ * (even in the face of an exception).
+ *
+ * @param resourceInfo The ResourceInfo object
+ * @param ostream The output stream to write to
+ * @param ranges Enumeration of the ranges the client wanted to retrieve
+ * @param contentType Content type of the resource
+ * @exception IOException if an input/output error occurs
+ */
+ private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream,
+ Enumeration ranges, String contentType)
+ throws IOException {
+
+ IOException exception = null;
+
+ while ( (exception == null) && (ranges.hasMoreElements()) ) {
+
+ InputStream resourceInputStream = resourceInfo.getStream();
+ InputStream istream = // FIXME: internationalization???????
+ new BufferedInputStream(resourceInputStream, input);
+
+ Range currentRange = (Range) ranges.nextElement();
+
+ // Writing MIME header.
+ ostream.println("--" + mimeSeparation);
+ if (contentType != null)
+ ostream.println("Content-Type: " + contentType);
+ ostream.println("Content-Range: bytes " + currentRange.start
+ + "-" + currentRange.end + "/"
+ + currentRange.length);
+ ostream.println();
+
+ // Printing content
+ exception = copyRange(istream, ostream, currentRange.start,
+ currentRange.end);
+
+ try {
+ istream.close();
+ } catch (Throwable t) {
+ ;
+ }
+
+ }
+
+ ostream.print("--" + mimeSeparation + "--");
+
+ // Rethrow any exception that has occurred
+ if (exception != null)
+ throw exception;
+
+ }
- writer.print("<td align=\"right\"><tt>");
- writer.print(renderLastModified(childResourceInfo.date));
- writer.print("</tt></td>\r\n");
- writer.print("</tr>\r\n");
+ /**
+ * Copy the contents of the specified input stream to the specified
+ * output stream, and ensure that both streams are closed before returning
+ * (even in the face of an exception).
+ *
+ * @param resourceInfo The ResourceInfo object
+ * @param writer The writer to write to
+ * @param ranges Enumeration of the ranges the client wanted to retrieve
+ * @param contentType Content type of the resource
+ * @exception IOException if an input/output error occurs
+ */
+ private void copy(ResourceInfo resourceInfo, PrintWriter writer,
+ Enumeration ranges, String contentType)
+ throws IOException {
+
+ IOException exception = null;
+
+ while ( (exception == null) && (ranges.hasMoreElements()) ) {
+
+ InputStream resourceInputStream = resourceInfo.getStream();
+ Reader reader = new InputStreamReader(resourceInputStream);
+
+ Range currentRange = (Range) ranges.nextElement();
+
+ // Writing MIME header.
+ writer.println("--" + mimeSeparation);
+ if (contentType != null)
+ writer.println("Content-Type: " + contentType);
+ writer.println("Content-Range: bytes " + currentRange.start
+ + "-" + currentRange.end + "/"
+ + currentRange.length);
+ writer.println();
+
+ // Printing content
+ exception = copyRange(reader, writer, currentRange.start,
+ currentRange.end);
+
+ try {
+ reader.close();
+ } catch (Throwable t) {
+ ;
}
-
- } catch (NamingException e) {
- // Something went wrong
- e.printStackTrace();
+
}
+
+ writer.print("--" + mimeSeparation + "--");
+
+ // Rethrow any exception that has occurred
+ if (exception != null)
+ throw exception;
+
+ }
- // Render the page footer
- writer.print("<tr><td colspan=\"3\"> </td></tr>\r\n");
- writer.print("<tr><td colspan=\"3\" bgcolor=\"#cccccc\">");
- writer.print("<font size=\"-1\">");
- writer.print(Globals.SERVER_INFO);
- writer.print("</font></td></tr>\r\n");
- writer.print("</table>\r\n");
- writer.print("</body>\r\n");
- writer.print("</html>\r\n");
-
- // Return an input stream to the underlying bytes
- writer.flush();
- return (new ByteArrayInputStream(stream.toByteArray()));
+ /**
+ * Copy the contents of the specified input stream to the specified
+ * output stream, and ensure that both streams are closed before returning
+ * (even in the face of an exception).
+ *
+ * @param istream The input stream to read from
+ * @param ostream The output stream to write to
+ * @return Exception which occured during processing
+ */
+ private IOException copyRange(InputStream istream,
+ ServletOutputStream ostream) {
+
+ // Copy the input stream to the output stream
+ IOException exception = null;
+ byte buffer[] = new byte[input];
+ int len = buffer.length;
+ while (true) {
+ try {
+ len = istream.read(buffer);
+ if (len == -1)
+ break;
+ ostream.write(buffer, 0, len);
+ } catch (IOException e) {
+ exception = e;
+ len = -1;
+ break;
+ }
+ }
+ return exception;
+
}
/**
- * Render the last modified date and time for the specified timestamp.
+ * Copy the contents of the specified input stream to the specified
+ * output stream, and ensure that both streams are closed before returning
+ * (even in the face of an exception).
*
- * @param lastModified Last modified date and time, in milliseconds since
- * the epoch
+ * @param reader The reader to read from
+ * @param writer The writer to write to
+ * @return Exception which occured during processing
*/
- private String renderLastModified(long lastModified) {
+ private IOException copyRange(Reader reader, PrintWriter writer) {
+
+ // Copy the input stream to the output stream
+ IOException exception = null;
+ char buffer[] = new char[input];
+ int len = buffer.length;
+ while (true) {
+ try {
+ len = reader.read(buffer);
+ if (len == -1)
+ break;
+ writer.write(buffer, 0, len);
+ } catch (IOException e) {
+ exception = e;
+ len = -1;
+ break;
+ }
+ }
+ return exception;
+
+ }
- return (formats[0].format(new Date(lastModified)));
+ /**
+ * Copy the contents of the specified input stream to the specified
+ * output stream, and ensure that both streams are closed before returning
+ * (even in the face of an exception).
+ *
+ * @param istream The input stream to read from
+ * @param ostream The output stream to write to
+ * @param start Start of the range which will be copied
+ * @param end End of the range which will be copied
+ * @return Exception which occured during processing
+ */
+ private IOException copyRange(InputStream istream,
+ ServletOutputStream ostream,
+ long start, long end) {
+
+ if (debug > 10)
+ System.out.println("Serving bytes:" + start + "-" + end);
+
+ try {
+ istream.skip(start);
+ } catch (IOException e) {
+ return e;
+ }
+
+ IOException exception = null;
+ long bytesToRead = end - start + 1;
+
+ byte buffer[] = new byte[input];
+ int len = buffer.length;
+ while ( (bytesToRead > 0) && (len >= buffer.length)) {
+ try {
+ len = istream.read(buffer);
+ if (bytesToRead >= len) {
+ ostream.write(buffer, 0, len);
+ bytesToRead -= len;
+ } else {
+ ostream.write(buffer, 0, (int) bytesToRead);
+ bytesToRead = 0;
+ }
+ } catch (IOException e) {
+ exception = e;
+ len = -1;
+ }
+ if (len < buffer.length)
+ break;
+ }
+
+ return exception;
+
}
/**
- * Render the specified file size (in bytes).
+ * Copy the contents of the specified input stream to the specified
+ * output stream, and ensure that both streams are closed before returning
+ * (even in the face of an exception).
*
- * @param size File size (in bytes)
+ * @param reader The reader to read from
+ * @param writer The writer to write to
+ * @param start Start of the range which will be copied
+ * @param end End of the range which will be copied
+ * @return Exception which occured during processing
*/
- private String renderSize(long size) {
+ private IOException copyRange(Reader reader, PrintWriter writer,
+ long start, long end) {
+
+ try {
+ reader.skip(start);
+ } catch (IOException e) {
+ return e;
+ }
+
+ IOException exception = null;
+ long bytesToRead = end - start + 1;
+
+ char buffer[] = new char[input];
+ int len = buffer.length;
+ while ( (bytesToRead > 0) && (len >= buffer.length)) {
+ try {
+ len = reader.read(buffer);
+ if (bytesToRead >= len) {
+ writer.write(buffer, 0, len);
+ bytesToRead -= len;
+ } else {
+ writer.write(buffer, 0, (int) bytesToRead);
+ bytesToRead = 0;
+ }
+ } catch (IOException e) {
+ exception = e;
+ len = -1;
+ }
+ if (len < buffer.length)
+ break;
+ }
+
+ return exception;
+
+ }
- long leftSide = size / 1024;
- long rightSide = (size % 1024) / 103; // Makes 1 digit
- if ((leftSide == 0) && (rightSide == 0) && (size > 0))
- rightSide = 1;
- return ("" + leftSide + "." + rightSide + " kb");
+ /**
+ * Check to see if a default page exists.
+ *
+ * @param pathname Pathname of the file to be served
+ */
+ private ResourceInfo checkWelcomeFiles(String pathname,
+ DirContext resources) {
+
+ String collectionName = pathname;
+ if (!pathname.endsWith("/")) {
+ collectionName += "/";
+ }
+
+ // Refresh our currently defined set of welcome files
+ synchronized (welcomes) {
+ welcomes = (String[]) getServletContext().getAttribute
+ (Globals.WELCOME_FILES_ATTR);
+ if (welcomes == null)
+ welcomes = new String[0];
+ }
+ // Serve a welcome resource or file if one exists
+ for (int i = 0; i < welcomes.length; i++) {
+
+ // Does the specified resource exist?
+ String resourceName = collectionName + welcomes[i];
+ ResourceInfo resourceInfo =
+ new ResourceInfo(resourceName, resources);
+ if (resourceInfo.exists()) {
+ return resourceInfo;
+ }
+
+ }
+
+ return null;
+
}