Issue #4282 - Review HttpParser handling in case of no content.

Fixed handling of returning true from headerComplete() and
contentComplete().

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2019-11-11 01:13:13 +01:00
parent 1d8a2fe03a
commit 47447b5cf5
2 changed files with 702 additions and 44 deletions

View File

@ -134,6 +134,7 @@ public class HttpParser
CHUNK_SIZE,
CHUNK_PARAMS,
CHUNK,
CONTENT_END,
TRAILER,
END,
CLOSE, // The associated stream/endpoint should be closed
@ -564,15 +565,19 @@ public class HttpParser
{
boolean handleHeader = _handler.headerComplete();
_headerComplete = true;
boolean handleContentMessage = handleContentMessage();
return handleHeader || handleContentMessage;
if (handleHeader)
return true;
setState(State.CONTENT_END);
return handleContentMessage();
}
private boolean handleContentMessage()
{
boolean handleContent = _handler.contentComplete();
boolean handleMessage = _handler.messageComplete();
return handleContent || handleMessage;
if (handleContent)
return true;
setState(State.END);
return _handler.messageComplete();
}
/* Parse a request or response line
@ -743,7 +748,7 @@ public class HttpParser
case LF:
setState(State.HEADER);
handle = _responseHandler.startResponse(_version, _responseStatus, null);
_responseHandler.startResponse(_version, _responseStatus, null);
break;
default:
@ -762,10 +767,11 @@ public class HttpParser
// HTTP/0.9
if (complianceViolation(HttpComplianceSection.NO_HTTP_0_9, "No request version"))
throw new BadMessageException("HTTP/0.9 not supported");
handle = _requestHandler.startRequest(_methodString, _uri.toString(), HttpVersion.HTTP_0_9);
setState(State.END);
_requestHandler.startRequest(_methodString, _uri.toString(), HttpVersion.HTTP_0_9);
setState(State.CONTENT);
_endOfContent = EndOfContent.NO_CONTENT;
BufferUtil.clear(buffer);
handle |= handleHeaderContentMessage();
handle = handleHeaderContentMessage();
break;
case ALPHA:
@ -841,7 +847,7 @@ public class HttpParser
if (_responseHandler != null)
{
setState(State.HEADER);
handle = _responseHandler.startResponse(_version, _responseStatus, null);
_responseHandler.startResponse(_version, _responseStatus, null);
}
else
{
@ -849,10 +855,11 @@ public class HttpParser
if (complianceViolation(HttpComplianceSection.NO_HTTP_0_9, "No request version"))
throw new BadMessageException("HTTP/0.9 not supported");
handle = _requestHandler.startRequest(_methodString, _uri.toString(), HttpVersion.HTTP_0_9);
setState(State.END);
_requestHandler.startRequest(_methodString, _uri.toString(), HttpVersion.HTTP_0_9);
setState(State.CONTENT);
_endOfContent = EndOfContent.NO_CONTENT;
BufferUtil.clear(buffer);
handle |= handleHeaderContentMessage();
handle = handleHeaderContentMessage();
}
break;
@ -879,7 +886,7 @@ public class HttpParser
setState(State.HEADER);
handle = _requestHandler.startRequest(_methodString, _uri.toString(), _version);
_requestHandler.startRequest(_methodString, _uri.toString(), _version);
continue;
case ALPHA:
@ -901,7 +908,7 @@ public class HttpParser
case LF:
String reason = takeString();
setState(State.HEADER);
handle = _responseHandler.startResponse(_version, _responseStatus, reason);
_responseHandler.startResponse(_version, _responseStatus, reason);
continue;
case ALPHA:
@ -1207,11 +1214,6 @@ public class HttpParser
_headerComplete = true;
return handle;
}
case NO_CONTENT:
{
setState(State.END);
return handleHeaderContentMessage();
}
default:
{
setState(State.CONTENT);
@ -1508,8 +1510,16 @@ public class HttpParser
// Handle HEAD response
if (_responseStatus > 0 && _headResponse)
{
setState(State.END);
return handleContentMessage();
if (_state != State.CONTENT_END)
{
setState(State.CONTENT_END);
return handleContentMessage();
}
else
{
setState(State.END);
return _handler.messageComplete();
}
}
else
{
@ -1528,11 +1538,18 @@ public class HttpParser
// handle end states
if (_state == State.END)
{
// eat white space
while (buffer.remaining() > 0 && buffer.get(buffer.position()) <= HttpTokens.SPACE)
// Eat CR or LF white space, but not SP.
int whiteSpace = 0;
while (buffer.remaining() > 0)
{
byte b = buffer.get(buffer.position());
if (b != HttpTokens.CARRIAGE_RETURN && b != HttpTokens.LINE_FEED)
break;
buffer.get();
++whiteSpace;
}
if (debug && whiteSpace > 0)
LOG.debug("Discarded {} CR or LF characters", whiteSpace);
}
else if (isClose() || isClosed())
{
@ -1540,18 +1557,13 @@ public class HttpParser
}
// Handle EOF
if (_eof && !buffer.hasRemaining())
if (isAtEOF() && !buffer.hasRemaining())
{
switch (_state)
{
case CLOSED:
break;
case START:
setState(State.CLOSED);
_handler.earlyEOF();
break;
case END:
case CLOSE:
setState(State.CLOSED);
@ -1562,13 +1574,18 @@ public class HttpParser
if (_fieldState == FieldState.FIELD)
{
// Be forgiving of missing last CRLF
setState(State.CONTENT_END);
boolean handle = handleContentMessage();
if (handle && _state == State.CONTENT_END)
return true;
setState(State.CLOSED);
return handleContentMessage();
return handle;
}
setState(State.CLOSED);
_handler.earlyEOF();
break;
case START:
case CONTENT:
case CHUNKED_CONTENT:
case CHUNK_SIZE:
@ -1614,17 +1631,28 @@ public class HttpParser
protected boolean parseContent(ByteBuffer buffer)
{
int remaining = buffer.remaining();
if (remaining == 0 && _state == State.CONTENT)
if (remaining == 0)
{
long content = _contentLength - _contentPosition;
if (content == 0)
switch (_state)
{
setState(State.END);
return handleContentMessage();
case CONTENT:
long content = _contentLength - _contentPosition;
if (_endOfContent == EndOfContent.NO_CONTENT || content == 0)
{
setState(State.CONTENT_END);
return handleContentMessage();
}
break;
case CONTENT_END:
setState(_endOfContent == EndOfContent.EOF_CONTENT ? State.CLOSED : State.END);
return _handler.messageComplete();
default:
// No bytes to parse, return immediately.
return false;
}
}
// Handle _content
// Handle content.
while (_state.ordinal() < State.TRAILER.ordinal() && remaining > 0)
{
switch (_state)
@ -1640,9 +1668,9 @@ public class HttpParser
case CONTENT:
{
long content = _contentLength - _contentPosition;
if (content == 0)
if (_endOfContent == EndOfContent.NO_CONTENT || content == 0)
{
setState(State.END);
setState(State.CONTENT_END);
return handleContentMessage();
}
else
@ -1665,7 +1693,7 @@ public class HttpParser
if (_contentPosition == _contentLength)
{
setState(State.END);
setState(State.CONTENT_END);
return handleContentMessage();
}
}
@ -1790,10 +1818,10 @@ public class HttpParser
break;
}
case CLOSED:
case CONTENT_END:
{
BufferUtil.clear(buffer);
return false;
setState(_endOfContent == EndOfContent.EOF_CONTENT ? State.CLOSED : State.END);
return _handler.messageComplete();
}
default:
@ -1877,8 +1905,8 @@ public class HttpParser
return String.format("%s{s=%s,%d of %d}",
getClass().getSimpleName(),
_state,
_contentPosition,
_contentLength);
getContentRead(),
getContentLength());
}
/* Event Handler interface
@ -1941,6 +1969,7 @@ public class HttpParser
/**
* @return the size in bytes of the per parser header cache
*/
// TODO: move this to the Parser in Jetty 10.
int getHeaderCacheSize();
}
@ -1954,6 +1983,7 @@ public class HttpParser
* @param version the http version in use
* @return true if handling parsing should return.
*/
// TODO: make this a void method in Jetty 10.
boolean startRequest(String method, String uri, HttpVersion version);
}
@ -1967,6 +1997,7 @@ public class HttpParser
* @param reason the response reason phrase
* @return true if handling parsing should return
*/
// TODO: make this a void method in Jetty 10.
boolean startResponse(HttpVersion version, int status, String reason);
}

View File

@ -41,6 +41,7 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -2227,6 +2228,629 @@ public class HttpParserTest
assertNull(_bad);
}
@Test
public void testForContentLengthZeroHeaderCompleteTrueDoesNotEmitContentComplete()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean headerComplete()
{
super.headerComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler);
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 200 OK\r\n" +
"Content-Length: 0\r\n" +
"\r\n");
boolean handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertFalse(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(_contentCompleted);
assertTrue(_messageCompleted);
}
@Test
public void testForEmptyChunkedContentHeaderCompleteTrueDoesNotEmitContentComplete()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean headerComplete()
{
super.headerComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler);
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 200 OK\r\n" +
"Transfer-Encoding: chunked\r\n" +
"\r\n" +
"0\r\n" +
"\r\n");
boolean handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertFalse(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(_contentCompleted);
assertTrue(_messageCompleted);
}
@Test
public void testForContentLengthZeroContentCompleteTrueDoesNotEmitMessageComplete()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean contentComplete()
{
super.contentComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler);
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 200 OK\r\n" +
"Content-Length: 0\r\n" +
"\r\n");
boolean handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(_messageCompleted);
}
@Test
public void testForEmptyChunkedContentContentCompleteTrueDoesNotEmitMessageComplete()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean contentComplete()
{
super.contentComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler);
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 200 OK\r\n" +
"Transfer-Encoding: chunked\r\n" +
"\r\n" +
"0\r\n" +
"\r\n");
boolean handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(_messageCompleted);
}
@Test
public void testHeaderAfterContentLengthZeroContentCompleteTrue()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean contentComplete()
{
super.contentComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler);
String header = "Header: Foobar\r\n";
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 200 OK\r\n" +
"Content-Length: 0\r\n" +
"\r\n" +
header);
boolean handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertEquals(header, BufferUtil.toString(buffer));
assertTrue(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertEquals(header, BufferUtil.toString(buffer));
assertTrue(_messageCompleted);
}
@Test
public void testSmallContentLengthContentCompleteTrue()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean contentComplete()
{
super.contentComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler);
String header = "Header: Foobar\r\n";
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 200 OK\r\n" +
"Content-Length: 1\r\n" +
"\r\n" +
"0" +
header);
boolean handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertEquals(header, BufferUtil.toString(buffer));
assertTrue(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertEquals(header, BufferUtil.toString(buffer));
assertTrue(_messageCompleted);
}
@Test
public void testHeaderAfterSmallContentLengthContentCompleteTrue()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean contentComplete()
{
super.contentComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler);
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 200 OK\r\n" +
"Content-Length: 1\r\n" +
"\r\n" +
"0");
boolean handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertTrue(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertTrue(_messageCompleted);
}
@Test
public void testEOFContentContentCompleteTrue()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean contentComplete()
{
super.contentComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler);
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 200 OK\r\n" +
"\r\n" +
"0");
boolean handle = parser.parseNext(buffer);
assertFalse(handle);
assertFalse(buffer.hasRemaining());
assertEquals("0", _content);
assertFalse(_contentCompleted);
assertFalse(_messageCompleted);
parser.atEOF();
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertTrue(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertTrue(_messageCompleted);
}
@Test
public void testHEADRequestHeaderCompleteTrue()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean headerComplete()
{
super.headerComplete();
return true;
}
@Override
public boolean contentComplete()
{
super.contentComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler);
parser.setHeadResponse(true);
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 200 OK\r\n" +
"\r\n");
boolean handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertFalse(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertTrue(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertTrue(_messageCompleted);
}
@Test
public void testNoContentHeaderCompleteTrue()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean headerComplete()
{
super.headerComplete();
return true;
}
@Override
public boolean contentComplete()
{
super.contentComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler);
// HTTP 304 does not have a body.
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 304 Not Modified\r\n" +
"\r\n");
boolean handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertFalse(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertTrue(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertTrue(_messageCompleted);
}
@Test
public void testCRLFAfterResponseHeaderCompleteTrue()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean headerComplete()
{
super.headerComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler);
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 304 Not Modified\r\n" +
"\r\n" +
"\r\n" +
"\r\n" +
"HTTP/1.1 200 OK\r\n" +
"Content-Length: 0\r\n" +
"\r\n" +
"\r\n" +
"\r\n" +
"HTTP/1.1 303 See Other\r\n" +
"Content-Length: 0\r\n" +
"\r\n");
boolean handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertEquals("304", _uriOrStatus);
assertFalse(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertTrue(_contentCompleted);
assertTrue(_messageCompleted);
// Parse next response.
parser.reset();
init();
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertEquals("200", _uriOrStatus);
assertFalse(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertTrue(_contentCompleted);
assertTrue(_messageCompleted);
// Parse next response.
parser.reset();
init();
handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertEquals("303", _uriOrStatus);
assertFalse(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertTrue(_contentCompleted);
assertTrue(_messageCompleted);
}
@Test
public void testCRLFAfterResponseContentCompleteTrue()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean contentComplete()
{
super.contentComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler);
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 304 Not Modified\r\n" +
"\r\n" +
"\r\n" +
"\r\n" +
"HTTP/1.1 200 OK\r\n" +
"Content-Length: 0\r\n" +
"\r\n" +
"\r\n" +
"\r\n" +
"HTTP/1.1 303 See Other\r\n" +
"Content-Length: 0\r\n" +
"\r\n");
boolean handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertEquals("304", _uriOrStatus);
assertTrue(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertTrue(_messageCompleted);
// Parse next response.
parser.reset();
init();
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertEquals("200", _uriOrStatus);
assertTrue(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(buffer.hasRemaining());
assertTrue(_messageCompleted);
// Parse next response.
parser.reset();
init();
handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertEquals("303", _uriOrStatus);
assertTrue(_contentCompleted);
assertFalse(_messageCompleted);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertTrue(_messageCompleted);
}
@Test
public void testCRLFAfterResponseMessageCompleteFalse()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean messageComplete()
{
super.messageComplete();
return false;
}
};
HttpParser parser = new HttpParser(handler);
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 304 Not Modified\r\n" +
"\r\n" +
"\r\n" +
"\r\n" +
"HTTP/1.1 200 OK\r\n" +
"Content-Length: 0\r\n" +
"\r\n" +
"\r\n" +
"\r\n" +
"HTTP/1.1 303 See Other\r\n" +
"Content-Length: 0\r\n" +
"\r\n");
boolean handle = parser.parseNext(buffer);
assertFalse(handle);
assertTrue(buffer.hasRemaining());
assertEquals("304", _uriOrStatus);
assertTrue(_contentCompleted);
assertTrue(_messageCompleted);
// Parse next response.
parser.reset();
init();
handle = parser.parseNext(buffer);
assertFalse(handle);
assertTrue(buffer.hasRemaining());
assertEquals("200", _uriOrStatus);
assertTrue(_contentCompleted);
assertTrue(_messageCompleted);
// Parse next response.
parser.reset();
init();
handle = parser.parseNext(buffer);
assertFalse(handle);
assertFalse(buffer.hasRemaining());
assertEquals("303", _uriOrStatus);
assertTrue(_contentCompleted);
assertTrue(_messageCompleted);
}
@Test
public void testSPAfterResponseMessageCompleteFalse()
{
HttpParser.ResponseHandler handler = new Handler()
{
@Override
public boolean messageComplete()
{
super.messageComplete();
return false;
}
};
HttpParser parser = new HttpParser(handler);
ByteBuffer buffer = BufferUtil.toBuffer(
"HTTP/1.1 304 Not Modified\r\n" +
"\r\n" +
" " + // Single SP.
"HTTP/1.1 200 OK\r\n" +
"Content-Length: 0\r\n" +
"\r\n");
boolean handle = parser.parseNext(buffer);
assertFalse(handle);
assertTrue(buffer.hasRemaining());
assertEquals("304", _uriOrStatus);
assertTrue(_contentCompleted);
assertTrue(_messageCompleted);
// Parse next response.
parser.reset();
init();
handle = parser.parseNext(buffer);
assertFalse(handle);
assertFalse(buffer.hasRemaining());
assertNotNull(_bad);
buffer = BufferUtil.toBuffer(
"HTTP/1.1 200 OK\r\n" +
"Content-Length: 0\r\n" +
"\r\n" +
" " + // Single SP.
"HTTP/1.1 303 See Other\r\n" +
"Content-Length: 0\r\n" +
"\r\n");
parser = new HttpParser(handler);
handle = parser.parseNext(buffer);
assertFalse(handle);
assertTrue(buffer.hasRemaining());
assertEquals("200", _uriOrStatus);
assertTrue(_contentCompleted);
assertTrue(_messageCompleted);
// Parse next response.
parser.reset();
init();
handle = parser.parseNext(buffer);
assertFalse(handle);
assertFalse(buffer.hasRemaining());
assertNotNull(_bad);
}
@BeforeEach
public void init()
{
@ -2239,6 +2863,7 @@ public class HttpParserTest
_val = null;
_headers = 0;
_headerCompleted = false;
_contentCompleted = false;
_messageCompleted = false;
_complianceViolation.clear();
}
@ -2257,6 +2882,7 @@ public class HttpParserTest
private int _headers;
private boolean _early;
private boolean _headerCompleted;
private boolean _contentCompleted;
private boolean _messageCompleted;
private final List<HttpComplianceSection> _complianceViolation = new ArrayList<>();
@ -2322,6 +2948,7 @@ public class HttpParserTest
@Override
public boolean contentComplete()
{
_contentCompleted = true;
return false;
}