You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@camel.apache.org by "Federico Mariani (Jira)" <ji...@apache.org> on 2026/09/18 13:41:00 UTC
[jira] [Resolved] (CAMEL-24811) camel-servlet - async=true without executorRef completes the AsyncContext before a truly-asynchronous route finishes
[ https://issues.apache.org/jira/browse/CAMEL-24811?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Federico Mariani resolved CAMEL-24811.
--------------------------------------
Resolution: Fixed
> camel-servlet - async=true without executorRef completes the AsyncContext before a truly-asynchronous route finishes
> --------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-24811
> URL: https://issues.apache.org/jira/browse/CAMEL-24811
> Project: Camel
> Issue Type: Bug
> Components: camel-http-common, camel-servlet
> Affects Versions: 4.21.0, 4.22.0
> Reporter: Federico Mariani
> Assignee: Federico Mariani
> Priority: Major
> Fix For: 4.18.5, 4.22.2, 4.23.0
>
>
> When CamelHttpTransportServlet is configured with async=true and no executorRef (the simplest, most common async setup), CamelServlet.doServiceAsync() calls AsyncContext.complete() unconditionally in a finally block right after doService() returns, without waiting for any in-flight asynchronous processing.
> doExecute() returns a CompletionStage when the route's processor is a genuinely-async AsyncProcessor (processAsync() resumes on another thread, e.g. a downstream client callback thread). doService() is void, so that CompletionStage is silently discarded. If the processor really does resume on another thread, AsyncContext.complete() (and, on Tomcat, ResponseFacade recycling) races the delayed afterProcess()/writeResponse() call.
> Observed impact:
> * On Tomcat, when the eventual writeResponse() call loses the race, it throws:
> {code}
> java.lang.IllegalStateException: The response object has been recycled and is no longer associated with this facade
> at org.apache.catalina.connector.ResponseFacade.checkFacade(ResponseFacade.java:427)
> at org.apache.camel.http.common.DefaultHttpBinding.doWriteResponse(DefaultHttpBinding.java:432)
> at org.apache.camel.http.common.CamelServlet.afterProcess(CamelServlet.java:419)
> at org.apache.camel.http.common.CamelServlet.lambda$tryAsyncProcess$0(CamelServlet.java:378)
> {code}
> * Otherwise, the async context is completed with whatever (if anything) was already written, and the route finishes its remaining steps after the client has already received a response - i.e. the client gets a stale/incomplete response and route side effects run "in the background" unexpectedly.
> Reported with a route that performs a CXF SOAP call (concurrent requests cause CXF's HTTPConduit to hand the response callback to a background thread, which is what exposes the race - a single isolated request usually completes fast enough on the calling thread to win the race and hide the bug).
> Reproduced without CXF: a route with .delay(300).asyncDelayed() behind async=true/no executorRef, serviced via an embedded Undertow container (camel-servlet's own test harness), returns HTTP 200 with an *empty body* every time, since AsyncContext.complete() fires before the delayed continuation writes the response.
> The sibling code path used when executorRef IS configured (doAsyncExecution()) already does this correctly - it chains AsyncContext.complete() onto promise.whenComplete(...). This bug predates the 4.21/4.22 upgrade: it was introduced by CAMEL-11731 (Camel 3.7, 2020) when true async processing was added to doExecute()/tryAsyncProcess(), without updating the no-executorRef doServiceAsync() dispatch path to match.
> A fix and PR follow.
> _Claude Code on behalf of Croway_
--
This message was sent by Atlassian Jira
(v8.20.10#820010)