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)

Reply via email to