Merge pull request #2476 from lachlan-roberts/jetty-9.4.x-1027-MultiPart-Cleanup

MultiPart Cleanup
This commit is contained in:
Greg Wilkins 2018-05-04 21:10:50 +10:00 committed by GitHub
commit c865aa1be7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 1268 additions and 1413 deletions

View File

@ -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:

View File

@ -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;
@ -184,18 +183,12 @@ 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)
{ {
@ -204,27 +197,18 @@ public class MultiPartFormInputStream
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
{ {
@ -240,9 +224,6 @@ public class MultiPartFormInputStream
} }
} }
/**
* @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
{ {
@ -287,17 +259,13 @@ public class MultiPartFormInputStream
// 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;
} }
} }
@ -315,26 +283,25 @@ public class MultiPartFormInputStream
/** /**
* 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)
{ {
@ -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
{ {
@ -491,11 +456,9 @@ 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
{ {
@ -508,8 +471,7 @@ public class MultiPartFormInputStream
/** /**
* 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<>();
@ -574,13 +534,8 @@ public class MultiPartFormInputStream
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())
{ {
@ -638,9 +595,7 @@ public class MultiPartFormInputStream
catch (Throwable e) catch (Throwable e)
{ {
_err = e; _err = e;
return;
} }
} }
class Handler implements MultiPartParser.Handler class Handler implements MultiPartParser.Handler
@ -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();
@ -853,4 +808,19 @@ public class MultiPartFormInputStream
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;
}
} }

View File

@ -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
@ -74,6 +72,7 @@ public class MultiPartParser
} }
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:
@ -251,6 +194,7 @@ public class MultiPartParser
} }
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
/** /**
* 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)
{ {
@ -356,8 +300,6 @@ public class MultiPartParser
_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;
} }
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
@ -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)
@ -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()
{ {
} }
} }

View File

@ -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,20 +40,16 @@ 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
{ {
@ -122,12 +117,10 @@ public class MultiPartFormInputStreamTest
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
@ -224,7 +217,6 @@ public class MultiPartFormInputStreamTest
@Test @Test
public void testNoBody() public void testNoBody()
throws Exception
{ {
String body = ""; String body = "";
@ -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 = " ";
@ -377,7 +366,6 @@ public class MultiPartFormInputStreamTest
} }
@Test @Test
public void testLeadingWhitespaceBodyWithoutCRLF() public void testLeadingWhitespaceBodyWithoutCRLF()
throws Exception throws Exception
@ -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
@ -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,7 +526,6 @@ public class MultiPartFormInputStreamTest
} }
@Test @Test
public void testPartFileNotDeleted() throws Exception public void testPartFileNotDeleted() throws Exception
{ {
@ -556,10 +535,10 @@ 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");
@ -579,10 +558,10 @@ 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();
@ -628,7 +607,6 @@ 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" +
@ -674,7 +652,6 @@ 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" +
@ -787,7 +764,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("Taken on Aug 22 \\ 2012.jpg")); assertThat(parts.iterator().next().getSubmittedFileName(), is("Taken on Aug 22 \\ 2012.jpg"));
} }
@Test @Test
@ -809,7 +786,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"));
} }
@Test @Test
@ -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
@ -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()),
@ -893,9 +864,12 @@ public class MultiPartFormInputStreamTest
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());
@ -921,7 +895,7 @@ public class MultiPartFormInputStreamTest
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));
@ -936,7 +910,7 @@ 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
} }
@ -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)
{ {

View File

@ -39,7 +39,9 @@ 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);
@ -49,10 +51,11 @@ public class MultiPartParserTest
@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));
@ -61,7 +64,9 @@ public class MultiPartParserTest
@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");
@ -93,10 +98,11 @@ public class MultiPartParserTest
@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));
@ -105,10 +111,11 @@ public class MultiPartParserTest
@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));
@ -141,10 +148,11 @@ public class MultiPartParserTest
@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));
@ -164,15 +172,14 @@ public class MultiPartParserTest
}; };
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); parser.parse(data, false);
assertThat(parser.getState(), is(State.FIRST_OCTETS)); assertThat(parser.getState(), is(State.FIRST_OCTETS));
assertThat(data.remaining(), is(7)); assertThat(data.remaining(), is(7));
@ -185,9 +192,7 @@ public class MultiPartParserTest
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"
@ -205,9 +210,7 @@ public class MultiPartParserTest
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");
@ -225,9 +228,7 @@ public class MultiPartParserTest
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"
+ "-"); + "-");
@ -248,9 +249,7 @@ public class MultiPartParserTest
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");
@ -281,9 +280,7 @@ public class MultiPartParserTest
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"
@ -302,9 +299,7 @@ public class MultiPartParserTest
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"
@ -327,10 +322,8 @@ public class MultiPartParserTest
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"
@ -382,7 +375,8 @@ public class MultiPartParserTest
} }
@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");
@ -410,7 +404,8 @@ public class MultiPartParserTest
@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");
@ -448,12 +443,16 @@ public class MultiPartParserTest
} }
@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)
@ -471,21 +470,18 @@ public class MultiPartParserTest
"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()
{ {
@ -567,7 +563,8 @@ 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);
@ -597,7 +594,7 @@ public class MultiPartParserTest
, "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>>"
@ -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();
@ -680,7 +677,8 @@ public class MultiPartParserTest
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();
} }
} }
} }

View File

@ -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
@ -130,7 +124,8 @@ 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);
@ -138,8 +133,8 @@ public class MultiPartBenchmark
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");
*/
} }
@ -187,24 +166,25 @@ public class MultiPartBenchmark
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());
// TODO this is using the http version of part (which should be the same anyway)
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;
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
{ {
@ -248,7 +227,8 @@ public class MultiPartBenchmark
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();
} }
} }
@ -256,9 +236,8 @@ public class MultiPartBenchmark
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();