Merge remote-tracking branch 'origin/jetty-9.4.x'
This commit is contained in:
commit
51d7f859c4
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue