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

shashank updated CAMEL-25854:
-----------------------------
    Description: 
{{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: a setup whose rest-dsl consumers 
registered first and that has a plain endpoint overlapping their paths (for 
example {{matchOnUriPrefix=true}} on {{/}}) can notice it, so there is a note 
in the 4.24 upgrade guide. Undertow keeps a plain endpoint on {{/}} with 
{{matchOnUriPrefix=true}} as the default handler of its path handler and leaves 
no default handler when it is removed; {{CamelPathHandler}} now puts its 
default handler back then, so the rest-dsl consumers are served again once such 
an endpoint stops.

*Tests*: new {{RestUndertowAndPlainConsumerSamePortTest}} (a rest-dsl GET 
{{/users/{id}}} and a plain {{undertow:http://localhost:port/plain}} on the 
same port). On main all 4 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. {{testPlainConsumerOnRootTakesThePathsUntilItStops}} adds a plain 
endpoint on {{/}} with {{matchOnUriPrefix=true}} (it takes {{/users/123}}) and 
stops it; without the {{CamelPathHandler}} change {{/users/123}} then gets 404. 
With the fix all 4 pass, and the camel-undertow unit tests run 239 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._


  was:
{{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._



> 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
>            Priority: Major
>
> {{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: a setup whose rest-dsl 
> consumers registered first and that has a plain endpoint overlapping their 
> paths (for example {{matchOnUriPrefix=true}} on {{/}}) can notice it, so 
> there is a note in the 4.24 upgrade guide. Undertow keeps a plain endpoint on 
> {{/}} with {{matchOnUriPrefix=true}} as the default handler of its path 
> handler and leaves no default handler when it is removed; 
> {{CamelPathHandler}} now puts its default handler back then, so the rest-dsl 
> consumers are served again once such an endpoint stops.
> *Tests*: new {{RestUndertowAndPlainConsumerSamePortTest}} (a rest-dsl GET 
> {{/users/{id}}} and a plain {{undertow:http://localhost:port/plain}} on the 
> same port). On main all 4 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. {{testPlainConsumerOnRootTakesThePathsUntilItStops}} adds a plain 
> endpoint on {{/}} with {{matchOnUriPrefix=true}} (it takes {{/users/123}}) 
> and stops it; without the {{CamelPathHandler}} change {{/users/123}} then 
> gets 404. With the fix all 4 pass, and the camel-undertow unit tests run 239 
> 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