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:
Greg Wilkins 2017-01-10 16:19:40 +11:00
parent 058e4658e4
commit d2b8980243
2 changed files with 117 additions and 10 deletions

View File

@ -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:

View File

@ -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
{ {