remm 01/11/28 13:10:45 Modified: catalina/src/share/org/apache/catalina/servlets DefaultServlet.java Log: - Add support for upload resuming, which can be useful when uploading large files over the internet. - Based on a patch submitted to the Slide project by Mike Gartrell <mike_gartrell at hp.com> Revision Changes Path 1.45 +165 -13 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.44 retrieving revision 1.45 diff -u -r1.44 -r1.45 --- DefaultServlet.java 2001/11/16 22:53:01 1.44 +++ DefaultServlet.java 2001/11/28 21:10:45 1.45 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/DefaultServlet.java,v 1.44 2001/11/16 22:53:01 patrickl Exp $ - * $Revision: 1.44 $ - * $Date: 2001/11/16 22:53:01 $ + * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/DefaultServlet.java,v 1.45 2001/11/28 21:10:45 remm Exp $ + * $Revision: 1.45 $ + * $Date: 2001/11/28 21:10:45 $ * * ==================================================================== * @@ -73,6 +73,7 @@ import java.io.InputStream; import java.io.IOException; import java.io.PrintWriter; +import java.io.RandomAccessFile; import java.io.Reader; import java.io.InputStreamReader; import java.io.Writer; @@ -123,7 +124,7 @@ * * @author Craig R. McClanahan * @author Remy Maucherat - * @version $Revision: 1.44 $ $Date: 2001/11/16 22:53:01 $ + * @version $Revision: 1.45 $ $Date: 2001/11/28 21:10:45 $ */ public class DefaultServlet @@ -234,6 +235,12 @@ 'A', 'B', 'C', 'D', 'E', 'F'}; + /** + * Size of file transfer buffer in bytes. + */ + private static final int BUFFER_SIZE = 4096; + + // ----------------------------------------------------- Static Initializer @@ -582,12 +589,6 @@ return; } - // Looking for a Content-Range header - if (req.getHeader("Content-Range") != null) { - // No content range header is supported - resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); - } - // Retrieve the resources DirContext resources = getResources(); @@ -604,8 +605,31 @@ } boolean result = true; + + // Temp. content file used to support partial PUT + File contentFile = null; + + // Input stream for temp. content file used to support partial PUT + FileInputStream contentFileInStream = null; + + ResourceInfo resourceInfo = new ResourceInfo(path, resources); + Range range = parseContentRange(req, resp); + + InputStream resourceInputStream = null; + + // Append data specified in ranges to existing content for this + // resource - create a temp. file on the local filesystem to + // perform this operation + // Assume just one range is specified for now + if (range != null) { + contentFile = executePartialPut(req, range, path); + resourceInputStream = new FileInputStream(contentFile); + } else { + resourceInputStream = req.getInputStream(); + } + try { - Resource newResource = new Resource(req.getInputStream()); + Resource newResource = new Resource(resourceInputStream); // FIXME: Add attributes if (exists) { resources.rebind(path, newResource); @@ -630,6 +654,73 @@ /** + * Handle a partial PUT. New content specified in request is appended to + * existing content in oldRevisionContent (if present). This code does + * not support simultaneous partial updates to the same resource. + */ + protected File executePartialPut(HttpServletRequest req, Range range, + String path) + throws IOException { + + // Append data specified in ranges to existing content for this + // resource - create a temp. file on the local filesystem to + // perform this operation + File tempDir = (File) getServletContext().getAttribute + ("javax.servlet.context.tempdir"); + // Convert all '/' characters to '.' in resourcePath + String convertedResourcePath = path.replace('/', '.'); + File contentFile = new File(tempDir, convertedResourcePath); + if (contentFile.createNewFile()) { + // Clean up contentFile when Tomcat is terminated + contentFile.deleteOnExit(); + } + + RandomAccessFile randAccessContentFile = + new RandomAccessFile(contentFile, "rw"); + + Resource oldResource = null; + try { + Object obj = getResources().lookup(path); + if (obj instanceof Resource) + oldResource = (Resource) obj; + } catch (NamingException e) { + } + + // Copy data in oldRevisionContent to contentFile + if (oldResource != null) { + BufferedInputStream bufOldRevStream = + new BufferedInputStream(oldResource.streamContent(), + BUFFER_SIZE); + + int numBytesRead; + byte[] copyBuffer = new byte[BUFFER_SIZE]; + while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) { + randAccessContentFile.write(copyBuffer, 0, numBytesRead); + } + + bufOldRevStream.close(); + } + + randAccessContentFile.setLength(range.length); + + // Append data in request input stream to contentFile + randAccessContentFile.seek(range.start); + int numBytesRead; + byte[] transferBuffer = new byte[BUFFER_SIZE]; + BufferedInputStream requestBufInStream = + new BufferedInputStream(req.getInputStream(), BUFFER_SIZE); + while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) { + randAccessContentFile.write(transferBuffer, 0, numBytesRead); + } + randAccessContentFile.close(); + requestBufInStream.close(); + + return contentFile; + + } + + + /** * Process a POST request for the specified resource. * * @param request The servlet request we are processing @@ -1264,6 +1355,67 @@ /** + * Parse the content-range header. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @return Range + */ + protected Range parseContentRange(HttpServletRequest request, + HttpServletResponse response) + throws IOException { + + // Retrieving the content-range header (if any is specified + String rangeHeader = request.getHeader("Content-Range"); + + if (rangeHeader == null) + return null; + + // bytes is the only range unit supported + if (!rangeHeader.startsWith("bytes")) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + rangeHeader = rangeHeader.substring(6).trim(); + + int dashPos = rangeHeader.indexOf('-'); + int slashPos = rangeHeader.indexOf('/'); + + if (dashPos == -1) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + if (slashPos == -1) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + Range range = new Range(); + + try { + range.start = Long.parseLong(rangeHeader.substring(0, dashPos)); + range.end = + Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos)); + range.length = Long.parseLong + (rangeHeader.substring(slashPos + 1, rangeHeader.length())); + } catch (NumberFormatException e) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + if (!range.validate()) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + return range; + + } + + + /** * Parse the range header. * * @param request The servlet request we are processing @@ -2084,8 +2236,8 @@ * Validate range. */ public boolean validate() { - return ( (start >= 0) && (end >= 0) && (length > 0) - && (start <= end) && (end < length) ); + return ( (start >= 0) && (end >= 0) && (start <= end) + && (length > 0) && (end < length) ); } public void recycle() {
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>