Merge remote-tracking branch 'origin/jetty-8'

Conflicts:
	jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java
	jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java
	jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java
This commit is contained in:
Jan Bartel 2013-07-04 10:58:28 +10:00
commit 1762ab7de5
3 changed files with 174 additions and 36 deletions

View File

@ -336,11 +336,33 @@ public class B64Code
if (encoded==null)
return null;
ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);
decode(encoded, bout);
return bout.toByteArray();
}
/* ------------------------------------------------------------ */
/**
* Base 64 decode as described in RFC 2045.
* <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
* @param encoded String to decode.
* @param output stream for decoded bytes
* @return byte array containing the decoded form of the input.
* @throws IllegalArgumentException if the input is not a valid
* B64 encoding.
*/
static public void decode (String encoded, ByteArrayOutputStream bout)
{
if (encoded==null)
return;
if (bout == null)
throw new IllegalArgumentException("No outputstream for decoded bytes");
int ci=0;
byte nibbles[] = new byte[4];
int s=0;
ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);
while (ci<encoded.length())
{
char c=encoded.charAt(ci++);
@ -375,8 +397,9 @@ public class B64Code
}
return bout.toByteArray();
return;
}
public static void encode(int value,Appendable buf) throws IOException
{

View File

@ -20,15 +20,14 @@ package org.eclipse.jetty.util;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
@ -502,11 +501,13 @@ public class MultiPartInputStreamParser
// Read each part
boolean lastPart=false;
String contentDisposition=null;
String contentType=null;
String contentTransferEncoding=null;
outer:while(!lastPart)
{
String contentDisposition=null;
String contentType=null;
String contentTransferEncoding=null;
MultiMap headers = new MultiMap();
while(true)
{
@ -577,13 +578,21 @@ public class MultiPartInputStreamParser
continue;
}
//Have a new Part
MultiPart part = new MultiPart(name, filename);
part.setHeaders(headers);
part.setContentType(contentType);
_parts.add(name, part);
part.open();
InputStream partInput = null;
if ("base64".equalsIgnoreCase(contentTransferEncoding))
{
_in = new Base64InputStream(_in);
partInput = new Base64InputStream((ReadLineInputStream)_in);
}
else if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding))
{
_in = new FilterInputStream(_in)
partInput = new FilterInputStream(_in)
{
@Override
public int read() throws IOException
@ -604,17 +613,10 @@ public class MultiPartInputStreamParser
}
};
}
else
partInput = _in;
//Have a new Part
MultiPart part = new MultiPart(name, filename);
part.setHeaders(headers);
part.setContentType(contentType);
_parts.add(name, part);
part.open();
try
{
int state=-2;
@ -626,33 +628,38 @@ public class MultiPartInputStreamParser
while(true)
{
int b=0;
while((c=(state!=-2)?state:_in.read())!=-1)
while((c=(state!=-2)?state:partInput.read())!=-1)
{
total ++;
if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
throw new IllegalStateException("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
state=-2;
// look for CR and/or LF
if(c==13||c==10)
{
if(c==13)
{
_in.mark(1);
int tmp=_in.read();
partInput.mark(1);
int tmp=partInput.read();
if (tmp!=10)
_in.reset();
partInput.reset();
else
state=tmp;
}
break;
}
// look for boundary
// Look for boundary
if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
{
b++;
}
else
{
// this is not a boundary
// Got a character not part of the boundary, so we don't have the boundary marker.
// Write out as many chars as we matched, then the char we're looking at.
if(cr)
part.write(13);
@ -667,7 +674,8 @@ public class MultiPartInputStreamParser
part.write(c);
}
}
// check partial boundary
// Check for incomplete boundary match, writing out the chars we matched along the way
if((b>0&&b<byteBoundary.length-2)||(b==byteBoundary.length-1))
{
if(cr)
@ -680,15 +688,18 @@ public class MultiPartInputStreamParser
part.write(byteBoundary,0,b);
b=-1;
}
// boundary match
// Boundary match. If we've run out of input or we matched the entire final boundary marker, then this is the last part.
if(b>0||c==-1)
{
if(b==byteBoundary.length)
lastPart=true;
if(state==10)
state=-2;
break;
}
// handle CR LF
if(cr)
part.write(13);
@ -764,14 +775,15 @@ public class MultiPartInputStreamParser
private static class Base64InputStream extends InputStream
{
BufferedReader _in;
ReadLineInputStream _in;
String _line;
byte[] _buffer;
int _pos;
public Base64InputStream (InputStream in)
public Base64InputStream(ReadLineInputStream rlis)
{
_in = new BufferedReader(new InputStreamReader(in));
_in = rlis;
}
@Override
@ -779,18 +791,29 @@ public class MultiPartInputStreamParser
{
if (_buffer==null || _pos>= _buffer.length)
{
_line = _in.readLine();
//Any CR and LF will be consumed by the readLine() call.
//We need to put them back into the bytes returned from this
//method because the parsing of the multipart content uses them
//as markers to determine when we've reached the end of a part.
_line = _in.readLine();
if (_line==null)
return -1;
return -1; //nothing left
if (_line.startsWith("--"))
_buffer=(_line+"\r\n").getBytes();
_buffer=(_line+"\r\n").getBytes(); //boundary marking end of part
else if (_line.length()==0)
_buffer="\r\n".getBytes();
_buffer="\r\n".getBytes(); //blank line
else
_buffer=B64Code.decode(_line);
{
ByteArrayOutputStream baos = new ByteArrayOutputStream((4*_line.length()/3)+2);
B64Code.decode(_line, baos);
baos.write(13);
baos.write(10);
_buffer = baos.toByteArray();
}
_pos=0;
}
return _buffer[_pos++];
}
}

View File

@ -724,6 +724,98 @@ public class MultiPartInputStreamTest
assertEquals(5, p.getSize());
}
@Test
public void testBase64EncodedContent () throws Exception
{
String contentWithEncodedPart =
"--AaB03x\r\n"+
"Content-disposition: form-data; name=\"other\"\r\n"+
"Content-Type: text/plain\r\n"+
"\r\n"+
"other" + "\r\n"+
"--AaB03x\r\n"+
"Content-disposition: form-data; name=\"stuff\"; filename=\"stuff.txt\"\r\n"+
"Content-Transfer-Encoding: base64\r\n"+
"Content-Type: application/octet-stream\r\n"+
"\r\n"+
B64Code.encode("hello jetty") + "\r\n"+
"--AaB03x\r\n"+
"Content-disposition: form-data; name=\"final\"\r\n"+
"Content-Type: text/plain\r\n"+
"\r\n"+
"the end" + "\r\n"+
"--AaB03x--\r\n";
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contentWithEncodedPart.getBytes()),
_contentType,
config,
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
assertEquals(3, parts.size());
Part p1 = mpis.getPart("other");
assertNotNull(p1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IO.copy(p1.getInputStream(), baos);
assertEquals("other", baos.toString("US-ASCII"));
Part p2 = mpis.getPart("stuff");
assertNotNull(p2);
baos = new ByteArrayOutputStream();
IO.copy(p2.getInputStream(), baos);
assertEquals("hello jetty", baos.toString("US-ASCII"));
Part p3 = mpis.getPart("final");
assertNotNull(p3);
baos = new ByteArrayOutputStream();
IO.copy(p3.getInputStream(), baos);
assertEquals("the end", baos.toString("US-ASCII"));
}
@Test
public void testQuotedPrintableEncoding () throws Exception
{
String contentWithEncodedPart =
"--AaB03x\r\n"+
"Content-disposition: form-data; name=\"other\"\r\n"+
"Content-Type: text/plain\r\n"+
"\r\n"+
"other" + "\r\n"+
"--AaB03x\r\n"+
"Content-disposition: form-data; name=\"stuff\"; filename=\"stuff.txt\"\r\n"+
"Content-Transfer-Encoding: quoted-printable\r\n"+
"Content-Type: text/plain\r\n"+
"\r\n"+
"truth=3Dbeauty" + "\r\n"+
"--AaB03x--\r\n";
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contentWithEncodedPart.getBytes()),
_contentType,
config,
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
assertEquals(2, parts.size());
Part p1 = mpis.getPart("other");
assertNotNull(p1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IO.copy(p1.getInputStream(), baos);
assertEquals("other", baos.toString("US-ASCII"));
Part p2 = mpis.getPart("stuff");
assertNotNull(p2);
baos = new ByteArrayOutputStream();
IO.copy(p2.getInputStream(), baos);
assertEquals("truth=beauty", baos.toString("US-ASCII"));
}
private String createMultipartRequestString(String filename)
{
int length = filename.length();