[jboss-cvs] jboss-seam/src/main/org/jboss/seam/servlet ...
Shane Bryzak
Shane_Bryzak at symantec.com
Sun Feb 4 03:11:34 EST 2007
User: sbryzak2
Date: 07/02/04 03:11:34
Modified: src/main/org/jboss/seam/servlet MultipartRequest.java
SeamMultipartFilter.java
Log:
JBSEAM-705, JBSEAM-706 file upload control improvements
Revision Changes Path
1.4 +375 -273 jboss-seam/src/main/org/jboss/seam/servlet/MultipartRequest.java
(In the diff below, changes in quantity of whitespace are not shown.)
Index: MultipartRequest.java
===================================================================
RCS file: /cvsroot/jboss/jboss-seam/src/main/org/jboss/seam/servlet/MultipartRequest.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -b -r1.3 -r1.4
--- MultipartRequest.java 30 Jan 2007 10:47:23 -0000 1.3
+++ MultipartRequest.java 4 Feb 2007 08:11:34 -0000 1.4
@@ -1,8 +1,15 @@
package org.jboss.seam.servlet;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
+import java.rmi.server.UID;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
@@ -24,31 +31,108 @@
{
public static final String WWW_FORM_URLENCODED_TYPE = "application/x-www-form-urlencoded";
- private class PartWrapper
+ private static final String PARAM_NAME = "name";
+ private static final String PARAM_FILENAME = "filename";
+ private static final String PARAM_CONTENT_TYPE = "Content-Type";
+
+ private static final int BUFFER_SIZE = 2048;
+ private static final int CHUNK_SIZE = 512;
+
+ private boolean createTempFiles = false;
+
+ private Map<String,Param> parameters = null;
+
+ private enum ReadState { BOUNDARY, HEADERS, DATA }
+
+ private static final byte CR = 0x0d;
+ private static final byte LF = 0x0a;
+ private static final byte[] CR_LF = {CR,LF};
+
+
+ private abstract class Param
{
- private Map<String,Object> params;
- private byte[] data;
- private String contentType;
- private String fileName;
+ private String name;
- public PartWrapper()
+ public Param(String name)
{
- params = new HashMap<String,Object>();
+ this.name = name;
}
- public Map<String,Object> getParams()
+ public String getName()
{
- return params;
+ return name;
}
- public void setData(byte[] data)
+ public abstract void appendData(byte[] data, int start, int length)
+ throws IOException;
+ }
+
+ private class ValueParam extends Param
+ {
+ private Object value = null;
+ private ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ public ValueParam(String name)
{
- this.data = data;
+ super(name);
}
- public byte[] getData()
+ @Override
+ public void appendData(byte[] data, int start, int length)
+ throws IOException
+ {
+ buf.write(data, start, length);
+ }
+
+ public void complete()
+ {
+ String val = new String(buf.toByteArray());
+ if (value == null)
+ {
+ value = val;
+ }
+ else
+ {
+ if (!(value instanceof List))
+ {
+ List<String> v = new ArrayList<String>();
+ v.add((String) value);
+ value = v;
+ }
+
+ ((List) value).add(val);
+ }
+ buf.reset();
+ }
+
+ public Object getValue()
+ {
+ return value;
+ }
+ }
+
+ private class FileParam extends Param
+ {
+ private String filename;
+ private String contentType;
+
+ private ByteArrayOutputStream bOut = null;
+ private FileOutputStream fOut = null;
+ private File tempFile = null;
+
+ public FileParam(String name)
+ {
+ super(name);
+ }
+
+ public String getFilename()
+ {
+ return filename;
+ }
+
+ public void setFilename(String filename)
{
- return data;
+ this.filename = filename;
}
public String getContentType()
@@ -61,32 +145,97 @@
this.contentType = contentType;
}
- public String getFileName()
+ public void createTempFile()
{
- return fileName;
+ try
+ {
+ tempFile = File.createTempFile(new UID().toString().replace(":", "-"), ".upload");
+ tempFile.deleteOnExit();
+ fOut = new FileOutputStream(tempFile);
+ }
+ catch (IOException ex)
+ {
+ throw new RuntimeException("Could not create temporary file");
+ }
}
- public void setFileName(String fileName)
+ @Override
+ public void appendData(byte[] data, int start, int length)
+ throws IOException
{
- this.fileName = fileName;
+ if (fOut != null)
+ {
+ fOut.write(data, start, length);
+ }
+ else
+ {
+ if (bOut == null) bOut = new ByteArrayOutputStream();
+ bOut.write(data, start, length);
}
}
- private HttpServletRequest request;
+ public byte[] getData()
+ {
+ if (bOut != null)
+ {
+ return bOut.toByteArray();
+ }
+ else if (tempFile != null)
+ {
+ if (tempFile.exists())
+ {
+ try
+ {
+ FileInputStream fIn = new FileInputStream(tempFile);
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ byte[] buf = new byte[512];
+ int read = fIn.read(buf);
+ while (read != -1)
+ {
+ bOut.write(buf, 0, read);
+ read = fIn.read(buf);
+ }
+ bOut.flush();
+ return bOut.toByteArray();
+ }
+ catch (IOException ex) { /* too bad? */}
+ }
+ }
- private Map<String,PartWrapper> parameters;
+ return null;
+ }
- public MultipartRequest(HttpServletRequest request)
+ public InputStream getInputStream()
+ {
+ if (bOut != null)
+ {
+ return new ByteArrayInputStream(bOut.toByteArray());
+ }
+ else
+ {
+ try
+ {
+ return new FileInputStream(tempFile);
+ }
+ catch (FileNotFoundException ex) { }
+ }
+
+ return null;
+ }
+ }
+
+ private HttpServletRequest request;
+
+ public MultipartRequest(HttpServletRequest request, boolean createTempFiles)
{
super(request);
this.request = request;
+ this.createTempFiles = createTempFiles;
}
private void parseRequest()
{
- parameters = new HashMap<String,PartWrapper>();
-
- boundaryMarker = getBoundary(request.getContentType());
+ byte[] boundaryMarker = getBoundaryMarker(request.getContentType());
if (boundaryMarker == null)
{
throw new RuntimeException("the request was rejected because "
@@ -95,181 +244,147 @@
// String charEncoding = request.getCharacterEncoding();
+ parameters = new HashMap<String,Param>();
+
try
{
- buffer = new byte[4096];
- bufferEnd = 0;
- input = request.getInputStream();
+ byte[] buffer = new byte[BUFFER_SIZE];
+ Map<String,String> headers = new HashMap<String,String>();
- pos = 0;
+ ReadState readState = ReadState.BOUNDARY;
- fillBuffer();
+ InputStream input = request.getInputStream();
+ int read = input.read(buffer);
+ int pos = 0;
- byte[] boundary = readNextBoundary();
+ Param p = null;
- while (boundary != null || fillBuffer())
+ while (read != -1)
{
- if (boundary != null)
- parseBoundary(boundary);
- boundary = readNextBoundary();
- }
- }
- catch (IOException ex)
+ for (int i = 0; i < read; i++)
{
-
+ switch (readState)
+ {
+ case BOUNDARY:
+ {
+ if (checkSequence(buffer, i, boundaryMarker) && checkSequence(buffer, i + 2, CR_LF))
+ {
+ readState = ReadState.HEADERS;
+ i += 2;
+ pos = i + 1;
}
+ break;
}
-
- private static final String FILE_CONTENT_TYPE = "Content-Type";
- private static final String FILE_NAME = "filename";
-
- private void parseBoundary(byte[] boundary)
+ case HEADERS:
{
- PartWrapper entry = new PartWrapper();
-
- int start = 0;
+ if (checkSequence(buffer, i, CR_LF))
+ {
+ parseParams(new String(buffer, pos, i - pos - 1), ";", headers);
+ if (checkSequence(buffer, i + CR_LF.length, CR_LF))
+ {
+ readState = ReadState.DATA;
+ i += CR_LF.length;
+ pos = i + 1;
- for (int i = 0; i < boundary.length; i++)
+ String paramName = headers.get(PARAM_NAME);
+ if (paramName != null)
{
- if (checkSequence(boundary, i, CR_LF))
+ if (headers.containsKey(PARAM_FILENAME))
{
- if (start < i - CR_LF.length)
+ FileParam fp = new FileParam(paramName);
+ if (createTempFiles) fp.createTempFile();
+ fp.setContentType(headers.get(PARAM_CONTENT_TYPE));
+ fp.setFilename(headers.get(PARAM_FILENAME));
+ p = fp;
+ }
+ else
{
- String line = new String(boundary, start, i - CR_LF.length - start);
- parseParams(line, ";", entry.getParams());
- start = i;
+ p = new ValueParam(paramName);
+ }
+
+ parameters.put(paramName, p);
}
- if (checkSequence(boundary, i + CR_LF.length, CR_LF))
+ headers.clear();
+ }
+ else
{
- start = i + CR_LF.length;
- break;
+ pos = i + 1;
}
}
+ break;
}
-
- // Extract the content type and filename
- for (String key : entry.getParams().keySet())
+ case DATA:
{
- Object val = entry.getParams().get(key);
- if (val instanceof String)
+ if (checkSequence(buffer, i - boundaryMarker.length - CR_LF.length, CR_LF) &&
+ checkSequence(buffer, i, boundaryMarker))
{
- String s = (String) entry.getParams().get(key);
- if (s != null)
+ if (pos < i - boundaryMarker.length - CR_LF.length - 1)
{
- if (entry.getContentType() == null && FILE_CONTENT_TYPE.equalsIgnoreCase(key))
- entry.setContentType(s);
- else if (entry.getFileName() == null && FILE_NAME.equalsIgnoreCase(key))
- entry.setFileName(s);
- }
- }
+ p.appendData(buffer, pos, i - pos - boundaryMarker.length - CR_LF.length - 1);
}
- byte[] data = new byte[boundary.length - start];
- System.arraycopy(boundary, start, data, 0, data.length);
- entry.setData(data);
+ if (p instanceof ValueParam) ((ValueParam) p).complete();
- if (entry.getParams().containsKey("name"))
- parameters.put((String) entry.getParams().get("name"), entry);
+ if (checkSequence(buffer, i + CR_LF.length, CR_LF))
+ {
+ i += CR_LF.length;
+ pos = i + 1;
}
-
- /**
- * The boundary marker bytes. Each part is separated by a boundary marker
- */
- private byte[] boundaryMarker;
-
- /**
- *
- */
- private InputStream input;
-
- /**
- * Read buffer
- */
- private byte[] buffer;
-
- /**
- * The read position in the buffer
- */
- private int pos;
-
- /**
- * Total bytes read
- */
- private int totalRead;
-
- /**
- * The last written byte position in the buffer
- */
- private int bufferEnd;
-
- /**
- * Reads more data into the buffer if possible, shuffling the buffer
- * contents if required
- *
- */
- private boolean fillBuffer()
- throws IOException
+ else
{
- if (totalRead >= request.getContentLength())
- return false;
+ pos = i;
+ }
- // If pos > 0, move the contents to the front of the buffer to make space
- if (pos > 0)
+ readState = ReadState.HEADERS;
+ }
+ else if (i > (pos + boundaryMarker.length + CHUNK_SIZE + CR_LF.length))
{
- System.arraycopy(buffer, pos, buffer, 0, bufferEnd - pos);
- bufferEnd -= pos;
- pos = 0;
+ p.appendData(buffer, pos, CHUNK_SIZE);
+ pos += CHUNK_SIZE;
+ }
+ break;
+ }
+ }
}
- // If the buffer is full, extend it
- if (pos == 0 && bufferEnd >= buffer.length - 1)
+ if (pos < read)
{
- byte[] newBuffer = new byte[buffer.length * 2];
- System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
- buffer = newBuffer;
+ // move the bytes that weren't read to the start of the buffer
+ int bytesNotRead = read - pos;
+ System.arraycopy(buffer, pos, buffer, 0, bytesNotRead);
+ read = input.read(buffer, bytesNotRead, buffer.length - bytesNotRead);
+ read += bytesNotRead;
}
-
- int read = input.read(buffer, bufferEnd, buffer.length - bufferEnd);
-
- if (read != -1)
+ else
{
- bufferEnd += read;
- totalRead += read;
+ read = input.read(buffer);
}
- return read != -1;
+ pos = 0;
+ }
+ }
+ catch (IOException ex)
+ {
+ throw new RuntimeException("IO Error parsing multipart request", ex);
+ }
}
- private static final byte CR = 0x0d;
- private static final byte LF = 0x0a;
- private static final byte[] CR_LF = {CR,LF};
-
- private byte[] readNextBoundary()
- throws IOException
+ private byte[] getBoundaryMarker(String contentType)
{
+ Map<String, Object> params = parseParams(contentType, ";");
+ String boundaryStr = (String) params.get("boundary");
- int boundaryStart = -1;
+ if (boundaryStr == null) return null;
- for (int i = pos; i < bufferEnd; i++)
- {
- if (boundaryStart == -1 && checkSequence(buffer, i, boundaryMarker) &&
- checkSequence(buffer, i + CR_LF.length, CR_LF))
+ try
{
- // First boundary marker
- boundaryStart = i + CR_LF.length;
+ return boundaryStr.getBytes("ISO-8859-1");
}
- else if (boundaryStart != -1 && checkSequence(buffer, i, boundaryMarker))
+ catch (UnsupportedEncodingException e)
{
- // Second boundary marker
- byte[] boundary = new byte[i - boundaryMarker.length - boundaryStart - 4];
- System.arraycopy(buffer, boundaryStart, boundary, 0, boundary.length);
- pos = i - boundaryMarker.length;
- return boundary;
- }
+ return boundaryStr.getBytes();
}
-
- return null;
}
/**
@@ -288,34 +403,24 @@
for (int i = 0; i < seq.length; i++)
{
- if (data[pos - seq.length + i] != seq[i])
+ if (data[(pos - seq.length) + i + 1] != seq[i])
return false;
}
return true;
}
- private byte[] getBoundary(String contentType)
- {
- Map<String, Object> params = parseParams(contentType, ";");
- String boundaryStr = (String) params.get("boundary");
-
- if (boundaryStr == null) return null;
+ private static final Pattern PARAM_VALUE_PATTERN = Pattern
+ .compile("^\\s*([^\\s=]+)\\s*[=:]\\s*([^\\s]+)\\s*$");
- try
+ private Map parseParams(String paramStr, String separator)
{
- return boundaryStr.getBytes("ISO-8859-1");
- }
- catch (UnsupportedEncodingException e)
- {
- return boundaryStr.getBytes();
- }
+ Map<String,String> paramMap = new HashMap<String, String>();
+ parseParams(paramStr, separator, paramMap);
+ return paramMap;
}
- private static final Pattern PARAM_VALUE_PATTERN = Pattern
- .compile("^\\s*([^\\s=]+)\\s*[=:]\\s*([^\\s]+)\\s*$");
-
- private void parseParams(String paramStr, String separator, Map<String,Object> target)
+ private void parseParams(String paramStr, String separator, Map paramMap)
{
String[] parts = paramStr.split("[" + separator + "]");
@@ -331,37 +436,16 @@
if (value.startsWith("\"") && value.endsWith("\""))
value = value.substring(1, value.length() - 1);
- if (target.containsKey(key))
- {
- Object v = target.get(key);
- if (v instanceof List)
- ((List) v).add(value);
- else if (v instanceof String)
- {
- List<String> vals = new ArrayList<String>();
- vals.add((String) v);
- vals.add(value);
- target.put(key, value);
- }
- else
- {
- List vals = new ArrayList();
- vals.add(v);
- vals.add(value);
- target.put(key, value);
- }
- }
- else
- target.put(key, value);
+ paramMap.put(key, value);
}
}
}
- private Map<String, Object> parseParams(String paramStr, String separator)
+ private Param getParam(String name)
{
- Map<String, Object> target = new HashMap<String, Object>();
- parseParams(paramStr, separator, target);
- return target;
+ if (parameters == null)
+ parseRequest();
+ return parameters.get(name);
}
@Override
@@ -375,59 +459,72 @@
public byte[] getFileBytes(String name)
{
- if (parameters == null)
- parseRequest();
+ Param p = getParam(name);
+ return (p != null && p instanceof FileParam) ?
+ ((FileParam) p).getData() : null;
+ }
- PartWrapper wrapper = parameters.get(name);
- return wrapper != null ? wrapper.getData() : null;
+ public InputStream getFileInputStream(String name)
+ {
+ Param p = getParam(name);
+ return (p != null && p instanceof FileParam) ?
+ ((FileParam) p).getInputStream() : null;
}
public String getFileContentType(String name)
{
- if (parameters == null)
- parseRequest();
-
- PartWrapper wrapper = parameters.get(name);
- return wrapper != null ? wrapper.getContentType() : null;
+ Param p = getParam(name);
+ return (p != null && p instanceof FileParam) ?
+ ((FileParam) p).getContentType() : null;
}
public String getFileName(String name)
{
- if (parameters == null)
- parseRequest();
-
- PartWrapper wrapper = parameters.get(name);
- return wrapper != null ? wrapper.getFileName() : null;
+ Param p = getParam(name);
+ return (p != null && p instanceof FileParam) ?
+ ((FileParam) p).getContentType() : null;
}
@Override
public String getParameter(String name)
{
- if (parameters == null)
- parseRequest();
-
- PartWrapper wrapper = parameters.get(name);
- return wrapper != null ? new String(wrapper.getData()) : super.getParameter(name);
+ Param p = getParam(name);
+ if (p != null && p instanceof ValueParam)
+ {
+ ValueParam vp = (ValueParam) p;
+ if (vp.getValue() instanceof String) return (String) vp.getValue();
+ }
+ else
+ {
+ return super.getParameter(name);
+ }
-// String[] values = (String[]) parameters.get(name);
-//
-// if (values == null)
-// return null;
-//
-// return values[0];
+ return null;
}
@Override
public String[] getParameterValues(String name)
{
- if (parameters == null)
- parseRequest();
-
-// return (String[]) parameters.get(name);
-
- PartWrapper wrapper = parameters.get(name);
- return wrapper != null ? new String[] { new String(parameters.get(name).getData()) } :
- super.getParameterValues(name);
+ Param p = getParam(name);
+ if (p != null && p instanceof ValueParam)
+ {
+ ValueParam vp = (ValueParam) p;
+ if (vp.getValue() instanceof List)
+ {
+ List vals = (List) vp.getValue();
+ String[] values = new String[vals.size()];
+ vals.toArray(values);
+ return values;
+ }
+ else
+ {
+ return new String[] {(String) vp.getValue()};
+ }
+ }
+ else
+ {
+ return super.getParameterValues(name);
+ }
}
@Override
@@ -436,21 +533,26 @@
if (parameters == null)
parseRequest();
- Map<String,Object> params = new HashMap<String,Object>();
+ Map<String,Object> params = super.getParameterMap();
for (String name : parameters.keySet())
{
- PartWrapper w = parameters.get(name);
- params.put(name, new String(w.getData()));
+ Param p = parameters.get(name);
+ if (p instanceof ValueParam)
+ {
+ ValueParam vp = (ValueParam) p;
+ if (vp.getValue() instanceof String)
+ {
+ params.put(name, vp.getValue());
+ }
+ else if (vp.getValue() instanceof List)
+ {
+ params.put(name, getParameterValues(name));
+ }
}
-
- return params;
}
- @Override
- public Object getAttribute(String string)
- {
- return super.getAttribute(string);
+ return params;
}
@Override
1.3 +17 -2 jboss-seam/src/main/org/jboss/seam/servlet/SeamMultipartFilter.java
(In the diff below, changes in quantity of whitespace are not shown.)
Index: SeamMultipartFilter.java
===================================================================
RCS file: /cvsroot/jboss/jboss-seam/src/main/org/jboss/seam/servlet/SeamMultipartFilter.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -b -r1.2 -r1.3
--- SeamMultipartFilter.java 4 Feb 2007 02:30:49 -0000 1.2
+++ SeamMultipartFilter.java 4 Feb 2007 08:11:34 -0000 1.3
@@ -3,6 +3,7 @@
import java.io.IOException;
import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@@ -13,6 +14,20 @@
{
public static final String MULTIPART = "multipart/";
+ private boolean createTempFiles = false;
+
+ @Override
+ public void init(FilterConfig filterConfig)
+ throws ServletException
+ {
+ super.init(filterConfig);
+ String param = filterConfig.getInitParameter("createTempFiles");
+ if (param != null)
+ {
+ createTempFiles = "true".equals(param);
+ }
+ }
+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
@@ -26,7 +41,7 @@
if (isMultipartRequest(httpRequest))
{
- chain.doFilter(new MultipartRequest(httpRequest), response);
+ chain.doFilter(new MultipartRequest(httpRequest, createTempFiles), response);
}
else
{
More information about the jboss-cvs-commits
mailing list