Merge remote-tracking branch 'eclipse/jetty-9.4.x-1027-Multipart' into jetty-9.4.x-1027-Multipart

This commit is contained in:
Lachlan Roberts 2018-03-13 11:49:00 +11:00
commit 0d4e202343
2 changed files with 99 additions and 110 deletions

View File

@ -18,11 +18,6 @@
package org.eclipse.jetty.http; package org.eclipse.jetty.http;
import static org.eclipse.jetty.http.HttpTokens.CARRIAGE_RETURN;
import static org.eclipse.jetty.http.HttpTokens.LINE_FEED;
import static org.eclipse.jetty.http.HttpTokens.SPACE;
import static org.eclipse.jetty.http.HttpTokens.TAB;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
@ -118,6 +113,14 @@ 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)':';
static final byte TAB= 0x09;
static final byte LINE_FEED= 0x0A;
static final byte CARRIAGE_RETURN= 0x0D;
static final byte SPACE= 0x20;
static final byte[] CRLF = {CARRIAGE_RETURN,LINE_FEED};
static final byte SEMI_COLON= (byte)';';
// States // States
public enum FieldState public enum FieldState
@ -146,8 +149,7 @@ public class MultiPartParser
private final boolean DEBUG=LOG.isDebugEnabled(); private final boolean DEBUG=LOG.isDebugEnabled();
private final Handler _handler; private final Handler _handler;
private final String _boundary; private final SearchPattern _delimiterSearch;
private final SearchPattern _search;
private String _fieldName; private String _fieldName;
private String _fieldValue; private String _fieldValue;
@ -155,7 +157,6 @@ public class MultiPartParser
private FieldState _fieldState = FieldState.FIELD; private FieldState _fieldState = FieldState.FIELD;
private int _partialBoundary = 2; // No CRLF if no preamble private int _partialBoundary = 2; // No CRLF if no preamble
private boolean _cr; private boolean _cr;
private boolean _quote;
private final StringBuilder _string=new StringBuilder(); private final StringBuilder _string=new StringBuilder();
private int _length; private int _length;
@ -165,8 +166,7 @@ public class MultiPartParser
public MultiPartParser(Handler handler, String boundary) public MultiPartParser(Handler handler, String boundary)
{ {
_handler = handler; _handler = handler;
_boundary = boundary; _delimiterSearch = SearchPattern.compile("\r\n--"+boundary);
_search = SearchPattern.compile("\r\n--"+boundary);
} }
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
@ -318,7 +318,7 @@ public class MultiPartParser
continue; continue;
case BODY_PART: case BODY_PART:
handle = parseFields(buffer); handle = parseMimePartHeaders(buffer);
break; break;
case PART: case PART:
@ -351,10 +351,10 @@ public class MultiPartParser
{ {
if (_partialBoundary>0) if (_partialBoundary>0)
{ {
int partial = _search.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==_search.getLength()) if (partial==_delimiterSearch.getLength())
{ {
buffer.position(buffer.position()+partial-_partialBoundary); buffer.position(buffer.position()+partial-_partialBoundary);
_partialBoundary = 0; _partialBoundary = 0;
@ -370,15 +370,15 @@ public class MultiPartParser
_partialBoundary = 0; _partialBoundary = 0;
} }
int delimiter = _search.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()+_search.getLength()); buffer.position(delimiter-buffer.arrayOffset()+_delimiterSearch.getLength());
setState(State.DELIMITER); setState(State.DELIMITER);
return; return;
} }
_partialBoundary = _search.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; return;
@ -428,7 +428,7 @@ public class MultiPartParser
/* /*
* Parse the message headers and return true if the handler has signaled for a return * Parse the message headers and return true if the handler has signaled for a return
*/ */
protected boolean parseFields(ByteBuffer buffer) protected boolean parseMimePartHeaders(ByteBuffer buffer)
{ {
// Process headers // Process headers
while (_state==State.BODY_PART && buffer.hasRemaining()) while (_state==State.BODY_PART && buffer.hasRemaining())
@ -443,8 +443,8 @@ public class MultiPartParser
case FIELD: case FIELD:
switch(b) switch(b)
{ {
case HttpTokens.SPACE: case SPACE:
case HttpTokens.TAB: case TAB:
{ {
// Folded field value! // Folded field value!
@ -467,7 +467,7 @@ public class MultiPartParser
break; break;
} }
case HttpTokens.LINE_FEED: case LINE_FEED:
{ {
handleField(); handleField();
setState(State.PART); setState(State.PART);
@ -478,10 +478,6 @@ public class MultiPartParser
default: default:
{ {
// now handle the ch
if (b<HttpTokens.SPACE)
throw new BadMessageException();
// process previous header // process previous header
handleField(); handleField();
@ -495,101 +491,96 @@ public class MultiPartParser
break; break;
case IN_NAME: case IN_NAME:
if (b==HttpTokens.COLON) switch(b)
{ {
_fieldName=takeString(); case COLON:
_length=-1; _fieldName=takeString();
setState(FieldState.VALUE); _length=-1;
break; setState(FieldState.VALUE);
break;
case SPACE:
//Ignore trailing whitespaces
setState(FieldState.AFTER_NAME);
break;
default:
_string.append((char)b);
_length=_string.length();
break;
} }
break;
if (b>HttpTokens.SPACE)
{
_string.append((char)b);
_length=_string.length();
break;
}
//Ignore trailing whitespaces
if (b==HttpTokens.SPACE)
{
setState(FieldState.AFTER_NAME);
break;
}
throw new IllegalCharacterException(_state,b,buffer);
case AFTER_NAME: case AFTER_NAME:
if (b==HttpTokens.COLON) switch(b)
{ {
_fieldName=takeString(); case COLON:
_length=-1; _fieldName=takeString();
setState(FieldState.VALUE); _length=-1;
break; setState(FieldState.VALUE);
break;
case LINE_FEED:
_fieldName=takeString();
_string.setLength(0);
_fieldValue="";
_length=-1;
break;
case SPACE:
break;
default:
throw new IllegalCharacterException(_state,b,buffer);
} }
break;
if (b==HttpTokens.LINE_FEED)
{
_fieldName=takeString();
_string.setLength(0);
_fieldValue="";
_length=-1;
}
if (b==HttpTokens.SPACE)
break;
throw new IllegalCharacterException(_state,b,buffer);
case VALUE: case VALUE:
if (b>HttpTokens.SPACE || b<0) switch(b)
{ {
_string.append((char)(0xff&b)); case LINE_FEED:
_length=_string.length(); _string.setLength(0);
setState(FieldState.IN_VALUE); _fieldValue="";
break; _length=-1;
setState(FieldState.FIELD);
break;
case SPACE:
case TAB:
break;
default:
_string.append((char)(0xff&b));
_length=_string.length();
setState(FieldState.IN_VALUE);
break;
} }
break;
if (b==HttpTokens.SPACE || b==HttpTokens.TAB)
break;
if (b==HttpTokens.LINE_FEED)
{
_string.setLength(0);
_fieldValue="";
_length=-1;
setState(FieldState.FIELD);
break;
}
throw new IllegalCharacterException(_state,b,buffer);
case IN_VALUE: case IN_VALUE:
if (b>=HttpTokens.SPACE || b<0 || b==HttpTokens.TAB) switch(b)
{ {
if (_fieldValue!=null) case SPACE:
{ _string.append((char)(0xff&b));
setString(_fieldValue); break;
_fieldValue=null;
}
_string.append((char)(0xff&b));
if (b>HttpTokens.SPACE || b<0)
_length=_string.length();
break;
}
if (b==HttpTokens.LINE_FEED) case LINE_FEED:
{ if (_length > 0)
if (_length > 0) {
{ _fieldValue=takeString();
_fieldValue=takeString(); _length=-1;
_length=-1; }
} setState(FieldState.FIELD);
setState(FieldState.FIELD); break;
break;
}
throw new IllegalCharacterException(_state,b,buffer); default:
_string.append((char)(0xff&b));
if (b>SPACE || b<0)
_length=_string.length();
break;
}
break;
default: default:
throw new IllegalStateException(_state.toString()); throw new IllegalStateException(_state.toString());
@ -714,11 +705,11 @@ public class MultiPartParser
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
@SuppressWarnings("serial") @SuppressWarnings("serial")
private static class IllegalCharacterException extends BadMessageException private static class IllegalCharacterException extends IllegalArgumentException
{ {
private IllegalCharacterException(State state,byte ch,ByteBuffer buffer) private IllegalCharacterException(State state,byte ch,ByteBuffer buffer)
{ {
super(400,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)));
} }

View File

@ -25,11 +25,9 @@ import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.eclipse.jetty.http.MultiPartParser.Handler;
import org.eclipse.jetty.http.MultiPartParser.State; import org.eclipse.jetty.http.MultiPartParser.State;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
public class MultiPartParserTest public class MultiPartParserTest
@ -158,6 +156,7 @@ public class MultiPartParserTest
@Override @Override
public void parsedHeader(String name, String value) public void parsedHeader(String name, String value)
{ {
System.err.println("Value='"+value+"'");
fields.add(name+": "+value); fields.add(name+": "+value);
} }
@ -181,9 +180,8 @@ public class MultiPartParserTest
parser.parse(data,false); parser.parse(data,false);
assertTrue(parser.isState(State.PART)); assertTrue(parser.isState(State.PART));
assertThat(data.remaining(),is(7)); assertThat(data.remaining(),is(7));
System.err.println(fields);
assertThat(fields,Matchers.contains("name0: value0","name1: value1", "name2: value 2", "<<COMPLETE>>")); assertThat(fields,Matchers.contains("name0: value0","name1: value1", "name2: value 2", "<<COMPLETE>>"));
} }
@Test @Test