Merge pull request #2476 from lachlan-roberts/jetty-9.4.x-1027-MultiPart-Cleanup
MultiPart Cleanup
This commit is contained in:
commit
c865aa1be7
|
@ -447,7 +447,7 @@ public class HttpParser
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
enum CharState { ILLEGAL, CR, LF, LEGAL }
|
enum CharState { ILLEGAL, CR, LF, LEGAL }
|
||||||
private final static CharState[] __charState;
|
public final static CharState[] TOKEN_CHAR;
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
// token = 1*tchar
|
// token = 1*tchar
|
||||||
|
@ -462,38 +462,38 @@ public class HttpParser
|
||||||
// ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
|
// ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
|
||||||
// quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
// quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||||
|
|
||||||
__charState=new CharState[256];
|
TOKEN_CHAR =new CharState[256];
|
||||||
Arrays.fill(__charState,CharState.ILLEGAL);
|
Arrays.fill(TOKEN_CHAR,CharState.ILLEGAL);
|
||||||
__charState[LINE_FEED]=CharState.LF;
|
TOKEN_CHAR[LINE_FEED]=CharState.LF;
|
||||||
__charState[CARRIAGE_RETURN]=CharState.CR;
|
TOKEN_CHAR[CARRIAGE_RETURN]=CharState.CR;
|
||||||
__charState[TAB]=CharState.LEGAL;
|
TOKEN_CHAR[TAB]=CharState.LEGAL;
|
||||||
__charState[SPACE]=CharState.LEGAL;
|
TOKEN_CHAR[SPACE]=CharState.LEGAL;
|
||||||
|
|
||||||
__charState['!']=CharState.LEGAL;
|
TOKEN_CHAR['!']=CharState.LEGAL;
|
||||||
__charState['#']=CharState.LEGAL;
|
TOKEN_CHAR['#']=CharState.LEGAL;
|
||||||
__charState['$']=CharState.LEGAL;
|
TOKEN_CHAR['$']=CharState.LEGAL;
|
||||||
__charState['%']=CharState.LEGAL;
|
TOKEN_CHAR['%']=CharState.LEGAL;
|
||||||
__charState['&']=CharState.LEGAL;
|
TOKEN_CHAR['&']=CharState.LEGAL;
|
||||||
__charState['\'']=CharState.LEGAL;
|
TOKEN_CHAR['\'']=CharState.LEGAL;
|
||||||
__charState['*']=CharState.LEGAL;
|
TOKEN_CHAR['*']=CharState.LEGAL;
|
||||||
__charState['+']=CharState.LEGAL;
|
TOKEN_CHAR['+']=CharState.LEGAL;
|
||||||
__charState['-']=CharState.LEGAL;
|
TOKEN_CHAR['-']=CharState.LEGAL;
|
||||||
__charState['.']=CharState.LEGAL;
|
TOKEN_CHAR['.']=CharState.LEGAL;
|
||||||
__charState['^']=CharState.LEGAL;
|
TOKEN_CHAR['^']=CharState.LEGAL;
|
||||||
__charState['_']=CharState.LEGAL;
|
TOKEN_CHAR['_']=CharState.LEGAL;
|
||||||
__charState['`']=CharState.LEGAL;
|
TOKEN_CHAR['`']=CharState.LEGAL;
|
||||||
__charState['|']=CharState.LEGAL;
|
TOKEN_CHAR['|']=CharState.LEGAL;
|
||||||
__charState['~']=CharState.LEGAL;
|
TOKEN_CHAR['~']=CharState.LEGAL;
|
||||||
|
|
||||||
__charState['"']=CharState.LEGAL;
|
TOKEN_CHAR['"']=CharState.LEGAL;
|
||||||
|
|
||||||
__charState['\\']=CharState.LEGAL;
|
TOKEN_CHAR['\\']=CharState.LEGAL;
|
||||||
__charState['(']=CharState.LEGAL;
|
TOKEN_CHAR['(']=CharState.LEGAL;
|
||||||
__charState[')']=CharState.LEGAL;
|
TOKEN_CHAR[')']=CharState.LEGAL;
|
||||||
Arrays.fill(__charState,0x21,0x27+1,CharState.LEGAL);
|
Arrays.fill(TOKEN_CHAR,0x21,0x27+1,CharState.LEGAL);
|
||||||
Arrays.fill(__charState,0x2A,0x5B+1,CharState.LEGAL);
|
Arrays.fill(TOKEN_CHAR,0x2A,0x5B+1,CharState.LEGAL);
|
||||||
Arrays.fill(__charState,0x5D,0x7E+1,CharState.LEGAL);
|
Arrays.fill(TOKEN_CHAR,0x5D,0x7E+1,CharState.LEGAL);
|
||||||
Arrays.fill(__charState,0x80,0xFF+1,CharState.LEGAL);
|
Arrays.fill(TOKEN_CHAR,0x80,0xFF+1,CharState.LEGAL);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,7 +502,7 @@ public class HttpParser
|
||||||
{
|
{
|
||||||
byte ch = buffer.get();
|
byte ch = buffer.get();
|
||||||
|
|
||||||
CharState s = __charState[0xff & ch];
|
CharState s = TOKEN_CHAR[0xff & ch];
|
||||||
switch(s)
|
switch(s)
|
||||||
{
|
{
|
||||||
case ILLEGAL:
|
case ILLEGAL:
|
||||||
|
|
|
@ -52,7 +52,7 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MultiPartInputStream
|
* MultiPartInputStream
|
||||||
*
|
* <p>
|
||||||
* Handle a MultiPart Mime input stream, breaking it up on the boundary into files and strings.
|
* Handle a MultiPart Mime input stream, breaking it up on the boundary into files and strings.
|
||||||
*
|
*
|
||||||
* @see <a href="https://tools.ietf.org/html/rfc7578">https://tools.ietf.org/html/rfc7578</a>
|
* @see <a href="https://tools.ietf.org/html/rfc7578">https://tools.ietf.org/html/rfc7578</a>
|
||||||
|
@ -60,19 +60,18 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
public class MultiPartFormInputStream
|
public class MultiPartFormInputStream
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(MultiPartFormInputStream.class);
|
private static final Logger LOG = Log.getLogger(MultiPartFormInputStream.class);
|
||||||
private final int _bufferSize = 16 * 1024;
|
private static final MultiMap<Part> EMPTY_MAP = new MultiMap<>(Collections.emptyMap());
|
||||||
public static final MultipartConfigElement __DEFAULT_MULTIPART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
|
private InputStream _in;
|
||||||
public static final MultiMap<Part> EMPTY_MAP = new MultiMap<>(Collections.emptyMap());
|
private MultipartConfigElement _config;
|
||||||
protected InputStream _in;
|
private String _contentType;
|
||||||
protected MultipartConfigElement _config;
|
private MultiMap<Part> _parts;
|
||||||
protected String _contentType;
|
private Throwable _err;
|
||||||
protected MultiMap<Part> _parts;
|
private File _tmpDir;
|
||||||
protected Throwable _err;
|
private File _contextTmpDir;
|
||||||
protected File _tmpDir;
|
private boolean _deleteOnExit;
|
||||||
protected File _contextTmpDir;
|
private boolean _writeFilesWithFilenames;
|
||||||
protected boolean _deleteOnExit;
|
private boolean _parsed;
|
||||||
protected boolean _writeFilesWithFilenames;
|
private int _bufferSize = 16 * 1024;
|
||||||
protected boolean _parsed;
|
|
||||||
|
|
||||||
public class MultiPart implements Part
|
public class MultiPart implements Part
|
||||||
{
|
{
|
||||||
|
@ -86,7 +85,7 @@ public class MultiPartFormInputStream
|
||||||
protected long _size = 0;
|
protected long _size = 0;
|
||||||
protected boolean _temporary = true;
|
protected boolean _temporary = true;
|
||||||
|
|
||||||
public MultiPart(String name, String filename) throws IOException
|
public MultiPart(String name, String filename)
|
||||||
{
|
{
|
||||||
_name = name;
|
_name = name;
|
||||||
_filename = filename;
|
_filename = filename;
|
||||||
|
@ -95,7 +94,7 @@ public class MultiPartFormInputStream
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("Part{n=%s,fn=%s,ct=%s,s=%d,tmp=%b,file=%s}",_name,_filename,_contentType,_size,_temporary,_file);
|
return String.format("Part{n=%s,fn=%s,ct=%s,s=%d,tmp=%b,file=%s}", _name, _filename, _contentType, _size, _temporary, _file);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setContentType(String contentType)
|
protected void setContentType(String contentType)
|
||||||
|
@ -147,7 +146,7 @@ public class MultiPartFormInputStream
|
||||||
&& _size + length > MultiPartFormInputStream.this._config.getFileSizeThreshold() && _file == null)
|
&& _size + length > MultiPartFormInputStream.this._config.getFileSizeThreshold() && _file == null)
|
||||||
createFile();
|
createFile();
|
||||||
|
|
||||||
_out.write(bytes,offset,length);
|
_out.write(bytes, offset, length);
|
||||||
_size += length;
|
_size += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,9 +158,9 @@ public class MultiPartFormInputStream
|
||||||
final boolean USER = true;
|
final boolean USER = true;
|
||||||
final boolean WORLD = false;
|
final boolean WORLD = false;
|
||||||
|
|
||||||
_file = File.createTempFile("MultiPart","",MultiPartFormInputStream.this._tmpDir);
|
_file = File.createTempFile("MultiPart", "", MultiPartFormInputStream.this._tmpDir);
|
||||||
_file.setReadable(false,WORLD); // (reset) disable it for everyone first
|
_file.setReadable(false, WORLD); // (reset) disable it for everyone first
|
||||||
_file.setReadable(true,USER); // enable for user only
|
_file.setReadable(true, USER); // enable for user only
|
||||||
|
|
||||||
if (_deleteOnExit)
|
if (_deleteOnExit)
|
||||||
_file.deleteOnExit();
|
_file.deleteOnExit();
|
||||||
|
@ -184,47 +183,32 @@ public class MultiPartFormInputStream
|
||||||
_headers = headers;
|
_headers = headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see javax.servlet.http.Part#getContentType()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getContentType()
|
public String getContentType()
|
||||||
{
|
{
|
||||||
return _contentType;
|
return _contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see javax.servlet.http.Part#getHeader(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getHeader(String name)
|
public String getHeader(String name)
|
||||||
{
|
{
|
||||||
if (name == null)
|
if (name == null)
|
||||||
return null;
|
return null;
|
||||||
return _headers.getValue(StringUtil.asciiToLowerCase(name),0);
|
return _headers.getValue(StringUtil.asciiToLowerCase(name), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see javax.servlet.http.Part#getHeaderNames()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<String> getHeaderNames()
|
public Collection<String> getHeaderNames()
|
||||||
{
|
{
|
||||||
return _headers.keySet();
|
return _headers.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see javax.servlet.http.Part#getHeaders(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<String> getHeaders(String name)
|
public Collection<String> getHeaders(String name)
|
||||||
{
|
{
|
||||||
return _headers.getValues(name);
|
return _headers.getValues(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see javax.servlet.http.Part#getInputStream()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getInputStream() throws IOException
|
public InputStream getInputStream() throws IOException
|
||||||
{
|
{
|
||||||
|
@ -236,13 +220,10 @@ public class MultiPartFormInputStream
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// part content is in memory
|
// part content is in memory
|
||||||
return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size());
|
return new ByteArrayInputStream(_bout.getBuf(), 0, _bout.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see javax.servlet.http.Part#getSubmittedFileName()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getSubmittedFileName()
|
public String getSubmittedFileName()
|
||||||
{
|
{
|
||||||
|
@ -256,27 +237,18 @@ public class MultiPartFormInputStream
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see javax.servlet.http.Part#getName()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getName()
|
public String getName()
|
||||||
{
|
{
|
||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see javax.servlet.http.Part#getSize()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public long getSize()
|
public long getSize()
|
||||||
{
|
{
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see javax.servlet.http.Part#write(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void write(String fileName) throws IOException
|
public void write(String fileName) throws IOException
|
||||||
{
|
{
|
||||||
|
@ -285,19 +257,15 @@ public class MultiPartFormInputStream
|
||||||
_temporary = false;
|
_temporary = false;
|
||||||
|
|
||||||
// part data is only in the ByteArrayOutputStream and never been written to disk
|
// part data is only in the ByteArrayOutputStream and never been written to disk
|
||||||
_file = new File(_tmpDir,fileName);
|
_file = new File(_tmpDir, fileName);
|
||||||
|
|
||||||
BufferedOutputStream bos = null;
|
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(_file)))
|
||||||
try
|
|
||||||
{
|
{
|
||||||
bos = new BufferedOutputStream(new FileOutputStream(_file));
|
|
||||||
_bout.writeTo(bos);
|
_bout.writeTo(bos);
|
||||||
bos.flush();
|
bos.flush();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (bos != null)
|
|
||||||
bos.close();
|
|
||||||
_bout = null;
|
_bout = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,33 +276,32 @@ public class MultiPartFormInputStream
|
||||||
|
|
||||||
Path src = _file.toPath();
|
Path src = _file.toPath();
|
||||||
Path target = src.resolveSibling(fileName);
|
Path target = src.resolveSibling(fileName);
|
||||||
Files.move(src,target,StandardCopyOption.REPLACE_EXISTING);
|
Files.move(src, target, StandardCopyOption.REPLACE_EXISTING);
|
||||||
_file = target.toFile();
|
_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
|
@Override
|
||||||
public void delete() throws IOException
|
public void delete() throws IOException
|
||||||
{
|
{
|
||||||
if (_file != null && _file.exists())
|
if (_file != null && _file.exists())
|
||||||
_file.delete();
|
if (!_file.delete())
|
||||||
|
throw new IOException("Could Not Delete File");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only remove tmp files.
|
* Only remove tmp files.
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException if unable to delete the file
|
||||||
* if unable to delete the file
|
|
||||||
*/
|
*/
|
||||||
public void cleanUp() throws IOException
|
public void cleanUp() throws IOException
|
||||||
{
|
{
|
||||||
if (_temporary && _file != null && _file.exists())
|
if (_temporary && _file != null && _file.exists())
|
||||||
_file.delete();
|
if (!_file.delete())
|
||||||
|
throw new IOException("Could Not Delete File");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -359,14 +326,10 @@ public class MultiPartFormInputStream
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param in
|
* @param in Request input stream
|
||||||
* Request input stream
|
* @param contentType Content-Type header
|
||||||
* @param contentType
|
* @param config MultipartConfigElement
|
||||||
* Content-Type header
|
* @param contextTmpDir javax.servlet.context.tempdir
|
||||||
* @param config
|
|
||||||
* MultipartConfigElement
|
|
||||||
* @param contextTmpDir
|
|
||||||
* javax.servlet.context.tempdir
|
|
||||||
*/
|
*/
|
||||||
public MultiPartFormInputStream(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
|
public MultiPartFormInputStream(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
|
||||||
{
|
{
|
||||||
|
@ -402,7 +365,7 @@ public class MultiPartFormInputStream
|
||||||
Collection<List<Part>> values = _parts.values();
|
Collection<List<Part>> values = _parts.values();
|
||||||
for (List<Part> partList : values)
|
for (List<Part> partList : values)
|
||||||
{
|
{
|
||||||
if(partList.size() != 0)
|
if (partList.size() != 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,7 +387,7 @@ public class MultiPartFormInputStream
|
||||||
List<Part> parts = new ArrayList<>();
|
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);
|
parts.addAll(asList);
|
||||||
}
|
}
|
||||||
return parts;
|
return parts;
|
||||||
|
@ -447,8 +410,8 @@ public class MultiPartFormInputStream
|
||||||
{
|
{
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
MultiException err = new MultiException();
|
|
||||||
|
|
||||||
|
MultiException err = null;
|
||||||
for (Part p : parts)
|
for (Part p : parts)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -457,11 +420,14 @@ public class MultiPartFormInputStream
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
if (err == null)
|
||||||
|
err = new MultiException();
|
||||||
err.add(e);
|
err.add(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_parts.clear();
|
_parts.clear();
|
||||||
|
|
||||||
|
if (err != null)
|
||||||
err.ifExceptionThrowRuntime();
|
err.ifExceptionThrowRuntime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,8 +435,7 @@ public class MultiPartFormInputStream
|
||||||
* Parse, if necessary, the multipart data and return the list of Parts.
|
* Parse, if necessary, the multipart data and return the list of Parts.
|
||||||
*
|
*
|
||||||
* @return the parts
|
* @return the parts
|
||||||
* @throws IOException
|
* @throws IOException if unable to get the parts
|
||||||
* if unable to get the parts
|
|
||||||
*/
|
*/
|
||||||
public Collection<Part> getParts() throws IOException
|
public Collection<Part> getParts() throws IOException
|
||||||
{
|
{
|
||||||
|
@ -482,7 +447,7 @@ public class MultiPartFormInputStream
|
||||||
List<Part> parts = new ArrayList<>();
|
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);
|
parts.addAll(asList);
|
||||||
}
|
}
|
||||||
return parts;
|
return parts;
|
||||||
|
@ -491,25 +456,22 @@ public class MultiPartFormInputStream
|
||||||
/**
|
/**
|
||||||
* Get the named Part.
|
* Get the named Part.
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name the part name
|
||||||
* the part name
|
|
||||||
* @return the parts
|
* @return the parts
|
||||||
* @throws IOException
|
* @throws IOException if unable to get the part
|
||||||
* if unable to get the part
|
|
||||||
*/
|
*/
|
||||||
public Part getPart(String name) throws IOException
|
public Part getPart(String name) throws IOException
|
||||||
{
|
{
|
||||||
if(!_parsed)
|
if (!_parsed)
|
||||||
parse();
|
parse();
|
||||||
throwIfError();
|
throwIfError();
|
||||||
return _parts.getValue(name,0);
|
return _parts.getValue(name, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throws an exception if one has been latched.
|
* Throws an exception if one has been latched.
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException the exception (if present)
|
||||||
* the exception (if present)
|
|
||||||
*/
|
*/
|
||||||
protected void throwIfError() throws IOException
|
protected void throwIfError() throws IOException
|
||||||
{
|
{
|
||||||
|
@ -526,7 +488,6 @@ public class MultiPartFormInputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse, if necessary, the multipart stream.
|
* Parse, if necessary, the multipart stream.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
protected void parse()
|
protected void parse()
|
||||||
{
|
{
|
||||||
|
@ -537,7 +498,6 @@ public class MultiPartFormInputStream
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
// initialize
|
// initialize
|
||||||
_parts = new MultiMap<>();
|
_parts = new MultiMap<>();
|
||||||
|
|
||||||
|
@ -556,7 +516,7 @@ public class MultiPartFormInputStream
|
||||||
if (f.isAbsolute())
|
if (f.isAbsolute())
|
||||||
_tmpDir = f;
|
_tmpDir = f;
|
||||||
else
|
else
|
||||||
_tmpDir = new File(_contextTmpDir,_config.getLocation());
|
_tmpDir = new File(_contextTmpDir, _config.getLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_tmpDir.exists())
|
if (!_tmpDir.exists())
|
||||||
|
@ -566,21 +526,16 @@ public class MultiPartFormInputStream
|
||||||
int bstart = _contentType.indexOf("boundary=");
|
int bstart = _contentType.indexOf("boundary=");
|
||||||
if (bstart >= 0)
|
if (bstart >= 0)
|
||||||
{
|
{
|
||||||
int bend = _contentType.indexOf(";",bstart);
|
int bend = _contentType.indexOf(";", bstart);
|
||||||
bend = (bend < 0?_contentType.length():bend);
|
bend = (bend < 0 ? _contentType.length() : bend);
|
||||||
contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart,bend)).trim());
|
contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart, bend)).trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
Handler handler = new Handler();
|
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];
|
byte[] data = new byte[_bufferSize];
|
||||||
int len = 0;
|
int len;
|
||||||
|
|
||||||
/*
|
|
||||||
* keep running total of size of bytes read from input and throw an exception if exceeds MultipartConfigElement._maxRequestSize
|
|
||||||
*/
|
|
||||||
long total = 0;
|
long total = 0;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
|
@ -590,6 +545,8 @@ public class MultiPartFormInputStream
|
||||||
|
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// keep running total of size of bytes read from input and throw an exception if exceeds MultipartConfigElement._maxRequestSize
|
||||||
total += len;
|
total += len;
|
||||||
if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
|
if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
|
||||||
{
|
{
|
||||||
|
@ -599,16 +556,16 @@ public class MultiPartFormInputStream
|
||||||
|
|
||||||
ByteBuffer buffer = BufferUtil.toBuffer(data);
|
ByteBuffer buffer = BufferUtil.toBuffer(data);
|
||||||
buffer.limit(len);
|
buffer.limit(len);
|
||||||
if (parser.parse(buffer,false))
|
if (parser.parse(buffer, false))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(buffer.hasRemaining())
|
if (buffer.hasRemaining())
|
||||||
throw new IllegalStateException("Buffer did not fully consume");
|
throw new IllegalStateException("Buffer did not fully consume");
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (len == -1)
|
else if (len == -1)
|
||||||
{
|
{
|
||||||
parser.parse(BufferUtil.EMPTY_BUFFER,true);
|
parser.parse(BufferUtil.EMPTY_BUFFER, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,16 +588,14 @@ public class MultiPartFormInputStream
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
{
|
{
|
||||||
LOG.debug("Parsing Complete {} err={}",parser,_err);
|
LOG.debug("Parsing Complete {} err={}", parser, _err);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
_err = e;
|
_err = e;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Handler implements MultiPartParser.Handler
|
class Handler implements MultiPartParser.Handler
|
||||||
|
@ -660,7 +615,7 @@ public class MultiPartFormInputStream
|
||||||
public void parsedField(String key, String value)
|
public void parsedField(String key, String value)
|
||||||
{
|
{
|
||||||
// Add to headers and mark if one of these fields. //
|
// Add to headers and mark if one of these fields. //
|
||||||
headers.put(StringUtil.asciiToLowerCase(key),value);
|
headers.put(StringUtil.asciiToLowerCase(key), value);
|
||||||
if (key.equalsIgnoreCase("content-disposition"))
|
if (key.equalsIgnoreCase("content-disposition"))
|
||||||
contentDisposition = value;
|
contentDisposition = value;
|
||||||
else if (key.equalsIgnoreCase("content-type"))
|
else if (key.equalsIgnoreCase("content-type"))
|
||||||
|
@ -674,9 +629,9 @@ public class MultiPartFormInputStream
|
||||||
@Override
|
@Override
|
||||||
public boolean headerComplete()
|
public boolean headerComplete()
|
||||||
{
|
{
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
{
|
{
|
||||||
LOG.debug("headerComplete {}",this);
|
LOG.debug("headerComplete {}", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -688,7 +643,7 @@ public class MultiPartFormInputStream
|
||||||
throw new IOException("Missing content-disposition");
|
throw new IOException("Missing content-disposition");
|
||||||
}
|
}
|
||||||
|
|
||||||
QuotedStringTokenizer tok = new QuotedStringTokenizer(contentDisposition,";",false,true);
|
QuotedStringTokenizer tok = new QuotedStringTokenizer(contentDisposition, ";", false, true);
|
||||||
String name = null;
|
String name = null;
|
||||||
String filename = null;
|
String filename = null;
|
||||||
while (tok.hasMoreTokens())
|
while (tok.hasMoreTokens())
|
||||||
|
@ -717,10 +672,10 @@ public class MultiPartFormInputStream
|
||||||
|
|
||||||
|
|
||||||
// create the new part
|
// create the new part
|
||||||
_part = new MultiPart(name,filename);
|
_part = new MultiPart(name, filename);
|
||||||
_part.setHeaders(headers);
|
_part.setHeaders(headers);
|
||||||
_part.setContentType(contentType);
|
_part.setContentType(contentType);
|
||||||
_parts.add(name,_part);
|
_parts.add(name, _part);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -744,14 +699,14 @@ public class MultiPartFormInputStream
|
||||||
@Override
|
@Override
|
||||||
public boolean content(ByteBuffer buffer, boolean last)
|
public boolean content(ByteBuffer buffer, boolean last)
|
||||||
{
|
{
|
||||||
if(_part == null)
|
if (_part == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (BufferUtil.hasContent(buffer))
|
if (BufferUtil.hasContent(buffer))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_part.write(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining());
|
_part.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
@ -786,7 +741,7 @@ public class MultiPartFormInputStream
|
||||||
public void earlyEOF()
|
public void earlyEOF()
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Early EOF {}",MultiPartFormInputStream.this);
|
LOG.debug("Early EOF {}", MultiPartFormInputStream.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset()
|
public void reset()
|
||||||
|
@ -819,7 +774,7 @@ public class MultiPartFormInputStream
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
private String value(String nameEqualsValue)
|
private static String value(String nameEqualsValue)
|
||||||
{
|
{
|
||||||
int idx = nameEqualsValue.indexOf('=');
|
int idx = nameEqualsValue.indexOf('=');
|
||||||
String value = nameEqualsValue.substring(idx + 1).trim();
|
String value = nameEqualsValue.substring(idx + 1).trim();
|
||||||
|
@ -827,7 +782,7 @@ public class MultiPartFormInputStream
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
private String filenameValue(String nameEqualsValue)
|
private static String filenameValue(String nameEqualsValue)
|
||||||
{
|
{
|
||||||
int idx = nameEqualsValue.indexOf('=');
|
int idx = nameEqualsValue.indexOf('=');
|
||||||
String value = nameEqualsValue.substring(idx + 1).trim();
|
String value = nameEqualsValue.substring(idx + 1).trim();
|
||||||
|
@ -841,7 +796,7 @@ public class MultiPartFormInputStream
|
||||||
value = value.substring(1);
|
value = value.substring(1);
|
||||||
char last = value.charAt(value.length() - 1);
|
char last = value.charAt(value.length() - 1);
|
||||||
if (last == '"' || last == '\'')
|
if (last == '"' || last == '\'')
|
||||||
value = value.substring(0,value.length() - 1);
|
value = value.substring(0, value.length() - 1);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -850,7 +805,22 @@ public class MultiPartFormInputStream
|
||||||
// form a valid escape sequence to remain as many browsers
|
// form a valid escape sequence to remain as many browsers
|
||||||
// even on *nix systems will not escape a filename containing
|
// even on *nix systems will not escape a filename containing
|
||||||
// backslashes
|
// backslashes
|
||||||
return QuotedStringTokenizer.unquoteOnly(value,true);
|
return QuotedStringTokenizer.unquoteOnly(value, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the size of buffer used to read data from the input stream
|
||||||
|
*/
|
||||||
|
public int getBufferSize()
|
||||||
|
{
|
||||||
|
return _bufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bufferSize the size of buffer used to read data from the input stream
|
||||||
|
*/
|
||||||
|
public void setBufferSize(int bufferSize)
|
||||||
|
{
|
||||||
|
_bufferSize = bufferSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ package org.eclipse.jetty.http;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpParser.RequestHandler;
|
import org.eclipse.jetty.http.HttpParser.RequestHandler;
|
||||||
|
@ -31,7 +30,9 @@ import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/** A parser for MultiPart content type.
|
|
||||||
|
/**
|
||||||
|
* A parser for MultiPart content type.
|
||||||
*
|
*
|
||||||
* @see <a href="https://tools.ietf.org/html/rfc2046#section-5.1">https://tools.ietf.org/html/rfc2046#section-5.1</a>
|
* @see <a href="https://tools.ietf.org/html/rfc2046#section-5.1">https://tools.ietf.org/html/rfc2046#section-5.1</a>
|
||||||
* @see <a href="https://tools.ietf.org/html/rfc2045">https://tools.ietf.org/html/rfc2045</a>
|
* @see <a href="https://tools.ietf.org/html/rfc2045">https://tools.ietf.org/html/rfc2045</a>
|
||||||
|
@ -40,14 +41,11 @@ public class MultiPartParser
|
||||||
{
|
{
|
||||||
public static final Logger LOG = Log.getLogger(MultiPartParser.class);
|
public static final Logger LOG = Log.getLogger(MultiPartParser.class);
|
||||||
|
|
||||||
static final byte COLON = (byte)':';
|
private static final byte COLON = (byte)':';
|
||||||
static final byte TAB = 0x09;
|
private static final byte TAB = 0x09;
|
||||||
static final byte LINE_FEED = 0x0A;
|
private static final byte LINE_FEED = 0x0A;
|
||||||
static final byte CARRIAGE_RETURN = 0x0D;
|
private static final byte CARRIAGE_RETURN = 0x0D;
|
||||||
static final byte SPACE = 0x20;
|
private static final byte SPACE = 0x20;
|
||||||
static final byte[] CRLF =
|
|
||||||
{ CARRIAGE_RETURN, LINE_FEED };
|
|
||||||
static final byte SEMI_COLON = (byte)';';
|
|
||||||
|
|
||||||
// States
|
// States
|
||||||
public enum FieldState
|
public enum FieldState
|
||||||
|
@ -73,7 +71,8 @@ public class MultiPartParser
|
||||||
END
|
END
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static EnumSet<State> __delimiterStates = EnumSet.of(State.DELIMITER,State.DELIMITER_CLOSE,State.DELIMITER_PADDING);
|
private final static EnumSet<State> __delimiterStates = EnumSet.of(State.DELIMITER, State.DELIMITER_CLOSE, State.DELIMITER_PADDING);
|
||||||
|
private final static int MAX_HEADER_LINE_LENGTH = 998;
|
||||||
|
|
||||||
private final boolean DEBUG = LOG.isDebugEnabled();
|
private final boolean DEBUG = LOG.isDebugEnabled();
|
||||||
private final Handler _handler;
|
private final Handler _handler;
|
||||||
|
@ -92,7 +91,6 @@ public class MultiPartParser
|
||||||
private int _length;
|
private int _length;
|
||||||
|
|
||||||
private int _totalHeaderLineLength = -1;
|
private int _totalHeaderLineLength = -1;
|
||||||
private int _maxHeaderLineLength = 998;
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
public MultiPartParser(Handler handler, String boundary)
|
public MultiPartParser(Handler handler, String boundary)
|
||||||
|
@ -130,62 +128,7 @@ public class MultiPartParser
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
enum CharState
|
private static boolean hasNextByte(ByteBuffer buffer)
|
||||||
{
|
|
||||||
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 )
|
|
||||||
|
|
||||||
__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['!'] = 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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
|
||||||
private boolean hasNextByte(ByteBuffer buffer)
|
|
||||||
{
|
{
|
||||||
return BufferUtil.hasContent(buffer);
|
return BufferUtil.hasContent(buffer);
|
||||||
}
|
}
|
||||||
|
@ -196,7 +139,7 @@ public class MultiPartParser
|
||||||
|
|
||||||
byte ch = buffer.get();
|
byte ch = buffer.get();
|
||||||
|
|
||||||
CharState s = __charState[0xff & ch];
|
HttpParser.CharState s = HttpParser.TOKEN_CHAR[0xff & ch];
|
||||||
switch (s)
|
switch (s)
|
||||||
{
|
{
|
||||||
case LF:
|
case LF:
|
||||||
|
@ -223,7 +166,7 @@ public class MultiPartParser
|
||||||
|
|
||||||
case ILLEGAL:
|
case ILLEGAL:
|
||||||
default:
|
default:
|
||||||
throw new IllegalCharacterException(_state,ch,buffer);
|
throw new IllegalCharacterException(_state, ch, buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,14 +186,15 @@ public class MultiPartParser
|
||||||
{
|
{
|
||||||
String s = _string.toString();
|
String s = _string.toString();
|
||||||
// trim trailing whitespace.
|
// trim trailing whitespace.
|
||||||
if (s.length()>_length)
|
if (s.length() > _length)
|
||||||
s = s.substring(0,_length);
|
s = s.substring(0, _length);
|
||||||
_string.reset();
|
_string.reset();
|
||||||
_length = -1;
|
_length = -1;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse until next Event.
|
* Parse until next Event.
|
||||||
*
|
*
|
||||||
|
@ -261,7 +205,7 @@ public class MultiPartParser
|
||||||
public boolean parse(ByteBuffer buffer, boolean last)
|
public boolean parse(ByteBuffer buffer, boolean last)
|
||||||
{
|
{
|
||||||
boolean handle = false;
|
boolean handle = false;
|
||||||
while (handle == false && BufferUtil.hasContent(buffer))
|
while (!handle && BufferUtil.hasContent(buffer))
|
||||||
{
|
{
|
||||||
switch (_state)
|
switch (_state)
|
||||||
{
|
{
|
||||||
|
@ -304,14 +248,14 @@ public class MultiPartParser
|
||||||
{
|
{
|
||||||
_state = State.END;
|
_state = State.END;
|
||||||
|
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("messageComplete {}", this);
|
LOG.debug("messageComplete {}", this);
|
||||||
|
|
||||||
return _handler.messageComplete();
|
return _handler.messageComplete();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("earlyEOF {}", this);
|
LOG.debug("earlyEOF {}", this);
|
||||||
|
|
||||||
_handler.earlyEOF();
|
_handler.earlyEOF();
|
||||||
|
@ -327,7 +271,7 @@ public class MultiPartParser
|
||||||
{
|
{
|
||||||
if (_partialBoundary > 0)
|
if (_partialBoundary > 0)
|
||||||
{
|
{
|
||||||
int partial = _delimiterSearch.startsWith(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining(),_partialBoundary);
|
int partial = _delimiterSearch.startsWith(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining(), _partialBoundary);
|
||||||
if (partial > 0)
|
if (partial > 0)
|
||||||
{
|
{
|
||||||
if (partial == _delimiterSearch.getLength())
|
if (partial == _delimiterSearch.getLength())
|
||||||
|
@ -346,7 +290,7 @@ public class MultiPartParser
|
||||||
_partialBoundary = 0;
|
_partialBoundary = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int delimiter = _delimiterSearch.match(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining());
|
int delimiter = _delimiterSearch.match(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
|
||||||
if (delimiter >= 0)
|
if (delimiter >= 0)
|
||||||
{
|
{
|
||||||
buffer.position(delimiter - buffer.arrayOffset() + _delimiterSearch.getLength());
|
buffer.position(delimiter - buffer.arrayOffset() + _delimiterSearch.getLength());
|
||||||
|
@ -354,10 +298,8 @@ public class MultiPartParser
|
||||||
return;
|
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);
|
BufferUtil.clear(buffer);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
|
@ -373,8 +315,8 @@ public class MultiPartParser
|
||||||
{
|
{
|
||||||
setState(State.BODY_PART);
|
setState(State.BODY_PART);
|
||||||
|
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("startPart {}",this);
|
LOG.debug("startPart {}", this);
|
||||||
|
|
||||||
_handler.startPart();
|
_handler.startPart();
|
||||||
return;
|
return;
|
||||||
|
@ -400,7 +342,6 @@ public class MultiPartParser
|
||||||
|
|
||||||
case DELIMITER_PADDING:
|
case DELIMITER_PADDING:
|
||||||
default:
|
default:
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -422,7 +363,7 @@ public class MultiPartParser
|
||||||
if (b != LINE_FEED)
|
if (b != LINE_FEED)
|
||||||
_totalHeaderLineLength++;
|
_totalHeaderLineLength++;
|
||||||
|
|
||||||
if (_totalHeaderLineLength > _maxHeaderLineLength)
|
if (_totalHeaderLineLength > MAX_HEADER_LINE_LENGTH)
|
||||||
throw new IllegalStateException("Header Line Exceeded Max Length");
|
throw new IllegalStateException("Header Line Exceeded Max Length");
|
||||||
|
|
||||||
switch (_fieldState)
|
switch (_fieldState)
|
||||||
|
@ -460,7 +401,7 @@ public class MultiPartParser
|
||||||
setState(State.FIRST_OCTETS);
|
setState(State.FIRST_OCTETS);
|
||||||
_partialBoundary = 2; // CRLF is option for empty parts
|
_partialBoundary = 2; // CRLF is option for empty parts
|
||||||
|
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("headerComplete {}", this);
|
LOG.debug("headerComplete {}", this);
|
||||||
|
|
||||||
if (_handler.headerComplete())
|
if (_handler.headerComplete())
|
||||||
|
@ -498,7 +439,7 @@ public class MultiPartParser
|
||||||
|
|
||||||
case LINE_FEED:
|
case LINE_FEED:
|
||||||
{
|
{
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Line Feed in Name {}", this);
|
LOG.debug("Line Feed in Name {}", this);
|
||||||
|
|
||||||
handleField();
|
handleField();
|
||||||
|
@ -533,7 +474,7 @@ public class MultiPartParser
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalCharacterException(_state,b,buffer);
|
throw new IllegalCharacterException(_state, b, buffer);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -596,11 +537,11 @@ public class MultiPartParser
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
private void handleField()
|
private void handleField()
|
||||||
{
|
{
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("parsedField: _fieldName={} _fieldValue={} {}", _fieldName, _fieldValue, this);
|
LOG.debug("parsedField: _fieldName={} _fieldValue={} {}", _fieldName, _fieldValue, this);
|
||||||
|
|
||||||
if (_fieldName != null && _fieldValue != null)
|
if (_fieldName != null && _fieldValue != null)
|
||||||
_handler.parsedField(_fieldName,_fieldValue);
|
_handler.parsedField(_fieldName, _fieldValue);
|
||||||
_fieldName = _fieldValue = null;
|
_fieldName = _fieldValue = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,7 +553,7 @@ public class MultiPartParser
|
||||||
// Starts With
|
// Starts With
|
||||||
if (_partialBoundary > 0)
|
if (_partialBoundary > 0)
|
||||||
{
|
{
|
||||||
int partial = _delimiterSearch.startsWith(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining(),_partialBoundary);
|
int partial = _delimiterSearch.startsWith(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining(), _partialBoundary);
|
||||||
if (partial > 0)
|
if (partial > 0)
|
||||||
{
|
{
|
||||||
if (partial == _delimiterSearch.getLength())
|
if (partial == _delimiterSearch.getLength())
|
||||||
|
@ -621,10 +562,10 @@ public class MultiPartParser
|
||||||
setState(State.DELIMITER);
|
setState(State.DELIMITER);
|
||||||
_partialBoundary = 0;
|
_partialBoundary = 0;
|
||||||
|
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Content={}, Last={} {}",BufferUtil.toDetailString(BufferUtil.EMPTY_BUFFER),true,this);
|
LOG.debug("Content={}, Last={} {}", BufferUtil.toDetailString(BufferUtil.EMPTY_BUFFER), true, this);
|
||||||
|
|
||||||
return _handler.content(BufferUtil.EMPTY_BUFFER,true);
|
return _handler.content(BufferUtil.EMPTY_BUFFER, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_partialBoundary = partial;
|
_partialBoundary = partial;
|
||||||
|
@ -643,16 +584,16 @@ public class MultiPartParser
|
||||||
content.limit(_partialBoundary);
|
content.limit(_partialBoundary);
|
||||||
_partialBoundary = 0;
|
_partialBoundary = 0;
|
||||||
|
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Content={}, Last={} {}",BufferUtil.toDetailString(content),false,this);
|
LOG.debug("Content={}, Last={} {}", BufferUtil.toDetailString(content), false, this);
|
||||||
|
|
||||||
if (_handler.content(content,false))
|
if (_handler.content(content, false))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains
|
// Contains
|
||||||
int delimiter = _delimiterSearch.match(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining());
|
int delimiter = _delimiterSearch.match(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
|
||||||
if (delimiter >= 0)
|
if (delimiter >= 0)
|
||||||
{
|
{
|
||||||
ByteBuffer content = buffer.slice();
|
ByteBuffer content = buffer.slice();
|
||||||
|
@ -661,41 +602,41 @@ public class MultiPartParser
|
||||||
buffer.position(delimiter - buffer.arrayOffset() + _delimiterSearch.getLength());
|
buffer.position(delimiter - buffer.arrayOffset() + _delimiterSearch.getLength());
|
||||||
setState(State.DELIMITER);
|
setState(State.DELIMITER);
|
||||||
|
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Content={}, Last={} {}",BufferUtil.toDetailString(content),true,this);
|
LOG.debug("Content={}, Last={} {}", BufferUtil.toDetailString(content), true, this);
|
||||||
|
|
||||||
return _handler.content(content,true);
|
return _handler.content(content, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ends With
|
// Ends With
|
||||||
_partialBoundary = _delimiterSearch.endsWith(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining());
|
_partialBoundary = _delimiterSearch.endsWith(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
|
||||||
if (_partialBoundary > 0)
|
if (_partialBoundary > 0)
|
||||||
{
|
{
|
||||||
ByteBuffer content = buffer.slice();
|
ByteBuffer content = buffer.slice();
|
||||||
content.limit(content.limit() - _partialBoundary);
|
content.limit(content.limit() - _partialBoundary);
|
||||||
|
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Content={}, Last={} {}",BufferUtil.toDetailString(content),false,this);
|
LOG.debug("Content={}, Last={} {}", BufferUtil.toDetailString(content), false, this);
|
||||||
|
|
||||||
BufferUtil.clear(buffer);
|
BufferUtil.clear(buffer);
|
||||||
return _handler.content(content,false);
|
return _handler.content(content, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is normal content with no delimiter
|
// There is normal content with no delimiter
|
||||||
ByteBuffer content = buffer.slice();
|
ByteBuffer content = buffer.slice();
|
||||||
|
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Content={}, Last={} {}",BufferUtil.toDetailString(content),false,this);
|
LOG.debug("Content={}, Last={} {}", BufferUtil.toDetailString(content), false, this);
|
||||||
|
|
||||||
BufferUtil.clear(buffer);
|
BufferUtil.clear(buffer);
|
||||||
return _handler.content(content,false);
|
return _handler.content(content, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
private void setState(State state)
|
private void setState(State state)
|
||||||
{
|
{
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
LOG.debug("{} --> {}",_state,state);
|
LOG.debug("{} --> {}", _state, state);
|
||||||
_state = state;
|
_state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,7 +644,7 @@ public class MultiPartParser
|
||||||
private void setState(FieldState state)
|
private void setState(FieldState state)
|
||||||
{
|
{
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
LOG.debug("{}:{} --> {}",_state,_fieldState,state);
|
LOG.debug("{}:{} --> {}", _state, _fieldState, state);
|
||||||
_fieldState = state;
|
_fieldState = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -711,7 +652,7 @@ public class MultiPartParser
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("%s{s=%s}",getClass().getSimpleName(),_state);
|
return String.format("%s{s=%s}", getClass().getSimpleName(), _state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -724,30 +665,32 @@ public class MultiPartParser
|
||||||
*/
|
*/
|
||||||
public interface Handler
|
public interface Handler
|
||||||
{
|
{
|
||||||
public default void startPart()
|
default void startPart()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public default void parsedField(String name, String value)
|
@SuppressWarnings("unused")
|
||||||
|
default void parsedField(String name, String value)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public default boolean headerComplete()
|
default boolean headerComplete()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public default boolean content(ByteBuffer item, boolean last)
|
@SuppressWarnings("unused")
|
||||||
|
default boolean content(ByteBuffer item, boolean last)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public default boolean messageComplete()
|
default boolean messageComplete()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public default void earlyEOF()
|
default void earlyEOF()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -758,9 +701,9 @@ public class MultiPartParser
|
||||||
{
|
{
|
||||||
private IllegalCharacterException(State state, byte ch, ByteBuffer buffer)
|
private IllegalCharacterException(State state, byte ch, ByteBuffer buffer)
|
||||||
{
|
{
|
||||||
super(String.format("Illegal character 0x%X",ch));
|
super(String.format("Illegal character 0x%X", ch));
|
||||||
// Bug #460642 - don't reveal buffers to end user
|
// Bug #460642 - don't reveal buffers to end user
|
||||||
LOG.warn(String.format("Illegal character 0x%X in state=%s for buffer %s",ch,state,BufferUtil.toDetailString(buffer)));
|
LOG.warn(String.format("Illegal character 0x%X in state=%s for buffer %s", ch, state, BufferUtil.toDetailString(buffer)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
@ -41,30 +40,26 @@ import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.servlet.MultipartConfigElement;
|
import javax.servlet.MultipartConfigElement;
|
||||||
import javax.servlet.ReadListener;
|
import javax.servlet.ReadListener;
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.ServletInputStream;
|
import javax.servlet.ServletInputStream;
|
||||||
import javax.servlet.http.Part;
|
import javax.servlet.http.Part;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.MultiPartFormInputStream.MultiPart;
|
import org.eclipse.jetty.http.MultiPartFormInputStream.MultiPart;
|
||||||
import org.eclipse.jetty.util.B64Code;
|
import org.eclipse.jetty.util.B64Code;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.util.IO;
|
||||||
import org.hamcrest.Matchers;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MultiPartInputStreamTest
|
* MultiPartInputStreamTest
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class MultiPartFormInputStreamTest
|
public class MultiPartFormInputStreamTest
|
||||||
{
|
{
|
||||||
private static final String FILENAME = "stuff.txt";
|
private static final String FILENAME = "stuff.txt";
|
||||||
protected String _contentType = "multipart/form-data, boundary=AaB03x";
|
protected String _contentType = "multipart/form-data, boundary=AaB03x";
|
||||||
protected String _multi = createMultipartRequestString(FILENAME);
|
protected String _multi = createMultipartRequestString(FILENAME);
|
||||||
protected String _dirname = System.getProperty("java.io.tmpdir")+File.separator+"myfiles-"+TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
protected String _dirname = System.getProperty("java.io.tmpdir") + File.separator + "myfiles-" + TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||||
protected File _tmpDir = new File(_dirname);
|
protected File _tmpDir = new File(_dirname);
|
||||||
|
|
||||||
public MultiPartFormInputStreamTest ()
|
public MultiPartFormInputStreamTest()
|
||||||
{
|
{
|
||||||
_tmpDir.deleteOnExit();
|
_tmpDir.deleteOnExit();
|
||||||
}
|
}
|
||||||
|
@ -74,24 +69,24 @@ public class MultiPartFormInputStreamTest
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
String boundary = "X0Y0";
|
String boundary = "X0Y0";
|
||||||
String str = "--" + boundary + "\r\n"+
|
String str = "--" + boundary + "\r\n" +
|
||||||
"Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"+
|
"Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n" +
|
||||||
"Content-Type: application/octet-stream\r\n\r\n"+
|
"Content-Type: application/octet-stream\r\n\r\n" +
|
||||||
"How now brown cow."+
|
"How now brown cow." +
|
||||||
"\r\n--" + boundary + "-\r\n"
|
"\r\n--" + boundary + "-\r\n"
|
||||||
+ "Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"
|
+ "Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"
|
||||||
+ "\r\n";
|
+ "\r\n";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
||||||
"multipart/form-data, boundary="+boundary,
|
"multipart/form-data, boundary=" + boundary,
|
||||||
config,
|
config,
|
||||||
_tmpDir);
|
_tmpDir);
|
||||||
mpis.setDeleteOnExit(true);
|
mpis.setDeleteOnExit(true);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mpis.getParts();
|
mpis.getParts();
|
||||||
fail ("Incomplete Multipart");
|
fail("Incomplete Multipart");
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
@ -118,16 +113,14 @@ public class MultiPartFormInputStreamTest
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
||||||
"multipart/form-data, boundary="+boundary,
|
"multipart/form-data, boundary=" + boundary,
|
||||||
config,
|
config,
|
||||||
_tmpDir);
|
_tmpDir);
|
||||||
mpis.setDeleteOnExit(true);
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = mpis.getParts();
|
|
||||||
assertTrue(mpis.getParts().isEmpty());
|
assertTrue(mpis.getParts().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEmpty()
|
public void testEmpty()
|
||||||
throws Exception
|
throws Exception
|
||||||
|
@ -141,7 +134,7 @@ public class MultiPartFormInputStreamTest
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(str.getBytes()),
|
||||||
"multipart/form-data, boundary="+boundary,
|
"multipart/form-data, boundary=" + boundary,
|
||||||
config,
|
config,
|
||||||
_tmpDir);
|
_tmpDir);
|
||||||
mpis.setDeleteOnExit(true);
|
mpis.setDeleteOnExit(true);
|
||||||
|
@ -152,30 +145,30 @@ public class MultiPartFormInputStreamTest
|
||||||
public void testNoBoundaryRequest()
|
public void testNoBoundaryRequest()
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
String str = "--\r\n"+
|
String str = "--\r\n" +
|
||||||
"Content-Disposition: form-data; name=\"fileName\"\r\n"+
|
"Content-Disposition: form-data; name=\"fileName\"\r\n" +
|
||||||
"Content-Type: text/plain; charset=US-ASCII\r\n"+
|
"Content-Type: text/plain; charset=US-ASCII\r\n" +
|
||||||
"Content-Transfer-Encoding: 8bit\r\n"+
|
"Content-Transfer-Encoding: 8bit\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"abc\r\n"+
|
"abc\r\n" +
|
||||||
"--\r\n"+
|
"--\r\n" +
|
||||||
"Content-Disposition: form-data; name=\"desc\"\r\n"+
|
"Content-Disposition: form-data; name=\"desc\"\r\n" +
|
||||||
"Content-Type: text/plain; charset=US-ASCII\r\n"+
|
"Content-Type: text/plain; charset=US-ASCII\r\n" +
|
||||||
"Content-Transfer-Encoding: 8bit\r\n"+
|
"Content-Transfer-Encoding: 8bit\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"123\r\n"+
|
"123\r\n" +
|
||||||
"--\r\n"+
|
"--\r\n" +
|
||||||
"Content-Disposition: form-data; name=\"title\"\r\n"+
|
"Content-Disposition: form-data; name=\"title\"\r\n" +
|
||||||
"Content-Type: text/plain; charset=US-ASCII\r\n"+
|
"Content-Type: text/plain; charset=US-ASCII\r\n" +
|
||||||
"Content-Transfer-Encoding: 8bit\r\n"+
|
"Content-Transfer-Encoding: 8bit\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"ttt\r\n"+
|
"ttt\r\n" +
|
||||||
"--\r\n"+
|
"--\r\n" +
|
||||||
"Content-Disposition: form-data; name=\"datafile5239138112980980385.txt\"; filename=\"datafile5239138112980980385.txt\"\r\n"+
|
"Content-Disposition: form-data; name=\"datafile5239138112980980385.txt\"; filename=\"datafile5239138112980980385.txt\"\r\n" +
|
||||||
"Content-Type: application/octet-stream; charset=ISO-8859-1\r\n"+
|
"Content-Type: application/octet-stream; charset=ISO-8859-1\r\n" +
|
||||||
"Content-Transfer-Encoding: binary\r\n"+
|
"Content-Transfer-Encoding: binary\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"000\r\n"+
|
"000\r\n" +
|
||||||
"----\r\n";
|
"----\r\n";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
|
@ -224,7 +217,6 @@ public class MultiPartFormInputStreamTest
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoBody()
|
public void testNoBody()
|
||||||
throws Exception
|
|
||||||
{
|
{
|
||||||
String body = "";
|
String body = "";
|
||||||
|
|
||||||
|
@ -237,7 +229,7 @@ public class MultiPartFormInputStreamTest
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mpis.getParts();
|
mpis.getParts();
|
||||||
fail ("Missing initial multi part boundary");
|
fail("Missing initial multi part boundary");
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
@ -250,7 +242,8 @@ public class MultiPartFormInputStreamTest
|
||||||
public void testBodyAlreadyConsumed()
|
public void testBodyAlreadyConsumed()
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
ServletInputStream is = new ServletInputStream() {
|
ServletInputStream is = new ServletInputStream()
|
||||||
|
{
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFinished()
|
public boolean isFinished()
|
||||||
|
@ -270,7 +263,7 @@ public class MultiPartFormInputStreamTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException
|
public int read()
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -288,14 +281,11 @@ public class MultiPartFormInputStreamTest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWhitespaceBodyWithCRLF()
|
public void testWhitespaceBodyWithCRLF()
|
||||||
throws Exception
|
|
||||||
{
|
{
|
||||||
String whitespace = " \n\n\n\r\n\r\n\r\n\r\n";
|
String whitespace = " \n\n\n\r\n\r\n\r\n\r\n";
|
||||||
|
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(whitespace.getBytes()),
|
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(whitespace.getBytes()),
|
||||||
_contentType,
|
_contentType,
|
||||||
|
@ -315,7 +305,6 @@ public class MultiPartFormInputStreamTest
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWhitespaceBody()
|
public void testWhitespaceBody()
|
||||||
throws Exception
|
|
||||||
{
|
{
|
||||||
String whitespace = " ";
|
String whitespace = " ";
|
||||||
|
|
||||||
|
@ -328,7 +317,7 @@ public class MultiPartFormInputStreamTest
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mpis.getParts();
|
mpis.getParts();
|
||||||
fail ("Multipart missing body");
|
fail("Multipart missing body");
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
@ -340,16 +329,16 @@ public class MultiPartFormInputStreamTest
|
||||||
public void testLeadingWhitespaceBodyWithCRLF()
|
public void testLeadingWhitespaceBodyWithCRLF()
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
String body = " \n\n\n\r\n\r\n\r\n\r\n"+
|
String body = " \n\n\n\r\n\r\n\r\n\r\n" +
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"field1\"\r\n"+
|
"content-disposition: form-data; name=\"field1\"\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"Joe Blow\r\n"+
|
"Joe Blow\r\n" +
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"stuff\"; filename=\"" + "foo.txt" + "\"\r\n"+
|
"content-disposition: form-data; name=\"stuff\"; filename=\"" + "foo.txt" + "\"\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+"aaaa"+
|
"\r\n" + "aaaa" +
|
||||||
"bbbbb"+"\r\n" +
|
"bbbbb" + "\r\n" +
|
||||||
"--AaB03x--\r\n";
|
"--AaB03x--\r\n";
|
||||||
|
|
||||||
|
|
||||||
|
@ -377,21 +366,20 @@ public class MultiPartFormInputStreamTest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLeadingWhitespaceBodyWithoutCRLF()
|
public void testLeadingWhitespaceBodyWithoutCRLF()
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
String body = " "+
|
String body = " " +
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"field1\"\r\n"+
|
"content-disposition: form-data; name=\"field1\"\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"Joe Blow\r\n"+
|
"Joe Blow\r\n" +
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"stuff\"; filename=\"" + "foo.txt" + "\"\r\n"+
|
"content-disposition: form-data; name=\"stuff\"; filename=\"" + "foo.txt" + "\"\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+"aaaa"+
|
"\r\n" + "aaaa" +
|
||||||
"bbbbb"+"\r\n" +
|
"bbbbb" + "\r\n" +
|
||||||
"--AaB03x--\r\n";
|
"--AaB03x--\r\n";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
|
@ -408,16 +396,11 @@ public class MultiPartFormInputStreamTest
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
Part stuff = mpis.getPart("stuff");
|
Part stuff = mpis.getPart("stuff");
|
||||||
assertThat(stuff, notNullValue());
|
assertThat(stuff, notNullValue());
|
||||||
baos = new ByteArrayOutputStream();
|
|
||||||
IO.copy(stuff.getInputStream(), baos);
|
IO.copy(stuff.getInputStream(), baos);
|
||||||
assertTrue(baos.toString("US-ASCII").contains("bbbbb"));
|
assertTrue(baos.toString("US-ASCII").contains("bbbbb"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoLimits()
|
public void testNoLimits()
|
||||||
throws Exception
|
throws Exception
|
||||||
|
@ -433,7 +416,7 @@ public class MultiPartFormInputStreamTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRequestTooBig ()
|
public void testRequestTooBig()
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 60, 100, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 60, 100, 50);
|
||||||
|
@ -456,7 +439,7 @@ public class MultiPartFormInputStreamTest
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRequestTooBigThrowsErrorOnGetParts ()
|
public void testRequestTooBigThrowsErrorOnGetParts()
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 60, 100, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 60, 100, 50);
|
||||||
|
@ -465,12 +448,11 @@ public class MultiPartFormInputStreamTest
|
||||||
config,
|
config,
|
||||||
_tmpDir);
|
_tmpDir);
|
||||||
mpis.setDeleteOnExit(true);
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = null;
|
|
||||||
|
|
||||||
//cause parsing
|
//cause parsing
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parts = mpis.getParts();
|
mpis.getParts();
|
||||||
fail("Request should have exceeded maxRequestSize");
|
fail("Request should have exceeded maxRequestSize");
|
||||||
}
|
}
|
||||||
catch (IllegalStateException e)
|
catch (IllegalStateException e)
|
||||||
|
@ -481,7 +463,7 @@ public class MultiPartFormInputStreamTest
|
||||||
//try again
|
//try again
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parts = mpis.getParts();
|
mpis.getParts();
|
||||||
fail("Request should have exceeded maxRequestSize");
|
fail("Request should have exceeded maxRequestSize");
|
||||||
}
|
}
|
||||||
catch (IllegalStateException e)
|
catch (IllegalStateException e)
|
||||||
|
@ -500,10 +482,9 @@ public class MultiPartFormInputStreamTest
|
||||||
config,
|
config,
|
||||||
_tmpDir);
|
_tmpDir);
|
||||||
mpis.setDeleteOnExit(true);
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = null;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parts = mpis.getParts();
|
mpis.getParts();
|
||||||
fail("stuff.txt should have been larger than maxFileSize");
|
fail("stuff.txt should have been larger than maxFileSize");
|
||||||
}
|
}
|
||||||
catch (IllegalStateException e)
|
catch (IllegalStateException e)
|
||||||
|
@ -522,10 +503,9 @@ public class MultiPartFormInputStreamTest
|
||||||
config,
|
config,
|
||||||
_tmpDir);
|
_tmpDir);
|
||||||
mpis.setDeleteOnExit(true);
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = null;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parts = mpis.getParts(); //caused parsing
|
mpis.getParts(); //caused parsing
|
||||||
fail("stuff.txt should have been larger than maxFileSize");
|
fail("stuff.txt should have been larger than maxFileSize");
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
catch (Throwable e)
|
||||||
|
@ -536,7 +516,7 @@ public class MultiPartFormInputStreamTest
|
||||||
//test again after the parsing
|
//test again after the parsing
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parts = mpis.getParts(); //caused parsing
|
mpis.getParts(); //caused parsing
|
||||||
fail("stuff.txt should have been larger than maxFileSize");
|
fail("stuff.txt should have been larger than maxFileSize");
|
||||||
}
|
}
|
||||||
catch (IllegalStateException e)
|
catch (IllegalStateException e)
|
||||||
|
@ -546,9 +526,8 @@ public class MultiPartFormInputStreamTest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPartFileNotDeleted () throws Exception
|
public void testPartFileNotDeleted() throws Exception
|
||||||
{
|
{
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
|
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
|
||||||
|
@ -556,13 +535,13 @@ public class MultiPartFormInputStreamTest
|
||||||
config,
|
config,
|
||||||
_tmpDir);
|
_tmpDir);
|
||||||
mpis.setDeleteOnExit(true);
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = mpis.getParts();
|
mpis.getParts();
|
||||||
|
|
||||||
MultiPart part = (MultiPart)mpis.getPart("stuff");
|
MultiPart part = (MultiPart)mpis.getPart("stuff");
|
||||||
File stuff = ((MultiPartFormInputStream.MultiPart)part).getFile();
|
File stuff = part.getFile();
|
||||||
assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file
|
assertThat(stuff, notNullValue()); // longer than 100 bytes, should already be a tmp file
|
||||||
part.write("tptfd.txt");
|
part.write("tptfd.txt");
|
||||||
File tptfd = new File (_dirname+File.separator+"tptfd.txt");
|
File tptfd = new File(_dirname + File.separator + "tptfd.txt");
|
||||||
assertThat(tptfd.exists(), is(true));
|
assertThat(tptfd.exists(), is(true));
|
||||||
assertThat(stuff.exists(), is(false)); //got renamed
|
assertThat(stuff.exists(), is(false)); //got renamed
|
||||||
part.cleanUp();
|
part.cleanUp();
|
||||||
|
@ -571,7 +550,7 @@ public class MultiPartFormInputStreamTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPartTmpFileDeletion () throws Exception
|
public void testPartTmpFileDeletion() throws Exception
|
||||||
{
|
{
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
|
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
|
||||||
|
@ -579,12 +558,12 @@ public class MultiPartFormInputStreamTest
|
||||||
config,
|
config,
|
||||||
_tmpDir);
|
_tmpDir);
|
||||||
mpis.setDeleteOnExit(true);
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = mpis.getParts();
|
mpis.getParts();
|
||||||
|
|
||||||
MultiPart part = (MultiPart)mpis.getPart("stuff");
|
MultiPart part = (MultiPart)mpis.getPart("stuff");
|
||||||
File stuff = ((MultiPartFormInputStream.MultiPart)part).getFile();
|
File stuff = part.getFile();
|
||||||
assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file
|
assertThat(stuff, notNullValue()); // longer than 100 bytes, should already be a tmp file
|
||||||
assertThat (stuff.exists(), is(true));
|
assertThat(stuff.exists(), is(true));
|
||||||
part.cleanUp();
|
part.cleanUp();
|
||||||
assertThat(stuff.exists(), is(false)); //tmp file was removed after cleanup
|
assertThat(stuff.exists(), is(false)); //tmp file was removed after cleanup
|
||||||
}
|
}
|
||||||
|
@ -593,14 +572,14 @@ public class MultiPartFormInputStreamTest
|
||||||
public void testLFOnlyRequest()
|
public void testLFOnlyRequest()
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
String str = "--AaB03x\n"+
|
String str = "--AaB03x\n" +
|
||||||
"content-disposition: form-data; name=\"field1\"\n"+
|
"content-disposition: form-data; name=\"field1\"\n" +
|
||||||
"\n"+
|
"\n" +
|
||||||
"Joe Blow"+
|
"Joe Blow" +
|
||||||
"\r\n--AaB03x\n"+
|
"\r\n--AaB03x\n" +
|
||||||
"content-disposition: form-data; name=\"field2\"\n"+
|
"content-disposition: form-data; name=\"field2\"\n" +
|
||||||
"\n"+
|
"\n" +
|
||||||
"Other"+
|
"Other" +
|
||||||
"\r\n--AaB03x--\n";
|
"\r\n--AaB03x--\n";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
|
@ -628,16 +607,15 @@ public class MultiPartFormInputStreamTest
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCROnlyRequest()
|
public void testCROnlyRequest()
|
||||||
throws Exception
|
|
||||||
{
|
{
|
||||||
String str = "--AaB03x\r"+
|
String str = "--AaB03x\r" +
|
||||||
"content-disposition: form-data; name=\"field1\"\r"+
|
"content-disposition: form-data; name=\"field1\"\r" +
|
||||||
"\r"+
|
"\r" +
|
||||||
"Joe Blow\r"+
|
"Joe Blow\r" +
|
||||||
"--AaB03x\r"+
|
"--AaB03x\r" +
|
||||||
"content-disposition: form-data; name=\"field2\"\r"+
|
"content-disposition: form-data; name=\"field2\"\r" +
|
||||||
"\r"+
|
"\r" +
|
||||||
"Other\r"+
|
"Other\r" +
|
||||||
"--AaB03x--\r";
|
"--AaB03x--\r";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
|
@ -666,7 +644,7 @@ public class MultiPartFormInputStreamTest
|
||||||
IO.copy(p2.getInputStream(), baos);
|
IO.copy(p2.getInputStream(), baos);
|
||||||
assertThat(baos.toString("UTF-8"), is("Other"));
|
assertThat(baos.toString("UTF-8"), is("Other"));
|
||||||
}
|
}
|
||||||
catch(Throwable e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
assertTrue(e.getMessage().contains("Bad EOL"));
|
assertTrue(e.getMessage().contains("Bad EOL"));
|
||||||
}
|
}
|
||||||
|
@ -674,17 +652,16 @@ public class MultiPartFormInputStreamTest
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCRandLFMixRequest()
|
public void testCRandLFMixRequest()
|
||||||
throws Exception
|
|
||||||
{
|
{
|
||||||
String str = "--AaB03x\r"+
|
String str = "--AaB03x\r" +
|
||||||
"content-disposition: form-data; name=\"field1\"\r"+
|
"content-disposition: form-data; name=\"field1\"\r" +
|
||||||
"\r"+
|
"\r" +
|
||||||
"\nJoe Blow\n"+
|
"\nJoe Blow\n" +
|
||||||
"\r"+
|
"\r" +
|
||||||
"--AaB03x\r"+
|
"--AaB03x\r" +
|
||||||
"content-disposition: form-data; name=\"field2\"\r"+
|
"content-disposition: form-data; name=\"field2\"\r" +
|
||||||
"\r"+
|
"\r" +
|
||||||
"Other\r"+
|
"Other\r" +
|
||||||
"--AaB03x--\r";
|
"--AaB03x--\r";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
|
@ -712,18 +689,18 @@ public class MultiPartFormInputStreamTest
|
||||||
IO.copy(p2.getInputStream(), baos);
|
IO.copy(p2.getInputStream(), baos);
|
||||||
assertThat(baos.toString("UTF-8"), is("Other"));
|
assertThat(baos.toString("UTF-8"), is("Other"));
|
||||||
}
|
}
|
||||||
catch(Throwable e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
assertTrue(e.getMessage().contains("Bad EOL"));
|
assertTrue(e.getMessage().contains("Bad EOL"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBufferOverflowNoCRLF () throws Exception
|
public void testBufferOverflowNoCRLF() throws Exception
|
||||||
{
|
{
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
baos.write("--AaB03x\r\n".getBytes());
|
baos.write("--AaB03x\r\n".getBytes());
|
||||||
for (int i=0; i< 3000; i++) //create content that will overrun default buffer size of BufferedInputStream
|
for (int i = 0; i < 3000; i++) //create content that will overrun default buffer size of BufferedInputStream
|
||||||
{
|
{
|
||||||
baos.write('a');
|
baos.write('a');
|
||||||
}
|
}
|
||||||
|
@ -737,7 +714,7 @@ public class MultiPartFormInputStreamTest
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mpis.getParts();
|
mpis.getParts();
|
||||||
fail ("Header Line Exceeded Max Length");
|
fail("Header Line Exceeded Max Length");
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
|
@ -747,14 +724,14 @@ public class MultiPartFormInputStreamTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCharsetEncoding () throws Exception
|
public void testCharsetEncoding() throws Exception
|
||||||
{
|
{
|
||||||
String contentType = "multipart/form-data; boundary=TheBoundary; charset=ISO-8859-1";
|
String contentType = "multipart/form-data; boundary=TheBoundary; charset=ISO-8859-1";
|
||||||
String str = "--TheBoundary\r\n"+
|
String str = "--TheBoundary\r\n" +
|
||||||
"content-disposition: form-data; name=\"field1\"\r\n"+
|
"content-disposition: form-data; name=\"field1\"\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"\nJoe Blow\n"+
|
"\nJoe Blow\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"--TheBoundary--\r\n";
|
"--TheBoundary--\r\n";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
|
@ -772,11 +749,11 @@ public class MultiPartFormInputStreamTest
|
||||||
public void testBadlyEncodedFilename() throws Exception
|
public void testBadlyEncodedFilename() throws Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
String contents = "--AaB03x\r\n"+
|
String contents = "--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"stuff\"; filename=\"" +"Taken on Aug 22 \\ 2012.jpg" + "\"\r\n"+
|
"content-disposition: form-data; name=\"stuff\"; filename=\"" + "Taken on Aug 22 \\ 2012.jpg" + "\"\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+"stuff"+
|
"\r\n" + "stuff" +
|
||||||
"aaa"+"\r\n" +
|
"aaa" + "\r\n" +
|
||||||
"--AaB03x--\r\n";
|
"--AaB03x--\r\n";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
|
@ -787,18 +764,18 @@ public class MultiPartFormInputStreamTest
|
||||||
mpis.setDeleteOnExit(true);
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = mpis.getParts();
|
Collection<Part> parts = mpis.getParts();
|
||||||
assertThat(parts.size(), is(1));
|
assertThat(parts.size(), is(1));
|
||||||
assertThat(((MultiPartFormInputStream.MultiPart)parts.iterator().next()).getSubmittedFileName(), is("Taken on Aug 22 \\ 2012.jpg"));
|
assertThat(parts.iterator().next().getSubmittedFileName(), is("Taken on Aug 22 \\ 2012.jpg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBadlyEncodedMSFilename() throws Exception
|
public void testBadlyEncodedMSFilename() throws Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
String contents = "--AaB03x\r\n"+
|
String contents = "--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"stuff\"; filename=\"" +"c:\\this\\really\\is\\some\\path\\to\\a\\file.txt" + "\"\r\n"+
|
"content-disposition: form-data; name=\"stuff\"; filename=\"" + "c:\\this\\really\\is\\some\\path\\to\\a\\file.txt" + "\"\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+"stuff"+
|
"\r\n" + "stuff" +
|
||||||
"aaa"+"\r\n" +
|
"aaa" + "\r\n" +
|
||||||
"--AaB03x--\r\n";
|
"--AaB03x--\r\n";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
|
@ -809,17 +786,17 @@ public class MultiPartFormInputStreamTest
|
||||||
mpis.setDeleteOnExit(true);
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = mpis.getParts();
|
Collection<Part> parts = mpis.getParts();
|
||||||
assertThat(parts.size(), is(1));
|
assertThat(parts.size(), is(1));
|
||||||
assertThat(((MultiPartFormInputStream.MultiPart)parts.iterator().next()).getSubmittedFileName(), is("c:\\this\\really\\is\\some\\path\\to\\a\\file.txt"));
|
assertThat(parts.iterator().next().getSubmittedFileName(), is("c:\\this\\really\\is\\some\\path\\to\\a\\file.txt"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCorrectlyEncodedMSFilename() throws Exception
|
public void testCorrectlyEncodedMSFilename() throws Exception
|
||||||
{
|
{
|
||||||
String contents = "--AaB03x\r\n"+
|
String contents = "--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"stuff\"; filename=\"" +"c:\\\\this\\\\really\\\\is\\\\some\\\\path\\\\to\\\\a\\\\file.txt" + "\"\r\n"+
|
"content-disposition: form-data; name=\"stuff\"; filename=\"" + "c:\\\\this\\\\really\\\\is\\\\some\\\\path\\\\to\\\\a\\\\file.txt" + "\"\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+"stuff"+
|
"\r\n" + "stuff" +
|
||||||
"aaa"+"\r\n" +
|
"aaa" + "\r\n" +
|
||||||
"--AaB03x--\r\n";
|
"--AaB03x--\r\n";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
|
@ -830,13 +807,7 @@ public class MultiPartFormInputStreamTest
|
||||||
mpis.setDeleteOnExit(true);
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = mpis.getParts();
|
Collection<Part> parts = mpis.getParts();
|
||||||
assertThat(parts.size(), is(1));
|
assertThat(parts.size(), is(1));
|
||||||
assertThat(((MultiPartFormInputStream.MultiPart)parts.iterator().next()).getSubmittedFileName(), is("c:\\this\\really\\is\\some\\path\\to\\a\\file.txt"));
|
assertThat(parts.iterator().next().getSubmittedFileName(), is("c:\\this\\really\\is\\some\\path\\to\\a\\file.txt"));
|
||||||
}
|
|
||||||
|
|
||||||
public void testMulti ()
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
testMulti(FILENAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -847,18 +818,18 @@ public class MultiPartFormInputStreamTest
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteFilesIfContentDispositionFilename ()
|
public void testWriteFilesIfContentDispositionFilename()
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
String s = "--AaB03x\r\n"+
|
String s = "--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"field1\"; filename=\"frooble.txt\"\r\n"+
|
"content-disposition: form-data; name=\"field1\"; filename=\"frooble.txt\"\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"Joe Blow\r\n"+
|
"Joe Blow\r\n" +
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"stuff\"\r\n"+
|
"content-disposition: form-data; name=\"stuff\"\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+"sss"+
|
"\r\n" + "sss" +
|
||||||
"aaa"+"\r\n" +
|
"aaa" + "\r\n" +
|
||||||
"--AaB03x--\r\n";
|
"--AaB03x--\r\n";
|
||||||
//all default values for multipartconfig, ie file size threshold 0
|
//all default values for multipartconfig, ie file size threshold 0
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname);
|
||||||
|
@ -872,7 +843,7 @@ public class MultiPartFormInputStreamTest
|
||||||
assertThat(parts.size(), is(2));
|
assertThat(parts.size(), is(2));
|
||||||
Part field1 = mpis.getPart("field1"); //has a filename, should be written to a file
|
Part field1 = mpis.getPart("field1"); //has a filename, should be written to a file
|
||||||
File f = ((MultiPartFormInputStream.MultiPart)field1).getFile();
|
File f = ((MultiPartFormInputStream.MultiPart)field1).getFile();
|
||||||
assertThat(f,notNullValue()); // longer than 100 bytes, should already be a tmp file
|
assertThat(f, notNullValue()); // longer than 100 bytes, should already be a tmp file
|
||||||
|
|
||||||
Part stuff = mpis.getPart("stuff");
|
Part stuff = mpis.getPart("stuff");
|
||||||
f = ((MultiPartFormInputStream.MultiPart)stuff).getFile(); //should only be in memory, no filename
|
f = ((MultiPartFormInputStream.MultiPart)stuff).getFile(); //should only be in memory, no filename
|
||||||
|
@ -880,7 +851,7 @@ public class MultiPartFormInputStreamTest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void testMulti(String filename) throws IOException, ServletException, InterruptedException
|
private void testMulti(String filename) throws IOException
|
||||||
{
|
{
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(createMultipartRequestString(filename).getBytes()),
|
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(createMultipartRequestString(filename).getBytes()),
|
||||||
|
@ -891,21 +862,24 @@ public class MultiPartFormInputStreamTest
|
||||||
Collection<Part> parts = mpis.getParts();
|
Collection<Part> parts = mpis.getParts();
|
||||||
assertThat(parts.size(), is(2));
|
assertThat(parts.size(), is(2));
|
||||||
Part field1 = mpis.getPart("field1"); //field 1 too small to go into tmp file, should be in internal buffer
|
Part field1 = mpis.getPart("field1"); //field 1 too small to go into tmp file, should be in internal buffer
|
||||||
assertThat(field1,notNullValue());
|
assertThat(field1, notNullValue());
|
||||||
assertThat(field1.getName(),is("field1"));
|
assertThat(field1.getName(), is("field1"));
|
||||||
InputStream is = field1.getInputStream();
|
|
||||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
try (InputStream is = field1.getInputStream())
|
||||||
|
{
|
||||||
IO.copy(is, os);
|
IO.copy(is, os);
|
||||||
|
}
|
||||||
assertEquals("Joe Blow", new String(os.toByteArray()));
|
assertEquals("Joe Blow", new String(os.toByteArray()));
|
||||||
assertEquals(8, field1.getSize());
|
assertEquals(8, field1.getSize());
|
||||||
|
|
||||||
assertNotNull(((MultiPartFormInputStream.MultiPart)field1).getBytes());//in internal buffer
|
assertNotNull(((MultiPartFormInputStream.MultiPart)field1).getBytes());//in internal buffer
|
||||||
field1.write("field1.txt");
|
field1.write("field1.txt");
|
||||||
assertNull(((MultiPartFormInputStream.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");
|
File f = new File(_dirname + File.separator + "field1.txt");
|
||||||
assertTrue(f.exists());
|
assertTrue(f.exists());
|
||||||
field1.write("another_field1.txt"); //write after having already written
|
field1.write("another_field1.txt"); //write after having already written
|
||||||
File f2 = new File(_dirname+File.separator+"another_field1.txt");
|
File f2 = new File(_dirname + File.separator + "another_field1.txt");
|
||||||
assertTrue(f2.exists());
|
assertTrue(f2.exists());
|
||||||
assertFalse(f.exists()); //should have been renamed
|
assertFalse(f.exists()); //should have been renamed
|
||||||
field1.delete(); //file should be deleted
|
field1.delete(); //file should be deleted
|
||||||
|
@ -914,21 +888,21 @@ public class MultiPartFormInputStreamTest
|
||||||
|
|
||||||
MultiPart stuff = (MultiPart)mpis.getPart("stuff");
|
MultiPart stuff = (MultiPart)mpis.getPart("stuff");
|
||||||
assertThat(stuff.getSubmittedFileName(), is(filename));
|
assertThat(stuff.getSubmittedFileName(), is(filename));
|
||||||
assertThat(stuff.getContentType(),is("text/plain"));
|
assertThat(stuff.getContentType(), is("text/plain"));
|
||||||
assertThat(stuff.getHeader("Content-Type"),is("text/plain"));
|
assertThat(stuff.getHeader("Content-Type"), is("text/plain"));
|
||||||
assertThat(stuff.getHeaders("content-type").size(),is(1));
|
assertThat(stuff.getHeaders("content-type").size(), is(1));
|
||||||
assertThat(stuff.getHeader("content-disposition"),is("form-data; name=\"stuff\"; filename=\"" + filename + "\""));
|
assertThat(stuff.getHeader("content-disposition"), is("form-data; name=\"stuff\"; filename=\"" + filename + "\""));
|
||||||
assertThat(stuff.getHeaderNames().size(),is(2));
|
assertThat(stuff.getHeaderNames().size(), is(2));
|
||||||
assertThat(stuff.getSize(),is(51L));
|
assertThat(stuff.getSize(), is(51L));
|
||||||
|
|
||||||
File tmpfile = ((MultiPartFormInputStream.MultiPart)stuff).getFile();
|
File tmpfile = stuff.getFile();
|
||||||
assertThat(tmpfile,notNullValue()); // longer than 50 bytes, should already be a tmp file
|
assertThat(tmpfile, notNullValue()); // longer than 50 bytes, should already be a tmp file
|
||||||
assertThat(stuff.getBytes(),nullValue()); //not in an internal buffer
|
assertThat(stuff.getBytes(), nullValue()); //not in an internal buffer
|
||||||
assertThat(tmpfile.exists(),is(true));
|
assertThat(tmpfile.exists(), is(true));
|
||||||
assertThat(tmpfile.getName(),is(not("stuff with space.txt")));
|
assertThat(tmpfile.getName(), is(not("stuff with space.txt")));
|
||||||
stuff.write(filename);
|
stuff.write(filename);
|
||||||
f = new File(_dirname+File.separator+filename);
|
f = new File(_dirname + File.separator + filename);
|
||||||
assertThat(f.exists(),is(true));
|
assertThat(f.exists(), is(true));
|
||||||
assertThat(tmpfile.exists(), is(false));
|
assertThat(tmpfile.exists(), is(false));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -936,25 +910,25 @@ public class MultiPartFormInputStreamTest
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
fail("Part.getInputStream() after file rename operation");
|
fail("Part.getInputStream() after file rename operation: " + e.getMessage());
|
||||||
}
|
}
|
||||||
f.deleteOnExit(); //clean up after test
|
f.deleteOnExit(); //clean up after test
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultiSameNames ()
|
public void testMultiSameNames()
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
String sameNames = "--AaB03x\r\n"+
|
String sameNames = "--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"stuff\"; filename=\"stuff1.txt\"\r\n"+
|
"content-disposition: form-data; name=\"stuff\"; filename=\"stuff1.txt\"\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"00000\r\n"+
|
"00000\r\n" +
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"stuff\"; filename=\"stuff2.txt\"\r\n"+
|
"content-disposition: form-data; name=\"stuff\"; filename=\"stuff2.txt\"\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"110000000000000000000000000000000000000000000000000\r\n"+
|
"110000000000000000000000000000000000000000000000000\r\n" +
|
||||||
"--AaB03x--\r\n";
|
"--AaB03x--\r\n";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
|
@ -965,7 +939,7 @@ public class MultiPartFormInputStreamTest
|
||||||
mpis.setDeleteOnExit(true);
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = mpis.getParts();
|
Collection<Part> parts = mpis.getParts();
|
||||||
assertEquals(2, parts.size());
|
assertEquals(2, parts.size());
|
||||||
for (Part p:parts)
|
for (Part p : parts)
|
||||||
assertEquals("stuff", p.getName());
|
assertEquals("stuff", p.getName());
|
||||||
|
|
||||||
//if they all have the name name, then only retrieve the first one
|
//if they all have the name name, then only retrieve the first one
|
||||||
|
@ -975,25 +949,25 @@ public class MultiPartFormInputStreamTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBase64EncodedContent () throws Exception
|
public void testBase64EncodedContent() throws Exception
|
||||||
{
|
{
|
||||||
String contentWithEncodedPart =
|
String contentWithEncodedPart =
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"Content-disposition: form-data; name=\"other\"\r\n"+
|
"Content-disposition: form-data; name=\"other\"\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"other" + "\r\n"+
|
"other" + "\r\n" +
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"Content-disposition: form-data; name=\"stuff\"; filename=\"stuff.txt\"\r\n"+
|
"Content-disposition: form-data; name=\"stuff\"; filename=\"stuff.txt\"\r\n" +
|
||||||
"Content-Transfer-Encoding: base64\r\n"+
|
"Content-Transfer-Encoding: base64\r\n" +
|
||||||
"Content-Type: application/octet-stream\r\n"+
|
"Content-Type: application/octet-stream\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
B64Code.encode("hello jetty") + "\r\n"+
|
B64Code.encode("hello jetty") + "\r\n" +
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"Content-disposition: form-data; name=\"final\"\r\n"+
|
"Content-disposition: form-data; name=\"final\"\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"the end" + "\r\n"+
|
"the end" + "\r\n" +
|
||||||
"--AaB03x--\r\n";
|
"--AaB03x--\r\n";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
|
@ -1025,20 +999,20 @@ public class MultiPartFormInputStreamTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQuotedPrintableEncoding () throws Exception
|
public void testQuotedPrintableEncoding() throws Exception
|
||||||
{
|
{
|
||||||
String contentWithEncodedPart =
|
String contentWithEncodedPart =
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"Content-disposition: form-data; name=\"other\"\r\n"+
|
"Content-disposition: form-data; name=\"other\"\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"other" + "\r\n"+
|
"other" + "\r\n" +
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"Content-disposition: form-data; name=\"stuff\"; filename=\"stuff.txt\"\r\n"+
|
"Content-disposition: form-data; name=\"stuff\"; filename=\"stuff.txt\"\r\n" +
|
||||||
"Content-Transfer-Encoding: quoted-printable\r\n"+
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"truth=3Dbeauty" + "\r\n"+
|
"truth=3Dbeauty" + "\r\n" +
|
||||||
"--AaB03x--\r\n";
|
"--AaB03x--\r\n";
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(contentWithEncodedPart.getBytes()),
|
MultiPartFormInputStream mpis = new MultiPartFormInputStream(new ByteArrayInputStream(contentWithEncodedPart.getBytes()),
|
||||||
|
@ -1099,13 +1073,13 @@ public class MultiPartFormInputStreamTest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String createMultipartRequestString(String filename)
|
private static String createMultipartRequestString(String filename)
|
||||||
{
|
{
|
||||||
int length = filename.length();
|
int length = filename.length();
|
||||||
String name = filename;
|
String name = filename;
|
||||||
if (length > 10)
|
if (length > 10)
|
||||||
name = filename.substring(0,10);
|
name = filename.substring(0, 10);
|
||||||
StringBuffer filler = new StringBuffer();
|
StringBuilder filler = new StringBuilder();
|
||||||
int i = name.length();
|
int i = name.length();
|
||||||
while (i < 51)
|
while (i < 51)
|
||||||
{
|
{
|
||||||
|
@ -1113,15 +1087,15 @@ public class MultiPartFormInputStreamTest
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "--AaB03x\r\n"+
|
return "--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"field1\"; filename=\"frooble.txt\"\r\n"+
|
"content-disposition: form-data; name=\"field1\"; filename=\"frooble.txt\"\r\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"Joe Blow\r\n"+
|
"Joe Blow\r\n" +
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"stuff\"; filename=\"" + filename + "\"\r\n"+
|
"content-disposition: form-data; name=\"stuff\"; filename=\"" + filename + "\"\r\n" +
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n" +
|
||||||
"\r\n"+name+
|
"\r\n" + name +
|
||||||
filler.toString()+"\r\n" +
|
filler.toString() + "\r\n" +
|
||||||
"--AaB03x--\r\n";
|
"--AaB03x--\r\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,115 +39,123 @@ public class MultiPartParserTest
|
||||||
@Test
|
@Test
|
||||||
public void testEmptyPreamble()
|
public void testEmptyPreamble()
|
||||||
{
|
{
|
||||||
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler(){},"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler()
|
||||||
|
{
|
||||||
|
}, "BOUNDARY");
|
||||||
ByteBuffer data = BufferUtil.toBuffer("");
|
ByteBuffer data = BufferUtil.toBuffer("");
|
||||||
|
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.PREAMBLE));
|
assertThat(parser.getState(), is(State.PREAMBLE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoPreamble()
|
public void testNoPreamble()
|
||||||
{
|
{
|
||||||
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler(){},"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler()
|
||||||
ByteBuffer data = BufferUtil.toBuffer("");
|
{
|
||||||
|
}, "BOUNDARY");
|
||||||
|
ByteBuffer data = BufferUtil.toBuffer("--BOUNDARY \r\n");
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("--BOUNDARY \r\n");
|
parser.parse(data, false);
|
||||||
parser.parse(data,false);
|
|
||||||
assertTrue(parser.isState(State.BODY_PART));
|
assertTrue(parser.isState(State.BODY_PART));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPreamble()
|
public void testPreamble()
|
||||||
{
|
{
|
||||||
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler(){},"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler()
|
||||||
|
{
|
||||||
|
}, "BOUNDARY");
|
||||||
ByteBuffer data;
|
ByteBuffer data;
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("This is not part of a part\r\n");
|
data = BufferUtil.toBuffer("This is not part of a part\r\n");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.PREAMBLE));
|
assertThat(parser.getState(), is(State.PREAMBLE));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("More data that almost includes \n--BOUNDARY but no CR before.");
|
data = BufferUtil.toBuffer("More data that almost includes \n--BOUNDARY but no CR before.");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.PREAMBLE));
|
assertThat(parser.getState(), is(State.PREAMBLE));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("Could be a boundary \r\n--BOUNDAR");
|
data = BufferUtil.toBuffer("Could be a boundary \r\n--BOUNDAR");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.PREAMBLE));
|
assertThat(parser.getState(), is(State.PREAMBLE));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("but not it isn't \r\n--BOUN");
|
data = BufferUtil.toBuffer("but not it isn't \r\n--BOUN");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.PREAMBLE));
|
assertThat(parser.getState(), is(State.PREAMBLE));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("DARX nor is this");
|
data = BufferUtil.toBuffer("DARX nor is this");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.PREAMBLE));
|
assertThat(parser.getState(), is(State.PREAMBLE));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPreambleCompleteBoundary()
|
public void testPreambleCompleteBoundary()
|
||||||
{
|
{
|
||||||
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler(){},"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler()
|
||||||
ByteBuffer data;
|
{
|
||||||
|
}, "BOUNDARY");
|
||||||
|
ByteBuffer data = BufferUtil.toBuffer("This is not part of a part\r\n--BOUNDARY \r\n");
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("This is not part of a part\r\n--BOUNDARY \r\n");
|
parser.parse(data, false);
|
||||||
parser.parse(data,false);
|
assertThat(parser.getState(), is(State.BODY_PART));
|
||||||
assertThat(parser.getState(),is(State.BODY_PART));
|
assertThat(data.remaining(), is(0));
|
||||||
assertThat(data.remaining(),is(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPreambleSplitBoundary()
|
public void testPreambleSplitBoundary()
|
||||||
{
|
{
|
||||||
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler(){},"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler()
|
||||||
ByteBuffer data;
|
{
|
||||||
|
}, "BOUNDARY");
|
||||||
|
ByteBuffer data = BufferUtil.toBuffer("This is not part of a part\r\n");
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("This is not part of a part\r\n");
|
parser.parse(data, false);
|
||||||
parser.parse(data,false);
|
assertThat(parser.getState(), is(State.PREAMBLE));
|
||||||
assertThat(parser.getState(),is(State.PREAMBLE));
|
assertThat(data.remaining(), is(0));
|
||||||
assertThat(data.remaining(),is(0));
|
|
||||||
data = BufferUtil.toBuffer("-");
|
data = BufferUtil.toBuffer("-");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.PREAMBLE));
|
assertThat(parser.getState(), is(State.PREAMBLE));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
data = BufferUtil.toBuffer("-");
|
data = BufferUtil.toBuffer("-");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.PREAMBLE));
|
assertThat(parser.getState(), is(State.PREAMBLE));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
data = BufferUtil.toBuffer("B");
|
data = BufferUtil.toBuffer("B");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.PREAMBLE));
|
assertThat(parser.getState(), is(State.PREAMBLE));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
data = BufferUtil.toBuffer("OUNDARY-");
|
data = BufferUtil.toBuffer("OUNDARY-");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.DELIMITER_CLOSE));
|
assertThat(parser.getState(), is(State.DELIMITER_CLOSE));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
data = BufferUtil.toBuffer("ignore\r");
|
data = BufferUtil.toBuffer("ignore\r");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.DELIMITER_PADDING));
|
assertThat(parser.getState(), is(State.DELIMITER_PADDING));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
data = BufferUtil.toBuffer("\n");
|
data = BufferUtil.toBuffer("\n");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.BODY_PART));
|
assertThat(parser.getState(), is(State.BODY_PART));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFirstPartNoFields()
|
public void testFirstPartNoFields()
|
||||||
{
|
{
|
||||||
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler(){},"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler()
|
||||||
ByteBuffer data = BufferUtil.toBuffer("");
|
{
|
||||||
|
}, "BOUNDARY");
|
||||||
|
ByteBuffer data = BufferUtil.toBuffer("--BOUNDARY\r\n\r\n");
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("--BOUNDARY\r\n\r\n");
|
parser.parse(data, false);
|
||||||
parser.parse(data,false);
|
assertThat(parser.getState(), is(State.FIRST_OCTETS));
|
||||||
assertThat(parser.getState(),is(State.FIRST_OCTETS));
|
assertThat(data.remaining(), is(0));
|
||||||
assertThat(data.remaining(),is(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -162,60 +170,55 @@ public class MultiPartParserTest
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(handler, "BOUNDARY");
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.toBuffer("");
|
ByteBuffer data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
|
||||||
+ "name0: value0\r\n"
|
+ "name0: value0\r\n"
|
||||||
+ "name1 :value1 \r\n"
|
+ "name1 :value1 \r\n"
|
||||||
+ "name2:value\r\n"
|
+ "name2:value\r\n"
|
||||||
+ " 2\r\n"
|
+ " 2\r\n"
|
||||||
+ "\r\n"
|
+ "\r\n"
|
||||||
+ "Content");
|
+ "Content");
|
||||||
parser.parse(data,false);
|
|
||||||
assertThat(parser.getState(),is(State.FIRST_OCTETS));
|
parser.parse(data, false);
|
||||||
assertThat(data.remaining(),is(7));
|
assertThat(parser.getState(), is(State.FIRST_OCTETS));
|
||||||
assertThat(handler.fields,Matchers.contains("name0: value0","name1: value1", "name2: value 2", "<<COMPLETE>>"));
|
assertThat(data.remaining(), is(7));
|
||||||
|
assertThat(handler.fields, Matchers.contains("name0: value0", "name1: value1", "name2: value 2", "<<COMPLETE>>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFirstPartNoContent()
|
public void testFirstPartNoContent()
|
||||||
{
|
{
|
||||||
TestHandler handler = new TestHandler();
|
TestHandler handler = new TestHandler();
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(handler, "BOUNDARY");
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.toBuffer("");
|
ByteBuffer data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
|
||||||
+ "name: value\r\n"
|
+ "name: value\r\n"
|
||||||
+ "\r\n"
|
+ "\r\n"
|
||||||
+ "\r\n"
|
+ "\r\n"
|
||||||
+ "--BOUNDARY");
|
+ "--BOUNDARY");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(), is(State.DELIMITER));
|
assertThat(parser.getState(), is(State.DELIMITER));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
assertThat(handler.fields,Matchers.contains("name: value", "<<COMPLETE>>"));
|
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>"));
|
||||||
assertThat(handler.content,Matchers.contains("<<LAST>>"));
|
assertThat(handler.content, Matchers.contains("<<LAST>>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFirstPartNoContentNoCRLF()
|
public void testFirstPartNoContentNoCRLF()
|
||||||
{
|
{
|
||||||
TestHandler handler = new TestHandler();
|
TestHandler handler = new TestHandler();
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(handler, "BOUNDARY");
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.toBuffer("");
|
ByteBuffer data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
|
||||||
+ "name: value\r\n"
|
+ "name: value\r\n"
|
||||||
+ "\r\n"
|
+ "\r\n"
|
||||||
+ "--BOUNDARY");
|
+ "--BOUNDARY");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(), is(State.DELIMITER));
|
assertThat(parser.getState(), is(State.DELIMITER));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
assertThat(handler.fields,Matchers.contains("name: value", "<<COMPLETE>>"));
|
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>"));
|
||||||
assertThat(handler.content,Matchers.contains("<<LAST>>"));
|
assertThat(handler.content, Matchers.contains("<<LAST>>"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,53 +226,49 @@ public class MultiPartParserTest
|
||||||
public void testFirstPartContentLookingLikeNoCRLF()
|
public void testFirstPartContentLookingLikeNoCRLF()
|
||||||
{
|
{
|
||||||
TestHandler handler = new TestHandler();
|
TestHandler handler = new TestHandler();
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(handler, "BOUNDARY");
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.toBuffer("");
|
ByteBuffer data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
|
||||||
+ "name: value\r\n"
|
+ "name: value\r\n"
|
||||||
+ "\r\n"
|
+ "\r\n"
|
||||||
+ "-");
|
+ "-");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
data = BufferUtil.toBuffer("Content!");
|
data = BufferUtil.toBuffer("Content!");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
|
|
||||||
|
|
||||||
assertThat(parser.getState(), is(State.OCTETS));
|
assertThat(parser.getState(), is(State.OCTETS));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
assertThat(handler.fields,Matchers.contains("name: value", "<<COMPLETE>>"));
|
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>"));
|
||||||
assertThat(handler.content,Matchers.contains("-","Content!"));
|
assertThat(handler.content, Matchers.contains("-", "Content!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFirstPartPartialContent()
|
public void testFirstPartPartialContent()
|
||||||
{
|
{
|
||||||
TestHandler handler = new TestHandler();
|
TestHandler handler = new TestHandler();
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(handler, "BOUNDARY");
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.toBuffer("");
|
ByteBuffer data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
|
||||||
+ "name: value\n"
|
+ "name: value\n"
|
||||||
+ "\r\n"
|
+ "\r\n"
|
||||||
+ "Hello\r\n");
|
+ "Hello\r\n");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.OCTETS));
|
assertThat(parser.getState(), is(State.OCTETS));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
assertThat(handler.fields,Matchers.contains("name: value", "<<COMPLETE>>"));
|
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>"));
|
||||||
assertThat(handler.content,Matchers.contains("Hello"));
|
assertThat(handler.content, Matchers.contains("Hello"));
|
||||||
|
|
||||||
data = BufferUtil.toBuffer(
|
data = BufferUtil.toBuffer(
|
||||||
"Now is the time for all good ment to come to the aid of the party.\r\n"
|
"Now is the time for all good ment to come to the aid of the party.\r\n"
|
||||||
+ "How now brown cow.\r\n"
|
+ "How now brown cow.\r\n"
|
||||||
+ "The quick brown fox jumped over the lazy dog.\r\n"
|
+ "The quick brown fox jumped over the lazy dog.\r\n"
|
||||||
+ "this is not a --BOUNDARY\r\n");
|
+ "this is not a --BOUNDARY\r\n");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(),is(State.OCTETS));
|
assertThat(parser.getState(), is(State.OCTETS));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
assertThat(handler.fields,Matchers.contains("name: value", "<<COMPLETE>>"));
|
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>"));
|
||||||
assertThat(handler.content,Matchers.contains("Hello","\r\n","Now is the time for all good ment to come to the aid of the party.\r\n"
|
assertThat(handler.content, Matchers.contains("Hello", "\r\n", "Now is the time for all good ment to come to the aid of the party.\r\n"
|
||||||
+ "How now brown cow.\r\n"
|
+ "How now brown cow.\r\n"
|
||||||
+ "The quick brown fox jumped over the lazy dog.\r\n"
|
+ "The quick brown fox jumped over the lazy dog.\r\n"
|
||||||
+ "this is not a --BOUNDARY"));
|
+ "this is not a --BOUNDARY"));
|
||||||
|
@ -279,20 +278,18 @@ public class MultiPartParserTest
|
||||||
public void testFirstPartShortContent()
|
public void testFirstPartShortContent()
|
||||||
{
|
{
|
||||||
TestHandler handler = new TestHandler();
|
TestHandler handler = new TestHandler();
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(handler, "BOUNDARY");
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.toBuffer("");
|
ByteBuffer data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
|
||||||
+ "name: value\n"
|
+ "name: value\n"
|
||||||
+ "\r\n"
|
+ "\r\n"
|
||||||
+ "Hello\r\n"
|
+ "Hello\r\n"
|
||||||
+ "--BOUNDARY");
|
+ "--BOUNDARY");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(), is(State.DELIMITER));
|
assertThat(parser.getState(), is(State.DELIMITER));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
assertThat(handler.fields,Matchers.contains("name: value", "<<COMPLETE>>"));
|
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>"));
|
||||||
assertThat(handler.content,Matchers.contains("Hello","<<LAST>>"));
|
assertThat(handler.content, Matchers.contains("Hello", "<<LAST>>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -300,11 +297,9 @@ public class MultiPartParserTest
|
||||||
public void testFirstPartLongContent()
|
public void testFirstPartLongContent()
|
||||||
{
|
{
|
||||||
TestHandler handler = new TestHandler();
|
TestHandler handler = new TestHandler();
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(handler, "BOUNDARY");
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.toBuffer("");
|
ByteBuffer data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
|
||||||
+ "name: value\n"
|
+ "name: value\n"
|
||||||
+ "\r\n"
|
+ "\r\n"
|
||||||
+ "Now is the time for all good ment to come to the aid of the party.\r\n"
|
+ "Now is the time for all good ment to come to the aid of the party.\r\n"
|
||||||
|
@ -312,25 +307,23 @@ public class MultiPartParserTest
|
||||||
+ "The quick brown fox jumped over the lazy dog.\r\n"
|
+ "The quick brown fox jumped over the lazy dog.\r\n"
|
||||||
+ "\r\n"
|
+ "\r\n"
|
||||||
+ "--BOUNDARY");
|
+ "--BOUNDARY");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(), is(State.DELIMITER));
|
assertThat(parser.getState(), is(State.DELIMITER));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
assertThat(handler.fields,Matchers.contains("name: value", "<<COMPLETE>>"));
|
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>"));
|
||||||
assertThat(handler.content,Matchers.contains("Now is the time for all good ment to come to the aid of the party.\r\n"
|
assertThat(handler.content, Matchers.contains("Now is the time for all good ment to come to the aid of the party.\r\n"
|
||||||
+ "How now brown cow.\r\n"
|
+ "How now brown cow.\r\n"
|
||||||
+ "The quick brown fox jumped over the lazy dog.\r\n","<<LAST>>"));
|
+ "The quick brown fox jumped over the lazy dog.\r\n", "<<LAST>>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFirstPartLongContentNoCarriageReturn()
|
public void testFirstPartLongContentNoCarriageReturn()
|
||||||
{
|
{
|
||||||
TestHandler handler = new TestHandler();
|
TestHandler handler = new TestHandler();
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(handler, "BOUNDARY");
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.toBuffer("");
|
|
||||||
|
|
||||||
//boundary still requires carriage return
|
//boundary still requires carriage return
|
||||||
data = BufferUtil.toBuffer("--BOUNDARY\n"
|
ByteBuffer data = BufferUtil.toBuffer("--BOUNDARY\n"
|
||||||
+ "name: value\n"
|
+ "name: value\n"
|
||||||
+ "\n"
|
+ "\n"
|
||||||
+ "Now is the time for all good men to come to the aid of the party.\n"
|
+ "Now is the time for all good men to come to the aid of the party.\n"
|
||||||
|
@ -338,13 +331,13 @@ public class MultiPartParserTest
|
||||||
+ "The quick brown fox jumped over the lazy dog.\n"
|
+ "The quick brown fox jumped over the lazy dog.\n"
|
||||||
+ "\r\n"
|
+ "\r\n"
|
||||||
+ "--BOUNDARY");
|
+ "--BOUNDARY");
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(), is(State.DELIMITER));
|
assertThat(parser.getState(), is(State.DELIMITER));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
assertThat(handler.fields,Matchers.contains("name: value", "<<COMPLETE>>"));
|
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>"));
|
||||||
assertThat(handler.content,Matchers.contains("Now is the time for all good men to come to the aid of the party.\n"
|
assertThat(handler.content, Matchers.contains("Now is the time for all good men to come to the aid of the party.\n"
|
||||||
+ "How now brown cow.\n"
|
+ "How now brown cow.\n"
|
||||||
+ "The quick brown fox jumped over the lazy dog.\n","<<LAST>>"));
|
+ "The quick brown fox jumped over the lazy dog.\n", "<<LAST>>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -361,30 +354,31 @@ public class MultiPartParserTest
|
||||||
@Override
|
@Override
|
||||||
public boolean content(ByteBuffer buffer, boolean last)
|
public boolean content(ByteBuffer buffer, boolean last)
|
||||||
{
|
{
|
||||||
BufferUtil.append(bytes,buffer);
|
BufferUtil.append(bytes, buffer);
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(handler, "BOUNDARY");
|
||||||
|
|
||||||
String preamble = "Blah blah blah\r\n--BOUNDARY\r\n\r\n";
|
String preamble = "Blah blah blah\r\n--BOUNDARY\r\n\r\n";
|
||||||
String epilogue = "\r\n--BOUNDARY\r\nBlah blah blah!\r\n";
|
String epilogue = "\r\n--BOUNDARY\r\nBlah blah blah!\r\n";
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.allocate(preamble.length()+random.length+epilogue.length());
|
ByteBuffer data = BufferUtil.allocate(preamble.length() + random.length + epilogue.length());
|
||||||
BufferUtil.append(data,BufferUtil.toBuffer(preamble));
|
BufferUtil.append(data, BufferUtil.toBuffer(preamble));
|
||||||
BufferUtil.append(data,ByteBuffer.wrap(random));
|
BufferUtil.append(data, ByteBuffer.wrap(random));
|
||||||
BufferUtil.append(data,BufferUtil.toBuffer(epilogue));
|
BufferUtil.append(data, BufferUtil.toBuffer(epilogue));
|
||||||
|
|
||||||
parser.parse(data,true);
|
parser.parse(data, true);
|
||||||
assertThat(parser.getState(), is(State.DELIMITER));
|
assertThat(parser.getState(), is(State.DELIMITER));
|
||||||
assertThat(data.remaining(),is(19));
|
assertThat(data.remaining(), is(19));
|
||||||
assertThat(bytes.array(),is(random));
|
assertThat(bytes.array(), is(random));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEpilogue() {
|
public void testEpilogue()
|
||||||
|
{
|
||||||
TestHandler handler = new TestHandler();
|
TestHandler handler = new TestHandler();
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(handler, "BOUNDARY");
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.toBuffer(""
|
ByteBuffer data = BufferUtil.toBuffer(""
|
||||||
+ "--BOUNDARY\r\n"
|
+ "--BOUNDARY\r\n"
|
||||||
|
@ -399,20 +393,21 @@ public class MultiPartParserTest
|
||||||
+ "--BOUNDARY");
|
+ "--BOUNDARY");
|
||||||
|
|
||||||
|
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(), is(State.DELIMITER));
|
assertThat(parser.getState(), is(State.DELIMITER));
|
||||||
assertThat(handler.fields,Matchers.contains("name: value", "<<COMPLETE>>"));
|
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>"));
|
||||||
assertThat(handler.content,Matchers.contains("Hello","<<LAST>>"));
|
assertThat(handler.content, Matchers.contains("Hello", "<<LAST>>"));
|
||||||
|
|
||||||
parser.parse(data,true);
|
parser.parse(data, true);
|
||||||
assertThat(parser.getState(), is(State.END));
|
assertThat(parser.getState(), is(State.END));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultipleContent() {
|
public void testMultipleContent()
|
||||||
|
{
|
||||||
TestHandler handler = new TestHandler();
|
TestHandler handler = new TestHandler();
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"BOUNDARY");
|
MultiPartParser parser = new MultiPartParser(handler, "BOUNDARY");
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.toBuffer(""
|
ByteBuffer data = BufferUtil.toBuffer(""
|
||||||
+ "--BOUNDARY\r\n"
|
+ "--BOUNDARY\r\n"
|
||||||
|
@ -430,62 +425,63 @@ public class MultiPartParserTest
|
||||||
+ "epilogue here");
|
+ "epilogue here");
|
||||||
|
|
||||||
/* Test First Content Section */
|
/* Test First Content Section */
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(), is(State.DELIMITER));
|
assertThat(parser.getState(), is(State.DELIMITER));
|
||||||
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>"));
|
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>"));
|
||||||
assertThat(handler.content, Matchers.contains("Hello","<<LAST>>"));
|
assertThat(handler.content, Matchers.contains("Hello", "<<LAST>>"));
|
||||||
|
|
||||||
/* Test Second Content Section */
|
/* Test Second Content Section */
|
||||||
parser.parse(data,false);
|
parser.parse(data, false);
|
||||||
assertThat(parser.getState(), is(State.DELIMITER));
|
assertThat(parser.getState(), is(State.DELIMITER));
|
||||||
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>","powerLevel: 9001","<<COMPLETE>>"));
|
assertThat(handler.fields, Matchers.contains("name: value", "<<COMPLETE>>", "powerLevel: 9001", "<<COMPLETE>>"));
|
||||||
assertThat(handler.content, Matchers.contains("Hello","<<LAST>>","secondary\r\ncontent","<<LAST>>"));
|
assertThat(handler.content, Matchers.contains("Hello", "<<LAST>>", "secondary\r\ncontent", "<<LAST>>"));
|
||||||
|
|
||||||
/* Test Progression to END State */
|
/* Test Progression to END State */
|
||||||
parser.parse(data,true);
|
parser.parse(data, true);
|
||||||
assertThat(parser.getState(), is(State.END));
|
assertThat(parser.getState(), is(State.END));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(), is(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCrAsLineTermination() {
|
public void testCrAsLineTermination()
|
||||||
|
{
|
||||||
TestHandler handler = new TestHandler()
|
TestHandler handler = new TestHandler()
|
||||||
{
|
{
|
||||||
@Override public boolean messageComplete(){ return true; }
|
@Override
|
||||||
|
public boolean messageComplete()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean content(ByteBuffer buffer, boolean last)
|
public boolean content(ByteBuffer buffer, boolean last)
|
||||||
{
|
{
|
||||||
super.content(buffer,last);
|
super.content(buffer, last);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"AaB03x");
|
MultiPartParser parser = new MultiPartParser(handler, "AaB03x");
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.toBuffer(
|
ByteBuffer data = BufferUtil.toBuffer(
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n" +
|
||||||
"content-disposition: form-data; name=\"field1\"\r\n"+
|
"content-disposition: form-data; name=\"field1\"\r\n" +
|
||||||
"\r"+
|
"\r" +
|
||||||
"Joe Blow\r\n"+
|
"Joe Blow\r\n" +
|
||||||
"--AaB03x--\r\n");
|
"--AaB03x--\r\n");
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parser.parse(data,true);
|
parser.parse(data, true);
|
||||||
fail("Invalid End of Line");
|
fail("Invalid End of Line");
|
||||||
}
|
}
|
||||||
catch(BadMessageException e) {
|
catch (BadMessageException e)
|
||||||
|
{
|
||||||
assertTrue(e.getMessage().contains("Bad EOL"));
|
assertTrue(e.getMessage().contains("Bad EOL"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void splitTest()
|
public void splitTest()
|
||||||
{
|
{
|
||||||
|
@ -500,13 +496,13 @@ public class MultiPartParserTest
|
||||||
@Override
|
@Override
|
||||||
public boolean content(ByteBuffer buffer, boolean last)
|
public boolean content(ByteBuffer buffer, boolean last)
|
||||||
{
|
{
|
||||||
super.content(buffer,last);
|
super.content(buffer, last);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"---------------------------9051914041544843365972754266");
|
MultiPartParser parser = new MultiPartParser(handler, "---------------------------9051914041544843365972754266");
|
||||||
ByteBuffer data = BufferUtil.toBuffer(""+
|
ByteBuffer data = BufferUtil.toBuffer("" +
|
||||||
"POST / HTTP/1.1\n" +
|
"POST / HTTP/1.1\n" +
|
||||||
"Host: localhost:8000\n" +
|
"Host: localhost:8000\n" +
|
||||||
"User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:29.0) Gecko/20100101 Firefox/29.0\n" +
|
"User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:29.0) Gecko/20100101 Firefox/29.0\n" +
|
||||||
|
@ -548,7 +544,7 @@ public class MultiPartParserTest
|
||||||
"\r\n" +
|
"\r\n" +
|
||||||
"-----------------------------9051914041544843365972754266\n" +
|
"-----------------------------9051914041544843365972754266\n" +
|
||||||
"Field1: value1\n" +
|
"Field1: value1\n" +
|
||||||
"\r\n"+
|
"\r\n" +
|
||||||
"But the amount of denudation which the strata have\n" +
|
"But the amount of denudation which the strata have\n" +
|
||||||
"in many places suffered, independently of the rate\n" +
|
"in many places suffered, independently of the rate\n" +
|
||||||
"of accumulation of the degraded matter, probably\n" +
|
"of accumulation of the degraded matter, probably\n" +
|
||||||
|
@ -567,39 +563,40 @@ public class MultiPartParserTest
|
||||||
|
|
||||||
|
|
||||||
int length = data.remaining();
|
int length = data.remaining();
|
||||||
for(int i = 0; i<length-1; i++){
|
for (int i = 0; i < length - 1; i++)
|
||||||
|
{
|
||||||
//partition 0 to i
|
//partition 0 to i
|
||||||
ByteBuffer dataSeg = data.slice();
|
ByteBuffer dataSeg = data.slice();
|
||||||
dataSeg.position(0);
|
dataSeg.position(0);
|
||||||
dataSeg.limit(i);
|
dataSeg.limit(i);
|
||||||
assertThat("First "+i,parser.parse(dataSeg,false),is(false));
|
assertThat("First " + i, parser.parse(dataSeg, false), is(false));
|
||||||
|
|
||||||
//partition i
|
//partition i
|
||||||
dataSeg = data.slice();
|
dataSeg = data.slice();
|
||||||
dataSeg.position(i);
|
dataSeg.position(i);
|
||||||
dataSeg.limit(i+1);
|
dataSeg.limit(i + 1);
|
||||||
assertThat("Second "+i,parser.parse(dataSeg,false),is(false));
|
assertThat("Second " + i, parser.parse(dataSeg, false), is(false));
|
||||||
|
|
||||||
//partition i to length
|
//partition i to length
|
||||||
dataSeg = data.slice();
|
dataSeg = data.slice();
|
||||||
dataSeg.position(i+1);
|
dataSeg.position(i + 1);
|
||||||
dataSeg.limit(length);
|
dataSeg.limit(length);
|
||||||
assertThat("Third "+i,parser.parse(dataSeg,true),is(true));
|
assertThat("Third " + i, parser.parse(dataSeg, true), is(true));
|
||||||
|
|
||||||
assertThat(handler.fields, Matchers.contains( "Content-Disposition: form-data; name=\"text\"","<<COMPLETE>>"
|
assertThat(handler.fields, Matchers.contains("Content-Disposition: form-data; name=\"text\"", "<<COMPLETE>>"
|
||||||
, "Content-Disposition: form-data; name=\"file1\"; filename=\"a.txt\""
|
, "Content-Disposition: form-data; name=\"file1\"; filename=\"a.txt\""
|
||||||
, "Content-Type: text/plain","<<COMPLETE>>"
|
, "Content-Type: text/plain", "<<COMPLETE>>"
|
||||||
, "Content-Disposition: form-data; name=\"file2\"; filename=\"a.html\""
|
, "Content-Disposition: form-data; name=\"file2\"; filename=\"a.html\""
|
||||||
, "Content-Type: text/html","<<COMPLETE>>"
|
, "Content-Type: text/html", "<<COMPLETE>>"
|
||||||
, "Field1: value1", "Field2: value2", "Field3: value3"
|
, "Field1: value1", "Field2: value2", "Field3: value3"
|
||||||
, "Field4: value4", "Field5: value5", "Field6: value6"
|
, "Field4: value4", "Field5: value5", "Field6: value6"
|
||||||
, "Field7: value7", "Field8: value8", "Field9: value 9", "<<COMPLETE>>"
|
, "Field7: value7", "Field8: value8", "Field9: value 9", "<<COMPLETE>>"
|
||||||
, "Field1: value1","<<COMPLETE>>"));
|
, "Field1: value1", "<<COMPLETE>>"));
|
||||||
|
|
||||||
|
|
||||||
assertThat(handler.contentString(), is(new String("text default"+"<<LAST>>"
|
assertThat(handler.contentString(), is("text default" + "<<LAST>>"
|
||||||
+ "Content of a.txt.\n"+"<<LAST>>"
|
+ "Content of a.txt.\n" + "<<LAST>>"
|
||||||
+ "<!DOCTYPE html><title>Content of a.html.</title>\n"+"<<LAST>>"
|
+ "<!DOCTYPE html><title>Content of a.html.</title>\n" + "<<LAST>>"
|
||||||
+ "<<LAST>>"
|
+ "<<LAST>>"
|
||||||
+ "But the amount of denudation which the strata have\n" +
|
+ "But the amount of denudation which the strata have\n" +
|
||||||
"in many places suffered, independently of the rate\n" +
|
"in many places suffered, independently of the rate\n" +
|
||||||
|
@ -612,7 +609,7 @@ public class MultiPartParserTest
|
||||||
"in height; for the gentle slope of the lava-streams,\n" +
|
"in height; for the gentle slope of the lava-streams,\n" +
|
||||||
"due to their formerly liquid state, showed at a glance\n" +
|
"due to their formerly liquid state, showed at a glance\n" +
|
||||||
"how far the hard, rocky beds had once extended into\n" +
|
"how far the hard, rocky beds had once extended into\n" +
|
||||||
"the open ocean.\n"+ "<<LAST>>")));
|
"the open ocean.\n" + "<<LAST>>"));
|
||||||
|
|
||||||
handler.clear();
|
handler.clear();
|
||||||
parser.reset();
|
parser.reset();
|
||||||
|
@ -634,7 +631,7 @@ public class MultiPartParserTest
|
||||||
@Override
|
@Override
|
||||||
public boolean content(ByteBuffer buffer, boolean last)
|
public boolean content(ByteBuffer buffer, boolean last)
|
||||||
{
|
{
|
||||||
super.content(buffer,last);
|
super.content(buffer, last);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,7 +642,7 @@ public class MultiPartParserTest
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MultiPartParser parser = new MultiPartParser(handler,"WebKitFormBoundary7MA4YWf7OaKlSxkTrZu0gW");
|
MultiPartParser parser = new MultiPartParser(handler, "WebKitFormBoundary7MA4YWf7OaKlSxkTrZu0gW");
|
||||||
ByteBuffer data = BufferUtil.toBuffer(""
|
ByteBuffer data = BufferUtil.toBuffer(""
|
||||||
+ "Content-Type: multipart/form-data; boundary=WebKitFormBoundary7MA4YWf7OaKlSxkTrZu0gW\r\n" +
|
+ "Content-Type: multipart/form-data; boundary=WebKitFormBoundary7MA4YWf7OaKlSxkTrZu0gW\r\n" +
|
||||||
"\r\n" +
|
"\r\n" +
|
||||||
|
@ -659,7 +656,7 @@ public class MultiPartParserTest
|
||||||
"&ᄈ취ᅢO\r\n" +
|
"&ᄈ취ᅢO\r\n" +
|
||||||
"--WebKitFormBoundary7MA4YWf7OaKlSxkTrZu0gW--");
|
"--WebKitFormBoundary7MA4YWf7OaKlSxkTrZu0gW--");
|
||||||
|
|
||||||
parser.parse(data,true);
|
parser.parse(data, true);
|
||||||
assertThat(parser.getState(), is(State.END));
|
assertThat(parser.getState(), is(State.END));
|
||||||
assertThat(handler.fields.size(), is(2));
|
assertThat(handler.fields.size(), is(2));
|
||||||
|
|
||||||
|
@ -674,13 +671,14 @@ public class MultiPartParserTest
|
||||||
@Override
|
@Override
|
||||||
public void parsedField(String name, String value)
|
public void parsedField(String name, String value)
|
||||||
{
|
{
|
||||||
fields.add(name+": "+value);
|
fields.add(name + ": " + value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String contentString()
|
public String contentString()
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for(String s : content) sb.append(s);
|
for (String s : content)
|
||||||
|
sb.append(s);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,11 +699,10 @@ public class MultiPartParserTest
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear()
|
||||||
|
{
|
||||||
fields.clear();
|
fields.clear();
|
||||||
content.clear();
|
content.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ package org.eclipse.jetty.http.jmh;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
|
@ -34,11 +33,6 @@ import javax.servlet.http.Part;
|
||||||
import org.eclipse.jetty.http.MultiPartFormInputStream;
|
import org.eclipse.jetty.http.MultiPartFormInputStream;
|
||||||
import org.eclipse.jetty.http.MultiPartCaptureTest.MultipartExpectations;
|
import org.eclipse.jetty.http.MultiPartCaptureTest.MultipartExpectations;
|
||||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.runner.Description;
|
|
||||||
import org.junit.runners.model.Statement;
|
|
||||||
import org.openjdk.jmh.annotations.Benchmark;
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||||
import org.openjdk.jmh.annotations.Level;
|
import org.openjdk.jmh.annotations.Level;
|
||||||
|
@ -48,7 +42,6 @@ import org.openjdk.jmh.annotations.Scope;
|
||||||
import org.openjdk.jmh.annotations.Setup;
|
import org.openjdk.jmh.annotations.Setup;
|
||||||
import org.openjdk.jmh.annotations.State;
|
import org.openjdk.jmh.annotations.State;
|
||||||
import org.openjdk.jmh.annotations.TearDown;
|
import org.openjdk.jmh.annotations.TearDown;
|
||||||
import org.openjdk.jmh.profile.CompilerProfiler;
|
|
||||||
import org.openjdk.jmh.runner.Runner;
|
import org.openjdk.jmh.runner.Runner;
|
||||||
import org.openjdk.jmh.runner.RunnerException;
|
import org.openjdk.jmh.runner.RunnerException;
|
||||||
import org.openjdk.jmh.runner.options.Options;
|
import org.openjdk.jmh.runner.options.Options;
|
||||||
|
@ -70,6 +63,7 @@ public class MultiPartBenchmark
|
||||||
|
|
||||||
|
|
||||||
public static List<String> data = new ArrayList<>();
|
public static List<String> data = new ArrayList<>();
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
// Capture of raw request body contents from various browsers
|
// Capture of raw request body contents from various browsers
|
||||||
|
@ -111,17 +105,17 @@ public class MultiPartBenchmark
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Param({"UTIL","HTTP"})
|
@Param({"UTIL", "HTTP"})
|
||||||
public static String parserType;
|
public static String parserType;
|
||||||
|
|
||||||
@Setup(Level.Trial)
|
@Setup(Level.Trial)
|
||||||
public static void setupTrial() throws Exception
|
public static void setupTrial() throws Exception
|
||||||
{
|
{
|
||||||
_file = File.createTempFile("test01",null);
|
_file = File.createTempFile("test01", null);
|
||||||
_file.deleteOnExit();
|
_file.deleteOnExit();
|
||||||
|
|
||||||
_numSections = 1;
|
_numSections = 1;
|
||||||
_numBytesPerSection = 1024*1024*10;
|
_numBytesPerSection = 1024 * 1024 * 10;
|
||||||
|
|
||||||
_contentType = "multipart/form-data, boundary=WebKitFormBoundary7MA4YWf7OaKlSxkTrZu0gW";
|
_contentType = "multipart/form-data, boundary=WebKitFormBoundary7MA4YWf7OaKlSxkTrZu0gW";
|
||||||
String initialBoundary = "--WebKitFormBoundary7MA4YWf7OaKlSxkTrZu0gW\r\n";
|
String initialBoundary = "--WebKitFormBoundary7MA4YWf7OaKlSxkTrZu0gW\r\n";
|
||||||
|
@ -130,16 +124,17 @@ public class MultiPartBenchmark
|
||||||
String headerStart = "Content-Disposition: form-data; name=\"";
|
String headerStart = "Content-Disposition: form-data; name=\"";
|
||||||
|
|
||||||
|
|
||||||
for(int i=0; i<_numSections; i++) {
|
for (int i = 0; i < _numSections; i++)
|
||||||
|
{
|
||||||
//boundary and headers
|
//boundary and headers
|
||||||
if(i==0)
|
if (i == 0)
|
||||||
Files.write(_file.toPath(), initialBoundary.getBytes(), StandardOpenOption.APPEND);
|
Files.write(_file.toPath(), initialBoundary.getBytes(), StandardOpenOption.APPEND);
|
||||||
else
|
else
|
||||||
Files.write(_file.toPath(), boundary.getBytes(), StandardOpenOption.APPEND);
|
Files.write(_file.toPath(), boundary.getBytes(), StandardOpenOption.APPEND);
|
||||||
|
|
||||||
Files.write(_file.toPath(), headerStart.getBytes(), StandardOpenOption.APPEND);
|
Files.write(_file.toPath(), headerStart.getBytes(), StandardOpenOption.APPEND);
|
||||||
Files.write(_file.toPath(), new String("part"+(i+1)).getBytes(), StandardOpenOption.APPEND);
|
Files.write(_file.toPath(), ("part" + (i + 1)).getBytes(), StandardOpenOption.APPEND);
|
||||||
Files.write(_file.toPath(), new String("\"\r\n\r\n").getBytes(), StandardOpenOption.APPEND);
|
Files.write(_file.toPath(), ("\"\r\n\r\n").getBytes(), StandardOpenOption.APPEND);
|
||||||
|
|
||||||
//append random data
|
//append random data
|
||||||
byte[] data = new byte[_numBytesPerSection];
|
byte[] data = new byte[_numBytesPerSection];
|
||||||
|
@ -149,22 +144,6 @@ public class MultiPartBenchmark
|
||||||
|
|
||||||
//closing boundary
|
//closing boundary
|
||||||
Files.write(_file.toPath(), closingBoundary.getBytes(), StandardOpenOption.APPEND);
|
Files.write(_file.toPath(), closingBoundary.getBytes(), StandardOpenOption.APPEND);
|
||||||
|
|
||||||
/*
|
|
||||||
// print out file to verify that it contains valid contents (just for testing)
|
|
||||||
InputStream in = Files.newInputStream(_file.toPath());
|
|
||||||
System.out.println();
|
|
||||||
while(in.available()>0) {
|
|
||||||
byte b[] = new byte[100];
|
|
||||||
int read = in.read(b,0,100);
|
|
||||||
for(int i=0; i<read; i++)
|
|
||||||
System.out.print((char)b[i]);
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
|
|
||||||
//exit
|
|
||||||
throw new RuntimeException("Stop Here");
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -180,31 +159,32 @@ public class MultiPartBenchmark
|
||||||
|
|
||||||
try (InputStream in = Files.newInputStream(multipartRawFile))
|
try (InputStream in = Files.newInputStream(multipartRawFile))
|
||||||
{
|
{
|
||||||
switch(parserType)
|
switch (parserType)
|
||||||
{
|
{
|
||||||
case "HTTP":
|
case "HTTP":
|
||||||
{
|
{
|
||||||
MultiPartFormInputStream parser = new MultiPartFormInputStream(in, _contentType, config, outputDir.toFile());
|
MultiPartFormInputStream parser = new MultiPartFormInputStream(in, _contentType, config, outputDir.toFile());
|
||||||
if(parser.getParts().size() != _numSections)
|
if (parser.getParts().size() != _numSections)
|
||||||
throw new IllegalStateException("Incorrect Parsing");
|
throw new IllegalStateException("Incorrect Parsing");
|
||||||
for(Part p : parser.getParts()) {
|
for (Part p : parser.getParts())
|
||||||
|
{
|
||||||
count += p.getSize();
|
count += p.getSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "UTIL":
|
case "UTIL":
|
||||||
{
|
{
|
||||||
org.eclipse.jetty.util.MultiPartInputStreamParser parser = new org.eclipse.jetty.util.MultiPartInputStreamParser(in, _contentType,config,outputDir.toFile());
|
org.eclipse.jetty.util.MultiPartInputStreamParser parser = new org.eclipse.jetty.util.MultiPartInputStreamParser(in, _contentType, config, outputDir.toFile());
|
||||||
|
if (parser.getParts().size() != _numSections)
|
||||||
// TODO this is using the http version of part (which should be the same anyway)
|
|
||||||
if(parser.getParts().size() != _numSections)
|
|
||||||
throw new IllegalStateException("Incorrect Parsing");
|
throw new IllegalStateException("Incorrect Parsing");
|
||||||
for(Part p : parser.getParts()) {
|
for (Part p : parser.getParts())
|
||||||
|
{
|
||||||
count += p.getSize();
|
count += p.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Unknown parserType Parameter");
|
throw new IllegalStateException("Unknown parserType Parameter");
|
||||||
}
|
}
|
||||||
|
@ -214,7 +194,6 @@ public class MultiPartBenchmark
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@TearDown(Level.Trial)
|
@TearDown(Level.Trial)
|
||||||
public static void stopTrial() throws Exception
|
public static void stopTrial() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -231,7 +210,7 @@ public class MultiPartBenchmark
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public long testParser() throws Exception
|
public long testParser() throws Exception
|
||||||
{
|
{
|
||||||
for(String multiPart : data)
|
for (String multiPart : data)
|
||||||
{
|
{
|
||||||
Path multipartRawFile = MavenTestingUtils.getTestResourcePathFile("multipart/" + multiPart + ".raw");
|
Path multipartRawFile = MavenTestingUtils.getTestResourcePathFile("multipart/" + multiPart + ".raw");
|
||||||
Path expectationPath = MavenTestingUtils.getTestResourcePathFile("multipart/" + multiPart + ".expected.txt");
|
Path expectationPath = MavenTestingUtils.getTestResourcePathFile("multipart/" + multiPart + ".expected.txt");
|
||||||
|
@ -243,22 +222,22 @@ public class MultiPartBenchmark
|
||||||
|
|
||||||
try (InputStream in = Files.newInputStream(multipartRawFile))
|
try (InputStream in = Files.newInputStream(multipartRawFile))
|
||||||
{
|
{
|
||||||
switch(parserType)
|
switch (parserType)
|
||||||
{
|
{
|
||||||
case "HTTP":
|
case "HTTP":
|
||||||
{
|
{
|
||||||
MultiPartFormInputStream parser = new MultiPartFormInputStream(in, multipartExpectations.contentType, config, outputDir.toFile());
|
MultiPartFormInputStream parser = new MultiPartFormInputStream(in, multipartExpectations.contentType, config, outputDir.toFile());
|
||||||
for(Part p : parser.getParts()) {
|
for (Part p : parser.getParts())
|
||||||
|
{
|
||||||
count += p.getSize();
|
count += p.getSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "UTIL":
|
case "UTIL":
|
||||||
{
|
{
|
||||||
org.eclipse.jetty.util.MultiPartInputStreamParser parser = new org.eclipse.jetty.util.MultiPartInputStreamParser(in,multipartExpectations.contentType,config,outputDir.toFile());
|
org.eclipse.jetty.util.MultiPartInputStreamParser parser = new org.eclipse.jetty.util.MultiPartInputStreamParser(in, multipartExpectations.contentType, config, outputDir.toFile());
|
||||||
|
for (Part p : parser.getParts())
|
||||||
// TODO this is using the http version of part (which should be the same anyway)
|
{
|
||||||
for(Part p : parser.getParts()) {
|
|
||||||
count += p.getSize();
|
count += p.getSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,14 +259,6 @@ public class MultiPartBenchmark
|
||||||
.measurementIterations(10)
|
.measurementIterations(10)
|
||||||
.forks(1)
|
.forks(1)
|
||||||
.threads(1)
|
.threads(1)
|
||||||
// .syncIterations(true) // Don't start all threads at same time
|
|
||||||
// .warmupTime(new TimeValue(10000,TimeUnit.MILLISECONDS))
|
|
||||||
// .measurementTime(new TimeValue(10000,TimeUnit.MILLISECONDS))
|
|
||||||
// .addProfiler(CompilerProfiler.class)
|
|
||||||
// .addProfiler(LinuxPerfProfiler.class)
|
|
||||||
// .addProfiler(LinuxPerfNormProfiler.class)
|
|
||||||
// .addProfiler(LinuxPerfAsmProfiler.class)
|
|
||||||
// .resultFormat(ResultFormatType.CSV)
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
new Runner(opt).run();
|
new Runner(opt).run();
|
||||||
|
|
Loading…
Reference in New Issue