shashank created CAMEL-25546:
--------------------------------

             Summary: camel-whatsapp - the producer never completes the 
exchange when the HTTP request fails without a response (connection refused, 
connect timeout, DNS failure), so the caller waits forever
                 Key: CAMEL-25546
                 URL: https://issues.apache.org/jira/browse/CAMEL-25546
             Project: Camel
          Issue Type: Bug
            Reporter: shashank
            Assignee: shashank


{{WhatsAppProducer.process}} is asynchronous: it calls 
{{service.sendMessage(exchange, callback, message)}} and returns {{false}}. 
{{WhatsAppServiceRestAPIAdapter.OutgoingMessageHandler.sendMessage}} (lines 
197-222 at main 9432ba16d026) sends the request and completes the exchange like 
this:

{code:java}
CompletableFuture<HttpResponse<String>> asyncResponse
        = httpClient.sendAsync(httpRequestBuilder.build(), 
HttpResponse.BodyHandlers.ofString());

asyncResponse.thenAccept(response -> {
    try {
        ...
    } finally {
        callback.done(false);
    }
});
{code}

{{thenAccept}} runs its function only when the future completes with a 
response. When the request fails without one (the connection is refused or 
reset, the host cannot be resolved, the connect timeout of 10 seconds expires), 
the future completes exceptionally, the function is skipped, and 
{{callback.done}} is never called. No exception is set either. So the exchange 
is never completed: a caller that waits for it ({{ProducerTemplate.send}}, a 
{{direct}} or HTTP consumer in front of the route) blocks forever, the exchange 
stays in flight, and a graceful shutdown waits for its timeout. Nothing is 
logged.

h3. Reproduction

New {{WhatsAppProducerFailureTest}}: the endpoint uses 
{{baseUri=http://localhost:<port>}} with a port nothing listens on, and the 
test sends asynchronously and waits 30 seconds for the exchange. On main, twice:
{noformat}
WhatsAppProducerFailureTest.failsTheExchangeWhenTheConnectionFails:81 ยป Timeout
{noformat}
Two more tests use a local HTTP server (an error response fails the exchange 
with the status and body, a 200 response returns a {{MessageResponse}}); they 
pass before and after and pin the unchanged paths.

h3. Proposed fix

Use {{whenComplete}}: for a failure without a response the exchange fails with 
that exception (for example {{java.net.ConnectException}}) and the callback is 
called; for a response the handling is the same as before (the two {{throw}} 
statements after {{exchange.setException}} are dropped, nothing observed them). 
No upgrade guide note: the only change is that an exchange that waited forever 
now fails. camel-whatsapp with the fix: 11 tests, 0 failures; 
{{WhatsAppServiceIT}} needs a WhatsApp account and was not run.

Found by a review of the producers that complete an exchange from a 
{{CompletableFuture}}, with a small TLA+ model (send, outcome of the request, 
callback): on main TLC reports a deadlock after 3 states when the request fails 
without a response (the exchange is never completed); with the fix every 
behaviour completes the exchange. Confirmed with the real producer.

Affected: main, camel-4.22.x, camel-4.18.x, camel-4.14.x (same code, GitHub 
contents API).

Duplicate check (2026-10-11): JIRA text "whatsapp" (11: CAMEL-21363 response 
parsing, CAMEL-23762 webhook signature, none about a hanging exchange); GitHub 
open pull requests for camel-whatsapp: none. No open pull request changes 
{{WhatsAppServiceRestAPIAdapter}}.

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to