shashank created CAMEL-25854:
--------------------------------
Summary: camel-undertow - rest-dsl and plain undertow consumers on
the same port: only the kind that registers first is served, and stopping the
last consumer of one kind stops the server for the other
Key: CAMEL-25854
URL: https://issues.apache.org/jira/browse/CAMEL-25854
Project: Camel
Issue Type: Bug
Components: camel-undertow
Reporter: shashank
Assignee: shashank
{{DefaultUndertowHost}} (main 20cdb309f7d0) runs one Undertow server per host
and port for the consumers of the component. It has two handlers:
{{RestRootHandler}} for the rest-dsl consumers and {{CamelRootHandler}} for the
plain {{undertow:}} endpoints. The server is given only one of them, chosen
from the first consumer that registers on the port:
{code:java}
// use the rest handler as its a rest consumer
serverHandler = consumer != null && consumer.isRest() ? restHandler :
rootHandler;
{code}
A later consumer of the other kind is still added to its own handler
({{registerHandler}}, the last if/else), but the server never calls that
handler. Its route starts normally and logs nothing, and every request to it
gets 404 "No matching path found". For example, take
{{restConfiguration().component("undertow").port(8080)}} with
{{rest("/users").get("{id}")}} next to
{{from("undertow:http://0.0.0.0:8080/health")}}. Only the kind whose route
starts first is served. A plain route usually starts first, because routes
built from the rest-dsl are added after the plain routes of the same builder,
so the REST API is the part that answers 404.
{{unregisterHandler}} also checks only one kind: it stops the server when the
kind of the consumer being removed has no consumer left
({{restHandler.consumers() <= 0}} or {{rootHandler.isEmpty()}}), even if
consumers of the other kind are still registered. Stopping or reloading the
last REST route therefore stops the server under the started plain undertow
routes, and the reverse also happens. Their clients get connection refused
until those routes are restarted.
*Fix*: the server always calls the handler of the plain endpoints. Its default
handler (used when no plain endpoint matches) is now the rest-dsl handler
instead of {{NotFoundHandler}}. The rest-dsl handler answers 404 "No matching
path found" (the same response as {{NotFoundHandler}}), or 405 when none of its
consumers matches either. The server stops only when neither kind has a
consumer left. When a path is served by both a plain endpoint and a rest-dsl
consumer, the plain endpoint takes it. Before the fix only one kind was ever
served, so no working setup changes.
*Tests*: new {{RestUndertowAndPlainConsumerSamePortTest}} (a rest-dsl GET
{{/users/{id}}} and a plain {{undertow:http://localhost:port/plain}} on the
same port). On main all 3 tests fail. {{testBothKindsAreServed}} and
{{testPlainConsumerAddedAfterRestConsumer}} fail with {{/users/123 ==>
expected: <200> but was: <404>}}. {{testServerKeepsServingWhenOneKindStops}}
stops the REST route and then calls {{/plain}}, which fails with {{expected:
<200> but was: <null>}} because the server was stopped and the connection was
refused. With the fix all 3 pass, and the camel-undertow unit tests run 238
tests with 0 failures (1 skipped).
*How it was found*: a review of shared per-port server state in the HTTP
consumer components, after the per-owner resource fixes CAMEL-25849 and
CAMEL-25850. The JUnit test above confirmed it.
*Affected*: main, camel-4.23.x, camel-4.22.x and camel-4.18.x all have the same
handler choice and the same stop condition in {{DefaultUndertowHost}}. This was
checked by reading the code, not by running it.
_Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)