[
https://issues.apache.org/jira/browse/CAMEL-24811?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Work on CAMEL-24811 started by Federico Mariani.
------------------------------------------------
> 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
>
> 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)