This is an automated email from the ASF dual-hosted git repository. rzo1 pushed a commit to branch tomee-10.x in repository https://gitbox.apache.org/repos/asf/tomee.git
commit 62b760424408b4c7ead8834e1165143c97996701 Author: Markus Jung <[email protected]> AuthorDate: Fri Aug 21 21:21:31 2026 +0200 align embedded http request body handling with Tomcat (cherry picked from commit 672e32f6c61537142bc114c75a89259ee7b8f116) --- .../openejb/server/httpd/HttpRequestImpl.java | 47 +++++++++++++++++++--- .../openejb/server/httpd/HttpRequestImplTest.java | 39 ++++++++++++++++++ 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpRequestImpl.java b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpRequestImpl.java index 910cd184cd..401877d696 100644 --- a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpRequestImpl.java +++ b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpRequestImpl.java @@ -76,6 +76,8 @@ public class HttpRequestImpl implements HttpRequest { private static final String MULTIPART_FORM_DATA = "multipart/form-data"; private static final String TRANSFER_ENCODING = "Transfer-Encoding"; private static final String CHUNKED = "chunked"; + private static final String MAX_BODY_SIZE_PROPERTY = "openejb.http.request.max-body-size"; + private static final int DEFAULT_MAX_BODY_SIZE = 2 * 1024 * 1024; // bytes, aligned with Tomcat's maxPostSize default public static final Class<?>[] SERVLET_CONTEXT_INTERFACES = new Class<?>[]{ServletContext.class}; public static final InvocationHandler SERVLET_CONTEXT_HANDLER = (proxy, method, args) -> null; @@ -684,6 +686,12 @@ public class HttpRequestImpl implements HttpRequest { // or multipart/form-data length = parseContentLength(); + final int maxBodySize = getMaxBodySize(); + if (length > maxBodySize) { + throw new IOException("Content-Length " + length + " exceeds the maximum allowed request body size (" + + maxBodySize + " bytes), set the '" + MAX_BODY_SIZE_PROPERTY + "' property to raise the limit"); + } + contentType = getHeader(HttpRequest.HEADER_CONTENT_TYPE); final boolean hasBody = hasBody(); @@ -725,6 +733,7 @@ public class HttpRequestImpl implements HttpRequest { } else if (hasBody && CHUNKED.equals(getHeader(TRANSFER_ENCODING))) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(4096); + final byte[] buffer = new byte[4096]; for (String line = in.readLine(); line != null; line = in.readLine()) { // read the size line which is in hex String sizeString = line.split(";", 2)[0]; @@ -733,10 +742,19 @@ public class HttpRequestImpl implements HttpRequest { // if size is 0 we are done if (size == 0) break; + if (size < 0 || size > maxBodySize - out.size()) { + throw new IOException("Chunked request body exceeds the maximum allowed request body size (" + + maxBodySize + " bytes), set the '" + MAX_BODY_SIZE_PROPERTY + "' property to raise the limit"); + } + // read the chunk and append to byte array - byte[] chunk = new byte[size]; - in.readFully(chunk); - out.write(chunk); + int remaining = size; + while (remaining > 0) { + final int len = Math.min(remaining, buffer.length); + in.readFully(buffer, 0, len); + out.write(buffer, 0, len); + remaining -= len; + } // read off the trailing new line characters after the chunk in.readLine(); @@ -759,10 +777,18 @@ public class HttpRequestImpl implements HttpRequest { private byte[] readContent(DataInput in) throws IOException { if (length >= 0) { - byte[] body = new byte[length]; - in.readFully(body); - return body; + final ByteArrayOutputStream out = new ByteArrayOutputStream(Math.min(length, 4096)); + final byte[] buffer = new byte[4096]; + int remaining = length; + while (remaining > 0) { + final int len = Math.min(remaining, buffer.length); + in.readFully(buffer, 0, len); + out.write(buffer, 0, len); + remaining -= len; + } + return out.toByteArray(); } else { + final int maxBodySize = getMaxBodySize(); ByteArrayOutputStream out = new ByteArrayOutputStream(4096); try { boolean atLineStart = true; @@ -785,6 +811,11 @@ public class HttpRequestImpl implements HttpRequest { atLineStart = false; } out.write(b); + + if (out.size() > maxBodySize) { + throw new IOException("Request body exceeds the maximum allowed request body size (" + + maxBodySize + " bytes), set the '" + MAX_BODY_SIZE_PROPERTY + "' property to raise the limit"); + } } } catch (EOFException e) { // done reading @@ -794,6 +825,10 @@ public class HttpRequestImpl implements HttpRequest { } } + private static int getMaxBodySize() { + return SystemInstance.get().getOptions().get(MAX_BODY_SIZE_PROPERTY, DEFAULT_MAX_BODY_SIZE); + } + private int parseContentLength() { // Content-length: 384 String len = getHeader(HttpRequest.HEADER_CONTENT_LENGTH); diff --git a/server/openejb-http/src/test/java/org/apache/openejb/server/httpd/HttpRequestImplTest.java b/server/openejb-http/src/test/java/org/apache/openejb/server/httpd/HttpRequestImplTest.java index dac71bd841..afc33490da 100644 --- a/server/openejb-http/src/test/java/org/apache/openejb/server/httpd/HttpRequestImplTest.java +++ b/server/openejb-http/src/test/java/org/apache/openejb/server/httpd/HttpRequestImplTest.java @@ -22,12 +22,17 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; public class HttpRequestImplTest { @Before @@ -63,4 +68,38 @@ public class HttpRequestImplTest { req.initPathFromContext("/api/bar"); // that's too late we tolerate a wrong context only if its value is "/" assertEquals("/foo/bar", req.getServletPath()); } + + @Test + public void oversizedContentLengthRejected() throws Exception { + final HttpRequestImpl req = new HttpRequestImpl(new URI("http://localhost:1234/foo")); + final String message = "POST /foo HTTP/1.1\r\nContent-Length: 2147483647\r\n\r\n"; + try { + req.readMessage(new ByteArrayInputStream(message.getBytes(StandardCharsets.ISO_8859_1))); + fail("Should have rejected the oversized Content-Length"); + } catch (final IOException expected) { + assertTrue(expected.getMessage(), expected.getMessage().contains("maximum allowed request body size")); + } + } + + @Test + public void oversizedChunkRejected() throws Exception { + final HttpRequestImpl req = new HttpRequestImpl(new URI("http://localhost:1234/foo")); + final String message = "POST /foo HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n7fffffff\r\n"; + try { + req.readMessage(new ByteArrayInputStream(message.getBytes(StandardCharsets.ISO_8859_1))); + fail("Should have rejected the oversized chunk"); + } catch (final IOException expected) { + assertTrue(expected.getMessage(), expected.getCause() instanceof IOException + && expected.getCause().getMessage().contains("maximum allowed request body size")); + } + } + + @Test + public void smallBodyStillRead() throws Exception { + final HttpRequestImpl req = new HttpRequestImpl(new URI("http://localhost:1234/foo")); + final String message = "POST /foo HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 7\r\n\r\na=1&b=2"; + assertTrue(req.readMessage(new ByteArrayInputStream(message.getBytes(StandardCharsets.ISO_8859_1)))); + assertEquals("1", req.getParameter("a")); + assertEquals("2", req.getParameter("b")); + } }
