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]>