Merge remote-tracking branch 'lachlan/jetty-9.4.x-1027-Multipart' into jetty-9.4.x-1027-Multipart
This commit is contained in:
commit
585f03464b
|
@ -47,22 +47,21 @@ import org.eclipse.jetty.util.LazyList;
|
|||
import org.eclipse.jetty.util.MultiException;
|
||||
import org.eclipse.jetty.util.MultiMap;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.ReadLineInputStream;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* MultiPartInputStream
|
||||
*
|
||||
* Handle a MultiPart Mime input stream, breaking it up on the boundary into files and strings.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc7578
|
||||
*/
|
||||
public class MultiPartInputStreamParser
|
||||
public class MultiPartFormInputStream
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(MultiPartInputStreamParser.class);
|
||||
private final int _bufferSize = 16*1024;
|
||||
public static final MultipartConfigElement __DEFAULT_MULTIPART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
|
||||
private static final Logger LOG = Log.getLogger(MultiPartFormInputStream.class);
|
||||
private final int _bufferSize = 16 * 1024;
|
||||
public static final MultipartConfigElement __DEFAULT_MULTIPART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
|
||||
public static final MultiMap<Part> EMPTY_MAP = new MultiMap<>(Collections.emptyMap());
|
||||
protected InputStream _in;
|
||||
protected MultipartConfigElement _config;
|
||||
|
@ -74,8 +73,6 @@ public class MultiPartInputStreamParser
|
|||
protected boolean _deleteOnExit;
|
||||
protected boolean _writeFilesWithFilenames;
|
||||
|
||||
|
||||
|
||||
public class MultiPart implements Part
|
||||
{
|
||||
protected String _name;
|
||||
|
@ -88,8 +85,7 @@ public class MultiPartInputStreamParser
|
|||
protected long _size = 0;
|
||||
protected boolean _temporary = true;
|
||||
|
||||
public MultiPart (String name, String filename)
|
||||
throws IOException
|
||||
public MultiPart(String name, String filename) throws IOException
|
||||
{
|
||||
_name = name;
|
||||
_filename = filename;
|
||||
|
@ -100,72 +96,69 @@ public class MultiPartInputStreamParser
|
|||
{
|
||||
return String.format("Part{n=%s,fn=%s,ct=%s,s=%d,t=%b,f=%s}",_name,_filename,_contentType,_size,_temporary,_file);
|
||||
}
|
||||
protected void setContentType (String contentType)
|
||||
|
||||
protected void setContentType(String contentType)
|
||||
{
|
||||
_contentType = contentType;
|
||||
}
|
||||
|
||||
|
||||
protected void open()
|
||||
throws IOException
|
||||
protected void open() throws IOException
|
||||
{
|
||||
//We will either be writing to a file, if it has a filename on the content-disposition
|
||||
//and otherwise a byte-array-input-stream, OR if we exceed the getFileSizeThreshold, we
|
||||
//will need to change to write to a file.
|
||||
// We will either be writing to a file, if it has a filename on the content-disposition
|
||||
// and otherwise a byte-array-input-stream, OR if we exceed the getFileSizeThreshold, we
|
||||
// will need to change to write to a file.
|
||||
if (isWriteFilesWithFilenames() && _filename != null && _filename.trim().length() > 0)
|
||||
{
|
||||
createFile();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Write to a buffer in memory until we discover we've exceed the
|
||||
//MultipartConfig fileSizeThreshold
|
||||
_out = _bout= new ByteArrayOutputStream2();
|
||||
// Write to a buffer in memory until we discover we've exceed the
|
||||
// MultipartConfig fileSizeThreshold
|
||||
_out = _bout = new ByteArrayOutputStream2();
|
||||
}
|
||||
}
|
||||
|
||||
protected void close()
|
||||
throws IOException
|
||||
protected void close() throws IOException
|
||||
{
|
||||
_out.close();
|
||||
}
|
||||
|
||||
|
||||
protected void write (int b)
|
||||
throws IOException
|
||||
protected void write(int b) throws IOException
|
||||
{
|
||||
if (MultiPartInputStreamParser.this._config.getMaxFileSize() > 0 && _size + 1 > MultiPartInputStreamParser.this._config.getMaxFileSize())
|
||||
throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
|
||||
if (MultiPartFormInputStream.this._config.getMaxFileSize() > 0 && _size + 1 > MultiPartFormInputStream.this._config.getMaxFileSize())
|
||||
throw new IllegalStateException("Multipart Mime part " + _name + " exceeds max filesize");
|
||||
|
||||
if (MultiPartInputStreamParser.this._config.getFileSizeThreshold() > 0 && _size + 1 > MultiPartInputStreamParser.this._config.getFileSizeThreshold() && _file==null)
|
||||
if (MultiPartFormInputStream.this._config.getFileSizeThreshold() > 0 && _size + 1 > MultiPartFormInputStream.this._config.getFileSizeThreshold()
|
||||
&& _file == null)
|
||||
createFile();
|
||||
|
||||
_out.write(b);
|
||||
_size ++;
|
||||
_size++;
|
||||
}
|
||||
|
||||
protected void write (byte[] bytes, int offset, int length)
|
||||
throws IOException
|
||||
protected void write(byte[] bytes, int offset, int length) throws IOException
|
||||
{
|
||||
if (MultiPartInputStreamParser.this._config.getMaxFileSize() > 0 && _size + length > MultiPartInputStreamParser.this._config.getMaxFileSize())
|
||||
throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
|
||||
if (MultiPartFormInputStream.this._config.getMaxFileSize() > 0 && _size + length > MultiPartFormInputStream.this._config.getMaxFileSize())
|
||||
throw new IllegalStateException("Multipart Mime part " + _name + " exceeds max filesize");
|
||||
|
||||
if (MultiPartInputStreamParser.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStreamParser.this._config.getFileSizeThreshold() && _file==null)
|
||||
if (MultiPartFormInputStream.this._config.getFileSizeThreshold() > 0
|
||||
&& _size + length > MultiPartFormInputStream.this._config.getFileSizeThreshold() && _file == null)
|
||||
createFile();
|
||||
|
||||
_out.write(bytes, offset, length);
|
||||
_out.write(bytes,offset,length);
|
||||
_size += length;
|
||||
}
|
||||
|
||||
protected void createFile ()
|
||||
throws IOException
|
||||
protected void createFile() throws IOException
|
||||
{
|
||||
/* Some statics just to make the code below easier to understand
|
||||
* This get optimized away during the compile anyway */
|
||||
/*
|
||||
* Some statics just to make the code below easier to understand This get optimized away during the compile anyway
|
||||
*/
|
||||
final boolean USER = true;
|
||||
final boolean WORLD = false;
|
||||
|
||||
_file = File.createTempFile("MultiPart", "", MultiPartInputStreamParser.this._tmpDir);
|
||||
|
||||
_file = File.createTempFile("MultiPart","",MultiPartFormInputStream.this._tmpDir);
|
||||
_file.setReadable(false,WORLD); // (reset) disable it for everyone first
|
||||
_file.setReadable(true,USER); // enable for user only
|
||||
|
||||
|
@ -176,7 +169,7 @@ public class MultiPartInputStreamParser
|
|||
|
||||
if (_size > 0 && _out != null)
|
||||
{
|
||||
//already written some bytes, so need to copy them into the file
|
||||
// already written some bytes, so need to copy them into the file
|
||||
_out.flush();
|
||||
_bout.writeTo(bos);
|
||||
_out.close();
|
||||
|
@ -185,8 +178,6 @@ public class MultiPartInputStreamParser
|
|||
_out = bos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected void setHeaders(MultiMap<String> headers)
|
||||
{
|
||||
_headers = headers;
|
||||
|
@ -206,10 +197,10 @@ public class MultiPartInputStreamParser
|
|||
*/
|
||||
@Override
|
||||
public String getHeader(String name)
|
||||
{
|
||||
{
|
||||
if (name == null)
|
||||
return null;
|
||||
return _headers.getValue(name.toLowerCase(Locale.ENGLISH), 0);
|
||||
return _headers.getValue(name.toLowerCase(Locale.ENGLISH),0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -227,7 +218,7 @@ public class MultiPartInputStreamParser
|
|||
@Override
|
||||
public Collection<String> getHeaders(String name)
|
||||
{
|
||||
return _headers.getValues(name);
|
||||
return _headers.getValues(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,19 +227,18 @@ public class MultiPartInputStreamParser
|
|||
@Override
|
||||
public InputStream getInputStream() throws IOException
|
||||
{
|
||||
if (_file != null)
|
||||
{
|
||||
//written to a file, whether temporary or not
|
||||
return new BufferedInputStream (new FileInputStream(_file));
|
||||
}
|
||||
else
|
||||
{
|
||||
//part content is in memory
|
||||
return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size());
|
||||
}
|
||||
if (_file != null)
|
||||
{
|
||||
// written to a file, whether temporary or not
|
||||
return new BufferedInputStream(new FileInputStream(_file));
|
||||
}
|
||||
else
|
||||
{
|
||||
// part content is in memory
|
||||
return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.Part#getSubmittedFileName()
|
||||
*/
|
||||
|
@ -260,7 +250,7 @@ public class MultiPartInputStreamParser
|
|||
|
||||
public byte[] getBytes()
|
||||
{
|
||||
if (_bout!=null)
|
||||
if (_bout != null)
|
||||
return _bout.toByteArray();
|
||||
return null;
|
||||
}
|
||||
|
@ -271,7 +261,7 @@ public class MultiPartInputStreamParser
|
|||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return _name;
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -293,8 +283,8 @@ public class MultiPartInputStreamParser
|
|||
{
|
||||
_temporary = false;
|
||||
|
||||
//part data is only in the ByteArrayOutputStream and never been written to disk
|
||||
_file = new File (_tmpDir, fileName);
|
||||
// part data is only in the ByteArrayOutputStream and never been written to disk
|
||||
_file = new File(_tmpDir,fileName);
|
||||
|
||||
BufferedOutputStream bos = null;
|
||||
try
|
||||
|
@ -312,19 +302,19 @@ public class MultiPartInputStreamParser
|
|||
}
|
||||
else
|
||||
{
|
||||
//the part data is already written to a temporary file, just rename it
|
||||
// the part data is already written to a temporary file, just rename it
|
||||
_temporary = false;
|
||||
|
||||
Path src = _file.toPath();
|
||||
Path target = src.resolveSibling(fileName);
|
||||
Files.move(src, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.move(src,target,StandardCopyOption.REPLACE_EXISTING);
|
||||
_file = target.toFile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the file, whether or not Part.write() was called on it
|
||||
* (ie no longer temporary)
|
||||
* Remove the file, whether or not Part.write() was called on it (ie no longer temporary)
|
||||
*
|
||||
* @see javax.servlet.http.Part#delete()
|
||||
*/
|
||||
@Override
|
||||
|
@ -337,7 +327,8 @@ public class MultiPartInputStreamParser
|
|||
/**
|
||||
* Only remove tmp files.
|
||||
*
|
||||
* @throws IOException if unable to delete the file
|
||||
* @throws IOException
|
||||
* if unable to delete the file
|
||||
*/
|
||||
public void cleanUp() throws IOException
|
||||
{
|
||||
|
@ -345,47 +336,48 @@ public class MultiPartInputStreamParser
|
|||
_file.delete();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the file
|
||||
*
|
||||
* @return the file, if any, the data has been written to.
|
||||
*/
|
||||
public File getFile ()
|
||||
public File getFile()
|
||||
{
|
||||
return _file;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the filename from the content-disposition.
|
||||
*
|
||||
* @return null or the filename
|
||||
*/
|
||||
public String getContentDispositionFilename ()
|
||||
public String getContentDispositionFilename()
|
||||
{
|
||||
return _filename;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param in Request input stream
|
||||
* @param contentType Content-Type header
|
||||
* @param config MultipartConfigElement
|
||||
* @param contextTmpDir javax.servlet.context.tempdir
|
||||
* @param in
|
||||
* Request input stream
|
||||
* @param contentType
|
||||
* Content-Type header
|
||||
* @param config
|
||||
* MultipartConfigElement
|
||||
* @param contextTmpDir
|
||||
* javax.servlet.context.tempdir
|
||||
*/
|
||||
public MultiPartInputStreamParser (InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
|
||||
public MultiPartFormInputStream(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
|
||||
{
|
||||
_contentType = contentType;
|
||||
_config = config;
|
||||
_contextTmpDir = contextTmpDir;
|
||||
if (_contextTmpDir == null)
|
||||
_contextTmpDir = new File (System.getProperty("java.io.tmpdir"));
|
||||
_contextTmpDir = new File(System.getProperty("java.io.tmpdir"));
|
||||
|
||||
if (_config == null)
|
||||
_config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath());
|
||||
|
||||
|
||||
if (in instanceof ServletInputStream)
|
||||
{
|
||||
if (((ServletInputStream)in).isFinished())
|
||||
|
@ -394,11 +386,12 @@ public class MultiPartInputStreamParser
|
|||
return;
|
||||
}
|
||||
}
|
||||
_in = new ReadLineInputStream(in);
|
||||
_in = new BufferedInputStream(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the already parsed parts.
|
||||
*
|
||||
* @return the parts that were parsed
|
||||
*/
|
||||
public Collection<Part> getParsedParts()
|
||||
|
@ -408,9 +401,9 @@ public class MultiPartInputStreamParser
|
|||
|
||||
Collection<List<Part>> values = _parts.values();
|
||||
List<Part> parts = new ArrayList<>();
|
||||
for (List<Part> o: values)
|
||||
for (List<Part> o : values)
|
||||
{
|
||||
List<Part> asList = LazyList.getList(o, false);
|
||||
List<Part> asList = LazyList.getList(o,false);
|
||||
parts.addAll(asList);
|
||||
}
|
||||
return parts;
|
||||
|
@ -419,20 +412,20 @@ public class MultiPartInputStreamParser
|
|||
/**
|
||||
* Delete any tmp storage for parts, and clear out the parts list.
|
||||
*
|
||||
* @throws MultiException if unable to delete the parts
|
||||
* @throws MultiException
|
||||
* if unable to delete the parts
|
||||
*/
|
||||
public void deleteParts ()
|
||||
throws MultiException
|
||||
public void deleteParts() throws MultiException
|
||||
{
|
||||
Collection<Part> parts = getParsedParts();
|
||||
MultiException err = new MultiException();
|
||||
for (Part p:parts)
|
||||
for (Part p : parts)
|
||||
{
|
||||
try
|
||||
{
|
||||
((MultiPartInputStreamParser.MultiPart)p).cleanUp();
|
||||
((MultiPart)p).cleanUp();
|
||||
}
|
||||
catch(Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
err.add(e);
|
||||
}
|
||||
|
@ -442,53 +435,51 @@ public class MultiPartInputStreamParser
|
|||
err.ifExceptionThrowMulti();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse, if necessary, the multipart data and return the list of Parts.
|
||||
*
|
||||
* @return the parts
|
||||
* @throws IOException if unable to get the parts
|
||||
* @throws IOException
|
||||
* if unable to get the parts
|
||||
*/
|
||||
public Collection<Part> getParts()
|
||||
throws IOException
|
||||
public Collection<Part> getParts() throws IOException
|
||||
{
|
||||
parse();
|
||||
throwIfError();
|
||||
|
||||
|
||||
Collection<List<Part>> values = _parts.values();
|
||||
List<Part> parts = new ArrayList<>();
|
||||
for (List<Part> o: values)
|
||||
for (List<Part> o : values)
|
||||
{
|
||||
List<Part> asList = LazyList.getList(o, false);
|
||||
List<Part> asList = LazyList.getList(o,false);
|
||||
parts.addAll(asList);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the named Part.
|
||||
*
|
||||
* @param name the part name
|
||||
* @param name
|
||||
* the part name
|
||||
* @return the parts
|
||||
* @throws IOException if unable to get the part
|
||||
* @throws IOException
|
||||
* if unable to get the part
|
||||
*/
|
||||
public Part getPart(String name)
|
||||
throws IOException
|
||||
public Part getPart(String name) throws IOException
|
||||
{
|
||||
parse();
|
||||
throwIfError();
|
||||
return _parts.getValue(name, 0);
|
||||
throwIfError();
|
||||
return _parts.getValue(name,0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if one has been latched.
|
||||
*
|
||||
* @throws IOException the exception (if present)
|
||||
* @throws IOException
|
||||
* the exception (if present)
|
||||
*/
|
||||
protected void throwIfError ()
|
||||
throws IOException
|
||||
protected void throwIfError() throws IOException
|
||||
{
|
||||
if (_err != null)
|
||||
{
|
||||
|
@ -505,34 +496,33 @@ public class MultiPartInputStreamParser
|
|||
* Parse, if necessary, the multipart stream.
|
||||
*
|
||||
*/
|
||||
protected void parse ()
|
||||
protected void parse()
|
||||
{
|
||||
//have we already parsed the input?
|
||||
// have we already parsed the input?
|
||||
if (_parts != null || _err != null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
|
||||
//initialize
|
||||
// initialize
|
||||
_parts = new MultiMap<>();
|
||||
|
||||
//if its not a multipart request, don't parse it
|
||||
// if its not a multipart request, don't parse it
|
||||
if (_contentType == null || !_contentType.startsWith("multipart/form-data"))
|
||||
return;
|
||||
|
||||
|
||||
//sort out the location to which to write the files
|
||||
// sort out the location to which to write the files
|
||||
if (_config.getLocation() == null)
|
||||
_tmpDir = _contextTmpDir;
|
||||
else if ("".equals(_config.getLocation()))
|
||||
_tmpDir = _contextTmpDir;
|
||||
else
|
||||
{
|
||||
File f = new File (_config.getLocation());
|
||||
File f = new File(_config.getLocation());
|
||||
if (f.isAbsolute())
|
||||
_tmpDir = f;
|
||||
else
|
||||
_tmpDir = new File (_contextTmpDir, _config.getLocation());
|
||||
_tmpDir = new File(_contextTmpDir,_config.getLocation());
|
||||
}
|
||||
|
||||
if (!_tmpDir.exists())
|
||||
|
@ -542,72 +532,74 @@ public class MultiPartInputStreamParser
|
|||
int bstart = _contentType.indexOf("boundary=");
|
||||
if (bstart >= 0)
|
||||
{
|
||||
int bend = _contentType.indexOf(";", bstart);
|
||||
bend = (bend < 0? _contentType.length(): bend);
|
||||
int bend = _contentType.indexOf(";",bstart);
|
||||
bend = (bend < 0?_contentType.length():bend);
|
||||
contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart,bend)).trim());
|
||||
}
|
||||
|
||||
|
||||
Handler handler = new Handler();
|
||||
MultiPartParser parser = new MultiPartParser(handler,contentTypeBoundary);
|
||||
|
||||
MultiPartParser parser = new MultiPartParser(handler,contentTypeBoundary);
|
||||
|
||||
// Create a buffer to store data from stream //
|
||||
byte[] data = new byte[_bufferSize];
|
||||
int len=0;
|
||||
int len = 0;
|
||||
|
||||
/* keep running total of size of bytes read from input
|
||||
* and throw an exception if exceeds MultipartConfigElement._maxRequestSize */
|
||||
long total = 0;
|
||||
/*
|
||||
* keep running total of size of bytes read from input and throw an exception if exceeds MultipartConfigElement._maxRequestSize
|
||||
*/
|
||||
long total = 0;
|
||||
|
||||
while(true)
|
||||
while (true)
|
||||
{
|
||||
|
||||
len = _in.read(data);
|
||||
|
||||
if(len > 0)
|
||||
if (len > 0)
|
||||
{
|
||||
total+=len;
|
||||
if(_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
|
||||
total += len;
|
||||
if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
|
||||
{
|
||||
_err = new IllegalStateException ("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
|
||||
_err = new IllegalStateException("Request exceeds maxRequestSize (" + _config.getMaxRequestSize() + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
ByteBuffer buffer = BufferUtil.toBuffer(data);
|
||||
buffer.limit(len);
|
||||
parser.parse(buffer, false);
|
||||
}
|
||||
else if (len == -1)
|
||||
if (parser.parse(buffer,false))
|
||||
break;
|
||||
|
||||
if(buffer.hasRemaining())
|
||||
throw new IllegalStateException("Buffer did not fully consume");
|
||||
|
||||
}
|
||||
else if (len == -1)
|
||||
{
|
||||
parser.parse(BufferUtil.EMPTY_BUFFER, true);
|
||||
parser.parse(BufferUtil.EMPTY_BUFFER,true);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//check for exceptions
|
||||
if(_err != null)
|
||||
// check for exceptions
|
||||
if (_err != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//check we read to the end of the message
|
||||
if(parser.getState() != MultiPartParser.State.END)
|
||||
// check we read to the end of the message
|
||||
if (parser.getState() != MultiPartParser.State.END)
|
||||
{
|
||||
if(parser.getState() == MultiPartParser.State.PREAMBLE)
|
||||
if (parser.getState() == MultiPartParser.State.PREAMBLE)
|
||||
_err = new IOException("Missing initial multi part boundary");
|
||||
else
|
||||
_err = new IOException("Incomplete Multipart");
|
||||
}
|
||||
|
||||
if(LOG.isDebugEnabled())
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Parsing Complete {} err={}",parser,_err);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
|
@ -616,98 +608,83 @@ public class MultiPartInputStreamParser
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class Handler implements MultiPartParser.Handler
|
||||
{
|
||||
|
||||
private MultiPart _part=null;
|
||||
private String contentDisposition=null;
|
||||
private String contentType=null;
|
||||
|
||||
private MultiPart _part = null;
|
||||
private String contentDisposition = null;
|
||||
private String contentType = null;
|
||||
private MultiMap<String> headers = new MultiMap<>();
|
||||
|
||||
|
||||
@Override
|
||||
public boolean messageComplete() { return true; }
|
||||
|
||||
public boolean messageComplete()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parsedField(String key, String value)
|
||||
{
|
||||
// Add to headers and mark if one of these fields. //
|
||||
headers.put(key.toLowerCase(Locale.ENGLISH), value);
|
||||
headers.put(key.toLowerCase(Locale.ENGLISH),value);
|
||||
if (key.equalsIgnoreCase("content-disposition"))
|
||||
contentDisposition=value;
|
||||
contentDisposition = value;
|
||||
else if (key.equalsIgnoreCase("content-type"))
|
||||
contentType = value;
|
||||
|
||||
contentType = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean headerComplete() {
|
||||
|
||||
try {
|
||||
|
||||
public boolean headerComplete()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Extract content-disposition
|
||||
boolean form_data=false;
|
||||
if(contentDisposition==null)
|
||||
boolean form_data = false;
|
||||
if (contentDisposition == null)
|
||||
{
|
||||
throw new IOException("Missing content-disposition");
|
||||
}
|
||||
|
||||
QuotedStringTokenizer tok=new QuotedStringTokenizer(contentDisposition,";", false, true);
|
||||
String name=null;
|
||||
String filename=null;
|
||||
while(tok.hasMoreTokens())
|
||||
|
||||
QuotedStringTokenizer tok = new QuotedStringTokenizer(contentDisposition,";",false,true);
|
||||
String name = null;
|
||||
String filename = null;
|
||||
while (tok.hasMoreTokens())
|
||||
{
|
||||
String t=tok.nextToken().trim();
|
||||
String tl=t.toLowerCase(Locale.ENGLISH);
|
||||
if(t.startsWith("form-data"))
|
||||
form_data=true;
|
||||
else if(tl.startsWith("name="))
|
||||
name=value(t);
|
||||
else if(tl.startsWith("filename="))
|
||||
filename=filenameValue(t);
|
||||
String t = tok.nextToken().trim();
|
||||
String tl = t.toLowerCase(Locale.ENGLISH);
|
||||
if (t.startsWith("form-data"))
|
||||
form_data = true;
|
||||
else if (tl.startsWith("name="))
|
||||
name = value(t);
|
||||
else if (tl.startsWith("filename="))
|
||||
filename = filenameValue(t);
|
||||
}
|
||||
|
||||
|
||||
// Check disposition
|
||||
if(!form_data)
|
||||
if (!form_data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//It is valid for reset and submit buttons to have an empty name.
|
||||
//If no name is supplied, the browser skips sending the info for that field.
|
||||
//However, if you supply the empty string as the name, the browser sends the
|
||||
//field, with name as the empty string. So, only continue this loop if we
|
||||
//have not yet seen a name field.
|
||||
if(name==null)
|
||||
// It is valid for reset and submit buttons to have an empty name.
|
||||
// If no name is supplied, the browser skips sending the info for that field.
|
||||
// However, if you supply the empty string as the name, the browser sends the
|
||||
// field, with name as the empty string. So, only continue this loop if we
|
||||
// have not yet seen a name field.
|
||||
if (name == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//create the new part
|
||||
_part = new MultiPart(name, filename);
|
||||
// create the new part
|
||||
_part = new MultiPart(name,filename);
|
||||
_part.setHeaders(headers);
|
||||
_part.setContentType(contentType);
|
||||
_parts.add(name, _part);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_err = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean content(ByteBuffer buffer, boolean last)
|
||||
{
|
||||
if (BufferUtil.hasContent(buffer))
|
||||
{
|
||||
_parts.add(name,_part);
|
||||
|
||||
try
|
||||
{
|
||||
//write the content data to the part
|
||||
_part.open();
|
||||
_part.write(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.remaining());
|
||||
_part.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
|
@ -715,13 +692,48 @@ public class MultiPartInputStreamParser
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (last)
|
||||
reset();
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
_err = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean content(ByteBuffer buffer, boolean last)
|
||||
{
|
||||
if (BufferUtil.hasContent(buffer))
|
||||
{
|
||||
try
|
||||
{
|
||||
_part.write(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
_err = e;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (last)
|
||||
{
|
||||
try
|
||||
{
|
||||
_part.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
_err = e;
|
||||
return true;
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
_part = null;
|
||||
|
@ -729,21 +741,27 @@ public class MultiPartInputStreamParser
|
|||
contentType = null;
|
||||
headers = new MultiMap<>();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void earlyEOF()
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Early EOF {}",MultiPartFormInputStream.this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setDeleteOnExit(boolean deleteOnExit)
|
||||
{
|
||||
_deleteOnExit = deleteOnExit;
|
||||
}
|
||||
|
||||
public void setWriteFilesWithFilenames (boolean writeFilesWithFilenames)
|
||||
public void setWriteFilesWithFilenames(boolean writeFilesWithFilenames)
|
||||
{
|
||||
_writeFilesWithFilenames = writeFilesWithFilenames;
|
||||
}
|
||||
|
||||
public boolean isWriteFilesWithFilenames ()
|
||||
|
||||
public boolean isWriteFilesWithFilenames()
|
||||
{
|
||||
return _writeFilesWithFilenames;
|
||||
}
|
||||
|
@ -753,42 +771,39 @@ public class MultiPartInputStreamParser
|
|||
return _deleteOnExit;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private String value(String nameEqualsValue)
|
||||
{
|
||||
int idx = nameEqualsValue.indexOf('=');
|
||||
String value = nameEqualsValue.substring(idx+1).trim();
|
||||
String value = nameEqualsValue.substring(idx + 1).trim();
|
||||
return QuotedStringTokenizer.unquoteOnly(value);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private String filenameValue(String nameEqualsValue)
|
||||
{
|
||||
int idx = nameEqualsValue.indexOf('=');
|
||||
String value = nameEqualsValue.substring(idx+1).trim();
|
||||
String value = nameEqualsValue.substring(idx + 1).trim();
|
||||
|
||||
if (value.matches(".??[a-z,A-Z]\\:\\\\[^\\\\].*"))
|
||||
{
|
||||
//incorrectly escaped IE filenames that have the whole path
|
||||
//we just strip any leading & trailing quotes and leave it as is
|
||||
char first=value.charAt(0);
|
||||
if (first=='"' || first=='\'')
|
||||
value=value.substring(1);
|
||||
char last=value.charAt(value.length()-1);
|
||||
if (last=='"' || last=='\'')
|
||||
value = value.substring(0,value.length()-1);
|
||||
// incorrectly escaped IE filenames that have the whole path
|
||||
// we just strip any leading & trailing quotes and leave it as is
|
||||
char first = value.charAt(0);
|
||||
if (first == '"' || first == '\'')
|
||||
value = value.substring(1);
|
||||
char last = value.charAt(value.length() - 1);
|
||||
if (last == '"' || last == '\'')
|
||||
value = value.substring(0,value.length() - 1);
|
||||
|
||||
return value;
|
||||
}
|
||||
else
|
||||
//unquote the string, but allow any backslashes that don't
|
||||
//form a valid escape sequence to remain as many browsers
|
||||
//even on *nix systems will not escape a filename containing
|
||||
//backslashes
|
||||
return QuotedStringTokenizer.unquoteOnly(value, true);
|
||||
// unquote the string, but allow any backslashes that don't
|
||||
// form a valid escape sequence to remain as many browsers
|
||||
// even on *nix systems will not escape a filename containing
|
||||
// backslashes
|
||||
return QuotedStringTokenizer.unquoteOnly(value,true);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -29,7 +29,6 @@ import org.eclipse.jetty.util.SearchPattern;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* RFC2046 and RFC7578
|
||||
|
@ -114,42 +113,42 @@ public class MultiPartParser
|
|||
{
|
||||
public static final Logger LOG = Log.getLogger(MultiPartParser.class);
|
||||
|
||||
static final byte COLON= (byte)':';
|
||||
static final byte TAB= 0x09;
|
||||
static final byte LINE_FEED= 0x0A;
|
||||
static final byte CARRIAGE_RETURN= 0x0D;
|
||||
static final byte SPACE= 0x20;
|
||||
static final byte[] CRLF = {CARRIAGE_RETURN,LINE_FEED};
|
||||
static final byte SEMI_COLON= (byte)';';
|
||||
|
||||
static final byte COLON = (byte)':';
|
||||
static final byte TAB = 0x09;
|
||||
static final byte LINE_FEED = 0x0A;
|
||||
static final byte CARRIAGE_RETURN = 0x0D;
|
||||
static final byte SPACE = 0x20;
|
||||
static final byte[] CRLF =
|
||||
{ CARRIAGE_RETURN, LINE_FEED };
|
||||
static final byte SEMI_COLON = (byte)';';
|
||||
|
||||
// States
|
||||
public enum FieldState
|
||||
{
|
||||
FIELD,
|
||||
IN_NAME,
|
||||
AFTER_NAME,
|
||||
AFTER_NAME,
|
||||
VALUE,
|
||||
IN_VALUE
|
||||
}
|
||||
|
||||
|
||||
// States
|
||||
public enum State
|
||||
{
|
||||
PREAMBLE,
|
||||
DELIMITER,
|
||||
DELIMITER_PADDING,
|
||||
DELIMITER_CLOSE,
|
||||
DELIMITER_PADDING,
|
||||
DELIMITER_CLOSE,
|
||||
BODY_PART,
|
||||
FIRST_OCTETS,
|
||||
OCTETS,
|
||||
EPILOGUE,
|
||||
FIRST_OCTETS,
|
||||
OCTETS,
|
||||
EPILOGUE,
|
||||
END
|
||||
}
|
||||
|
||||
private final static EnumSet<State> __delimiterStates = EnumSet.of(State.DELIMITER,State.DELIMITER_CLOSE,State.DELIMITER_PADDING);
|
||||
|
||||
private final boolean DEBUG=LOG.isDebugEnabled();
|
||||
|
||||
private final boolean DEBUG = LOG.isDebugEnabled();
|
||||
private final Handler _handler;
|
||||
private final SearchPattern _delimiterSearch;
|
||||
|
||||
|
@ -162,7 +161,7 @@ public class MultiPartParser
|
|||
private boolean _cr;
|
||||
private ByteBuffer _patternBuffer;
|
||||
|
||||
private final StringBuilder _string=new StringBuilder();
|
||||
private final StringBuilder _string = new StringBuilder();
|
||||
private int _length;
|
||||
|
||||
private int _totalHeaderLineLength = -1;
|
||||
|
@ -173,7 +172,7 @@ public class MultiPartParser
|
|||
{
|
||||
_handler = handler;
|
||||
|
||||
String delimiter = "\r\n--"+boundary;
|
||||
String delimiter = "\r\n--" + boundary;
|
||||
_patternBuffer = ByteBuffer.wrap(delimiter.getBytes(StandardCharsets.US_ASCII));
|
||||
_delimiterSearch = SearchPattern.compile(_patternBuffer.array());
|
||||
}
|
||||
|
@ -184,14 +183,13 @@ public class MultiPartParser
|
|||
_fieldState = FieldState.FIELD;
|
||||
_partialBoundary = 2; // No CRLF if no preamble
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public Handler getHandler()
|
||||
{
|
||||
return _handler;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public State getState()
|
||||
{
|
||||
|
@ -205,54 +203,57 @@ public class MultiPartParser
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
enum CharState { ILLEGAL, CR, LF, LEGAL }
|
||||
enum CharState
|
||||
{
|
||||
ILLEGAL, CR, LF, LEGAL
|
||||
}
|
||||
private final static CharState[] __charState;
|
||||
static
|
||||
{
|
||||
// token = 1*tchar
|
||||
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
|
||||
// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
|
||||
// / DIGIT / ALPHA
|
||||
// ; any VCHAR, except delimiters
|
||||
// quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
// qdtext = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
|
||||
// obs-text = %x80-FF
|
||||
// comment = "(" *( ctext / quoted-pair / comment ) ")"
|
||||
// ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
|
||||
// quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
// token = 1*tchar
|
||||
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
|
||||
// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
|
||||
// / DIGIT / ALPHA
|
||||
// ; any VCHAR, except delimiters
|
||||
// quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
// qdtext = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
|
||||
// obs-text = %x80-FF
|
||||
// comment = "(" *( ctext / quoted-pair / comment ) ")"
|
||||
// ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
|
||||
// quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
|
||||
__charState=new CharState[256];
|
||||
__charState = new CharState[256];
|
||||
Arrays.fill(__charState,CharState.ILLEGAL);
|
||||
__charState[LINE_FEED]=CharState.LF;
|
||||
__charState[CARRIAGE_RETURN]=CharState.CR;
|
||||
__charState[TAB]=CharState.LEGAL;
|
||||
__charState[SPACE]=CharState.LEGAL;
|
||||
__charState[LINE_FEED] = CharState.LF;
|
||||
__charState[CARRIAGE_RETURN] = CharState.CR;
|
||||
__charState[TAB] = CharState.LEGAL;
|
||||
__charState[SPACE] = CharState.LEGAL;
|
||||
|
||||
__charState['!']=CharState.LEGAL;
|
||||
__charState['#']=CharState.LEGAL;
|
||||
__charState['$']=CharState.LEGAL;
|
||||
__charState['%']=CharState.LEGAL;
|
||||
__charState['&']=CharState.LEGAL;
|
||||
__charState['\'']=CharState.LEGAL;
|
||||
__charState['*']=CharState.LEGAL;
|
||||
__charState['+']=CharState.LEGAL;
|
||||
__charState['-']=CharState.LEGAL;
|
||||
__charState['.']=CharState.LEGAL;
|
||||
__charState['^']=CharState.LEGAL;
|
||||
__charState['_']=CharState.LEGAL;
|
||||
__charState['`']=CharState.LEGAL;
|
||||
__charState['|']=CharState.LEGAL;
|
||||
__charState['~']=CharState.LEGAL;
|
||||
__charState['!'] = CharState.LEGAL;
|
||||
__charState['#'] = CharState.LEGAL;
|
||||
__charState['$'] = CharState.LEGAL;
|
||||
__charState['%'] = CharState.LEGAL;
|
||||
__charState['&'] = CharState.LEGAL;
|
||||
__charState['\''] = CharState.LEGAL;
|
||||
__charState['*'] = CharState.LEGAL;
|
||||
__charState['+'] = CharState.LEGAL;
|
||||
__charState['-'] = CharState.LEGAL;
|
||||
__charState['.'] = CharState.LEGAL;
|
||||
__charState['^'] = CharState.LEGAL;
|
||||
__charState['_'] = CharState.LEGAL;
|
||||
__charState['`'] = CharState.LEGAL;
|
||||
__charState['|'] = CharState.LEGAL;
|
||||
__charState['~'] = CharState.LEGAL;
|
||||
|
||||
__charState['"']=CharState.LEGAL;
|
||||
__charState['"'] = CharState.LEGAL;
|
||||
|
||||
__charState['\\']=CharState.LEGAL;
|
||||
__charState['(']=CharState.LEGAL;
|
||||
__charState[')']=CharState.LEGAL;
|
||||
Arrays.fill(__charState,0x21,0x27+1,CharState.LEGAL);
|
||||
Arrays.fill(__charState,0x2A,0x5B+1,CharState.LEGAL);
|
||||
Arrays.fill(__charState,0x5D,0x7E+1,CharState.LEGAL);
|
||||
Arrays.fill(__charState,0x80,0xFF+1,CharState.LEGAL);
|
||||
__charState['\\'] = CharState.LEGAL;
|
||||
__charState['('] = CharState.LEGAL;
|
||||
__charState[')'] = CharState.LEGAL;
|
||||
Arrays.fill(__charState,0x21,0x27 + 1,CharState.LEGAL);
|
||||
Arrays.fill(__charState,0x2A,0x5B + 1,CharState.LEGAL);
|
||||
Arrays.fill(__charState,0x5D,0x7E + 1,CharState.LEGAL);
|
||||
Arrays.fill(__charState,0x80,0xFF + 1,CharState.LEGAL);
|
||||
|
||||
}
|
||||
|
||||
|
@ -261,7 +262,7 @@ public class MultiPartParser
|
|||
{
|
||||
return BufferUtil.hasContent(buffer);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private byte getNextByte(ByteBuffer buffer)
|
||||
{
|
||||
|
@ -269,17 +270,17 @@ public class MultiPartParser
|
|||
byte ch = buffer.get();
|
||||
|
||||
CharState s = __charState[0xff & ch];
|
||||
switch(s)
|
||||
switch (s)
|
||||
{
|
||||
case LF:
|
||||
_cr=false;
|
||||
_cr = false;
|
||||
return ch;
|
||||
|
||||
case CR:
|
||||
if (_cr)
|
||||
throw new BadMessageException("Bad EOL");
|
||||
|
||||
_cr=true;
|
||||
|
||||
_cr = true;
|
||||
if (buffer.hasRemaining())
|
||||
return getNextByte(buffer);
|
||||
|
||||
|
@ -299,75 +300,75 @@ public class MultiPartParser
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private void setString(String s)
|
||||
{
|
||||
_string.setLength(0);
|
||||
_string.append(s);
|
||||
_length=s.length();
|
||||
_length = s.length();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private String takeString()
|
||||
{
|
||||
_string.setLength(_length);
|
||||
String s =_string.toString();
|
||||
String s = _string.toString();
|
||||
_string.setLength(0);
|
||||
_length=-1;
|
||||
_length = -1;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* Parse until next Event.
|
||||
* @param buffer the buffer to parse
|
||||
*
|
||||
* @param buffer
|
||||
* the buffer to parse
|
||||
* @return True if an {@link RequestHandler} method was called and it returned true;
|
||||
*/
|
||||
public boolean parse(ByteBuffer buffer, boolean last)
|
||||
{
|
||||
boolean handle = false;
|
||||
while(handle==false && BufferUtil.hasContent(buffer))
|
||||
while (handle == false && BufferUtil.hasContent(buffer))
|
||||
{
|
||||
switch(_state)
|
||||
switch (_state)
|
||||
{
|
||||
case PREAMBLE:
|
||||
parsePreamble(buffer);
|
||||
continue;
|
||||
|
||||
|
||||
case DELIMITER:
|
||||
case DELIMITER_PADDING:
|
||||
case DELIMITER_CLOSE:
|
||||
parseDelimiter(buffer);
|
||||
continue;
|
||||
|
||||
|
||||
case BODY_PART:
|
||||
handle = parseMimePartHeaders(buffer);
|
||||
break;
|
||||
|
||||
|
||||
case FIRST_OCTETS:
|
||||
case OCTETS:
|
||||
handle = parseOctetContent(buffer);
|
||||
break;
|
||||
|
||||
|
||||
case EPILOGUE:
|
||||
BufferUtil.clear(buffer);
|
||||
break;
|
||||
|
||||
|
||||
case END:
|
||||
handle = true;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(last && BufferUtil.isEmpty(buffer))
|
||||
|
||||
if (last && BufferUtil.isEmpty(buffer))
|
||||
{
|
||||
if(_state == State.EPILOGUE)
|
||||
if (_state == State.EPILOGUE)
|
||||
{
|
||||
_state = State.END;
|
||||
return _handler.messageComplete();
|
||||
|
@ -378,21 +379,21 @@ public class MultiPartParser
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private void parsePreamble(ByteBuffer buffer)
|
||||
{
|
||||
if (_partialBoundary>0)
|
||||
if (_partialBoundary > 0)
|
||||
{
|
||||
int partial = _delimiterSearch.startsWith(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.remaining(),_partialBoundary);
|
||||
if (partial>0)
|
||||
int partial = _delimiterSearch.startsWith(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining(),_partialBoundary);
|
||||
if (partial > 0)
|
||||
{
|
||||
if (partial==_delimiterSearch.getLength())
|
||||
if (partial == _delimiterSearch.getLength())
|
||||
{
|
||||
buffer.position(buffer.position()+partial-_partialBoundary);
|
||||
buffer.position(buffer.position() + partial - _partialBoundary);
|
||||
_partialBoundary = 0;
|
||||
setState(State.DELIMITER);
|
||||
return;
|
||||
|
@ -402,61 +403,61 @@ public class MultiPartParser
|
|||
BufferUtil.clear(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_partialBoundary = 0;
|
||||
}
|
||||
|
||||
int delimiter = _delimiterSearch.match(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.remaining());
|
||||
if (delimiter>=0)
|
||||
|
||||
int delimiter = _delimiterSearch.match(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining());
|
||||
if (delimiter >= 0)
|
||||
{
|
||||
buffer.position(delimiter-buffer.arrayOffset()+_delimiterSearch.getLength());
|
||||
buffer.position(delimiter - buffer.arrayOffset() + _delimiterSearch.getLength());
|
||||
setState(State.DELIMITER);
|
||||
return;
|
||||
}
|
||||
|
||||
_partialBoundary = _delimiterSearch.endsWith(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.remaining());
|
||||
_partialBoundary = _delimiterSearch.endsWith(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining());
|
||||
BufferUtil.clear(buffer);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private void parseDelimiter(ByteBuffer buffer)
|
||||
{
|
||||
{
|
||||
while (__delimiterStates.contains(_state) && hasNextByte(buffer))
|
||||
{
|
||||
byte b=getNextByte(buffer);
|
||||
if (b==0)
|
||||
byte b = getNextByte(buffer);
|
||||
if (b == 0)
|
||||
return;
|
||||
|
||||
if (b=='\n')
|
||||
|
||||
if (b == '\n')
|
||||
{
|
||||
setState(State.BODY_PART);
|
||||
_handler.startPart();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch(_state)
|
||||
switch (_state)
|
||||
{
|
||||
case DELIMITER:
|
||||
if (b=='-')
|
||||
if (b == '-')
|
||||
setState(State.DELIMITER_CLOSE);
|
||||
else
|
||||
setState(State.DELIMITER_PADDING);
|
||||
continue;
|
||||
|
||||
|
||||
case DELIMITER_CLOSE:
|
||||
if (b=='-')
|
||||
if (b == '-')
|
||||
{
|
||||
setState(State.EPILOGUE);
|
||||
return;
|
||||
}
|
||||
setState(State.DELIMITER_PADDING);
|
||||
continue;
|
||||
|
||||
|
||||
case DELIMITER_PADDING:
|
||||
default:
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -468,44 +469,43 @@ public class MultiPartParser
|
|||
protected boolean parseMimePartHeaders(ByteBuffer buffer)
|
||||
{
|
||||
// Process headers
|
||||
while (_state==State.BODY_PART && hasNextByte(buffer))
|
||||
while (_state == State.BODY_PART && hasNextByte(buffer))
|
||||
{
|
||||
// process each character
|
||||
byte b=getNextByte(buffer);
|
||||
if (b==0)
|
||||
byte b = getNextByte(buffer);
|
||||
if (b == 0)
|
||||
break;
|
||||
|
||||
if(b!=LINE_FEED)
|
||||
_totalHeaderLineLength++;
|
||||
|
||||
if(_totalHeaderLineLength > _maxHeaderLineLength)
|
||||
throw new IllegalStateException("Header Line Exceeded Max Length");
|
||||
|
||||
if (b != LINE_FEED)
|
||||
_totalHeaderLineLength++;
|
||||
|
||||
if (_totalHeaderLineLength > _maxHeaderLineLength)
|
||||
throw new IllegalStateException("Header Line Exceeded Max Length");
|
||||
|
||||
switch (_fieldState)
|
||||
{
|
||||
case FIELD:
|
||||
switch(b)
|
||||
switch (b)
|
||||
{
|
||||
case SPACE:
|
||||
case TAB:
|
||||
{
|
||||
// Folded field value!
|
||||
|
||||
if (_fieldName==null)
|
||||
|
||||
if (_fieldName == null)
|
||||
throw new IllegalStateException("First field folded");
|
||||
|
||||
if (_fieldValue==null)
|
||||
|
||||
if (_fieldValue == null)
|
||||
{
|
||||
_string.setLength(0);
|
||||
_length=0;
|
||||
_length = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
setString(_fieldValue);
|
||||
_string.append(' ');
|
||||
_length++;
|
||||
_fieldValue=null;
|
||||
_fieldValue = null;
|
||||
}
|
||||
setState(FieldState.VALUE);
|
||||
break;
|
||||
|
@ -530,100 +530,100 @@ public class MultiPartParser
|
|||
setState(FieldState.IN_NAME);
|
||||
_string.setLength(0);
|
||||
_string.append((char)b);
|
||||
_length=1;
|
||||
_length = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IN_NAME:
|
||||
switch(b)
|
||||
switch (b)
|
||||
{
|
||||
case COLON:
|
||||
_fieldName=takeString();
|
||||
_length=-1;
|
||||
_fieldName = takeString();
|
||||
_length = -1;
|
||||
setState(FieldState.VALUE);
|
||||
break;
|
||||
|
||||
|
||||
case SPACE:
|
||||
//Ignore trailing whitespaces
|
||||
// Ignore trailing whitespaces
|
||||
setState(FieldState.AFTER_NAME);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
_string.append((char)b);
|
||||
_length=_string.length();
|
||||
_length = _string.length();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case AFTER_NAME:
|
||||
switch(b)
|
||||
switch (b)
|
||||
{
|
||||
case COLON:
|
||||
_fieldName=takeString();
|
||||
_length=-1;
|
||||
_fieldName = takeString();
|
||||
_length = -1;
|
||||
setState(FieldState.VALUE);
|
||||
break;
|
||||
|
||||
|
||||
case LINE_FEED:
|
||||
_fieldName=takeString();
|
||||
_fieldName = takeString();
|
||||
_string.setLength(0);
|
||||
_fieldValue="";
|
||||
_length=-1;
|
||||
_fieldValue = "";
|
||||
_length = -1;
|
||||
break;
|
||||
|
||||
|
||||
case SPACE:
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
throw new IllegalCharacterException(_state,b,buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case VALUE:
|
||||
switch(b)
|
||||
switch (b)
|
||||
{
|
||||
case LINE_FEED:
|
||||
_string.setLength(0);
|
||||
_fieldValue="";
|
||||
_length=-1;
|
||||
_fieldValue = "";
|
||||
_length = -1;
|
||||
|
||||
setState(FieldState.FIELD);
|
||||
break;
|
||||
|
||||
|
||||
case SPACE:
|
||||
case TAB:
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
_string.append((char)(0xff&b));
|
||||
_length=_string.length();
|
||||
_string.append((char)(0xff & b));
|
||||
_length = _string.length();
|
||||
setState(FieldState.IN_VALUE);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IN_VALUE:
|
||||
switch(b)
|
||||
switch (b)
|
||||
{
|
||||
case SPACE:
|
||||
_string.append((char)(0xff&b));
|
||||
_string.append((char)(0xff & b));
|
||||
break;
|
||||
|
||||
case LINE_FEED:
|
||||
if (_length > 0)
|
||||
{
|
||||
_fieldValue=takeString();
|
||||
_length=-1;
|
||||
_totalHeaderLineLength=-1;
|
||||
_fieldValue = takeString();
|
||||
_length = -1;
|
||||
_totalHeaderLineLength = -1;
|
||||
}
|
||||
setState(FieldState.FIELD);
|
||||
break;
|
||||
|
||||
default:
|
||||
_string.append((char)(0xff&b));
|
||||
if (b>SPACE || b<0)
|
||||
_length=_string.length();
|
||||
_string.append((char)(0xff & b));
|
||||
if (b > SPACE || b < 0)
|
||||
_length = _string.length();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -635,95 +635,91 @@ public class MultiPartParser
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private void handleField()
|
||||
{
|
||||
if (_fieldName!=null && _fieldValue!=null)
|
||||
if (_fieldName != null && _fieldValue != null)
|
||||
_handler.parsedField(_fieldName,_fieldValue);
|
||||
_fieldName = _fieldValue = null;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
||||
protected boolean parseOctetContent(ByteBuffer buffer)
|
||||
{
|
||||
|
||||
//Starts With
|
||||
if (_partialBoundary>0)
|
||||
|
||||
// Starts With
|
||||
if (_partialBoundary > 0)
|
||||
{
|
||||
int partial = _delimiterSearch.startsWith(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.remaining(),_partialBoundary);
|
||||
if (partial>0)
|
||||
int partial = _delimiterSearch.startsWith(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining(),_partialBoundary);
|
||||
if (partial > 0)
|
||||
{
|
||||
if (partial==_delimiterSearch.getLength())
|
||||
if (partial == _delimiterSearch.getLength())
|
||||
{
|
||||
buffer.position(buffer.position() + _delimiterSearch.getLength() - _partialBoundary);
|
||||
setState(State.DELIMITER);
|
||||
_partialBoundary = 0;
|
||||
return _handler.content(BufferUtil.EMPTY_BUFFER, true);
|
||||
return _handler.content(BufferUtil.EMPTY_BUFFER,true);
|
||||
}
|
||||
|
||||
_partialBoundary = partial;
|
||||
BufferUtil.clear(buffer);
|
||||
BufferUtil.clear(buffer);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//output up to _partialBoundary of the search pattern
|
||||
// output up to _partialBoundary of the search pattern
|
||||
ByteBuffer content = _patternBuffer.slice();
|
||||
if (_state==State.FIRST_OCTETS)
|
||||
if (_state == State.FIRST_OCTETS)
|
||||
{
|
||||
setState(State.OCTETS);
|
||||
content.position(2);
|
||||
}
|
||||
content.limit(_partialBoundary);
|
||||
_partialBoundary = 0;
|
||||
|
||||
if (_handler.content(content, false))
|
||||
|
||||
if (_handler.content(content,false))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Contains
|
||||
int delimiter = _delimiterSearch.match(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.remaining());
|
||||
if (delimiter>=0)
|
||||
int delimiter = _delimiterSearch.match(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining());
|
||||
if (delimiter >= 0)
|
||||
{
|
||||
ByteBuffer content = buffer.slice();
|
||||
content.limit(delimiter - buffer.arrayOffset() - buffer.position());
|
||||
|
||||
|
||||
buffer.position(delimiter - buffer.arrayOffset() + _delimiterSearch.getLength());
|
||||
setState(State.DELIMITER);
|
||||
|
||||
return _handler.content(content, true);
|
||||
|
||||
return _handler.content(content,true);
|
||||
}
|
||||
|
||||
|
||||
// Ends With
|
||||
_partialBoundary = _delimiterSearch.endsWith(buffer.array(), buffer.arrayOffset()+buffer.position(), buffer.remaining());
|
||||
if(_partialBoundary > 0)
|
||||
_partialBoundary = _delimiterSearch.endsWith(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining());
|
||||
if (_partialBoundary > 0)
|
||||
{
|
||||
ByteBuffer content = buffer.slice();
|
||||
content.limit(content.limit() - _partialBoundary);
|
||||
|
||||
|
||||
BufferUtil.clear(buffer);
|
||||
return _handler.content(content, false);
|
||||
return _handler.content(content,false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// There is normal content with no delimiter
|
||||
ByteBuffer content = buffer.slice();
|
||||
BufferUtil.clear(buffer);
|
||||
return _handler.content(content, false);
|
||||
return _handler.content(content,false);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private void setState(State state)
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("{} --> {}",_state,state);
|
||||
_state=state;
|
||||
_state = state;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
@ -731,47 +727,59 @@ public class MultiPartParser
|
|||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("{}:{} --> {}",_state,_fieldState,state);
|
||||
_fieldState=state;
|
||||
_fieldState = state;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s{s=%s}",
|
||||
getClass().getSimpleName(),
|
||||
_state);
|
||||
return String.format("%s{s=%s}",getClass().getSimpleName(),_state);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Event Handler interface
|
||||
* These methods return true if the caller should process the events
|
||||
* so far received (eg return from parseNext and call HttpChannel.handle).
|
||||
* If multiple callbacks are called in sequence (eg
|
||||
* headerComplete then messageComplete) from the same point in the parsing
|
||||
* then it is sufficient for the caller to process the events only once.
|
||||
/*
|
||||
* Event Handler interface These methods return true if the caller should process the events so far received (eg return from parseNext and call
|
||||
* HttpChannel.handle). If multiple callbacks are called in sequence (eg headerComplete then messageComplete) from the same point in the parsing then it is
|
||||
* sufficient for the caller to process the events only once.
|
||||
*/
|
||||
public interface Handler
|
||||
{
|
||||
public default void startPart() {}
|
||||
public default void parsedField(String name, String value) {}
|
||||
public default boolean headerComplete() {return false;}
|
||||
|
||||
public default boolean content(ByteBuffer item, boolean last) {return false;}
|
||||
|
||||
public default boolean messageComplete() {return false;}
|
||||
public default void startPart()
|
||||
{
|
||||
}
|
||||
|
||||
public default void earlyEOF() {}
|
||||
public default void parsedField(String name, String value)
|
||||
{
|
||||
}
|
||||
|
||||
public default boolean headerComplete()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public default boolean content(ByteBuffer item, boolean last)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public default boolean messageComplete()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public default void earlyEOF()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
@SuppressWarnings("serial")
|
||||
private static class IllegalCharacterException extends IllegalArgumentException
|
||||
{
|
||||
private IllegalCharacterException(State state,byte ch,ByteBuffer buffer)
|
||||
private IllegalCharacterException(State state, byte ch, ByteBuffer buffer)
|
||||
{
|
||||
super(String.format("Illegal character 0x%X",ch));
|
||||
// Bug #460642 - don't reveal buffers to end user
|
||||
|
|
|
@ -44,7 +44,7 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.Part;
|
||||
|
||||
import org.eclipse.jetty.http.MultiPartInputStreamParser.MultiPart;
|
||||
import org.eclipse.jetty.http.MultiPartFormInputStream.MultiPart;
|
||||
import org.eclipse.jetty.util.B64Code;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.hamcrest.Matchers;
|
||||
|
@ -55,7 +55,7 @@ import org.junit.Test;
|
|||
*
|
||||
*
|
||||
*/
|
||||
public class MultiPartInputStreamTest
|
||||
public class MultiPartFormInputStreamTest
|
||||
{
|
||||
private static final String FILENAME = "stuff.txt";
|
||||
protected String _contentType = "multipart/form-data, boundary=AaB03x";
|
||||
|
@ -63,7 +63,7 @@ public class MultiPartInputStreamTest
|
|||
protected String _dirname = System.getProperty("java.io.tmpdir")+File.separator+"myfiles-"+TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
protected File _tmpDir = new File(_dirname);
|
||||
|
||||
public MultiPartInputStreamTest ()
|
||||
public MultiPartFormInputStreamTest ()
|
||||
{
|
||||
_tmpDir.deleteOnExit();
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ public class MultiPartInputStreamTest
|
|||
+ "\r\n";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
||||
"multipart/form-data, boundary="+boundary,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -116,7 +116,7 @@ public class MultiPartInputStreamTest
|
|||
"--" + boundary + "--" + delimiter;
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
||||
"multipart/form-data, boundary="+boundary,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -139,7 +139,7 @@ public class MultiPartInputStreamTest
|
|||
"--" + boundary + "--" + delimiter;
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
||||
"multipart/form-data, boundary="+boundary,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -178,7 +178,7 @@ public class MultiPartInputStreamTest
|
|||
"----\r\n";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
||||
"multipart/form-data",
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -213,7 +213,7 @@ public class MultiPartInputStreamTest
|
|||
throws Exception
|
||||
{
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
||||
"Content-type: text/plain",
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -228,7 +228,7 @@ public class MultiPartInputStreamTest
|
|||
String body = "";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(body.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(body.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -277,7 +277,7 @@ public class MultiPartInputStreamTest
|
|||
};
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(is,
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(is,
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -296,7 +296,7 @@ public class MultiPartInputStreamTest
|
|||
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(whitespace.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(whitespace.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -319,7 +319,7 @@ public class MultiPartInputStreamTest
|
|||
String whitespace = " ";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(whitespace.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(whitespace.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -353,7 +353,7 @@ public class MultiPartInputStreamTest
|
|||
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(body.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(body.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -394,7 +394,7 @@ public class MultiPartInputStreamTest
|
|||
"--AaB03x--\r\n";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(body.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(body.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -422,7 +422,7 @@ public class MultiPartInputStreamTest
|
|||
throws Exception
|
||||
{
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -436,7 +436,7 @@ public class MultiPartInputStreamTest
|
|||
throws Exception
|
||||
{
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 60, 100, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -459,7 +459,7 @@ public class MultiPartInputStreamTest
|
|||
throws Exception
|
||||
{
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 60, 100, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -494,7 +494,7 @@ public class MultiPartInputStreamTest
|
|||
throws Exception
|
||||
{
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 40, 1024, 30);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -516,7 +516,7 @@ public class MultiPartInputStreamTest
|
|||
throws Exception
|
||||
{
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 40, 1024, 30);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -527,9 +527,8 @@ public class MultiPartInputStreamTest
|
|||
parts = mpis.getParts(); //caused parsing
|
||||
fail("stuff.txt should have been larger than maxFileSize");
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
catch (Throwable e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
assertTrue(e.getMessage().startsWith("Multipart Mime part"));
|
||||
}
|
||||
|
||||
|
@ -551,7 +550,7 @@ public class MultiPartInputStreamTest
|
|||
public void testPartFileNotDeleted () throws Exception
|
||||
{
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -559,7 +558,7 @@ public class MultiPartInputStreamTest
|
|||
Collection<Part> parts = mpis.getParts();
|
||||
|
||||
MultiPart part = (MultiPart)mpis.getPart("stuff");
|
||||
File stuff = ((MultiPartInputStreamParser.MultiPart)part).getFile();
|
||||
File stuff = ((MultiPartFormInputStream.MultiPart)part).getFile();
|
||||
assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file
|
||||
part.write("tptfd.txt");
|
||||
File tptfd = new File (_dirname+File.separator+"tptfd.txt");
|
||||
|
@ -574,7 +573,7 @@ public class MultiPartInputStreamTest
|
|||
public void testPartTmpFileDeletion () throws Exception
|
||||
{
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -582,7 +581,7 @@ public class MultiPartInputStreamTest
|
|||
Collection<Part> parts = mpis.getParts();
|
||||
|
||||
MultiPart part = (MultiPart)mpis.getPart("stuff");
|
||||
File stuff = ((MultiPartInputStreamParser.MultiPart)part).getFile();
|
||||
File stuff = ((MultiPartFormInputStream.MultiPart)part).getFile();
|
||||
assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file
|
||||
assertThat (stuff.exists(), is(true));
|
||||
part.cleanUp();
|
||||
|
@ -604,7 +603,7 @@ public class MultiPartInputStreamTest
|
|||
"\r\n--AaB03x--\n";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -641,7 +640,7 @@ public class MultiPartInputStreamTest
|
|||
"--AaB03x--\r";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -688,7 +687,7 @@ public class MultiPartInputStreamTest
|
|||
"--AaB03x--\r";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -729,7 +728,7 @@ public class MultiPartInputStreamTest
|
|||
}
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(baos.toByteArray()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(baos.toByteArray()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -758,7 +757,7 @@ public class MultiPartInputStreamTest
|
|||
"--TheBoundary--\r\n";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
||||
contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -780,14 +779,14 @@ public class MultiPartInputStreamTest
|
|||
"--AaB03x--\r\n";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contents.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(contents.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
mpis.setDeleteOnExit(true);
|
||||
Collection<Part> parts = mpis.getParts();
|
||||
assertThat(parts.size(), is(1));
|
||||
assertThat(((MultiPartInputStreamParser.MultiPart)parts.iterator().next()).getSubmittedFileName(), is("Taken on Aug 22 \\ 2012.jpg"));
|
||||
assertThat(((MultiPartFormInputStream.MultiPart)parts.iterator().next()).getSubmittedFileName(), is("Taken on Aug 22 \\ 2012.jpg"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -802,14 +801,14 @@ public class MultiPartInputStreamTest
|
|||
"--AaB03x--\r\n";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contents.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(contents.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
mpis.setDeleteOnExit(true);
|
||||
Collection<Part> parts = mpis.getParts();
|
||||
assertThat(parts.size(), is(1));
|
||||
assertThat(((MultiPartInputStreamParser.MultiPart)parts.iterator().next()).getSubmittedFileName(), is("c:\\this\\really\\is\\some\\path\\to\\a\\file.txt"));
|
||||
assertThat(((MultiPartFormInputStream.MultiPart)parts.iterator().next()).getSubmittedFileName(), is("c:\\this\\really\\is\\some\\path\\to\\a\\file.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -823,14 +822,14 @@ public class MultiPartInputStreamTest
|
|||
"--AaB03x--\r\n";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contents.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(contents.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
mpis.setDeleteOnExit(true);
|
||||
Collection<Part> parts = mpis.getParts();
|
||||
assertThat(parts.size(), is(1));
|
||||
assertThat(((MultiPartInputStreamParser.MultiPart)parts.iterator().next()).getSubmittedFileName(), is("c:\\this\\really\\is\\some\\path\\to\\a\\file.txt"));
|
||||
assertThat(((MultiPartFormInputStream.MultiPart)parts.iterator().next()).getSubmittedFileName(), is("c:\\this\\really\\is\\some\\path\\to\\a\\file.txt"));
|
||||
}
|
||||
|
||||
public void testMulti ()
|
||||
|
@ -862,7 +861,7 @@ public class MultiPartInputStreamTest
|
|||
"--AaB03x--\r\n";
|
||||
//all default values for multipartconfig, ie file size threshold 0
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(s.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(s.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -871,11 +870,11 @@ public class MultiPartInputStreamTest
|
|||
Collection<Part> parts = mpis.getParts();
|
||||
assertThat(parts.size(), is(2));
|
||||
Part field1 = mpis.getPart("field1"); //has a filename, should be written to a file
|
||||
File f = ((MultiPartInputStreamParser.MultiPart)field1).getFile();
|
||||
File f = ((MultiPartFormInputStream.MultiPart)field1).getFile();
|
||||
assertThat(f,notNullValue()); // longer than 100 bytes, should already be a tmp file
|
||||
|
||||
Part stuff = mpis.getPart("stuff");
|
||||
f = ((MultiPartInputStreamParser.MultiPart)stuff).getFile(); //should only be in memory, no filename
|
||||
f = ((MultiPartFormInputStream.MultiPart)stuff).getFile(); //should only be in memory, no filename
|
||||
assertThat(f, nullValue());
|
||||
}
|
||||
|
||||
|
@ -883,7 +882,7 @@ public class MultiPartInputStreamTest
|
|||
private void testMulti(String filename) throws IOException, ServletException, InterruptedException
|
||||
{
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(createMultipartRequestString(filename).getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(createMultipartRequestString(filename).getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -899,9 +898,9 @@ public class MultiPartInputStreamTest
|
|||
assertEquals("Joe Blow", new String(os.toByteArray()));
|
||||
assertEquals(8, field1.getSize());
|
||||
|
||||
assertNotNull(((MultiPartInputStreamParser.MultiPart)field1).getBytes());//in internal buffer
|
||||
assertNotNull(((MultiPartFormInputStream.MultiPart)field1).getBytes());//in internal buffer
|
||||
field1.write("field1.txt");
|
||||
assertNull(((MultiPartInputStreamParser.MultiPart)field1).getBytes());//no longer in internal buffer
|
||||
assertNull(((MultiPartFormInputStream.MultiPart)field1).getBytes());//no longer in internal buffer
|
||||
File f = new File (_dirname+File.separator+"field1.txt");
|
||||
assertTrue(f.exists());
|
||||
field1.write("another_field1.txt"); //write after having already written
|
||||
|
@ -921,7 +920,7 @@ public class MultiPartInputStreamTest
|
|||
assertThat(stuff.getHeaderNames().size(),is(2));
|
||||
assertThat(stuff.getSize(),is(51L));
|
||||
|
||||
File tmpfile = ((MultiPartInputStreamParser.MultiPart)stuff).getFile();
|
||||
File tmpfile = ((MultiPartFormInputStream.MultiPart)stuff).getFile();
|
||||
assertThat(tmpfile,notNullValue()); // longer than 50 bytes, should already be a tmp file
|
||||
assertThat(stuff.getBytes(),nullValue()); //not in an internal buffer
|
||||
assertThat(tmpfile.exists(),is(true));
|
||||
|
@ -958,7 +957,7 @@ public class MultiPartInputStreamTest
|
|||
"--AaB03x--\r\n";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(sameNames.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(sameNames.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -997,7 +996,7 @@ public class MultiPartInputStreamTest
|
|||
"--AaB03x--\r\n";
|
||||
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contentWithEncodedPart.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(contentWithEncodedPart.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
||||
|
@ -1041,7 +1040,7 @@ public class MultiPartInputStreamTest
|
|||
"truth=3Dbeauty" + "\r\n"+
|
||||
"--AaB03x--\r\n";
|
||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contentWithEncodedPart.getBytes()),
|
||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(contentWithEncodedPart.getBytes()),
|
||||
_contentType,
|
||||
config,
|
||||
_tmpDir);
|
|
@ -55,7 +55,15 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* MultiPartInputStream
|
||||
*
|
||||
* Handle a MultiPart Mime input stream, breaking it up on the boundary into files and strings.
|
||||
*
|
||||
* Non Compliance warnings are documented by the method {@link #getNonComplianceWarnings()}
|
||||
*
|
||||
* @deprecated Replaced by {@link org.eclipse.jetty.http#MultiPartFormInputStream}
|
||||
* The code for MultiPartInputStream is slower than its replacement MultiPartFormInputStream. However
|
||||
* this class accepts formats non compliant the RFC that the new MultiPartFormInputStream does not accept.
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public class MultiPartInputStreamParser
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(MultiPartInputStreamParser.class);
|
||||
|
@ -71,7 +79,7 @@ public class MultiPartInputStreamParser
|
|||
protected boolean _deleteOnExit;
|
||||
protected boolean _writeFilesWithFilenames;
|
||||
|
||||
EnumSet<NonCompliance> nonComplianceWarnings = EnumSet.noneOf(NonCompliance.class);
|
||||
private EnumSet<NonCompliance> nonComplianceWarnings = EnumSet.noneOf(NonCompliance.class);
|
||||
public enum NonCompliance
|
||||
{
|
||||
CR_TERMINATION,
|
||||
|
@ -80,15 +88,12 @@ public class MultiPartInputStreamParser
|
|||
BASE64_TRANSFER_ENCODING,
|
||||
QUOTED_PRINTABLE_TRANSFER_ENCODING
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an EnumSet of non compliances with the RFC that were accepted by this parser
|
||||
*/
|
||||
public EnumSet<NonCompliance> getNonComplianceWarnings()
|
||||
{
|
||||
EnumSet<Termination> term = ((ReadLineInputStream)_in).getLineTerminations();
|
||||
|
||||
if(term.contains(Termination.CR))
|
||||
nonComplianceWarnings.add(NonCompliance.CR_TERMINATION);
|
||||
if(term.contains(Termination.LF))
|
||||
nonComplianceWarnings.add(NonCompliance.LF_TERMINATION);
|
||||
|
||||
{
|
||||
return nonComplianceWarnings;
|
||||
}
|
||||
|
||||
|
@ -838,6 +843,13 @@ public class MultiPartInputStreamParser
|
|||
{
|
||||
while(line!=null)
|
||||
line=((ReadLineInputStream)_in).readLine();
|
||||
|
||||
EnumSet<Termination> term = ((ReadLineInputStream)_in).getLineTerminations();
|
||||
|
||||
if(term.contains(Termination.CR))
|
||||
nonComplianceWarnings.add(NonCompliance.CR_TERMINATION);
|
||||
if(term.contains(Termination.LF))
|
||||
nonComplianceWarnings.add(NonCompliance.LF_TERMINATION);
|
||||
}
|
||||
else
|
||||
throw new IOException("Incomplete parts");
|
||||
|
@ -846,6 +858,7 @@ public class MultiPartInputStreamParser
|
|||
{
|
||||
_err = e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setDeleteOnExit(boolean deleteOnExit)
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.eclipse.jetty.util.MultiPartInputStreamParser.NonCompliance;
|
|||
*
|
||||
* Read from an input stream, accepting CR/LF, LF or just CR.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ReadLineInputStream extends BufferedInputStream
|
||||
{
|
||||
boolean _seenCRLF;
|
||||
|
|
|
@ -47,7 +47,6 @@ import javax.servlet.http.Part;
|
|||
|
||||
import org.eclipse.jetty.util.MultiPartInputStreamParser.MultiPart;
|
||||
import org.eclipse.jetty.util.MultiPartInputStreamParser.NonCompliance;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
|
@ -55,6 +54,7 @@ import org.junit.Test;
|
|||
*
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MultiPartInputStreamTest
|
||||
{
|
||||
private static final String FILENAME = "stuff.txt";
|
||||
|
|
Loading…
Reference in New Issue