395215 Multipart mime with just LF and no CRLF

This commit is contained in:
Jan Bartel 2012-11-29 15:55:14 +11:00
parent 5ee2ea85bb
commit 9f5801c3fd
6 changed files with 179 additions and 58 deletions

View File

@ -74,7 +74,7 @@ import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.MultiPartInputStream;
import org.eclipse.jetty.util.MultiPartInputStreamParser;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.UrlEncoded;
@ -134,7 +134,7 @@ public class Request implements HttpServletRequest
public void requestDestroyed(ServletRequestEvent sre)
{
//Clean up any tmp files created by MultiPartInputStream
MultiPartInputStream mpis = (MultiPartInputStream)sre.getServletRequest().getAttribute(__MULTIPART_INPUT_STREAM);
MultiPartInputStreamParser mpis = (MultiPartInputStreamParser)sre.getServletRequest().getAttribute(__MULTIPART_INPUT_STREAM);
if (mpis != null)
{
ContextHandler.Context context = (ContextHandler.Context)sre.getServletRequest().getAttribute(__MULTIPART_CONTEXT);
@ -203,7 +203,7 @@ public class Request implements HttpServletRequest
private long _timeStamp;
private long _dispatchTime;
private HttpURI _uri;
private MultiPartInputStream _multiPartInputStream; //if the request is a multi-part mime
private MultiPartInputStreamParser _multiPartInputStream; //if the request is a multi-part mime
/* ------------------------------------------------------------ */
public Request(HttpChannel<?> channel, HttpInput<?> input)
@ -2000,7 +2000,7 @@ public class Request implements HttpServletRequest
if (config == null)
throw new IllegalStateException("No multipart config for servlet");
_multiPartInputStream = new MultiPartInputStream(getInputStream(),
_multiPartInputStream = new MultiPartInputStreamParser(getInputStream(),
getContentType(),config,
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
@ -2008,7 +2008,7 @@ public class Request implements HttpServletRequest
Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
for (Part p:parts)
{
MultiPartInputStream.MultiPart mp = (MultiPartInputStream.MultiPart)p;
MultiPartInputStreamParser.MultiPart mp = (MultiPartInputStreamParser.MultiPart)p;
if (mp.getContentDispositionFilename() == null && mp.getFile() == null)
{
//Servlet Spec 3.0 pg 23, parts without filenames must be put into init params
@ -2039,7 +2039,7 @@ public class Request implements HttpServletRequest
if (config == null)
throw new IllegalStateException("No multipart config for servlet");
_multiPartInputStream = new MultiPartInputStream(getInputStream(),
_multiPartInputStream = new MultiPartInputStreamParser(getInputStream(),
getContentType(), config,
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
@ -2048,7 +2048,7 @@ public class Request implements HttpServletRequest
Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
for (Part p:parts)
{
MultiPartInputStream.MultiPart mp = (MultiPartInputStream.MultiPart)p;
MultiPartInputStreamParser.MultiPart mp = (MultiPartInputStreamParser.MultiPart)p;
if (mp.getContentDispositionFilename() == null && mp.getFile() == null)
{
//Servlet Spec 3.0 pg 23, parts without filenames must be put into init params

View File

@ -50,7 +50,7 @@ import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.MultiPartInputStream;
import org.eclipse.jetty.util.MultiPartInputStreamParser;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -204,7 +204,7 @@ public class RequestTest
@Override
public void requestDestroyed(ServletRequestEvent sre)
{
MultiPartInputStream m = (MultiPartInputStream)sre.getServletRequest().getAttribute(Request.__MULTIPART_INPUT_STREAM);
MultiPartInputStreamParser m = (MultiPartInputStreamParser)sre.getServletRequest().getAttribute(Request.__MULTIPART_INPUT_STREAM);
ContextHandler.Context c = (ContextHandler.Context)sre.getServletRequest().getAttribute(Request.__MULTIPART_CONTEXT);
assertNotNull (m);
assertNotNull (c);

View File

@ -48,7 +48,7 @@ import javax.servlet.http.Part;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.MultiPartInputStream;
import org.eclipse.jetty.util.MultiPartInputStreamParser;
import org.eclipse.jetty.util.StringUtil;
/* ------------------------------------------------------------ */
@ -141,7 +141,7 @@ public class MultiPartFilter implements Filter
}
MultipartConfigElement config = new MultipartConfigElement(tempdir.getCanonicalPath(), _maxFileSize, _maxRequestSize, _fileOutputBuffer);
MultiPartInputStream mpis = new MultiPartInputStream(in, content_type, config, tempdir);
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(in, content_type, config, tempdir);
mpis.setDeleteOnExit(_deleteFiles);
request.setAttribute(MULTIPART, mpis);
try
@ -153,7 +153,7 @@ public class MultiPartFilter implements Filter
while (itor.hasNext() && params.size() < _maxFormKeys)
{
Part p = itor.next();
MultiPartInputStream.MultiPart mp = (MultiPartInputStream.MultiPart)p;
MultiPartInputStreamParser.MultiPart mp = (MultiPartInputStreamParser.MultiPart)p;
if (mp.getFile() != null)
{
request.setAttribute(mp.getName(),mp.getFile());
@ -191,7 +191,7 @@ public class MultiPartFilter implements Filter
if (!_deleteFiles)
return;
MultiPartInputStream mpis = (MultiPartInputStream)request.getAttribute(MULTIPART);
MultiPartInputStreamParser mpis = (MultiPartInputStreamParser)request.getAttribute(MULTIPART);
if (mpis != null)
{
try

View File

@ -47,7 +47,7 @@ import javax.servlet.http.Part;
*
* Handle a MultiPart Mime input stream, breaking it up on the boundary into files and strings.
*/
public class MultiPartInputStream
public class MultiPartInputStreamParser
{
public static final MultipartConfigElement __DEFAULT_MULTIPART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
protected InputStream _in;
@ -113,10 +113,10 @@ public class MultiPartInputStream
protected void write (int b)
throws IOException
{
if (MultiPartInputStream.this._config.getMaxFileSize() > 0 && _size + 1 > MultiPartInputStream.this._config.getMaxFileSize())
if (MultiPartInputStreamParser.this._config.getMaxFileSize() > 0 && _size + 1 > MultiPartInputStreamParser.this._config.getMaxFileSize())
throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
if (MultiPartInputStream.this._config.getFileSizeThreshold() > 0 && _size + 1 > MultiPartInputStream.this._config.getFileSizeThreshold() && _file==null)
if (MultiPartInputStreamParser.this._config.getFileSizeThreshold() > 0 && _size + 1 > MultiPartInputStreamParser.this._config.getFileSizeThreshold() && _file==null)
createFile();
_out.write(b);
_size ++;
@ -125,10 +125,10 @@ public class MultiPartInputStream
protected void write (byte[] bytes, int offset, int length)
throws IOException
{
if (MultiPartInputStream.this._config.getMaxFileSize() > 0 && _size + length > MultiPartInputStream.this._config.getMaxFileSize())
if (MultiPartInputStreamParser.this._config.getMaxFileSize() > 0 && _size + length > MultiPartInputStreamParser.this._config.getMaxFileSize())
throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
if (MultiPartInputStream.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStream.this._config.getFileSizeThreshold() && _file==null)
if (MultiPartInputStreamParser.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStreamParser.this._config.getFileSizeThreshold() && _file==null)
createFile();
_out.write(bytes, offset, length);
@ -138,7 +138,7 @@ public class MultiPartInputStream
protected void createFile ()
throws IOException
{
_file = File.createTempFile("MultiPart", "", MultiPartInputStream.this._tmpDir);
_file = File.createTempFile("MultiPart", "", MultiPartInputStreamParser.this._tmpDir);
if (_deleteOnExit)
_file.deleteOnExit();
FileOutputStream fos = new FileOutputStream(_file);
@ -325,9 +325,9 @@ public class MultiPartInputStream
* @param config MultipartConfigElement
* @param contextTmpDir javax.servlet.context.tempdir
*/
public MultiPartInputStream (InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
public MultiPartInputStreamParser (InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
{
_in = new BufferedInputStream(in);
_in = new ReadLineInputStream(in);
_contentType = contentType;
_config = config;
_contextTmpDir = contextTmpDir;
@ -372,7 +372,7 @@ public class MultiPartInputStream
{
try
{
((MultiPartInputStream.MultiPart)p).cleanUp();
((MultiPartInputStreamParser.MultiPart)p).cleanUp();
}
catch(Exception e)
{
@ -470,8 +470,7 @@ public class MultiPartInputStream
byte[] byteBoundary=(boundary+"--").getBytes(StringUtil.__ISO_8859_1);
// Get first boundary
byte[] bytes=TypeUtil.readLine(_in);
String line=bytes==null?null:new String(bytes,"UTF-8");
String line = ((ReadLineInputStream)_in).readLine();
if(line==null || !line.equals(boundary))
{
throw new IOException("Missing initial multi part boundary");
@ -487,20 +486,20 @@ public class MultiPartInputStream
MultiMap headers = new MultiMap();
while(true)
{
bytes=TypeUtil.readLine(_in);
if(bytes==null)
line = ((ReadLineInputStream)_in).readLine();
//run out of input:
if (line == null)
break outer;
// If blank line, end of part headers
if(bytes.length==0)
//end of headers:
if ("".equals(line))
break;
total += bytes.length;
total += line.length();
if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
throw new IllegalStateException ("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
line=new String(bytes,"UTF-8");
//get content-disposition and content-type
int c=line.indexOf(':',0);
if(c>0)
@ -514,7 +513,6 @@ public class MultiPartInputStream
contentType = value;
if(key.equals("content-transfer-encoding"))
contentTransferEncoding=value;
}
}
@ -600,7 +598,7 @@ public class MultiPartInputStream
boolean cr=false;
boolean lf=false;
// loop for all lines`
// loop for all lines
while(true)
{
int b=0;
@ -615,7 +613,14 @@ public class MultiPartInputStream
if(c==13||c==10)
{
if(c==13)
state=_in.read();
{
_in.mark(1);
int tmp=_in.read();
if (tmp!=10)
_in.reset();
else
state=tmp;
}
break;
}
// look for boundary

View File

@ -22,6 +22,11 @@ import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* ReadLineInputStream
*
* Read from an input stream, accepting CR/LF, LF or just CR.
*/
public class ReadLineInputStream extends BufferedInputStream
{
boolean _seenCRLF;

View File

@ -43,7 +43,8 @@ import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import javax.servlet.http.Part;
import org.eclipse.jetty.util.MultiPartInputStream.MultiPart;
import org.eclipse.jetty.util.MultiPartInputStreamParser.MultiPart;
import org.hamcrest.core.IsNot;
import org.junit.Test;
/**
@ -75,7 +76,7 @@ public class MultiPartInputStreamTest
"\r\n--" + boundary + "-\r\n\r\n";
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(str.getBytes()),
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
"multipart/form-data, boundary="+boundary,
config,
_tmpDir);
@ -122,7 +123,7 @@ public class MultiPartInputStreamTest
"----\r\n";
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(str.getBytes()),
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
"multipart/form-data",
config,
_tmpDir);
@ -157,7 +158,7 @@ public class MultiPartInputStreamTest
throws Exception
{
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
"Content-type: text/plain",
config,
_tmpDir);
@ -170,7 +171,7 @@ public class MultiPartInputStreamTest
throws Exception
{
MultipartConfigElement config = new MultipartConfigElement(_dirname);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
_contentType,
config,
_tmpDir);
@ -184,7 +185,7 @@ public class MultiPartInputStreamTest
throws Exception
{
MultipartConfigElement config = new MultipartConfigElement(_dirname, 60, 100, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
_contentType,
config,
_tmpDir);
@ -206,7 +207,7 @@ public class MultiPartInputStreamTest
throws Exception
{
MultipartConfigElement config = new MultipartConfigElement(_dirname, 40, 1024, 30);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
_contentType,
config,
_tmpDir);
@ -227,7 +228,7 @@ public class MultiPartInputStreamTest
public void testPartFileNotDeleted () throws Exception
{
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
_contentType,
config,
_tmpDir);
@ -235,7 +236,7 @@ public class MultiPartInputStreamTest
Collection<Part> parts = mpis.getParts();
MultiPart part = (MultiPart)mpis.getPart("stuff");
File stuff = ((MultiPartInputStream.MultiPart)part).getFile();
File stuff = ((MultiPartInputStreamParser.MultiPart)part).getFile();
assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file
part.write("tptfd.txt");
File tptfd = new File (_dirname+File.separator+"tptfd.txt");
@ -246,26 +247,136 @@ public class MultiPartInputStreamTest
tptfd.deleteOnExit(); //clean up test
}
@Test
public void testPartTmpFileDeletion () throws Exception
{
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
_contentType,
config,
_tmpDir);
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
_contentType,
config,
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
MultiPart part = (MultiPart)mpis.getPart("stuff");
File stuff = ((MultiPartInputStream.MultiPart)part).getFile();
File stuff = ((MultiPartInputStreamParser.MultiPart)part).getFile();
assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file
assertThat (stuff.exists(), is(true));
part.cleanUp();
assertThat(stuff.exists(), is(false)); //tmp file was removed after cleanup
}
@Test
public void testLFOnlyRequest()
throws Exception
{
String str = "--AaB03x\n"+
"content-disposition: form-data; name=\"field1\"\n"+
"\n"+
"Joe Blow\n"+
"--AaB03x\n"+
"content-disposition: form-data; name=\"field2\"\n"+
"\n"+
"Other\n"+
"--AaB03x--\n";
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
_contentType,
config,
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
assertThat(parts.size(), is(2));
Part p1 = mpis.getPart("field1");
assertThat(p1, notNullValue());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IO.copy(p1.getInputStream(), baos);
assertThat(baos.toString("UTF-8"), is("Joe Blow"));
Part p2 = mpis.getPart("field2");
assertThat(p2, notNullValue());
baos = new ByteArrayOutputStream();
IO.copy(p2.getInputStream(), baos);
assertThat(baos.toString("UTF-8"), is("Other"));
}
@Test
public void testCROnlyRequest()
throws Exception
{
String str = "--AaB03x\r"+
"content-disposition: form-data; name=\"field1\"\r"+
"\r"+
"Joe Blow\r"+
"--AaB03x\r"+
"content-disposition: form-data; name=\"field2\"\r"+
"\r"+
"Other\r"+
"--AaB03x--\r";
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
_contentType,
config,
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
assertThat(parts.size(), is(2));
assertThat(parts.size(), is(2));
Part p1 = mpis.getPart("field1");
assertThat(p1, notNullValue());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IO.copy(p1.getInputStream(), baos);
assertThat(baos.toString("UTF-8"), is("Joe Blow"));
Part p2 = mpis.getPart("field2");
assertThat(p2, notNullValue());
baos = new ByteArrayOutputStream();
IO.copy(p2.getInputStream(), baos);
assertThat(baos.toString("UTF-8"), is("Other"));
}
@Test
public void testCRandLFMixRequest()
throws Exception
{
String str = "--AaB03x\r"+
"content-disposition: form-data; name=\"field1\"\r"+
"\r"+
"\nJoe Blow\n"+
"\r"+
"--AaB03x\r"+
"content-disposition: form-data; name=\"field2\"\r"+
"\r"+
"Other\r"+
"--AaB03x--\r";
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()),
_contentType,
config,
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
assertThat(parts.size(), is(2));
Part p1 = mpis.getPart("field1");
assertThat(p1, notNullValue());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IO.copy(p1.getInputStream(), baos);
assertThat(baos.toString("UTF-8"), is("\nJoe Blow\n"));
Part p2 = mpis.getPart("field2");
assertThat(p2, notNullValue());
baos = new ByteArrayOutputStream();
IO.copy(p2.getInputStream(), baos);
assertThat(baos.toString("UTF-8"), is("Other"));
}
public void testMulti ()
throws Exception
{
@ -281,7 +392,7 @@ public class MultiPartInputStreamTest
private void testMulti(String filename) throws IOException, ServletException
{
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString(filename).getBytes()),
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(createMultipartRequestString(filename).getBytes()),
_contentType,
config,
_tmpDir);
@ -297,9 +408,9 @@ public class MultiPartInputStreamTest
assertEquals("Joe Blow", new String(os.toByteArray()));
assertEquals(8, field1.getSize());
assertNotNull(((MultiPartInputStream.MultiPart)field1).getBytes());//in internal buffer
assertNotNull(((MultiPartInputStreamParser.MultiPart)field1).getBytes());//in internal buffer
field1.write("field1.txt");
assertNull(((MultiPartInputStream.MultiPart)field1).getBytes());//no longer in internal buffer
assertNull(((MultiPartInputStreamParser.MultiPart)field1).getBytes());//no longer in internal buffer
File f = new File (_dirname+File.separator+"field1.txt");
assertTrue(f.exists());
field1.write("another_field1.txt"); //write after having already written
@ -318,9 +429,9 @@ public class MultiPartInputStreamTest
assertThat(stuff.getHeader("content-disposition"),is("form-data; name=\"stuff\"; filename=\"" + filename + "\""));
assertThat(stuff.getHeaderNames().size(),is(2));
assertThat(stuff.getSize(),is(51L));
File tmpfile = ((MultiPartInputStream.MultiPart)stuff).getFile();
File tmpfile = ((MultiPartInputStreamParser.MultiPart)stuff).getFile();
assertThat(tmpfile,notNullValue()); // longer than 100 bytes, should already be a tmp file
assertThat(((MultiPartInputStream.MultiPart)stuff).getBytes(),nullValue()); //not in an internal buffer
assertThat(((MultiPartInputStreamParser.MultiPart)stuff).getBytes(),nullValue()); //not in an internal buffer
assertThat(tmpfile.exists(),is(true));
assertThat(tmpfile.getName(),is(not("stuff with space.txt")));
stuff.write(filename);
@ -355,7 +466,7 @@ public class MultiPartInputStreamTest
"--AaB03x--\r\n";
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(sameNames.getBytes()),
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(sameNames.getBytes()),
_contentType,
config,
_tmpDir);