Issue #612
Added support to parse and ignore HTTP/1.1 chunking trailers. Handle an early EOF within the trailer gracefully rather than with earlyEOF.
This commit is contained in:
parent
058e4658e4
commit
d2b8980243
|
@ -28,8 +28,6 @@ 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.HostPort;
|
|
||||||
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;
|
||||||
|
@ -133,6 +131,7 @@ public class HttpParser
|
||||||
CHUNK_SIZE,
|
CHUNK_SIZE,
|
||||||
CHUNK_PARAMS,
|
CHUNK_PARAMS,
|
||||||
CHUNK,
|
CHUNK,
|
||||||
|
CHUNK_TRAILER,
|
||||||
CHUNK_END,
|
CHUNK_END,
|
||||||
END,
|
END,
|
||||||
CLOSE, // The associated stream/endpoint should be closed
|
CLOSE, // The associated stream/endpoint should be closed
|
||||||
|
@ -682,7 +681,7 @@ public class HttpParser
|
||||||
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
|
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
|
||||||
{
|
{
|
||||||
LOG.warn("URI is too large >"+_maxHeaderBytes);
|
LOG.warn("URI is too large >"+_maxHeaderBytes);
|
||||||
throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
|
throw new BadMessageException(HttpStatus.URI_TOO_LONG_414);
|
||||||
}
|
}
|
||||||
_uri.append(array,p-1,len+1);
|
_uri.append(array,p-1,len+1);
|
||||||
buffer.position(i-buffer.arrayOffset());
|
buffer.position(i-buffer.arrayOffset());
|
||||||
|
@ -1383,14 +1382,16 @@ public class HttpParser
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EOF_CONTENT:
|
case EOF_CONTENT:
|
||||||
|
case CHUNK_END:
|
||||||
setState(State.CLOSED);
|
setState(State.CLOSED);
|
||||||
return _handler.messageComplete();
|
return _handler.messageComplete();
|
||||||
|
|
||||||
case CONTENT:
|
case CONTENT:
|
||||||
case CHUNKED_CONTENT:
|
case CHUNKED_CONTENT:
|
||||||
case CHUNK_SIZE:
|
case CHUNK_SIZE:
|
||||||
case CHUNK_PARAMS:
|
case CHUNK_PARAMS:
|
||||||
case CHUNK:
|
case CHUNK:
|
||||||
|
case CHUNK_TRAILER:
|
||||||
setState(State.CLOSED);
|
setState(State.CLOSED);
|
||||||
_handler.earlyEOF();
|
_handler.earlyEOF();
|
||||||
break;
|
break;
|
||||||
|
@ -1592,7 +1593,6 @@ public class HttpParser
|
||||||
|
|
||||||
case CHUNK_END:
|
case CHUNK_END:
|
||||||
{
|
{
|
||||||
// TODO handle chunk trailer
|
|
||||||
ch=next(buffer);
|
ch=next(buffer);
|
||||||
if (ch==0)
|
if (ch==0)
|
||||||
break;
|
break;
|
||||||
|
@ -1601,7 +1601,19 @@ public class HttpParser
|
||||||
setState(State.END);
|
setState(State.END);
|
||||||
return _handler.messageComplete();
|
return _handler.messageComplete();
|
||||||
}
|
}
|
||||||
throw new IllegalCharacterException(_state,ch,buffer);
|
setState(State.CHUNK_TRAILER);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CHUNK_TRAILER:
|
||||||
|
{
|
||||||
|
// TODO handle chunk trailer values
|
||||||
|
ch=next(buffer);
|
||||||
|
if (ch==0)
|
||||||
|
break;
|
||||||
|
if (ch == HttpTokens.LINE_FEED)
|
||||||
|
setState(State.CHUNK_END);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CLOSED:
|
case CLOSED:
|
||||||
|
|
|
@ -779,6 +779,101 @@ public class HttpParserTest
|
||||||
Assert.assertTrue(_messageCompleted);
|
Assert.assertTrue(_messageCompleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChunkParseTrailer() throws Exception
|
||||||
|
{
|
||||||
|
ByteBuffer buffer = BufferUtil.toBuffer(
|
||||||
|
"GET /chunk HTTP/1.0\r\n"
|
||||||
|
+ "Header1: value1\r\n"
|
||||||
|
+ "Transfer-Encoding: chunked\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "a;\r\n"
|
||||||
|
+ "0123456789\r\n"
|
||||||
|
+ "1a\r\n"
|
||||||
|
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
|
||||||
|
+ "0\r\n"
|
||||||
|
+ "Trailer: value\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.assertEquals(1, _headers);
|
||||||
|
Assert.assertEquals("Header1", _hdr[0]);
|
||||||
|
Assert.assertEquals("value1", _val[0]);
|
||||||
|
Assert.assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
|
||||||
|
|
||||||
|
Assert.assertTrue(_headerCompleted);
|
||||||
|
Assert.assertTrue(_messageCompleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChunkParseBadTrailer() throws Exception
|
||||||
|
{
|
||||||
|
ByteBuffer buffer = BufferUtil.toBuffer(
|
||||||
|
"GET /chunk HTTP/1.0\r\n"
|
||||||
|
+ "Header1: value1\r\n"
|
||||||
|
+ "Transfer-Encoding: chunked\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "a;\r\n"
|
||||||
|
+ "0123456789\r\n"
|
||||||
|
+ "1a\r\n"
|
||||||
|
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
|
||||||
|
+ "0\r\n"
|
||||||
|
+ "Trailer: value");
|
||||||
|
HttpParser.RequestHandler handler = new Handler();
|
||||||
|
HttpParser parser = new HttpParser(handler);
|
||||||
|
parseAll(parser, buffer);
|
||||||
|
parser.atEOF();
|
||||||
|
parser.parseNext(BufferUtil.EMPTY_BUFFER);
|
||||||
|
|
||||||
|
Assert.assertEquals("GET", _methodOrVersion);
|
||||||
|
Assert.assertEquals("/chunk", _uriOrStatus);
|
||||||
|
Assert.assertEquals("HTTP/1.0", _versionOrReason);
|
||||||
|
Assert.assertEquals(1, _headers);
|
||||||
|
Assert.assertEquals("Header1", _hdr[0]);
|
||||||
|
Assert.assertEquals("value1", _val[0]);
|
||||||
|
Assert.assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
|
||||||
|
|
||||||
|
Assert.assertTrue(_headerCompleted);
|
||||||
|
Assert.assertTrue(_early);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChunkParseNoTrailer() throws Exception
|
||||||
|
{
|
||||||
|
ByteBuffer buffer = BufferUtil.toBuffer(
|
||||||
|
"GET /chunk HTTP/1.0\r\n"
|
||||||
|
+ "Header1: value1\r\n"
|
||||||
|
+ "Transfer-Encoding: chunked\r\n"
|
||||||
|
+ "\r\n"
|
||||||
|
+ "a;\r\n"
|
||||||
|
+ "0123456789\r\n"
|
||||||
|
+ "1a\r\n"
|
||||||
|
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
|
||||||
|
+ "0\r\n");
|
||||||
|
HttpParser.RequestHandler handler = new Handler();
|
||||||
|
HttpParser parser = new HttpParser(handler);
|
||||||
|
parseAll(parser, buffer);
|
||||||
|
parser.atEOF();
|
||||||
|
parser.parseNext(BufferUtil.EMPTY_BUFFER);
|
||||||
|
|
||||||
|
Assert.assertEquals("GET", _methodOrVersion);
|
||||||
|
Assert.assertEquals("/chunk", _uriOrStatus);
|
||||||
|
Assert.assertEquals("HTTP/1.0", _versionOrReason);
|
||||||
|
Assert.assertEquals(1, _headers);
|
||||||
|
Assert.assertEquals("Header1", _hdr[0]);
|
||||||
|
Assert.assertEquals("value1", _val[0]);
|
||||||
|
Assert.assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
|
||||||
|
|
||||||
|
Assert.assertTrue(_headerCompleted);
|
||||||
|
Assert.assertTrue(_messageCompleted);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStartEOF() throws Exception
|
public void testStartEOF() throws Exception
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue