Merged branch 'jetty-9.4.x' into 'jetty-10.0.x'.

This commit is contained in:
Simone Bordet 2019-11-12 16:41:33 +01:00
commit 5f9edb1361
11 changed files with 798 additions and 190 deletions

View File

@ -42,6 +42,7 @@ public class HttpClientTransportOverHTTP extends AbstractConnectorHttpClientTran
public static final HttpDestination.Protocol HTTP11 = new HttpDestination.Protocol(List.of("http/1.1"), false);
private int headerCacheSize = 1024;
private boolean headerCacheCaseSensitive;
public HttpClientTransportOverHTTP()
{
@ -99,4 +100,15 @@ public class HttpClientTransportOverHTTP extends AbstractConnectorHttpClientTran
{
this.headerCacheSize = headerCacheSize;
}
@ManagedAttribute("Whether the header field cache is case sensitive")
public boolean isHeaderCacheCaseSensitive()
{
return headerCacheCaseSensitive;
}
public void setHeaderCacheCaseSensitive(boolean headerCacheCaseSensitive)
{
this.headerCacheCaseSensitive = headerCacheCaseSensitive;
}
}

View File

@ -48,7 +48,10 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
public HttpReceiverOverHTTP(HttpChannelOverHTTP channel)
{
super(channel);
parser = new HttpParser(this, -1, channel.getHttpDestination().getHttpClient().getHttpCompliance());
HttpClient httpClient = channel.getHttpDestination().getHttpClient();
parser = new HttpParser(this, -1, httpClient.getHttpCompliance());
parser.setHeaderCacheSize(((HttpClientTransportOverHTTP)httpClient.getTransport()).getHeaderCacheSize());
parser.setHeaderCacheCaseSensitive(((HttpClientTransportOverHTTP)httpClient.getTransport()).isHeaderCacheCaseSensitive());
}
@Override
@ -243,32 +246,18 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
}
@Override
public int getHeaderCacheSize()
{
HttpClientTransportOverHTTP transport = (HttpClientTransportOverHTTP)getHttpDestination().getHttpClient().getTransport();
return transport.getHeaderCacheSize();
}
@Override
public boolean isHeaderCacheCaseSensitive()
{
// TODO get from configuration
return false;
}
@Override
public boolean startResponse(HttpVersion version, int status, String reason)
public void startResponse(HttpVersion version, int status, String reason)
{
HttpExchange exchange = getHttpExchange();
if (exchange == null)
return false;
return;
String method = exchange.getRequest().getMethod();
parser.setHeadResponse(HttpMethod.HEAD.is(method) ||
(HttpMethod.CONNECT.is(method) && status == HttpStatus.OK_200));
exchange.getResponse().version(version).status(status).reason(reason);
return !responseBegin(exchange);
responseBegin(exchange);
}
@Override

View File

@ -151,21 +151,7 @@ public class ResponseContentParser extends StreamContentParser
}
@Override
public int getHeaderCacheSize()
{
// TODO: configure this
return 1024;
}
@Override
public boolean isHeaderCacheCaseSensitive()
{
// TODO get from configuration
return false;
}
@Override
public boolean startResponse(HttpVersion version, int status, String reason)
public void startResponse(HttpVersion version, int status, String reason)
{
// The HTTP request line does not exist in FCGI responses
throw new IllegalStateException();

View File

@ -24,7 +24,6 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import org.eclipse.jetty.http.HttpCompliance.Violation;
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
import org.eclipse.jetty.util.ArrayTernaryTrie;
import org.eclipse.jetty.util.ArrayTrie;
@ -35,6 +34,8 @@ import org.eclipse.jetty.util.Utf8StringBuilder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import static org.eclipse.jetty.http.HttpCompliance.RFC7230;
import static org.eclipse.jetty.http.HttpCompliance.Violation;
import static org.eclipse.jetty.http.HttpCompliance.Violation.CASE_SENSITIVE_FIELD_NAME;
import static org.eclipse.jetty.http.HttpCompliance.Violation.MULTIPLE_CONTENT_LENGTHS;
import static org.eclipse.jetty.http.HttpCompliance.Violation.NO_COLON_AFTER_FIELD_NAME;
@ -136,6 +137,7 @@ public class HttpParser
CHUNK_SIZE,
CHUNK_PARAMS,
CHUNK,
CONTENT_END,
TRAILER,
END,
CLOSE, // The associated stream/endpoint should be closed
@ -160,7 +162,6 @@ public class HttpParser
private int _headerBytes;
private boolean _host;
private boolean _headerComplete;
private volatile State _state = State.START;
private volatile FieldState _fieldState = FieldState.FIELD;
private volatile boolean _eof;
@ -179,9 +180,10 @@ public class HttpParser
private boolean _cr;
private ByteBuffer _contentChunk;
private Trie<HttpField> _fieldCache;
private int _length;
private final StringBuilder _string = new StringBuilder();
private int _headerCacheSize = 1024;
private boolean _headerCacheCaseSensitive;
static
{
@ -238,7 +240,7 @@ public class HttpParser
private static HttpCompliance compliance()
{
return HttpCompliance.RFC7230;
return RFC7230;
}
public HttpParser(RequestHandler handler)
@ -291,6 +293,26 @@ public class HttpParser
return _handler;
}
public int getHeaderCacheSize()
{
return _headerCacheSize;
}
public void setHeaderCacheSize(int headerCacheSize)
{
_headerCacheSize = headerCacheSize;
}
public boolean isHeaderCacheCaseSensitive()
{
return _headerCacheCaseSensitive;
}
public void setHeaderCacheCaseSensitive(boolean headerCacheCaseSensitive)
{
_headerCacheCaseSensitive = headerCacheCaseSensitive;
}
protected void checkViolation(Violation violation) throws BadMessageException
{
if (violation.isAllowedBy(_complianceMode))
@ -529,16 +551,19 @@ public class HttpParser
{
boolean handleHeader = _handler.headerComplete();
_headerComplete = true;
boolean handleContent = _handler.contentComplete();
boolean handleMessage = _handler.messageComplete();
return handleHeader || handleContent || handleMessage;
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
@ -708,7 +733,7 @@ public class HttpParser
case LF:
setState(State.HEADER);
handle = _responseHandler.startResponse(_version, _responseStatus, null);
_responseHandler.startResponse(_version, _responseStatus, null);
break;
default:
@ -726,10 +751,11 @@ public class HttpParser
case LF:
// HTTP/0.9
checkViolation(Violation.HTTP_0_9);
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:
@ -805,16 +831,17 @@ public class HttpParser
if (_responseHandler != null)
{
setState(State.HEADER);
handle = _responseHandler.startResponse(_version, _responseStatus, null);
_responseHandler.startResponse(_version, _responseStatus, null);
}
else
{
// HTTP/0.9
checkViolation(Violation.HTTP_0_9);
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;
@ -835,13 +862,13 @@ public class HttpParser
checkVersion();
// Should we try to cache header fields?
int headerCache = _handler.getHeaderCacheSize();
int headerCache = getHeaderCacheSize();
if (_fieldCache == null && _version.getVersion() >= HttpVersion.HTTP_1_1.getVersion() && headerCache > 0)
_fieldCache = new ArrayTernaryTrie<>(headerCache);
setState(State.HEADER);
handle = _requestHandler.startRequest(_methodString, _uri.toString(), _version);
_requestHandler.startRequest(_methodString, _uri.toString(), _version);
continue;
case ALPHA:
@ -863,7 +890,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:
@ -1167,11 +1194,6 @@ public class HttpParser
_headerComplete = true;
return handle;
}
case NO_CONTENT:
{
setState(State.END);
return handleHeaderContentMessage();
}
default:
{
setState(State.CONTENT);
@ -1217,7 +1239,7 @@ public class HttpParser
}
}
if (v != null && _handler.isHeaderCacheCaseSensitive())
if (v != null && isHeaderCacheCaseSensitive())
{
String ev = BufferUtil.toString(buffer, buffer.position() + n.length() + 1, v.length(), StandardCharsets.ISO_8859_1);
if (!v.equals(ev))
@ -1470,8 +1492,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
{
@ -1490,11 +1520,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 (debugEnabled && whiteSpace > 0)
LOG.debug("Discarded {} CR or LF characters", whiteSpace);
}
else if (isClose() || isClosed())
{
@ -1502,18 +1539,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);
@ -1524,13 +1556,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:
@ -1576,17 +1613,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)
@ -1602,9 +1650,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
@ -1627,7 +1675,7 @@ public class HttpParser
if (_contentPosition == _contentLength)
{
setState(State.END);
setState(State.CONTENT_END);
return handleContentMessage();
}
}
@ -1752,10 +1800,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:
@ -1839,8 +1887,8 @@ public class HttpParser
return String.format("%s{s=%s,%d of %d}",
getClass().getSimpleName(),
_state,
_contentPosition,
_contentLength);
getContentRead(),
getContentLength());
}
/* Event Handler interface
@ -1890,13 +1938,6 @@ public class HttpParser
default void badMessage(BadMessageException failure)
{
}
/**
* @return the size in bytes of the per parser header cache
*/
int getHeaderCacheSize();
boolean isHeaderCacheCaseSensitive();
}
public interface RequestHandler extends HttpHandler
@ -1907,9 +1948,8 @@ public class HttpParser
* @param method The method
* @param uri The raw bytes of the URI. These are copied into a ByteBuffer that will not be changed until this parser is reset and reused.
* @param version the http version in use
* @return true if handling parsing should return.
*/
boolean startRequest(String method, String uri, HttpVersion version);
void startRequest(String method, String uri, HttpVersion version);
}
public interface ResponseHandler extends HttpHandler
@ -1920,9 +1960,8 @@ public class HttpParser
* @param version the http version in use
* @param status the response status
* @param reason the response reason phrase
* @return true if handling parsing should return
*/
boolean startResponse(HttpVersion version, int status, String reason);
void startResponse(HttpVersion version, int status, String reason);
}
@SuppressWarnings("serial")

View File

@ -32,6 +32,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpGeneratorServerHTTPTest
@ -70,7 +71,7 @@ public class HttpGeneratorServerHTTPTest
assertEquals("OK??Test", _reason);
if (_content == null)
assertTrue(run.result._body == null, msg);
assertNull(run.result._body, msg);
else
assertThat(msg, run.result._contentLength, either(equalTo(_content.length())).or(equalTo(-1)));
}
@ -248,10 +249,9 @@ public class HttpGeneratorServerHTTPTest
}
@Override
public boolean startResponse(HttpVersion version, int status, String reason)
public void startResponse(HttpVersion version, int status, String reason)
{
_reason = reason;
return false;
}
@Override
@ -259,18 +259,6 @@ public class HttpGeneratorServerHTTPTest
{
throw failure;
}
@Override
public int getHeaderCacheSize()
{
return 1024;
}
@Override
public boolean isHeaderCacheCaseSensitive()
{
return false;
}
}
public static final String CONTENT = "The quick brown fox jumped over the lazy dog.\nNow is the time for all good men to come to the aid of the party\nThe moon is blue to a fish in love.\n";

View File

@ -43,6 +43,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;
@ -859,8 +860,9 @@ public class HttpParserTest
"HOST: localhost\r\n" +
"cOnNeCtIoN: ClOsE\r\n" +
"\r\n");
HttpParser.RequestHandler handler = new Handler(true);
HttpParser.RequestHandler handler = new Handler();
HttpParser parser = new HttpParser(handler, -1, HttpCompliance.LEGACY);
parser.setHeaderCacheCaseSensitive(true);
parseAll(parser, buffer);
assertNull(_bad);
assertEquals("GET", _methodOrVersion);
@ -2156,6 +2158,662 @@ public class HttpParserTest
assertNull(_bad);
}
@Test
public void testForHTTP09HeaderCompleteTrueDoesNotEmitContentComplete()
{
HttpParser.RequestHandler handler = new Handler()
{
@Override
public boolean headerComplete()
{
super.headerComplete();
return true;
}
};
HttpParser parser = new HttpParser(handler, HttpCompliance.RFC2616_LEGACY);
ByteBuffer buffer = BufferUtil.toBuffer("GET /path\r\n");
boolean handle = parser.parseNext(buffer);
assertTrue(handle);
assertFalse(buffer.hasRemaining());
assertFalse(_contentCompleted);
assertFalse(_messageCompleted);
assertEquals("GET", _methodOrVersion);
assertEquals("/path", _uriOrStatus);
assertEquals("HTTP/0.9", _versionOrReason);
assertEquals(-1, _headers);
// Need to parse more to advance the parser.
handle = parser.parseNext(buffer);
assertTrue(handle);
assertTrue(_contentCompleted);
assertTrue(_messageCompleted);
}
@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()
{
@ -2168,6 +2826,7 @@ public class HttpParserTest
_val = null;
_headers = 0;
_headerCompleted = false;
_contentCompleted = false;
_messageCompleted = false;
_complianceViolation.clear();
}
@ -2186,23 +2845,12 @@ public class HttpParserTest
private int _headers;
private boolean _early;
private boolean _headerCompleted;
private boolean _contentCompleted;
private boolean _messageCompleted;
private final List<ComplianceViolation> _complianceViolation = new ArrayList<>();
private class Handler implements HttpParser.RequestHandler, HttpParser.ResponseHandler, ComplianceViolation.Listener
{
private boolean _headerCacheCaseSensitive;
public Handler()
{
this(false);
}
public Handler(boolean headerCacheCaseSensitive)
{
_headerCacheCaseSensitive = headerCacheCaseSensitive;
}
@Override
public boolean content(ByteBuffer ref)
{
@ -2215,7 +2863,7 @@ public class HttpParserTest
}
@Override
public boolean startRequest(String method, String uri, HttpVersion version)
public void startRequest(String method, String uri, HttpVersion version)
{
_fields.clear();
_trailers.clear();
@ -2228,7 +2876,6 @@ public class HttpParserTest
_messageCompleted = false;
_headerCompleted = false;
_early = false;
return false;
}
@Override
@ -2263,6 +2910,7 @@ public class HttpParserTest
@Override
public boolean contentComplete()
{
_contentCompleted = true;
return false;
}
@ -2281,7 +2929,7 @@ public class HttpParserTest
}
@Override
public boolean startResponse(HttpVersion version, int status, String reason)
public void startResponse(HttpVersion version, int status, String reason)
{
_fields.clear();
_trailers.clear();
@ -2293,7 +2941,6 @@ public class HttpParserTest
_val = new String[10];
_messageCompleted = false;
_headerCompleted = false;
return false;
}
@Override
@ -2302,18 +2949,6 @@ public class HttpParserTest
_early = true;
}
@Override
public int getHeaderCacheSize()
{
return 1024;
}
@Override
public boolean isHeaderCacheCaseSensitive()
{
return _headerCacheCaseSensitive;
}
@Override
public void onComplianceViolation(ComplianceViolation.Mode mode, ComplianceViolation violation, String reason)
{

View File

@ -108,7 +108,7 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque
}
@Override
public boolean startRequest(String method, String uri, HttpVersion version)
public void startRequest(String method, String uri, HttpVersion version)
{
_metadata.setMethod(method);
_metadata.getURI().parseRequestTarget(method, uri);
@ -116,7 +116,6 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque
_unknownExpectation = false;
_expect100Continue = false;
_expect102Processing = false;
return false;
}
@Override
@ -514,18 +513,6 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque
return onRequestComplete();
}
@Override
public int getHeaderCacheSize()
{
return getHttpConfiguration().getHeaderCacheSize();
}
@Override
public boolean isHeaderCacheCaseSensitive()
{
return getHttpConfiguration().isHeaderCacheCaseSensitive();
}
@Override
public void onComplianceViolation(ComplianceViolation.Mode mode, ComplianceViolation violation, String details)
{

View File

@ -132,7 +132,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
protected HttpParser newHttpParser(HttpCompliance compliance)
{
return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize(), compliance);
HttpParser parser = new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize(), compliance);
parser.setHeaderCacheSize(getHttpConfiguration().getHeaderCacheSize());
parser.setHeaderCacheCaseSensitive(getHttpConfiguration().isHeaderCacheCaseSensitive());
return parser;
}
protected HttpParser.RequestHandler newRequestHandler()

View File

@ -381,18 +381,6 @@ public class LocalConnector extends AbstractConnector
return false;
}
@Override
public int getHeaderCacheSize()
{
return 0;
}
@Override
public boolean isHeaderCacheCaseSensitive()
{
return false;
}
@Override
public void earlyEOF()
{
@ -405,9 +393,8 @@ public class LocalConnector extends AbstractConnector
}
@Override
public boolean startResponse(HttpVersion version, int status, String reason)
public void startResponse(HttpVersion version, int status, String reason)
{
return false;
}
};

View File

@ -109,10 +109,10 @@ public class ExtendedServerTest extends HttpServerTestBase
return new HttpChannelOverHttp(this, getConnector(), getHttpConfiguration(), getEndPoint(), this)
{
@Override
public boolean startRequest(String method, String uri, HttpVersion version)
public void startRequest(String method, String uri, HttpVersion version)
{
getRequest().setAttribute("DispatchedAt", ((ExtendedEndPoint)getEndPoint()).getLastSelected());
return super.startRequest(method, uri, version);
super.startRequest(method, uri, version);
}
};
}

View File

@ -39,8 +39,6 @@ import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* A HTTP Testing helper class.
@ -70,8 +68,6 @@ import org.eclipse.jetty.util.log.Logger;
*/
public class HttpTester
{
private static final Logger LOG = Log.getLogger(HttpTester.class);
public abstract static class Input
{
protected final ByteBuffer _buffer;
@ -508,18 +504,6 @@ public class HttpTester
}
public abstract MetaData getInfo();
@Override
public int getHeaderCacheSize()
{
return 0;
}
@Override
public boolean isHeaderCacheCaseSensitive()
{
return false;
}
}
public static class Request extends Message implements HttpParser.RequestHandler
@ -528,12 +512,11 @@ public class HttpTester
private String _uri;
@Override
public boolean startRequest(String method, String uri, HttpVersion version)
public void startRequest(String method, String uri, HttpVersion version)
{
_method = method;
_uri = uri;
_version = version;
return false;
}
public String getMethod()
@ -580,12 +563,11 @@ public class HttpTester
private String _reason;
@Override
public boolean startResponse(HttpVersion version, int status, String reason)
public void startResponse(HttpVersion version, int status, String reason)
{
_version = version;
_status = status;
_reason = reason;
return false;
}
public int getStatus()