[ 
https://issues.apache.org/jira/browse/CAMEL-24811?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Federico Mariani reassigned CAMEL-24811:
----------------------------------------

    Assignee: 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)

Reply via email to