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

This commit is contained in:
Simone Bordet 2020-08-26 08:50:12 +02:00
commit 7abb9d3929
2 changed files with 80 additions and 7 deletions

View File

@ -51,6 +51,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
private RetainableByteBuffer networkBuffer; private RetainableByteBuffer networkBuffer;
private boolean shutdown; private boolean shutdown;
private boolean complete; private boolean complete;
private boolean unsolicited;
public HttpReceiverOverHTTP(HttpChannelOverHTTP channel) public HttpReceiverOverHTTP(HttpChannelOverHTTP channel)
{ {
@ -272,6 +273,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
public void startResponse(HttpVersion version, int status, String reason) public void startResponse(HttpVersion version, int status, String reason)
{ {
HttpExchange exchange = getHttpExchange(); HttpExchange exchange = getHttpExchange();
unsolicited = exchange == null;
if (exchange == null) if (exchange == null)
return; return;
@ -287,7 +289,8 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
public void parsedHeader(HttpField field) public void parsedHeader(HttpField field)
{ {
HttpExchange exchange = getHttpExchange(); HttpExchange exchange = getHttpExchange();
if (exchange == null) unsolicited |= exchange == null;
if (unsolicited)
return; return;
responseHeader(exchange, field); responseHeader(exchange, field);
@ -297,7 +300,8 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
public boolean headerComplete() public boolean headerComplete()
{ {
HttpExchange exchange = getHttpExchange(); HttpExchange exchange = getHttpExchange();
if (exchange == null) unsolicited |= exchange == null;
if (unsolicited)
return false; return false;
// Store the EndPoint is case of upgrades, tunnels, etc. // Store the EndPoint is case of upgrades, tunnels, etc.
@ -309,7 +313,8 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
public boolean content(ByteBuffer buffer) public boolean content(ByteBuffer buffer)
{ {
HttpExchange exchange = getHttpExchange(); HttpExchange exchange = getHttpExchange();
if (exchange == null) unsolicited |= exchange == null;
if (unsolicited)
return false; return false;
RetainableByteBuffer networkBuffer = this.networkBuffer; RetainableByteBuffer networkBuffer = this.networkBuffer;
@ -331,7 +336,8 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
public void parsedTrailer(HttpField trailer) public void parsedTrailer(HttpField trailer)
{ {
HttpExchange exchange = getHttpExchange(); HttpExchange exchange = getHttpExchange();
if (exchange == null) unsolicited |= exchange == null;
if (unsolicited)
return; return;
exchange.getResponse().trailer(trailer); exchange.getResponse().trailer(trailer);
@ -341,8 +347,12 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
public boolean messageComplete() public boolean messageComplete()
{ {
HttpExchange exchange = getHttpExchange(); HttpExchange exchange = getHttpExchange();
if (exchange == null) if (exchange == null || unsolicited)
{
// We received an unsolicited response from the server.
getHttpConnection().close();
return false; return false;
}
int status = exchange.getResponse().getStatus(); int status = exchange.getResponse().getStatus();
if (status != HttpStatus.CONTINUE_100) if (status != HttpStatus.CONTINUE_100)
@ -359,7 +369,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
{ {
HttpExchange exchange = getHttpExchange(); HttpExchange exchange = getHttpExchange();
HttpConnectionOverHTTP connection = getHttpConnection(); HttpConnectionOverHTTP connection = getHttpConnection();
if (exchange == null) if (exchange == null || unsolicited)
connection.close(); connection.close();
else else
failAndClose(new EOFException(String.valueOf(connection))); failAndClose(new EOFException(String.valueOf(connection)));
@ -369,7 +379,11 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
public void badMessage(BadMessageException failure) public void badMessage(BadMessageException failure)
{ {
HttpExchange exchange = getHttpExchange(); HttpExchange exchange = getHttpExchange();
if (exchange != null) if (exchange == null || unsolicited)
{
getHttpConnection().close();
}
else
{ {
HttpResponse response = exchange.getResponse(); HttpResponse response = exchange.getResponse();
response.status(failure.getCode()).reason(failure.getReason()); response.status(failure.getCode()).reason(failure.getReason());

View File

@ -1820,6 +1820,65 @@ public class HttpClientTest extends AbstractHttpClientServerTest
assertArrayEquals(bytes, baos.toByteArray()); assertArrayEquals(bytes, baos.toByteArray());
} }
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
public void testUnsolicitedResponseBytesFromServer(Scenario scenario) throws Exception
{
String response = "" +
"HTTP/1.1 408 Request Timeout\r\n" +
"Content-Length: 0\r\n" +
"Connection: close\r\n" +
"\r\n";
testUnsolicitedBytesFromServer(scenario, response);
}
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
public void testUnsolicitedInvalidBytesFromServer(Scenario scenario) throws Exception
{
String response = "ABCDEF";
testUnsolicitedBytesFromServer(scenario, response);
}
private void testUnsolicitedBytesFromServer(Scenario scenario, String bytesFromServer) throws Exception
{
try (ServerSocket server = new ServerSocket(0))
{
startClient(scenario, clientConnector ->
{
clientConnector.setSelectors(1);
HttpClientTransportOverHTTP transport = new HttpClientTransportOverHTTP(clientConnector);
transport.setConnectionPoolFactory(destination ->
{
ConnectionPool connectionPool = new DuplexConnectionPool(destination, 1, destination);
connectionPool.preCreateConnections(1);
return connectionPool;
});
return transport;
}, null);
String host = "localhost";
int port = server.getLocalPort();
// Resolve the destination which will pre-create a connection.
HttpDestination destination = client.resolveDestination(new Origin("http", host, port));
// Accept the connection and send an unsolicited 408.
try (Socket socket = server.accept())
{
OutputStream output = socket.getOutputStream();
output.write(bytesFromServer.getBytes(StandardCharsets.UTF_8));
output.flush();
}
// Give some time to the client to process the response.
Thread.sleep(1000);
AbstractConnectionPool pool = (AbstractConnectionPool)destination.getConnectionPool();
assertEquals(0, pool.getConnectionCount());
}
}
private void assertCopyRequest(Request original) private void assertCopyRequest(Request original)
{ {
Request copy = client.copyRequest((HttpRequest)original, original.getURI()); Request copy = client.copyRequest((HttpRequest)original, original.getURI());