This is an automated email from the ASF dual-hosted git repository.
jungm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomee.git
The following commit(s) were added to refs/heads/main by this push:
new 672e32f6c6 align embedded http request body handling with Tomcat
672e32f6c6 is described below
commit 672e32f6c61537142bc114c75a89259ee7b8f116
Author: Markus Jung <[email protected]>
AuthorDate: Fri Aug 21 21:21:31 2026 +0200
align embedded http request body handling with Tomcat
---
.../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"));
+ }
}