allthingssecurity opened a new pull request, #29039:
URL: https://github.com/apache/camel/pull/29039

   # Description
   
   [CAMEL-26460](https://issues.apache.org/jira/browse/CAMEL-26460)
   
   When one route consumes a plain undertow path template (such as 
`/orders/{id}`) and another route the static path of its base path (`/orders`, 
exact or with `matchOnUriPrefix=true`), `CamelRootHandler` serves both through 
one `CamelPathTemplateHandler`, registered as the *prefix* path of the base 
path, with the static path as its default handler. Several things went wrong, 
depending on which route started first:
   
   ```java
   
from("undertow:http://0.0.0.0:8080/orders/{id}";).routeId("order").setBody(simple("order
 ${header.id}"));
   
from("undertow:http://0.0.0.0:8080/orders";).routeId("orders").setBody(constant("all
 orders"));
   ```
   
   | Case | On main |
   |---|---|
   | template route first, then the static route; `GET /orders` | answered by 
the template route, `order ` (empty `id`): Undertow's `PathTemplateMatcher` 
matches the empty rest of the base path against `/{id}` |
   | `/items?matchOnUriPrefix=true` first, then `/items/{id}`; `GET /items` (or 
`/items/`) | answered by the template route, `item ` |
   | `/?matchOnUriPrefix=true` and `/{id}`; `GET /` | answered by the template 
route, with an empty `id` |
   | `/items?matchOnUriPrefix=true` first, then `/items/{id}`; stop the 
template route; `GET /items/a/b` | 404: removing the last template removed the 
prefix path of `/items`, which served the `matchOnUriPrefix` route too 
(`CamelPathTemplateHandler.isEmpty()` only counts templates) |
   | `/items/{id}` declared before `/items?matchOnUriPrefix=true` | the 
CamelContext does not start: `Duplicate handlers on a path '/items'` (the other 
order works) |
   | static exact route first, then the template; stop the template route | the 
static route still runs, but its requests get "connection refused" when these 
were the only paths of the server: `CamelPathHandler` keeps one map for exact 
and prefix paths, the template's prefix path had replaced the exact path in it, 
so after the removal the map was empty and `DefaultUndertowHost` stopped the 
server |
   | template route first, then the static route; stop the template route | 
`GET /orders` gets 404: the static path lived only in the default handler of 
the template handler, which went with the last template (and when these were 
the only paths of the server, it stopped) |
   
   When the static exact route starts first, `GET /orders` while both run is 
fine on main: Undertow's exact path wins over the prefix path. rest-dsl 
consumers go through `RestRootHandler` (CAMEL-25854) and are not affected. Path 
templates of plain undertow endpoints are not described on the component page, 
but they work and the variable comes in as a header.
   
   ## The fix
   
   - `CamelPathHandler` keeps the exact and the prefix paths in two maps; 
`getHandler(path)` returns the prefix handler (the template handler when there 
is one), else the exact one; a new `getExactHandler(path)`; `isEmpty()` counts 
both. Both maps are changed and read under its lock (`getHandler` and `isEmpty` 
did not take it before).
   - `CamelPathTemplateHandler.handleRequest`: a request for the base path 
itself goes to the static handler of the base path when it has one: a relative 
path of `""` (the prefix match of `/orders`) or `/` (`/orders/`, and the root 
path, as Undertow serves the prefix path `/` as its default handler, which 
keeps the whole request path as the relative path). With no static path it goes 
to the templates as before (so a template-only `/orders/{id}` still answers 
`/orders` with an empty `id`, unchanged).
   - `CamelRootHandler.add`:
     - a static exact path added when the templates of its base path came first 
is also registered as an exact path, as when it comes first. That registration 
is what tells a static exact path from a static prefix one later. (It is not 
added when the template handler's default already serves a static prefix route: 
that route keeps the prefix semantics, as before.)
     - a `matchOnUriPrefix` route added when the templates of its base path 
came first becomes the static path of the base path (the template handler's 
default) when the base path has no static path yet, as when it comes first; it 
was refused. A prefix route added when the base path already has a static route 
(exact or prefix) and templates is still refused, as on main.
   - `CamelRootHandler.remove`:
     - last template removed: the template handler goes; a static prefix path 
it served is registered again as the prefix path, an exact one stays registered 
as it is;
     - static path removed while templates remain: its exact registration goes, 
the templates stay;
     - static path without templates: the registration its handler has is 
removed (exact or prefix), instead of trusting the `matchOnUriPrefix` flag of 
the route being removed (routes of both kinds on one path share the handler of 
the first one; main left Undertow's exact path behind when the prefix route was 
the last to stop);
     - removing a template that is not registered returns (it threw a 
`NullPointerException`, or a `ClassCastException` when the base path has only a 
static path).
   
   The locking order is unchanged: `CamelRootHandler.add/remove` hold its lock 
(and `DefaultUndertowHost` holds its own around them), and the two maps change 
only under `CamelPathHandler`'s lock. Replacing the template handler by the 
static prefix handler is one `put`.
   
   Not changed (on main as well):
   - removing a plain consumer that never started (a route with 
`autoStartup=false`, or one whose start failed) still unregisters its path, so 
it decrements the count of a running consumer of the same path (it can drop the 
static path, or stop the server). `UndertowConsumer.doStop` always unregisters; 
CAMEL-26083 fixed this pairing for rest-dsl consumers only. Follow-up: 
CAMEL-26463.
   - an exact and a prefix static route of the same path still share the 
handler of the first one, registered with its kind.
   - path templates of plain endpoints are still not described on the component 
page; I can add a paragraph there in a follow-up if you want.
   
   Backport: main only (the routing change has an upgrade-guide entry, and it 
builds on the fallback handler of CAMEL-25854, which is on main only).
   
   ## Behaviour changes
   
   - `GET /orders` (the base path, also `/orders/`, and `/` for templates of 
the root path) now reaches the static route instead of the template route in 
the cases above. When the static route has `httpMethodRestrict`, such a request 
with another method now gets 405 from it instead of reaching the template 
route. After the static route stopped while the template route runs, `GET 
/orders` reaches the template route (empty `id`) as a template route on its own 
does (main answered 500 in that sequence: the exact path of the stopped static 
route was left behind).
   - A `matchOnUriPrefix` route declared after the template route of its base 
path now starts.
   - Stopping the template route keeps the static path served and its server 
running.
   
   Upgrade guide (4.24): one entry, next to the CAMEL-25854 entry about plain 
and rest-dsl consumers on one port, for the routing of the base path, the 405, 
and the prefix route that now starts. There is nothing to configure back: the 
static route is the one for that path (a route that wants the template to 
answer the base path does not also consume the static path).
   
   ## Tests
   
   New `UndertowTemplatePathAndStaticPathTest` (camel-undertow, JUnit 
assertions as in the module, 8 tests, real Undertow servers on three ports; 
each step asserts the HTTP code and body; no sleeps):
   - template first: `GET /orders`, `/orders/` and `/orders/7` while both run;
   - template first: stop the template route (static path served, template path 
404), start it again;
   - stop the static route then the template route, start the static route 
again (control, passes on main);
   - prefix route first: `GET /items`, `/items/`, `/items/a/b`, `/items/7`;
   - prefix route first: stop the template route (`/items/a/b`, `/items/7`, 
`/items` reach the prefix route), start it again;
   - template first, then a static route with `httpMethodRestrict=POST`: `GET 
/m` gets 405, `POST /m` reaches the static route, `GET /m/7` the template;
   - a `matchOnUriPrefix` route of `/` and a template `/{id}` on the third 
server: `GET /`, `/7`, `/a/b`;
   - static route first on a server of its own: stop the template route, the 
static path is still served; stop and start the static route.
   
   New `UndertowPrefixPathAfterTemplatePathTest` (1 test, its own class as the 
CamelContext does not start on main): the template route declared before the 
`matchOnUriPrefix` route: both start, `GET /goods`, `/goods/a/b`, `/goods/7`; 
the prefix route stops and starts again while the template route runs; the 
template route stops.
   
   `CamelRootHandlerTest` +5: both start orders, removing the template keeps 
the root handler non-empty and removing both leaves it empty; static removed 
first then template (control); prefix + template; removing a template that is 
not registered; an exact and a prefix route sharing one handler leave nothing 
behind.
   
   Negative controls (the main versions of the files compiled over the branch, 
run in plain JVMs):
   - all three files from main: 12 of the 16 tests of the three classes fail (7 
of the 8 route tests, the prefix-after-template test, 4 of the 7 handler tests; 
the controls and the two existing handler tests pass);
   - only `CamelPathTemplateHandler` from main: 3 fail (the base path of the 
prefix route, the root path, the prefix route declared after the template);
   - only `CamelRootHandler` from main: 8 fail (2 route tests, the 
prefix-after-template test, 5 handler tests).
   
   - The new and changed classes 20 times in one JVM: 320 tests, 0 failures.
   - camel-undertow, all tests of the module: 269, 0 failures, 1 skipped.
   - Formatter and import-sort: no changes.
   
   # Target
   
   - [x] I checked that the commit is targeting the correct branch (Camel 4 
uses the `main` branch)
   
   # Tracking
   - [x] If this is a large change, bug fix, or code improvement, I checked 
there is a [JIRA issue](https://issues.apache.org/jira/browse/CAMEL-26460) 
filed for the change (usually before you start working on it): 
[CAMEL-26460](https://issues.apache.org/jira/browse/CAMEL-26460).
   
   # Apache Camel coding standards and style
   
   - [x] I checked that each commit in the pull request has a meaningful 
subject line and body.
   - [ ] I have run `mvn clean install -DskipTests` locally from root folder 
and I have committed all auto-generated changes.
     (I built the core modules and camel-undertow and ran the tests above, 
including the formatter and import-sort plugins. No generated file changes. I 
did not run the full root build.)
   
   # AI-assisted contributions
   
   - [x] If this PR includes AI-generated code, commits have proper 
co-authorship attribution (e.g., `Co-authored-by` trailers) and the PR 
description identifies the AI tool used.
     This PR was prepared with Claude Code (Claude Opus 5.5). The commit 
carries a `Co-Authored-By` trailer.
   
   _Claude Code on behalf of allthingssecurity_
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to