HttpParser is not using cached HTTP Field values (#1979)

* HttpParser is not using cached HTTP Field values

Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
Greg Wilkins 2017-11-16 16:55:43 +01:00 committed by GitHub
parent 29373c6fb4
commit b5a38d82ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
import org.eclipse.jetty.util.ArrayTernaryTrie;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.Utf8StringBuilder;
@ -157,7 +157,6 @@ public class HttpParser
private HttpField _field;
private HttpHeader _header;
private String _headerString;
private HttpHeaderValue _value;
private String _valueString;
private int _responseStatus;
private int _headerBytes;
@ -173,14 +172,14 @@ public class HttpParser
private HttpVersion _version;
private Utf8StringBuilder _uri=new Utf8StringBuilder(INITIAL_URI_LENGTH); // Tune?
private EndOfContent _endOfContent;
private long _contentLength;
private long _contentLength = -1;
private long _contentPosition;
private int _chunkLength;
private int _chunkPosition;
private boolean _headResponse;
private boolean _cr;
private ByteBuffer _contentChunk;
private Trie<HttpField> _connectionFields;
private Trie<HttpField> _fieldCache;
private int _length;
private final StringBuilder _string=new StringBuilder();
@ -842,10 +841,10 @@ public class HttpParser
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
// 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();
_connectionFields=new ArrayTernaryTrie<>(header_cache);
_fieldCache=new ArrayTernaryTrie<>(header_cache);
}
setState(State.HEADER);
@ -913,16 +912,20 @@ public class HttpParser
break;
case TRANSFER_ENCODING:
if (_value==HttpHeaderValue.CHUNKED)
if (HttpHeaderValue.CHUNKED.is(_valueString))
{
_endOfContent=EndOfContent.CHUNKED_CONTENT;
_contentLength=-1;
}
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;
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");
}
break;
@ -932,15 +935,14 @@ public class HttpParser
if (!(_field instanceof HostPortHttpField) && _valueString!=null && !_valueString.isEmpty())
{
_field=new HostPortHttpField(_header,legacyString(_headerString,_header.asString()),_valueString);
add_to_connection_trie=_connectionFields!=null;
add_to_connection_trie=_fieldCache!=null;
}
break;
case CONNECTION:
// Don't cache if not persistent
if (_valueString!=null && _valueString.contains("close"))
_connectionFields=null;
// Don't cache headers if not persistent
if (HttpHeaderValue.CLOSE.is(_valueString) || new QuotedCSV(_valueString).getValues().stream().anyMatch(HttpHeaderValue.CLOSE::is))
_fieldCache=null;
break;
case AUTHORIZATION:
@ -951,18 +953,18 @@ public class HttpParser
case COOKIE:
case CACHE_CONTROL:
case USER_AGENT:
add_to_connection_trie=_connectionFields!=null && _field==null;
add_to_connection_trie=_fieldCache!=null && _field==null;
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)
_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));
@ -970,7 +972,6 @@ public class HttpParser
_headerString=_valueString=null;
_header=null;
_value=null;
_field=null;
}
@ -982,7 +983,6 @@ public class HttpParser
_headerString=_valueString=null;
_header=null;
_value=null;
_field=null;
}
@ -1141,7 +1141,7 @@ public class HttpParser
if (buffer.hasRemaining())
{
// 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)
field=CACHE.getBest(buffer,-1,buffer.remaining());
@ -1260,7 +1260,6 @@ public class HttpParser
_headerString=takeString();
_header=HttpHeader.CACHE.get(_headerString);
}
_value=null;
_string.setLength(0);
_valueString="";
_length=-1;
@ -1285,7 +1284,6 @@ public class HttpParser
if (b==HttpTokens.LINE_FEED)
{
_value=null;
_string.setLength(0);
_valueString="";
_length=-1;
@ -1314,7 +1312,6 @@ public class HttpParser
{
if (_length > 0)
{
_value=null;
_valueString=takeString();
_length=-1;
}
@ -1700,24 +1697,7 @@ public class HttpParser
/* ------------------------------------------------------------------------------- */
public Trie<HttpField> getFieldCache()
{
return _connectionFields;
}
/* ------------------------------------------------------------------------------- */
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();
return _fieldCache;
}
/* ------------------------------------------------------------------------------- */

View File

@ -802,6 +802,30 @@ public class HttpParserTest
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
public void testChunkParseTrailer() throws Exception
{