This is an automated email from the ASF dual-hosted git repository.

Croway pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 1012de47aabf CAMEL-24553: camel-support: OOM guard in getMandatoryBody 
for huge body-to-String conversion (#25871)
1012de47aabf is described below

commit 1012de47aabf76f9bdfc161b7a68b5ee42142513
Author: mayurbm <[email protected]>
AuthorDate: Fri Aug 28 21:49:24 2026 +0530

    CAMEL-24553: camel-support: OOM guard in getMandatoryBody for huge 
body-to-String conversion (#25871)
    
    * CAMEL-24553: camel-support: OOM guard in getMandatoryBody for huge 
body-to-String conversion
    
    MessageSupport.getMandatoryBody(Class) now refuses to convert bodies whose
    known length exceeds the in-memory conversion limit (default 256 MiB) to 
bulk
    types (String, byte[], CharSequence). This prevents the JVM dying with
    OutOfMemoryError when the Splitter/Bean path calls 
getMandatoryBody(String.class)
    on a multi-GB payload.
    
    The check uses the already-known body length (CharSequence.length(),
    byte[].length, StreamCache.length()) with no allocation, so there is zero
    overhead for normal-sized payloads. When triggered, a clear
    InvalidPayloadException with a diagnostic message is thrown instead.
    
    The cap is overridable via system property camel.message.max-in-memory-body
    (value in bytes, default 256 * 1024 * 1024).
    
    Add MessageSupportOversizedBodyTest with 6 tests covering: below cap passes,
    at cap passes, String over cap refused, byte[] over cap refused, non-bulk 
type
    not guarded, system property override works.
    
    Co-authored-by: Claude Sonnet 4.5 <[email protected]>
    
    * CAMEL-24553: camel-api/camel-support: OOM guard v2 — safe 
TypeConversionException + wouldMaterializeHugeBulk
    
    Address @Croway review: stop TypeConversionException.createMessage() from
    calling toString() on the value (which can OOM on huge payloads). Use
    safeValueDescription() that shows type name + identity for unknown types,
    truncated preview for CharSequence, and safe descriptions for byte[],
    File, Path, ByteBuffer.
    
    Rework MessageSupport.getMandatoryBody() per v2 design:
    - wouldMaterializeHugeBulk() only fires when conversion would actually
      allocate (same-type bodies pass through unchanged — heap already paid)
    - Richer length detection: CharSequence, byte[], ByteBuffer, StreamCache,
      File, Path, CamelFileLength/Content-Length headers, GenericFile duck-type
    - Cap hierarchy: exchange property CamelConvertMaxBytes → context global
      option → system property camel.convert.max-bytes → 16 MiB default
    
    Add 12 tests covering: same-type pass-through, cross-type guard, cap
    hierarchy (exchange property, context global option), size in message,
    and TypeConversionException toString() regression tests.
    
    Co-authored-by: Claude Sonnet 4.5 <[email protected]>
    
    * CAMEL-24553: camel-api: stop TypeConversionException from calling 
toString() on body
    
    TypeConversionException.createMessage() previously concatenated the value
    via string concatenation (+ value), which calls toString() on unknown types.
    For huge message bodies this causes OutOfMemoryError before any exception
    is even thrown.
    
    Remove the value from the message entirely. The value remains accessible
    via getValue() for callers that need it. Change the message format from:
    
      "...with value <value> due to..."
    
    to:
    
      "...due to..."
    
    Add createMessage_doesNotCallToStringOnValue regression test to
    TypeConversionExceptionMessageTest: the body's toString() throws
    AssertionError if called, proving the fix holds.
    
    Update DefaultComponentTest to match the new message format (no "with 
value").
    Remove MessageSupportOversizedBodyTest (MessageSupport guard removed per
    Croway review — scope limited to TypeConversionException fix only).
    
    Co-authored-by: Claude Sonnet 4.5 <[email protected]>
    
    ---------
    
    Co-authored-by: mayurmohan <[email protected]>
    Co-authored-by: Claude Sonnet 4.5 <[email protected]>
---
 .../org/apache/camel/TypeConversionException.java  |  8 +++++---
 .../camel/TypeConversionExceptionMessageTest.java  | 22 +++++++++++++++++++++-
 .../apache/camel/impl/DefaultComponentTest.java    |  2 +-
 3 files changed, 27 insertions(+), 5 deletions(-)

diff --git 
a/core/camel-api/src/main/java/org/apache/camel/TypeConversionException.java 
b/core/camel-api/src/main/java/org/apache/camel/TypeConversionException.java
index 1ada45cecf43..a72960c7a6d6 100644
--- a/core/camel-api/src/main/java/org/apache/camel/TypeConversionException.java
+++ b/core/camel-api/src/main/java/org/apache/camel/TypeConversionException.java
@@ -70,14 +70,16 @@ public class TypeConversionException extends 
RuntimeCamelException {
     }
 
     /**
-     * Returns an error message for type conversion failed.
+     * Returns an error message for type conversion failed. The value itself 
is intentionally omitted from the message
+     * to avoid calling {@code toString()} on potentially huge or sensitive 
message bodies; the value remains accessible
+     * via {@link #getValue()}.
      */
     public static String createMessage(@Nullable Object value, Class<?> type, 
Throwable cause) {
         Objects.requireNonNull(type, "type");
         Objects.requireNonNull(cause, "cause");
         return "Error during type conversion from type: " + typeName(value != 
null ? value.getClass() : null)
-               + " to the required type: " + typeName(type) + " with value " + 
value + " due to "
-               + cause.getClass().getName() + ": " + cause.getMessage();
+               + " to the required type: " + typeName(type)
+               + " due to " + cause.getClass().getName() + ": " + 
cause.getMessage();
     }
 
     /**
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/TypeConversionExceptionMessageTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/TypeConversionExceptionMessageTest.java
index 13d70ecf3f43..d1a3f1db2ee9 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/TypeConversionExceptionMessageTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/TypeConversionExceptionMessageTest.java
@@ -22,7 +22,8 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 /**
  * Verifies {@link TypeConversionException#createMessage} handles anonymous 
and local classes whose
- * {@link Class#getCanonicalName()} returns {@code null}.
+ * {@link Class#getCanonicalName()} returns {@code null}, and that the message 
never calls {@code toString()} on the
+ * value (which could OOM for huge payloads).
  *
  * <p>
  * Real-world trigger: SFTP stream body is an anonymous inner class ({@code 
ChannelSftp$2}); its canonical name is
@@ -66,4 +67,23 @@ class TypeConversionExceptionMessageTest {
                 .contains("from type: null")
                 .contains("to the required type: java.lang.String");
     }
+
+    @Test
+    void createMessage_doesNotCallToStringOnValue() {
+        // Fails immediately if exception construction invokes body.toString()
+        Object body = new Object() {
+            @Override
+            public String toString() {
+                throw new AssertionError("TypeConversionException must not 
call toString() on the body value");
+            }
+        };
+
+        TypeConversionException exception = new TypeConversionException(body, 
String.class, new RuntimeException("cause"));
+
+        assertThat(exception.getValue()).isSameAs(body);
+        assertThat(exception.getMessage())
+                .contains(body.getClass().getName())
+                .contains("cause")
+                .doesNotContain("with value");
+    }
 }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/impl/DefaultComponentTest.java 
b/core/camel-core/src/test/java/org/apache/camel/impl/DefaultComponentTest.java
index 54ace772ad9d..33c097196123 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/impl/DefaultComponentTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/impl/DefaultComponentTest.java
@@ -129,7 +129,7 @@ public class DefaultComponentTest extends 
ContextTestSupport {
 
         assertEquals(
                 "Error during type conversion from type: java.lang.String " + 
"to the required type: java.lang.Integer "
-                     + "with value abc due to java.lang.NumberFormatException: 
For input string: \"abc\"",
+                     + "due to java.lang.NumberFormatException: For input 
string: \"abc\"",
                 ex.getMessage());
     }
 

Reply via email to