diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpContentSizeHandler.java b/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpContentSizeHandler.java index 2b322fefa1262..fee9d227d8310 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpContentSizeHandler.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpContentSizeHandler.java @@ -26,7 +26,6 @@ import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; import org.elasticsearch.core.SuppressForbidden; @@ -92,7 +91,7 @@ public class Netty4HttpContentSizeHandler extends ChannelInboundHandlerAdapter { private final int maxContentLength; private final HttpRequestDecoder decoder; // need to reset decoder after sending 413 private int currentContentLength; // chunked encoding does not provide content length, need to track actual length - private boolean contentExpected; + private boolean ignoreContent; public Netty4HttpContentSizeHandler(HttpRequestDecoder decoder, int maxContentLength) { this.maxContentLength = maxContentLength; @@ -110,7 +109,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { } private void handleRequest(ChannelHandlerContext ctx, HttpRequest request) { - contentExpected = false; + ignoreContent = true; if (request.decoderResult().isFailure()) { ctx.fireChannelRead(request); return; @@ -135,13 +134,10 @@ private void handleRequest(ChannelHandlerContext ctx, HttpRequest request) { // See https://www.rfc-editor.org/rfc/rfc9110.html#section-10.1.1-11.3 // this content will result in HttpRequestDecoder failure and send downstream decoder.reset(); - ctx.writeAndFlush(TOO_LARGE.retainedDuplicate()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); - } else { - // Client is sending oversized content, we cannot safely take it. Closing channel. - ctx.writeAndFlush(TOO_LARGE_CLOSE.retainedDuplicate()).addListener(ChannelFutureListener.CLOSE); } + ctx.writeAndFlush(TOO_LARGE.retainedDuplicate()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } else { - contentExpected = true; + ignoreContent = false; currentContentLength = 0; if (isContinueExpected) { ctx.writeAndFlush(CONTINUE.retainedDuplicate()); @@ -152,7 +148,9 @@ private void handleRequest(ChannelHandlerContext ctx, HttpRequest request) { } private void handleContent(ChannelHandlerContext ctx, HttpContent msg) { - if (contentExpected) { + if (ignoreContent) { + msg.release(); + } else { currentContentLength += msg.content().readableBytes(); if (currentContentLength > maxContentLength) { msg.release(); @@ -160,11 +158,6 @@ private void handleContent(ChannelHandlerContext ctx, HttpContent msg) { } else { ctx.fireChannelRead(msg); } - } else { - msg.release(); - if (msg != LastHttpContent.EMPTY_LAST_CONTENT) { - ctx.close(); // there is no reliable recovery from unexpected content, closing channel - } } } diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpContentSizeHandlerTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpContentSizeHandlerTests.java index 3f8fe0075689f..36399c8d6d7a5 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpContentSizeHandlerTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpContentSizeHandlerTests.java @@ -192,18 +192,20 @@ public void testMixedContent() { } /** - * Assert that handler returns 413 Request Entity Too Large and close channel for - * oversized request with content. + * Assert that handler returns 413 Request Entity Too Large and skip following content. */ public void testEntityTooLargeWithContentWithoutExpect() { - var sendRequest = httpRequest(); - HttpUtil.setContentLength(sendRequest, OVERSIZED_LENGTH); - var unexpectedContent = lastHttpContent(OVERSIZED_LENGTH); - channel.writeInbound(encode(sendRequest, unexpectedContent)); - var resp = (FullHttpResponse) channel.readOutbound(); - assertEquals(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, resp.status()); - assertFalse(channel.isOpen()); - resp.release(); + for (int i = 0; i < REPS; i++) { + var sendRequest = httpRequest(); + HttpUtil.setContentLength(sendRequest, OVERSIZED_LENGTH); + var unexpectedContent = lastHttpContent(OVERSIZED_LENGTH); + channel.writeInbound(encode(sendRequest, unexpectedContent)); + var resp = (FullHttpResponse) channel.readOutbound(); + assertEquals(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, resp.status()); + resp.release(); + assertNull("request and content should not pass", channel.readInbound()); + assertTrue("should not close channel", channel.isOpen()); + } } /**