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)