You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2024/10/02 10:39:35 UTC

(camel-spring-boot) branch main updated: CAMEL-21300: camel-platform-http - Consumer should have option to control if writing response failing should cause Exchange to fail

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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git


The following commit(s) were added to refs/heads/main by this push:
     new 5380fdf6918 CAMEL-21300: camel-platform-http - Consumer should have option to control if writing response failing should cause Exchange to fail
5380fdf6918 is described below

commit 5380fdf69187315b0c06950ca1d0826639e7b40a
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Oct 2 12:37:43 2024 +0200

    CAMEL-21300: camel-platform-http - Consumer should have option to control if writing response failing should cause Exchange to fail
---
 .../catalog/components/platform-http.json          |  34 ++---
 .../src/main/docs/platform-http.json               |   7 ++
 .../PlatformHttpComponentConfiguration.java        |  16 +++
 .../http/springboot/PlatformHttpMessage.java       |   6 +-
 .../springboot/SpringBootPlatformHttpConsumer.java |  56 ++++++---
 .../platform/http/springboot/PlatformHttpBase.java |   6 +-
 ...SpringBootPlatformHttpHandleWriteErrorTest.java | 139 +++++++++++++++++++++
 7 files changed, 226 insertions(+), 38 deletions(-)

diff --git a/catalog/camel-catalog-provider-springboot/src/main/resources/org/apache/camel/springboot/catalog/components/platform-http.json b/catalog/camel-catalog-provider-springboot/src/main/resources/org/apache/camel/springboot/catalog/components/platform-http.json
index 9264fb10670..28631e49dcc 100644
--- a/catalog/camel-catalog-provider-springboot/src/main/resources/org/apache/camel/springboot/catalog/components/platform-http.json
+++ b/catalog/camel-catalog-provider-springboot/src/main/resources/org/apache/camel/springboot/catalog/components/platform-http.json
@@ -26,9 +26,10 @@
   },
   "componentProperties": {
     "bridgeErrorHandler": { "index": 0, "kind": "property", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the like [...]
-    "autowiredEnabled": { "index": 1, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
-    "engine": { "index": 2, "kind": "property", "displayName": "Engine", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.platform.http.spi.PlatformHttpEngine", "deprecated": false, "autowired": false, "secret": false, "description": "An HTTP Server engine implementation to serve the requests" },
-    "headerFilterStrategy": { "index": 3, "kind": "property", "displayName": "Header Filter Strategy", "group": "filter", "label": "filter", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel message." }
+    "handleWriteResponseError": { "index": 1, "kind": "property", "displayName": "Handle Write Response Error", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "When Camel is complete processing the message, and the HTTP server is writing response. This option controls whether Camel should catch any failure during writing response [...]
+    "autowiredEnabled": { "index": 2, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
+    "engine": { "index": 3, "kind": "property", "displayName": "Engine", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.platform.http.spi.PlatformHttpEngine", "deprecated": false, "autowired": false, "secret": false, "description": "An HTTP Server engine implementation to serve the requests" },
+    "headerFilterStrategy": { "index": 4, "kind": "property", "displayName": "Header Filter Strategy", "group": "filter", "label": "filter", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel message." }
   },
   "properties": {
     "path": { "index": 0, "kind": "path", "displayName": "Path", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The path under which this endpoint serves the HTTP requests, for proxy use 'proxy'" },
@@ -39,18 +40,19 @@
     "cookiePath": { "index": 5, "kind": "parameter", "displayName": "Cookie Path", "group": "consumer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "\/", "configurationClass": "org.apache.camel.component.platform.http.cookie.CookieConfiguration", "configurationField": "cookieConfiguration", "description": "Sets the URL path that must exist in the requested URL in order to send  [...]
     "cookieSameSite": { "index": 6, "kind": "parameter", "displayName": "Cookie Same Site", "group": "consumer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.platform.http.cookie.CookieConfiguration.CookieSameSite", "enum": [ "STRICT", "LAX", "NONE" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "Lax", "configurationClass": "org.apache.camel.component.platform.http.cookie.CookieConfiguration", "configurationField" [...]
     "cookieSecure": { "index": 7, "kind": "parameter", "displayName": "Cookie Secure", "group": "consumer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.platform.http.cookie.CookieConfiguration", "configurationField": "cookieConfiguration", "description": "Sets whether the cookie is only sent to the server with an encrypted reques [...]
-    "httpMethodRestrict": { "index": 8, "kind": "parameter", "displayName": "Http Method Restrict", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "A comma separated list of HTTP methods to serve, e.g. GET,POST . If no methods are specified, all methods will be served." },
-    "matchOnUriPrefix": { "index": 9, "kind": "parameter", "displayName": "Match On Uri Prefix", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether or not the consumer should try to find a target consumer by matching the URI prefix if no exact match is found." },
-    "muteException": { "index": 10, "kind": "parameter", "displayName": "Mute Exception", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "If enabled and an Exchange failed processing on the consumer side the response's body won't contain the exception's stack trace." },
-    "produces": { "index": 11, "kind": "parameter", "displayName": "Produces", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The content type this endpoint produces, such as application\/xml or application\/json." },
-    "returnHttpRequestHeaders": { "index": 12, "kind": "parameter", "displayName": "Return Http Request Headers", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to include HTTP request headers (Accept, User-Agent, etc.) into HTTP response produced by this endpoint." },
-    "useCookieHandler": { "index": 13, "kind": "parameter", "displayName": "Use Cookie Handler", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to enable the Cookie Handler that allows Cookie addition, expiry, and retrieval (currently only supported by camel-platform-http-vertx)" },
-    "useStreaming": { "index": 14, "kind": "parameter", "displayName": "Use Streaming", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to use streaming for large requests and responses (currently only supported by camel-platform-http-vertx)" },
-    "bridgeErrorHandler": { "index": 15, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming [...]
-    "exceptionHandler": { "index": 16, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By de [...]
-    "exchangePattern": { "index": 17, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
-    "fileNameExtWhitelist": { "index": 18, "kind": "parameter", "displayName": "File Name Ext Whitelist", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "A comma or whitespace separated list of file extensions. Uploads having these extensions will be stored locally. Null value or asterisk () will allow all files." },
-    "headerFilterStrategy": { "index": 19, "kind": "parameter", "displayName": "Header Filter Strategy", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom HeaderFilterStrategy to filter headers to and from Camel message." },
-    "platformHttpEngine": { "index": 20, "kind": "parameter", "displayName": "Platform Http Engine", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.platform.http.spi.PlatformHttpEngine", "deprecated": false, "autowired": false, "secret": false, "description": "An HTTP Server engine implementation to serve the requests of this endpoint." }
+    "handleWriteResponseError": { "index": 8, "kind": "parameter", "displayName": "Handle Write Response Error", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "When Camel is complete processing the message, and the HTTP server is writing response. This option controls whether Camel should catch any failure during writing respons [...]
+    "httpMethodRestrict": { "index": 9, "kind": "parameter", "displayName": "Http Method Restrict", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "A comma separated list of HTTP methods to serve, e.g. GET,POST . If no methods are specified, all methods will be served." },
+    "matchOnUriPrefix": { "index": 10, "kind": "parameter", "displayName": "Match On Uri Prefix", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether or not the consumer should try to find a target consumer by matching the URI prefix if no exact match is found." },
+    "muteException": { "index": 11, "kind": "parameter", "displayName": "Mute Exception", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "If enabled and an Exchange failed processing on the consumer side the response's body won't contain the exception's stack trace." },
+    "produces": { "index": 12, "kind": "parameter", "displayName": "Produces", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The content type this endpoint produces, such as application\/xml or application\/json." },
+    "returnHttpRequestHeaders": { "index": 13, "kind": "parameter", "displayName": "Return Http Request Headers", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to include HTTP request headers (Accept, User-Agent, etc.) into HTTP response produced by this endpoint." },
+    "useCookieHandler": { "index": 14, "kind": "parameter", "displayName": "Use Cookie Handler", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to enable the Cookie Handler that allows Cookie addition, expiry, and retrieval (currently only supported by camel-platform-http-vertx)" },
+    "useStreaming": { "index": 15, "kind": "parameter", "displayName": "Use Streaming", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to use streaming for large requests and responses (currently only supported by camel-platform-http-vertx)" },
+    "bridgeErrorHandler": { "index": 16, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming [...]
+    "exceptionHandler": { "index": 17, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By de [...]
+    "exchangePattern": { "index": 18, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+    "fileNameExtWhitelist": { "index": 19, "kind": "parameter", "displayName": "File Name Ext Whitelist", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "A comma or whitespace separated list of file extensions. Uploads having these extensions will be stored locally. Null value or asterisk () will allow all files." },
+    "headerFilterStrategy": { "index": 20, "kind": "parameter", "displayName": "Header Filter Strategy", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom HeaderFilterStrategy to filter headers to and from Camel message." },
+    "platformHttpEngine": { "index": 21, "kind": "parameter", "displayName": "Platform Http Engine", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.platform.http.spi.PlatformHttpEngine", "deprecated": false, "autowired": false, "secret": false, "description": "An HTTP Server engine implementation to serve the requests of this endpoint." }
   }
 }
diff --git a/components-starter/camel-platform-http-starter/src/main/docs/platform-http.json b/components-starter/camel-platform-http-starter/src/main/docs/platform-http.json
index bc30752131a..ac25fa04700 100644
--- a/components-starter/camel-platform-http-starter/src/main/docs/platform-http.json
+++ b/components-starter/camel-platform-http-starter/src/main/docs/platform-http.json
@@ -44,6 +44,13 @@
       "description": "An HTTP Server engine implementation to serve the requests. The option is a org.apache.camel.component.platform.http.spi.PlatformHttpEngine type.",
       "sourceType": "org.apache.camel.component.platform.http.springboot.PlatformHttpComponentConfiguration"
     },
+    {
+      "name": "camel.component.platform-http.handle-write-response-error",
+      "type": "java.lang.Boolean",
+      "description": "When Camel is complete processing the message, and the HTTP server is writing response. This option controls whether Camel should catch any failure during writing response and store this on the Exchange, which allows onCompletion\/UnitOfWork to regard the Exchange as failed and have access to the caused exception from the HTTP server.",
+      "sourceType": "org.apache.camel.component.platform.http.springboot.PlatformHttpComponentConfiguration",
+      "defaultValue": false
+    },
     {
       "name": "camel.component.platform-http.header-filter-strategy",
       "type": "org.apache.camel.spi.HeaderFilterStrategy",
diff --git a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpComponentConfiguration.java b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpComponentConfiguration.java
index ad12d9a12ec..f8a947347e9 100644
--- a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpComponentConfiguration.java
+++ b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpComponentConfiguration.java
@@ -51,6 +51,14 @@ public class PlatformHttpComponentConfiguration
      * with exceptions, that will be logged at WARN or ERROR level and ignored.
      */
     private Boolean bridgeErrorHandler = false;
+    /**
+     * When Camel is complete processing the message, and the HTTP server is
+     * writing response. This option controls whether Camel should catch any
+     * failure during writing response and store this on the Exchange, which
+     * allows onCompletion/UnitOfWork to regard the Exchange as failed and have
+     * access to the caused exception from the HTTP server.
+     */
+    private Boolean handleWriteResponseError = false;
     /**
      * Whether autowiring is enabled. This is used for automatic autowiring
      * options (the option must be marked as autowired) by looking up in the
@@ -80,6 +88,14 @@ public class PlatformHttpComponentConfiguration
         this.bridgeErrorHandler = bridgeErrorHandler;
     }
 
+    public Boolean getHandleWriteResponseError() {
+        return handleWriteResponseError;
+    }
+
+    public void setHandleWriteResponseError(Boolean handleWriteResponseError) {
+        this.handleWriteResponseError = handleWriteResponseError;
+    }
+
     public Boolean getAutowiredEnabled() {
         return autowiredEnabled;
     }
diff --git a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpMessage.java b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpMessage.java
index b3471309b35..4e8abf92fe3 100644
--- a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpMessage.java
+++ b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpMessage.java
@@ -98,7 +98,11 @@ public class PlatformHttpMessage extends DefaultMessage {
     }
 
     public PlatformHttpMessage newInstance() {
-        return new PlatformHttpMessage(this.request, this.response, this.getExchange(), this.binding, this.requestRead);
+        PlatformHttpMessage answer = new PlatformHttpMessage(this.request, this.response, this.getExchange(), this.binding, this.requestRead);
+        if (answer.camelContext == null) {
+            answer.setCamelContext(this.camelContext);
+        }
+        return answer;
     }
 
     public String toString() {
diff --git a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpConsumer.java b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpConsumer.java
index 8b4ed90f4e1..8a312416abf 100644
--- a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpConsumer.java
+++ b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpConsumer.java
@@ -19,7 +19,6 @@ package org.apache.camel.component.platform.http.springboot;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
-import java.io.IOException;
 import java.util.concurrent.CompletableFuture;
 import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePattern;
@@ -29,6 +28,7 @@ import org.apache.camel.SuspendableService;
 import org.apache.camel.component.platform.http.PlatformHttpEndpoint;
 import org.apache.camel.component.platform.http.spi.PlatformHttpConsumer;
 import org.apache.camel.http.common.DefaultHttpBinding;
+import org.apache.camel.http.common.HttpBinding;
 import org.apache.camel.http.common.HttpHelper;
 import org.apache.camel.support.DefaultConsumer;
 import org.slf4j.Logger;
@@ -39,7 +39,8 @@ public class SpringBootPlatformHttpConsumer extends DefaultConsumer implements P
 
     private static final Logger LOG = LoggerFactory.getLogger(SpringBootPlatformHttpConsumer.class);
 
-    private final DefaultHttpBinding binding;
+    private HttpBinding binding;
+    private final boolean handleWriteResponseError;
 
     public SpringBootPlatformHttpConsumer(PlatformHttpEndpoint endpoint, Processor processor) {
         super(endpoint, processor);
@@ -48,6 +49,14 @@ public class SpringBootPlatformHttpConsumer extends DefaultConsumer implements P
         this.binding.setMuteException(endpoint.isMuteException());
         this.binding.setFileNameExtWhitelist(endpoint.getFileNameExtWhitelist());
         this.binding.setUseReaderForPayload(!endpoint.isUseStreaming());
+        this.handleWriteResponseError = endpoint.isHandleWriteResponseError();
+    }
+
+    /**
+     * Used for testing purposes
+     */
+    void setBinding(HttpBinding binding) {
+        this.binding = binding;
     }
 
     @Override
@@ -112,36 +121,47 @@ public class SpringBootPlatformHttpConsumer extends DefaultConsumer implements P
         } catch (Exception e) {
             exchange.setException(e);
         } finally {
-            afterProcess(response, exchange, true);
+            afterProcess(response, exchange);
         }
     }
 
-    protected void afterProcess(HttpServletResponse response, Exchange exchange, boolean rethrow)
-            throws IOException, ServletException {
+    protected void afterProcess(HttpServletResponse response, Exchange exchange) throws Exception {
+        boolean writeFailure = false;
         try {
             // now lets output to the res
             if (LOG.isTraceEnabled()) {
                 LOG.trace("Writing res for exchangeId: {}", exchange.getExchangeId());
             }
             binding.writeResponse(exchange, response);
-        } catch (IOException e) {
-            LOG.error("Error processing request", e);
-            if (rethrow) {
-                throw e;
-            } else {
-                exchange.setException(e);
-            }
         } catch (Exception e) {
-            LOG.error("Error processing request", e);
-            if (rethrow) {
-                throw new ServletException(e);
-            } else {
-                exchange.setException(e);
-            }
+            writeFailure = true;
+            handleFailure(exchange, e);
         } finally {
             doneUoW(exchange);
             releaseExchange(exchange, false);
         }
+        try {
+            if (writeFailure && !response.isCommitted()) {
+                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            }
+        } catch (Exception e) {
+            // ignore
+        }
+
+    }
+
+    private void handleFailure(Exchange exchange, Throwable failure) {
+        getExceptionHandler().handleException(
+                "Failed writing HTTP response url: " + getEndpoint().getPath() + " due to: " + failure.getMessage(),
+                failure);
+        if (handleWriteResponseError) {
+            Exception existing = exchange.getException();
+            if (existing != null) {
+                failure.addSuppressed(existing);
+            }
+            exchange.setProperty(Exchange.EXCEPTION_CAUGHT, failure);
+            exchange.setException(failure);
+        }
     }
 
 }
diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpBase.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpBase.java
index dd3bb7ec2c0..79d5cb321d2 100644
--- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpBase.java
+++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpBase.java
@@ -30,20 +30,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 abstract class PlatformHttpBase {
 
     @Autowired
-    private TestRestTemplate restTemplate;
+    TestRestTemplate restTemplate;
 
     @Autowired
     CamelContext camelContext;
 
     @Test
-    public void testGet() {
+    public void testGet() throws Exception {
         waitUntilRouteIsStarted(1, getGetRouteId());
 
         Assertions.assertThat(restTemplate.getForEntity("/myget", String.class).getStatusCodeValue()).isEqualTo(200);
     }
 
     @Test
-    public void testPost() {
+    public void testPost() throws Exception {
         waitUntilRouteIsStarted(1, getPostRouteId());
 
         Assertions.assertThat(restTemplate.postForEntity("/mypost", "test", String.class).getBody()).isEqualTo("TEST");
diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpHandleWriteErrorTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpHandleWriteErrorTest.java
new file mode 100644
index 00000000000..37a3cb384bb
--- /dev/null
+++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpHandleWriteErrorTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.platform.http.springboot;
+
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.component.platform.http.PlatformHttpEndpoint;
+import org.apache.camel.component.platform.http.spi.PlatformHttpConsumer;
+import org.apache.camel.component.platform.http.spi.PlatformHttpEngine;
+import org.apache.camel.http.common.DefaultHttpBinding;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+import java.io.IOException;
+
+@EnableAutoConfiguration
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class,
+        SpringBootPlatformHttpHandleWriteErrorTest.class, SpringBootPlatformHttpHandleWriteErrorTest.TestConfiguration.class,
+        PlatformHttpComponentAutoConfiguration.class, SpringBootPlatformHttpAutoConfiguration.class })
+public class SpringBootPlatformHttpHandleWriteErrorTest extends PlatformHttpBase {
+
+    private static final String postRouteId = "SpringBootPlatformHttpHandleWriteErrorTest_mypost";
+    private static final String getRouteId = "SpringBootPlatformHttpHandleWriteErrorTest_myget";
+
+    @Test
+    @Override
+    public void testGet() throws Exception {
+        MockEndpoint me = camelContext.getEndpoint("mock:failure", MockEndpoint.class);
+        me.expectedMessageCount(0);
+
+        super.testGet();
+
+        me.assertIsSatisfied();
+    }
+
+    @Test
+    @Override
+    public void testPost() throws Exception {
+        MockEndpoint me = camelContext.getEndpoint("mock:failure", MockEndpoint.class);
+        me.expectedMessageCount(1);
+
+        waitUntilRouteIsStarted(1, getPostRouteId());
+        Assertions.assertThat(restTemplate.postForEntity("/mypost", "test", String.class).getStatusCode().value()).isEqualTo(500);
+
+        me.assertIsSatisfied();
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+    @Configuration
+    public static class TestConfiguration {
+
+        @Bean(name = "platform-http-engine")
+        public PlatformHttpEngine myHttpEngine(Environment env) {
+            int port = Integer.parseInt(env.getProperty("server.port", "8080"));
+            return new MyEngine(port);
+        }
+
+        @Bean
+        public RouteBuilder servletPlatformHttpRouteBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    onCompletion().onFailureOnly().to("log:failure").to("mock:failure");
+
+                    from("platform-http:/myget").id(getRouteId).setBody().constant("get");
+                    from("platform-http:/mypost?handleWriteResponseError=true").id(postRouteId).transform().body(String.class, b -> b.toUpperCase());
+                }
+            };
+        }
+    }
+
+    @Override
+    protected String getPostRouteId() {
+        return postRouteId;
+    }
+
+    @Override
+    protected String getGetRouteId() {
+        return getRouteId;
+    }
+
+    private static class MyErrorBinding extends DefaultHttpBinding {
+
+        @Override
+        public void writeResponse(Exchange exchange, HttpServletResponse response) throws IOException {
+            // force an exception during writing response to simulate error at that point
+            String uri = exchange.getMessage().getHeader(Exchange.HTTP_URI, String.class);
+            if ("/mypost".equals(uri)) {
+                throw new IOException("Forced error");
+            } else {
+                super.writeResponse(exchange, response);
+            }
+        }
+    }
+
+    private static class MyEngine extends SpringBootPlatformHttpEngine {
+
+        public MyEngine(int port) {
+            super(port);
+        }
+
+        @Override
+        public PlatformHttpConsumer createConsumer(PlatformHttpEndpoint endpoint, Processor processor) {
+            SpringBootPlatformHttpConsumer answer = new SpringBootPlatformHttpConsumer(endpoint, processor);
+            answer.setBinding(new MyErrorBinding());
+            return answer;
+        }
+    }
+}