work in progress
Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
parent
dc67cb5241
commit
b0325f8299
|
@ -28,10 +28,8 @@ 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;
|
||||||
import org.eclipse.jetty.util.ArrayTrie;
|
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.SearchPattern;
|
import org.eclipse.jetty.util.SearchPattern;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
@ -120,7 +118,6 @@ public class MultiPartParser
|
||||||
{
|
{
|
||||||
public static final Logger LOG = Log.getLogger(MultiPartParser.class);
|
public static final Logger LOG = Log.getLogger(MultiPartParser.class);
|
||||||
|
|
||||||
public final static Trie<MimeField> CACHE = new ArrayTrie<>(2048);
|
|
||||||
|
|
||||||
// States
|
// States
|
||||||
public enum FieldState
|
public enum FieldState
|
||||||
|
@ -129,11 +126,7 @@ public class MultiPartParser
|
||||||
IN_NAME,
|
IN_NAME,
|
||||||
AFTER_NAME,
|
AFTER_NAME,
|
||||||
VALUE,
|
VALUE,
|
||||||
IN_VALUE,
|
IN_VALUE
|
||||||
|
|
||||||
PARAM,
|
|
||||||
PARAM_NAME,
|
|
||||||
PARAM_VALUE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// States
|
// States
|
||||||
|
@ -155,10 +148,8 @@ public class MultiPartParser
|
||||||
private final Handler _handler;
|
private final Handler _handler;
|
||||||
private final String _boundary;
|
private final String _boundary;
|
||||||
private final SearchPattern _search;
|
private final SearchPattern _search;
|
||||||
private MimeField _field;
|
private String _fieldName;
|
||||||
private String _headerString;
|
private String _fieldValue;
|
||||||
private String _valueString;
|
|
||||||
private int _headerBytes;
|
|
||||||
|
|
||||||
private State _state = State.PREAMBLE;
|
private State _state = State.PREAMBLE;
|
||||||
private FieldState _fieldState = FieldState.FIELD;
|
private FieldState _fieldState = FieldState.FIELD;
|
||||||
|
@ -169,12 +160,6 @@ public class MultiPartParser
|
||||||
private final StringBuilder _string=new StringBuilder();
|
private final StringBuilder _string=new StringBuilder();
|
||||||
private int _length;
|
private int _length;
|
||||||
|
|
||||||
static
|
|
||||||
{
|
|
||||||
CACHE.put(new MimeField("Content-Disposition","form-data"));
|
|
||||||
CACHE.put(new MimeField("Content-Type","text/plain"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
public MultiPartParser(Handler handler, String boundary)
|
public MultiPartParser(Handler handler, String boundary)
|
||||||
|
@ -330,24 +315,30 @@ public class MultiPartParser
|
||||||
case DELIMITER_PADDING:
|
case DELIMITER_PADDING:
|
||||||
case DELIMITER_CLOSE:
|
case DELIMITER_CLOSE:
|
||||||
parseDelimiter(buffer);
|
parseDelimiter(buffer);
|
||||||
break;
|
continue;
|
||||||
|
|
||||||
|
|
||||||
case BODY_PART:
|
case BODY_PART:
|
||||||
handle = parseFields(buffer);
|
handle = parseFields(buffer);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PART:
|
case PART:
|
||||||
|
// TODO
|
||||||
|
handle = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case EPILOGUE:
|
||||||
|
// TODO
|
||||||
|
handle = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case END:
|
case END:
|
||||||
break;
|
// TODO
|
||||||
|
handle = true;
|
||||||
case EPILOGUE:
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
throw new IllegalStateException();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,37 +433,38 @@ public class MultiPartParser
|
||||||
*/
|
*/
|
||||||
protected boolean parseFields(ByteBuffer buffer)
|
protected boolean parseFields(ByteBuffer buffer)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
// Process headers
|
// Process headers
|
||||||
while ((_state==State.HEADER && buffer.hasRemaining())
|
while (_state==State.BODY_PART && buffer.hasRemaining())
|
||||||
{
|
{
|
||||||
// process each character
|
// process each character
|
||||||
byte b=next(buffer);
|
byte b=next(buffer);
|
||||||
if (b==0)
|
if (b==0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
switch (_fieldState)
|
switch (_fieldState)
|
||||||
{
|
{
|
||||||
case FIELD:
|
case FIELD:
|
||||||
switch(b)
|
switch(b)
|
||||||
{
|
{
|
||||||
case HttpTokens.COLON:
|
|
||||||
case HttpTokens.SPACE:
|
case HttpTokens.SPACE:
|
||||||
case HttpTokens.TAB:
|
case HttpTokens.TAB:
|
||||||
{
|
{
|
||||||
// header value without name - continuation?
|
// Folded field value!
|
||||||
if (_valueString==null)
|
|
||||||
|
if (_fieldName==null)
|
||||||
|
throw new IllegalStateException("First field folded");
|
||||||
|
|
||||||
|
if (_fieldValue==null)
|
||||||
{
|
{
|
||||||
_string.setLength(0);
|
_string.setLength(0);
|
||||||
_length=0;
|
_length=0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setString(_valueString);
|
setString(_fieldValue);
|
||||||
_string.append(' ');
|
_string.append(' ');
|
||||||
_length++;
|
_length++;
|
||||||
_valueString=null;
|
_fieldValue=null;
|
||||||
}
|
}
|
||||||
setState(FieldState.VALUE);
|
setState(FieldState.VALUE);
|
||||||
break;
|
break;
|
||||||
|
@ -480,76 +472,11 @@ public class MultiPartParser
|
||||||
|
|
||||||
case HttpTokens.LINE_FEED:
|
case HttpTokens.LINE_FEED:
|
||||||
{
|
{
|
||||||
// process previous header
|
handleField();
|
||||||
if (_state==State.HEADER)
|
setState(State.PART);
|
||||||
parsedHeader();
|
if (_handler.headerComplete())
|
||||||
else
|
return true;
|
||||||
parsedTrailer();
|
break;
|
||||||
|
|
||||||
_contentPosition=0;
|
|
||||||
|
|
||||||
// End of headers or trailers?
|
|
||||||
if (_state==State.TRAILER)
|
|
||||||
{
|
|
||||||
setState(State.END);
|
|
||||||
return _handler.messageComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Was there a required host header?
|
|
||||||
if (!_host && _version==HttpVersion.HTTP_1_1 && _requestHandler!=null)
|
|
||||||
{
|
|
||||||
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"No Host");
|
|
||||||
}
|
|
||||||
|
|
||||||
// is it a response that cannot have a body?
|
|
||||||
if (_responseHandler !=null && // response
|
|
||||||
(_responseStatus == 304 || // not-modified response
|
|
||||||
_responseStatus == 204 || // no-content response
|
|
||||||
_responseStatus < 200)) // 1xx response
|
|
||||||
_endOfContent=EndOfContent.NO_CONTENT; // ignore any other headers set
|
|
||||||
|
|
||||||
// else if we don't know framing
|
|
||||||
else if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
|
|
||||||
{
|
|
||||||
if (_responseStatus == 0 // request
|
|
||||||
|| _responseStatus == 304 // not-modified response
|
|
||||||
|| _responseStatus == 204 // no-content response
|
|
||||||
|| _responseStatus < 200) // 1xx response
|
|
||||||
_endOfContent=EndOfContent.NO_CONTENT;
|
|
||||||
else
|
|
||||||
_endOfContent=EndOfContent.EOF_CONTENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// How is the message ended?
|
|
||||||
switch (_endOfContent)
|
|
||||||
{
|
|
||||||
case EOF_CONTENT:
|
|
||||||
{
|
|
||||||
setState(State.EOF_CONTENT);
|
|
||||||
boolean handle=_handler.headerComplete();
|
|
||||||
_headerComplete=true;
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
case CHUNKED_CONTENT:
|
|
||||||
{
|
|
||||||
setState(State.CHUNKED_CONTENT);
|
|
||||||
boolean handle=_handler.headerComplete();
|
|
||||||
_headerComplete=true;
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
case NO_CONTENT:
|
|
||||||
{
|
|
||||||
setState(State.END);
|
|
||||||
return handleHeaderContentMessage();
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
setState(State.CONTENT);
|
|
||||||
boolean handle=_handler.headerComplete();
|
|
||||||
_headerComplete=true;
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -559,90 +486,7 @@ public class MultiPartParser
|
||||||
throw new BadMessageException();
|
throw new BadMessageException();
|
||||||
|
|
||||||
// process previous header
|
// process previous header
|
||||||
if (_state==State.HEADER)
|
handleField();
|
||||||
parsedHeader();
|
|
||||||
else
|
|
||||||
parsedTrailer();
|
|
||||||
|
|
||||||
// handle new header
|
|
||||||
if (buffer.hasRemaining())
|
|
||||||
{
|
|
||||||
// Try a look ahead for the known header name and value.
|
|
||||||
HttpField cached_field=_fieldCache==null?null:_fieldCache.getBest(buffer,-1,buffer.remaining());
|
|
||||||
if (cached_field==null)
|
|
||||||
cached_field=CACHE.getBest(buffer,-1,buffer.remaining());
|
|
||||||
|
|
||||||
if (cached_field!=null)
|
|
||||||
{
|
|
||||||
String n = cached_field.getName();
|
|
||||||
String v = cached_field.getValue();
|
|
||||||
|
|
||||||
if (!_compliances.contains(HttpComplianceSection.FIELD_NAME_CASE_INSENSITIVE))
|
|
||||||
{
|
|
||||||
// Have to get the fields exactly from the buffer to match case
|
|
||||||
String en = BufferUtil.toString(buffer,buffer.position()-1,n.length(),StandardCharsets.US_ASCII);
|
|
||||||
if (!n.equals(en))
|
|
||||||
{
|
|
||||||
handleViolation(HttpComplianceSection.FIELD_NAME_CASE_INSENSITIVE,en);
|
|
||||||
n = en;
|
|
||||||
cached_field = new HttpField(cached_field.getHeader(),n,v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v!=null && !_compliances.contains(HttpComplianceSection.CASE_INSENSITIVE_FIELD_VALUE_CACHE))
|
|
||||||
{
|
|
||||||
String ev = BufferUtil.toString(buffer,buffer.position()+n.length()+1,v.length(),StandardCharsets.ISO_8859_1);
|
|
||||||
if (!v.equals(ev))
|
|
||||||
{
|
|
||||||
handleViolation(HttpComplianceSection.CASE_INSENSITIVE_FIELD_VALUE_CACHE,ev+"!="+v);
|
|
||||||
v = ev;
|
|
||||||
cached_field = new HttpField(cached_field.getHeader(),n,v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_header=cached_field.getHeader();
|
|
||||||
_headerString=n;
|
|
||||||
|
|
||||||
if (v==null)
|
|
||||||
{
|
|
||||||
// Header only
|
|
||||||
setState(FieldState.VALUE);
|
|
||||||
_string.setLength(0);
|
|
||||||
_length=0;
|
|
||||||
buffer.position(buffer.position()+n.length()+1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Header and value
|
|
||||||
int pos=buffer.position()+n.length()+v.length()+1;
|
|
||||||
byte peek=buffer.get(pos);
|
|
||||||
|
|
||||||
if (peek==HttpTokens.CARRIAGE_RETURN || peek==HttpTokens.LINE_FEED)
|
|
||||||
{
|
|
||||||
_field=cached_field;
|
|
||||||
_valueString=v;
|
|
||||||
setState(FieldState.IN_VALUE);
|
|
||||||
|
|
||||||
if (peek==HttpTokens.CARRIAGE_RETURN)
|
|
||||||
{
|
|
||||||
_cr=true;
|
|
||||||
buffer.position(pos+1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
buffer.position(pos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
setState(FieldState.IN_VALUE);
|
|
||||||
setString(v);
|
|
||||||
buffer.position(pos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New header
|
// New header
|
||||||
setState(FieldState.IN_NAME);
|
setState(FieldState.IN_NAME);
|
||||||
|
@ -654,60 +498,49 @@ public class MultiPartParser
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IN_NAME:
|
case IN_NAME:
|
||||||
if (b>HttpTokens.SPACE && b!=HttpTokens.COLON)
|
if (b==HttpTokens.COLON)
|
||||||
{
|
{
|
||||||
if (_header!=null)
|
_fieldName=takeString();
|
||||||
{
|
_length=-1;
|
||||||
setString(_header.asString());
|
setState(FieldState.VALUE);
|
||||||
_header=null;
|
break;
|
||||||
_headerString=null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (b>HttpTokens.SPACE)
|
||||||
|
{
|
||||||
_string.append((char)b);
|
_string.append((char)b);
|
||||||
_length=_string.length();
|
_length=_string.length();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallthrough
|
//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)
|
if (b==HttpTokens.COLON)
|
||||||
{
|
{
|
||||||
if (_headerString==null)
|
_fieldName=takeString();
|
||||||
{
|
|
||||||
_headerString=takeString();
|
|
||||||
_header=HttpHeader.CACHE.get(_headerString);
|
|
||||||
}
|
|
||||||
_length=-1;
|
_length=-1;
|
||||||
|
|
||||||
setState(FieldState.VALUE);
|
setState(FieldState.VALUE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (b==HttpTokens.LINE_FEED)
|
if (b==HttpTokens.LINE_FEED)
|
||||||
{
|
{
|
||||||
if (_headerString==null)
|
_fieldName=takeString();
|
||||||
{
|
|
||||||
_headerString=takeString();
|
|
||||||
_header=HttpHeader.CACHE.get(_headerString);
|
|
||||||
}
|
|
||||||
_string.setLength(0);
|
_string.setLength(0);
|
||||||
_valueString="";
|
_fieldValue="";
|
||||||
_length=-1;
|
_length=-1;
|
||||||
|
|
||||||
if (!complianceViolation(HttpComplianceSection.FIELD_COLON,_headerString))
|
|
||||||
{
|
|
||||||
setState(FieldState.FIELD);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Ignore trailing whitespaces
|
if (b==HttpTokens.SPACE)
|
||||||
if (b==HttpTokens.SPACE && !complianceViolation(HttpComplianceSection.NO_WS_AFTER_FIELD_NAME,null))
|
|
||||||
{
|
|
||||||
setState(FieldState.AFTER_NAME);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalCharacterException(_state,b,buffer);
|
throw new IllegalCharacterException(_state,b,buffer);
|
||||||
|
|
||||||
|
@ -726,7 +559,7 @@ public class MultiPartParser
|
||||||
if (b==HttpTokens.LINE_FEED)
|
if (b==HttpTokens.LINE_FEED)
|
||||||
{
|
{
|
||||||
_string.setLength(0);
|
_string.setLength(0);
|
||||||
_valueString="";
|
_fieldValue="";
|
||||||
_length=-1;
|
_length=-1;
|
||||||
|
|
||||||
setState(FieldState.FIELD);
|
setState(FieldState.FIELD);
|
||||||
|
@ -737,11 +570,10 @@ public class MultiPartParser
|
||||||
case IN_VALUE:
|
case IN_VALUE:
|
||||||
if (b>=HttpTokens.SPACE || b<0 || b==HttpTokens.TAB)
|
if (b>=HttpTokens.SPACE || b<0 || b==HttpTokens.TAB)
|
||||||
{
|
{
|
||||||
if (_valueString!=null)
|
if (_fieldValue!=null)
|
||||||
{
|
{
|
||||||
setString(_valueString);
|
setString(_fieldValue);
|
||||||
_valueString=null;
|
_fieldValue=null;
|
||||||
_field=null;
|
|
||||||
}
|
}
|
||||||
_string.append((char)(0xff&b));
|
_string.append((char)(0xff&b));
|
||||||
if (b>HttpTokens.SPACE || b<0)
|
if (b>HttpTokens.SPACE || b<0)
|
||||||
|
@ -753,7 +585,7 @@ public class MultiPartParser
|
||||||
{
|
{
|
||||||
if (_length > 0)
|
if (_length > 0)
|
||||||
{
|
{
|
||||||
_valueString=takeString();
|
_fieldValue=takeString();
|
||||||
_length=-1;
|
_length=-1;
|
||||||
}
|
}
|
||||||
setState(FieldState.FIELD);
|
setState(FieldState.FIELD);
|
||||||
|
@ -767,11 +599,16 @@ public class MultiPartParser
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------- */
|
||||||
|
private void handleField()
|
||||||
|
{
|
||||||
|
if (_fieldName!=null && _fieldValue!=null)
|
||||||
|
_handler.parsedHeader(_fieldName,_fieldValue);
|
||||||
|
_fieldName = _fieldValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
@ -793,7 +630,7 @@ public class MultiPartParser
|
||||||
private void setState(FieldState state)
|
private void setState(FieldState state)
|
||||||
{
|
{
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
LOG.debug("{}:{} --> {}",_state,_field,state);
|
LOG.debug("{}:{} --> {}",_state,_fieldState,state);
|
||||||
_fieldState=state;
|
_fieldState=state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -819,8 +656,7 @@ public class MultiPartParser
|
||||||
*/
|
*/
|
||||||
public interface Handler
|
public interface Handler
|
||||||
{
|
{
|
||||||
public default void parsedHeader(MimeField field) {}
|
public default void parsedHeader(String name, String value) {}
|
||||||
public default void parsedParameter(MimeField field, String name, String value) {};
|
|
||||||
public default boolean headerComplete() {return false;}
|
public default boolean headerComplete() {return false;}
|
||||||
|
|
||||||
public default boolean content(ByteBuffer item, boolean last) {return false;}
|
public default boolean content(ByteBuffer item, boolean last) {return false;}
|
||||||
|
@ -843,24 +679,4 @@ public class MultiPartParser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static class MimeField
|
|
||||||
{
|
|
||||||
final String _name;
|
|
||||||
final String _value;
|
|
||||||
final String _string;
|
|
||||||
|
|
||||||
public MimeField(String name, String value)
|
|
||||||
{
|
|
||||||
_name = name;
|
|
||||||
_value = value;
|
|
||||||
_string = name + ": " + value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return _string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,10 @@ import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
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;
|
||||||
|
@ -75,7 +78,12 @@ public class MultiPartParserTest
|
||||||
assertThat(parser.getState(),is(State.PREAMBLE));
|
assertThat(parser.getState(),is(State.PREAMBLE));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(),is(0));
|
||||||
|
|
||||||
data = BufferUtil.toBuffer("but not it isn't");
|
data = BufferUtil.toBuffer("but not it isn't \r\n--BOUN");
|
||||||
|
parser.parse(data,false);
|
||||||
|
assertThat(parser.getState(),is(State.PREAMBLE));
|
||||||
|
assertThat(data.remaining(),is(0));
|
||||||
|
|
||||||
|
data = BufferUtil.toBuffer("DARX nor is this");
|
||||||
parser.parse(data,false);
|
parser.parse(data,false);
|
||||||
assertThat(parser.getState(),is(State.PREAMBLE));
|
assertThat(parser.getState(),is(State.PREAMBLE));
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(),is(0));
|
||||||
|
@ -129,5 +137,56 @@ public class MultiPartParserTest
|
||||||
assertThat(data.remaining(),is(0));
|
assertThat(data.remaining(),is(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFirstPartNoFields()
|
||||||
|
{
|
||||||
|
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler(){},"BOUNDARY");
|
||||||
|
ByteBuffer data = BufferUtil.toBuffer("");
|
||||||
|
|
||||||
|
data = BufferUtil.toBuffer("--BOUNDARY\r\n\r\n");
|
||||||
|
parser.parse(data,false);
|
||||||
|
assertTrue(parser.isState(State.PART));
|
||||||
|
assertThat(data.remaining(),is(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFirstPartFields()
|
||||||
|
{
|
||||||
|
List<String> fields = new ArrayList<>();
|
||||||
|
MultiPartParser parser = new MultiPartParser(new MultiPartParser.Handler()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parsedHeader(String name, String value)
|
||||||
|
{
|
||||||
|
new Throwable().printStackTrace();
|
||||||
|
fields.add(name+": "+value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean headerComplete()
|
||||||
|
{
|
||||||
|
fields.add("COMPLETE!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
},"BOUNDARY");
|
||||||
|
ByteBuffer data = BufferUtil.toBuffer("");
|
||||||
|
|
||||||
|
data = BufferUtil.toBuffer("--BOUNDARY\r\n"
|
||||||
|
+ "name0: value0\r\n"
|
||||||
|
+ "name1 :value1 \r\n"
|
||||||
|
+ "name2:value\r\n"
|
||||||
|
+ " 2\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "Content");
|
||||||
|
parser.parse(data,false);
|
||||||
|
assertTrue(parser.isState(State.PART));
|
||||||
|
assertThat(data.remaining(),is(7));
|
||||||
|
assertThat(fields,Matchers.contains("name0: value0","name1: value1", "name2: value 2", "COMPLETE!"));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue