rework and cleanup code for ensureConsumeAllOrNotPersistent

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2022-05-05 08:42:18 +10:00
parent 12f443db7d
commit 1c1847be6f
3 changed files with 89 additions and 24 deletions

View File

@ -19,6 +19,8 @@ import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.ListIterator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.CookieCompliance;
@ -26,6 +28,7 @@ import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.QuietException;
import org.eclipse.jetty.server.handler.ErrorProcessor;
@ -270,6 +273,8 @@ public interface Response extends Content.Writer
return;
}
Response.ensureConsumeAllOrNotPersistent(request, response);
if (status <= 0)
status = HttpStatus.INTERNAL_SERVER_ERROR_500;
if (message == null)
@ -317,6 +322,79 @@ public interface Response extends Content.Writer
return -1;
}
static void ensureConsumeAllOrNotPersistent(Request request, Response response)
{
switch (request.getConnectionMetaData().getHttpVersion())
{
case HTTP_1_0:
if (consumeAll(request))
return;
// Remove any keep-alive value in Connection headers
response.getHeaders().computeField(HttpHeader.CONNECTION, (h, fields) ->
{
if (fields == null || fields.isEmpty())
return null;
String v = fields.stream()
.flatMap(field -> Stream.of(field.getValues()).filter(s -> !HttpHeaderValue.KEEP_ALIVE.is(s)))
.collect(Collectors.joining(", "));
if (StringUtil.isEmpty(v))
return null;
return new HttpField(HttpHeader.CONNECTION, v);
});
break;
case HTTP_1_1:
if (consumeAll(request))
return;
// Add close value to Connection headers
response.getHeaders().computeField(HttpHeader.CONNECTION, (h, fields) ->
{
if (fields == null || fields.isEmpty())
return HttpFields.CONNECTION_CLOSE;
if (fields.stream().anyMatch(f -> f.contains(HttpHeaderValue.CLOSE.asString())))
{
if (fields.size() == 1)
{
HttpField f = fields.get(0);
if (HttpFields.CONNECTION_CLOSE.equals(f))
return f;
}
return new HttpField(HttpHeader.CONNECTION, fields.stream()
.flatMap(field -> Stream.of(field.getValues()).filter(s -> !HttpHeaderValue.KEEP_ALIVE.is(s)))
.collect(Collectors.joining(", ")));
}
return new HttpField(HttpHeader.CONNECTION,
Stream.concat(fields.stream()
.flatMap(field -> Stream.of(field.getValues()).filter(s -> !HttpHeaderValue.KEEP_ALIVE.is(s))),
Stream.of(HttpHeaderValue.CLOSE.asString()))
.collect(Collectors.joining(", ")));
});
break;
default:
break;
}
}
static boolean consumeAll(Request request)
{
while (true)
{
Content content = request.readContent();
if (content == null)
return false;
content.release();
if (content.isLast())
return true;
}
}
class Wrapper implements Response
{
private final Request _request;

View File

@ -36,6 +36,7 @@ import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Content;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.Blocking;
@ -468,7 +469,7 @@ public class ServletChannel implements Runnable
// from the failed dispatch, then we try to consume it here and if we fail we add a
// Connection:close. This can't be deferred to COMPLETE as the response will be committed
// by then.
ensureConsumeAllOrNotPersistent();
Response.ensureConsumeAllOrNotPersistent(_request, _request.getResponse());
ContextHandler.Context context = (ContextHandler.Context)_request.getAttribute(ErrorHandler.ERROR_CONTEXT);
Request.Processor errorProcessor = ErrorHandler.getErrorProcessor(getServer(), context == null ? null : context.getContextHandler());
@ -554,7 +555,7 @@ public class ServletChannel implements Runnable
// Indicate Connection:close if we can't consume all.
if (getResponse().getStatus() >= 200)
ensureConsumeAllOrNotPersistent();
Response.ensureConsumeAllOrNotPersistent(_request, _request.getResponse());
}
@ -599,19 +600,6 @@ public class ServletChannel implements Runnable
return !suspended;
}
private void ensureConsumeAllOrNotPersistent()
{
while (true)
{
Content content = getRequest().readContent();
if (content == null)
break;
content.release();
if (content.isLast())
break;
}
}
/**
* @param message the error message.
* @return true if we have sent an error, false if we have aborted.

View File

@ -49,7 +49,6 @@ import org.eclipse.jetty.server.Server;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -420,19 +419,19 @@ public class ErrorPageTest
}
}
@Disabled
@Test
public void testNoop() throws Exception
{
// The ServletContextHandler does not handle so should go to the servers ErrorProcessor.
String response = _connector.getResponse("GET /noop/info HTTP/1.0\r\n\r\n");
assertThat(response, Matchers.containsString("HTTP/1.1 404 Not Found"));
assertThat(response, Matchers.containsString("DISPATCH: ERROR"));
assertThat(response, Matchers.containsString("ERROR_PAGE: /GlobalErrorPage"));
assertThat(response, Matchers.containsString("ERROR_CODE: 404"));
assertThat(response, Matchers.containsString("ERROR_EXCEPTION: null"));
assertThat(response, Matchers.containsString("ERROR_EXCEPTION_TYPE: null"));
assertThat(response, Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.ee10.servlet.DefaultServlet-"));
assertThat(response, Matchers.containsString("ERROR_REQUEST_URI: /noop/info"));
assertThat(response, not(Matchers.containsString("DISPATCH: ERROR")));
assertThat(response, not(Matchers.containsString("ERROR_PAGE: /GlobalErrorPage")));
assertThat(response, not(Matchers.containsString("ERROR_CODE: 404")));
assertThat(response, not(Matchers.containsString("ERROR_EXCEPTION: null")));
assertThat(response, not(Matchers.containsString("ERROR_EXCEPTION_TYPE: null")));
assertThat(response, not(Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.ee10.servlet.DefaultServlet-")));
assertThat(response, not(Matchers.containsString("ERROR_REQUEST_URI: /noop/info")));
}
@Test