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

gnodet pushed a commit to branch fix/as2-issues
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 43d5068b57c2f31ac2c0f6177a824ed87b3849a0
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Mar 9 11:04:16 2026 +0100

    CAMEL-23064: Support non-standard content types in AS2 EDI payload
    
    Add GenericApplicationEntity to handle EDI payloads with content types
    not explicitly mapped (e.g. text/plain, application/octet-stream).
    Previously, unknown content types threw a CamelException. Also fix
    missing return statement in HttpMessageUtils.getEntity() for response
    branch.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../component/as2/api/entity/EntityParser.java     |   3 +
 .../as2/api/entity/GenericApplicationEntity.java   |  39 ++++++++
 .../camel/component/as2/api/util/EntityUtils.java  |   5 +-
 .../component/as2/api/util/HttpMessageUtils.java   |  59 +++++++++---
 .../api/entity/GenericApplicationEntityTest.java   | 100 +++++++++++++++++++++
 5 files changed, 192 insertions(+), 14 deletions(-)

diff --git 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/EntityParser.java
 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/EntityParser.java
index d6b4f84efeaa..7d2b25b6de84 100644
--- 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/EntityParser.java
+++ 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/EntityParser.java
@@ -523,6 +523,9 @@ public final class EntityParser {
                         contentTransferEncoding);
                 break;
             default:
+                // Accept non-standard content types (e.g., text/plain, 
application/octet-stream)
+                // that real-world AS2 partners may use to wrap EDI payloads
+                parseApplicationEDIEntity(message, entity.getContent(), 
inBuffer, contentType, contentTransferEncoding);
                 break;
         }
     }
diff --git 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/GenericApplicationEntity.java
 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/GenericApplicationEntity.java
new file mode 100644
index 000000000000..6bc3d2e7a49f
--- /dev/null
+++ 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/GenericApplicationEntity.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.as2.api.entity;
+
+import java.io.IOException;
+
+import org.apache.hc.core5.http.ContentType;
+
+/**
+ * A generic application entity that wraps EDI payloads with non-standard 
content types (e.g., text/plain,
+ * application/octet-stream). Real-world AS2 partners frequently send EDI 
payloads wrapped in content types other than
+ * the standard application/edifact, application/edi-x12, or 
application/edi-consent types.
+ */
+public class GenericApplicationEntity extends ApplicationEntity {
+
+    public GenericApplicationEntity(byte[] content, ContentType contentType, 
String contentTransferEncoding,
+                                    boolean isMainBody, String filename) {
+        super(content, contentType, contentTransferEncoding, isMainBody, 
filename);
+    }
+
+    @Override
+    public void close() throws IOException {
+        // do nothing
+    }
+}
diff --git 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java
 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java
index 3de97bda41dc..3bd4404ff917 100644
--- 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java
+++ 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java
@@ -33,6 +33,7 @@ import 
org.apache.camel.component.as2.api.entity.ApplicationEDIFACTEntity;
 import org.apache.camel.component.as2.api.entity.ApplicationEDIX12Entity;
 import org.apache.camel.component.as2.api.entity.ApplicationEntity;
 import org.apache.camel.component.as2.api.entity.ApplicationXMLEntity;
+import org.apache.camel.component.as2.api.entity.GenericApplicationEntity;
 import org.apache.camel.component.as2.api.entity.MimeEntity;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.commons.codec.DecoderException;
@@ -203,7 +204,9 @@ public final class EntityUtils {
             case AS2MediaType.APPLICATION_XML:
                 return new ApplicationXMLEntity(ediMessage, charset, 
contentTransferEncoding, isMainBody, filename);
             default:
-                throw new CamelException("Invalid EDI entity mime type: " + 
ediMessageContentType.getMimeType());
+                return new GenericApplicationEntity(
+                        ediMessage, ediMessageContentType, 
contentTransferEncoding,
+                        isMainBody, filename);
         }
 
     }
diff --git 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/HttpMessageUtils.java
 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/HttpMessageUtils.java
index d6a03c17dec9..7acc58adee41 100644
--- 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/HttpMessageUtils.java
+++ 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/HttpMessageUtils.java
@@ -75,7 +75,7 @@ public final class HttpMessageUtils {
         } else if (message instanceof BasicClassicHttpResponse httpResponse) {
             HttpEntity entity = httpResponse.getEntity();
             if (entity != null && type.isInstance(entity)) {
-                type.cast(entity);
+                return type.cast(entity);
             }
         }
         return null;
@@ -152,10 +152,23 @@ public final class HttpMessageUtils {
                 }
                 break;
             }
-            default:
-                throw new HttpException(
-                        "Failed to extract EDI message: invalid content type 
'" + contentType.getMimeType()
-                                        + "' for AS2 request message");
+            default: {
+                // Accept non-standard content types (e.g., text/plain, 
application/octet-stream)
+                // that real-world AS2 partners may use to wrap EDI payloads
+                if 
(decrpytingAndSigningInfo.getValidateSigningCertificateChain() != null) {
+                    throw new AS2InsufficientSecurityException("Failed to 
validate the signature");
+                }
+                if (decrpytingAndSigningInfo.getDecryptingPrivateKey() != 
null) {
+                    throw new AS2InsufficientSecurityException("Expected to be 
encrypted");
+                }
+                ediEntity = getEntity(message, ApplicationEntity.class);
+                if (ediEntity == null) {
+                    throw new HttpException(
+                            "Failed to extract EDI message: could not parse 
entity with content type '"
+                                            + contentType.getMimeType() + "'");
+                }
+                break;
+            }
         }
 
         return ediEntity;
@@ -274,10 +287,20 @@ public final class HttpMessageUtils {
                 ediEntity = 
extractEdiPayloadFromCompressedEntity(compressedDataEntity, 
decrpytingAndSigningInfo, false);
                 break;
             }
-            default:
-                throw new HttpException(
-                        "Failed to extract EDI payload: invalid content type 
'" + contentType.getMimeType()
-                                        + "' for AS2 enveloped entity");
+            default: {
+                // Accept non-standard content types within enveloped entities
+                if 
(decrpytingAndSigningInfo.getValidateSigningCertificateChain() != null) {
+                    throw new AS2InsufficientSecurityException("Failed to 
validate the signature");
+                }
+                if (entity instanceof ApplicationEntity) {
+                    ediEntity = (ApplicationEntity) entity;
+                } else {
+                    throw new HttpException(
+                            "Failed to extract EDI payload: invalid content 
type '" + contentType.getMimeType()
+                                            + "' for AS2 enveloped entity");
+                }
+                break;
+            }
         }
 
         return ediEntity;
@@ -325,10 +348,20 @@ public final class HttpMessageUtils {
                 }
                 break;
             }
-            default:
-                throw new HttpException(
-                        "Failed to extract EDI payload: invalid content type 
'" + contentType.getMimeType()
-                                        + "' for AS2 compressed entity");
+            default: {
+                // Accept non-standard content types within compressed entities
+                if (!hasValidSignature && 
decrpytingAndSigningInfo.getValidateSigningCertificateChain() != null) {
+                    throw new AS2InsufficientSecurityException("Failed to 
validate the signature");
+                }
+                if (entity instanceof ApplicationEntity) {
+                    ediEntity = (ApplicationEntity) entity;
+                } else {
+                    throw new HttpException(
+                            "Failed to extract EDI payload: invalid content 
type '" + contentType.getMimeType()
+                                            + "' for AS2 compressed entity");
+                }
+                break;
+            }
         }
 
         return ediEntity;
diff --git 
a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/GenericApplicationEntityTest.java
 
b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/GenericApplicationEntityTest.java
new file mode 100644
index 000000000000..ab00bfbe705b
--- /dev/null
+++ 
b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/GenericApplicationEntityTest.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.as2.api.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.Security;
+
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2TransferEncoding;
+import org.apache.camel.component.as2.api.util.EntityUtils;
+import org.apache.camel.component.as2.api.util.HttpMessageUtils;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.io.entity.BasicHttpEntity;
+import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class GenericApplicationEntityTest {
+
+    private static final String EDI_MESSAGE = 
"UNB+UNOA:1+005435656:1+006415160:1+060515:1434+00000000000778'";
+
+    @BeforeAll
+    static void setUp() {
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
+    @Test
+    void genericApplicationEntityPreservesContent() throws Exception {
+        byte[] content = EDI_MESSAGE.getBytes(StandardCharsets.US_ASCII);
+        ContentType contentType = ContentType.create("text/plain", 
StandardCharsets.US_ASCII);
+        GenericApplicationEntity entity = new 
GenericApplicationEntity(content, contentType, null, true, null);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        entity.writeTo(out);
+        assertArrayEquals(content, out.toByteArray());
+    }
+
+    @Test
+    void genericApplicationEntityGetEdiMessage() {
+        byte[] content = EDI_MESSAGE.getBytes(StandardCharsets.US_ASCII);
+        ContentType contentType = ContentType.create("text/plain", 
StandardCharsets.US_ASCII);
+        GenericApplicationEntity entity = new 
GenericApplicationEntity(content, contentType, null, true, null);
+
+        Object ediMessage = entity.getEdiMessage();
+        assertInstanceOf(String.class, ediMessage);
+        assertEquals(EDI_MESSAGE, ediMessage);
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = { "text/plain", "application/octet-stream" })
+    void extractEdiPayloadAcceptsNonStandardContentTypes(String mimeType) 
throws Exception {
+        byte[] content = EDI_MESSAGE.getBytes(StandardCharsets.US_ASCII);
+        ContentType contentType = ContentType.create(mimeType, 
StandardCharsets.US_ASCII);
+
+        BasicClassicHttpRequest request = new BasicClassicHttpRequest("POST", 
"/");
+        request.addHeader(AS2Header.CONTENT_TYPE, contentType.toString());
+        InputStream is = new ByteArrayInputStream(content);
+        request.setEntity(new BasicHttpEntity(is, content.length, 
contentType));
+
+        ApplicationEntity ediEntity = 
HttpMessageUtils.extractEdiPayload(request,
+                new HttpMessageUtils.DecrpytingAndSigningInfo(null, null));
+
+        assertNotNull(ediEntity, "EDI entity should not be null for content 
type: " + mimeType);
+        assertInstanceOf(GenericApplicationEntity.class, ediEntity);
+        assertEquals(EDI_MESSAGE, ediEntity.getEdiMessage().toString());
+    }
+
+    @Test
+    void createEDIEntityReturnsGenericForUnknownType() throws Exception {
+        byte[] content = EDI_MESSAGE.getBytes(StandardCharsets.US_ASCII);
+        ContentType contentType = ContentType.create("text/plain", 
StandardCharsets.US_ASCII);
+
+        ApplicationEntity entity = EntityUtils.createEDIEntity(content, 
contentType,
+                AS2TransferEncoding.NONE, false, "test.txt");
+
+        assertInstanceOf(GenericApplicationEntity.class, entity);
+    }
+}

Reply via email to