Merge remote-tracking branch 'origin/jetty-9.4.x'

This commit is contained in:
Greg Wilkins 2017-11-16 16:56:41 +01:00
commit 51d7f859c4
2 changed files with 45 additions and 41 deletions

View File

@ -22,13 +22,13 @@ import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import org.eclipse.jetty.http.HttpTokens.EndOfContent; import org.eclipse.jetty.http.HttpTokens.EndOfContent;
import org.eclipse.jetty.util.ArrayTernaryTrie; import org.eclipse.jetty.util.ArrayTernaryTrie;
import org.eclipse.jetty.util.ArrayTrie; import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie; import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.Utf8StringBuilder;
@ -155,7 +155,6 @@ public class HttpParser
private HttpField _field; private HttpField _field;
private HttpHeader _header; private HttpHeader _header;
private String _headerString; private String _headerString;
private HttpHeaderValue _value;
private String _valueString; private String _valueString;
private int _responseStatus; private int _responseStatus;
private int _headerBytes; private int _headerBytes;
@ -171,14 +170,14 @@ public class HttpParser
private HttpVersion _version; private HttpVersion _version;
private Utf8StringBuilder _uri=new Utf8StringBuilder(INITIAL_URI_LENGTH); // Tune? private Utf8StringBuilder _uri=new Utf8StringBuilder(INITIAL_URI_LENGTH); // Tune?
private EndOfContent _endOfContent; private EndOfContent _endOfContent;
private long _contentLength; private long _contentLength = -1;
private long _contentPosition; private long _contentPosition;
private int _chunkLength; private int _chunkLength;
private int _chunkPosition; private int _chunkPosition;
private boolean _headResponse; private boolean _headResponse;
private boolean _cr; private boolean _cr;
private ByteBuffer _contentChunk; private ByteBuffer _contentChunk;
private Trie<HttpField> _connectionFields; private Trie<HttpField> _fieldCache;
private int _length; private int _length;
private final StringBuilder _string=new StringBuilder(); private final StringBuilder _string=new StringBuilder();
@ -825,10 +824,10 @@ public class HttpParser
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version"); throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
// Should we try to cache header fields? // Should we try to cache header fields?
if (_connectionFields==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion() && _handler.getHeaderCacheSize()>0) if (_fieldCache==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion() && _handler.getHeaderCacheSize()>0)
{ {
int header_cache = _handler.getHeaderCacheSize(); int header_cache = _handler.getHeaderCacheSize();
_connectionFields=new ArrayTernaryTrie<>(header_cache); _fieldCache=new ArrayTernaryTrie<>(header_cache);
} }
setState(State.HEADER); setState(State.HEADER);
@ -896,16 +895,20 @@ public class HttpParser
break; break;
case TRANSFER_ENCODING: case TRANSFER_ENCODING:
if (_value==HttpHeaderValue.CHUNKED) if (HttpHeaderValue.CHUNKED.is(_valueString))
{ {
_endOfContent=EndOfContent.CHUNKED_CONTENT; _endOfContent=EndOfContent.CHUNKED_CONTENT;
_contentLength=-1; _contentLength=-1;
} }
else else
{ {
if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString())) List<String> values = new QuotedCSV(_valueString).getValues();
if (values.size()>0 && HttpHeaderValue.CHUNKED.is(values.get(values.size()-1)))
{
_endOfContent=EndOfContent.CHUNKED_CONTENT; _endOfContent=EndOfContent.CHUNKED_CONTENT;
else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString())) _contentLength=-1;
}
else if (values.stream().anyMatch(HttpHeaderValue.CHUNKED::is))
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad chunking"); throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad chunking");
} }
break; break;
@ -915,15 +918,14 @@ public class HttpParser
if (!(_field instanceof HostPortHttpField) && _valueString!=null && !_valueString.isEmpty()) if (!(_field instanceof HostPortHttpField) && _valueString!=null && !_valueString.isEmpty())
{ {
_field=new HostPortHttpField(_header,legacyString(_headerString,_header.asString()),_valueString); _field=new HostPortHttpField(_header,legacyString(_headerString,_header.asString()),_valueString);
add_to_connection_trie=_connectionFields!=null; add_to_connection_trie=_fieldCache!=null;
} }
break; break;
case CONNECTION: case CONNECTION:
// Don't cache if not persistent // Don't cache headers if not persistent
if (_valueString!=null && _valueString.contains("close")) if (HttpHeaderValue.CLOSE.is(_valueString) || new QuotedCSV(_valueString).getValues().stream().anyMatch(HttpHeaderValue.CLOSE::is))
_connectionFields=null; _fieldCache=null;
break; break;
case AUTHORIZATION: case AUTHORIZATION:
@ -934,18 +936,18 @@ public class HttpParser
case COOKIE: case COOKIE:
case CACHE_CONTROL: case CACHE_CONTROL:
case USER_AGENT: case USER_AGENT:
add_to_connection_trie=_connectionFields!=null && _field==null; add_to_connection_trie=_fieldCache!=null && _field==null;
break; break;
default: break; default: break;
} }
if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null) if (add_to_connection_trie && !_fieldCache.isFull() && _header!=null && _valueString!=null)
{ {
if (_field==null) if (_field==null)
_field=new HttpField(_header,legacyString(_headerString,_header.asString()),_valueString); _field=new HttpField(_header,legacyString(_headerString,_header.asString()),_valueString);
_connectionFields.put(_field); _fieldCache.put(_field);
} }
} }
_handler.parsedHeader(_field!=null?_field:new HttpField(_header,_headerString,_valueString)); _handler.parsedHeader(_field!=null?_field:new HttpField(_header,_headerString,_valueString));
@ -953,7 +955,6 @@ public class HttpParser
_headerString=_valueString=null; _headerString=_valueString=null;
_header=null; _header=null;
_value=null;
_field=null; _field=null;
} }
@ -965,7 +966,6 @@ public class HttpParser
_headerString=_valueString=null; _headerString=_valueString=null;
_header=null; _header=null;
_value=null;
_field=null; _field=null;
} }
@ -1124,7 +1124,7 @@ public class HttpParser
if (buffer.hasRemaining()) if (buffer.hasRemaining())
{ {
// Try a look ahead for the known header name and value. // Try a look ahead for the known header name and value.
HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining()); HttpField field=_fieldCache==null?null:_fieldCache.getBest(buffer,-1,buffer.remaining());
if (field==null) if (field==null)
field=CACHE.getBest(buffer,-1,buffer.remaining()); field=CACHE.getBest(buffer,-1,buffer.remaining());
@ -1243,7 +1243,6 @@ public class HttpParser
_headerString=takeString(); _headerString=takeString();
_header=HttpHeader.CACHE.get(_headerString); _header=HttpHeader.CACHE.get(_headerString);
} }
_value=null;
_string.setLength(0); _string.setLength(0);
_valueString=""; _valueString="";
_length=-1; _length=-1;
@ -1268,7 +1267,6 @@ public class HttpParser
if (b==HttpTokens.LINE_FEED) if (b==HttpTokens.LINE_FEED)
{ {
_value=null;
_string.setLength(0); _string.setLength(0);
_valueString=""; _valueString="";
_length=-1; _length=-1;
@ -1297,7 +1295,6 @@ public class HttpParser
{ {
if (_length > 0) if (_length > 0)
{ {
_value=null;
_valueString=takeString(); _valueString=takeString();
_length=-1; _length=-1;
} }
@ -1683,24 +1680,7 @@ public class HttpParser
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
public Trie<HttpField> getFieldCache() public Trie<HttpField> getFieldCache()
{ {
return _connectionFields; return _fieldCache;
}
/* ------------------------------------------------------------------------------- */
private String getProxyField(ByteBuffer buffer)
{
_string.setLength(0);
_length=0;
while (buffer.hasRemaining())
{
// process each character
byte ch=next(buffer);
if (ch<=' ')
return _string.toString();
_string.append((char)ch);
}
throw new BadMessageException();
} }
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */

View File

@ -802,6 +802,30 @@ public class HttpParserTest
Assert.assertTrue(_messageCompleted); Assert.assertTrue(_messageCompleted);
} }
@Test
public void testBadChunkParse() throws Exception
{
ByteBuffer buffer = BufferUtil.toBuffer(
"GET /chunk HTTP/1.0\r\n"
+ "Header1: value1\r\n"
+ "Transfer-Encoding: chunked, identity\r\n"
+ "\r\n"
+ "a;\r\n"
+ "0123456789\r\n"
+ "1a\r\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
+ "0\r\n"
+ "\r\n");
HttpParser.RequestHandler handler = new Handler();
HttpParser parser = new HttpParser(handler);
parseAll(parser, buffer);
Assert.assertEquals("GET", _methodOrVersion);
Assert.assertEquals("/chunk", _uriOrStatus);
Assert.assertEquals("HTTP/1.0", _versionOrReason);
Assert.assertThat(_bad,Matchers.containsString("Bad chunking"));
}
@Test @Test
public void testChunkParseTrailer() throws Exception public void testChunkParseTrailer() throws Exception
{ {