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

   # Description
   
   [CAMEL-26083](https://issues.apache.org/jira/browse/CAMEL-26083)
   
   `RestConsumerContextPathMatcher` keeps the compiled regex of each wildcard 
consumer path in a static map keyed only by the path string (`PATH_PATTERN`). 
`register(path)` puts the pattern, `unRegister(path)` removes it, and 
`matchWildCard` returns `false` for a path without a pattern (the last step of 
`matchBestPath`). The map is one per JVM, so every consumer with the same path 
shares one entry: the same wildcard path for two HTTP methods (`GET /files/*` 
and `POST /files/*`), on two ports, in two CamelContexts, or in two components. 
When one of them is stopped, suspended (netty-http with 
`send503whenSuspended=false`), removed or reloaded, its `unRegister` removes 
the pattern for all of them. Every other consumer of that path, still started, 
no longer matches: its requests get 404, or, when a broader wildcard consumer 
exists on the same server (`/*`), they are routed to that route instead.
   
   Callers: netty-http (`HttpServerMultiplexChannelHandler.addConsumer` / 
`removeConsumer`), servlet and jetty (`CamelServlet.connect` / `disconnect`), 
undertow (`RestRootHandler.addConsumer` / `removeConsumer`).
   
   Changes:
   - Each entry of the map now counts the consumers that registered the path. 
`register` adds one, `unRegister` takes one away and removes the pattern only 
when no consumer is left. Both use `ConcurrentHashMap.compute` / 
`computeIfPresent`, so a register and an unregister of the same path at the 
same time cannot lose a count.
   - I chose the count over compiling the pattern on demand in `matchWildCard`: 
on demand would also change `matchBestPath` for the callers that never register 
a path (camel-rest-openapi and camel-rest-postman). Today they only match a 
wildcard path when some other consumer of the JVM registered it; with the count 
they behave as before.
   - The count needs one `unRegister` per `register`. netty-http only 
unregisters a consumer it finds in its set (so the stop after a suspend does 
not unregister twice), and undertow removes in `UndertowConsumer.doStop` only. 
`CamelServlet` (servlet and jetty) did not pair them in two cases, and now 
unregisters only when `consumers.remove(endpointUri, consumer)` removed this 
consumer:
     - With an `httpRegistry` shared by servlets of different names (component 
option), `CamelHttpTransportServlet.connect` connects, and so registers, a 
consumer only in the servlet of its `servletName`, but 
`CamelServlet.disconnect` (not overridden) ran for every servlet of the 
registry and unregistered once per servlet. Stopping `GET /files/*` would then 
still remove the pattern of `POST /files/*`.
     - `HttpConsumer.doStart` sets `canConnected` before `connect`; a connect 
refused with "Duplicate request path" (it throws before it registers) was 
followed by a `disconnect` that unregistered a path it never registered, and 
also removed the map entry of the consumer that holds the endpoint. That entry 
now stays.
     A consumer that registers but is never disconnected (a servlet destroyed 
without disconnect, undertow's `unregisterHandler` returning early when its 
server is already gone) only keeps a pattern registered, which is harmless.
   - The javadoc of `register` says that each consumer unregisters its path 
once.
   - No option, generated file or upgrade guide change: nothing for users to 
change.
   - Backport: the same code is on camel-4.22.x, 4.18.x and 4.14.x (read, not 
run).
   
   Tests:
   - New `NettyHttpTwoRoutesSameWildcardPathStopOneRouteTest` 
(camel-netty-http), 2 tests, both fail on main:
     - `otherMethodOnSameWildcardPathStillServed`: `GET /files/*` and `POST 
/files/*` on one port; after `stopRoute("get")`, a POST gets 404 on main: 
`expected: <post> but was: <>`.
     - `sameWildcardPathOnOtherPortStillServed`: `/docs/*` on two ports plus a 
`/*` route on the second port; after stopping the first port's route, `/docs/b` 
on the second port goes to the `/*` route on main: `expected: <other-port> but 
was: <catch-all>`.
     Both requests are answered as expected before the stop in the same test.
   - New `ServletSharedHttpRegistryWildcardPathTest` (camel-servlet), 2 tests 
on a servlet component whose `httpRegistry` holds two 
`CamelHttpTransportServlet`s named `a` and `b`, without a servlet container 
(the matcher is checked with `matchBestPath`):
     - `stoppingOneConsumerKeepsTheWildcardPathOfTheOther`: `GET` and `POST` 
consumers of `/sharedregistry/files/*` on servlet `a`; after stopping the GET 
route the path must still match, and after stopping the POST route it must not.
     - `aConsumerThatFailsAsDuplicateDoesNotRemoveTheOther`: a second route 
with the same endpoint fails to start; the first consumer must still be in 
servlet `a` and its path must still match.
     Both fail on main and with only the matcher change: `the wildcard path of 
the POST consumer, still started, must match ==> expected: not <null>` and `the 
wildcard path of the first consumer, still started, must match ==> expected: 
not <null>`.
   - New test method `wildcardPathStaysRegisteredWhileAnotherConsumerUsesIt` in 
`RestConsumerContextPathMatcherTest` (camel-support): two registers, one 
unregister, the path still matches; after the second unregister it no longer 
matches. On main: `the wildcard path of the consumer that is still registered 
must match ==> expected: not <null>`.
   - With the fix: `RestConsumerContextPathMatcherTest` 13 run, 0 failures; 
unit tests of camel-servlet 112 run, 0 failures, 2 skipped, camel-http-common 
48 run, 0 failures, and camel-jetty 408 run, 0 failures, 23 skipped, 1 error 
(with the `CamelServlet` change; the error is `JettyXsltHttpTemplateTest`, 
`Unexpected end of file from server` when the xslt component loads its template 
from `http://0.0.0.0:<port>`, and it fails the same way here without the 
change); camel-netty-http 316 run, 0 failures, 8 skipped, and camel-undertow 
238 run, 0 failures, 1 skipped (with the matcher change; the `CamelServlet` 
change does not touch them). Built with camel-api, camel-util and camel-support 
in the reactor (and camel-netty for netty-http). The formatter and import-sort 
plugins ran.
   
   # 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) filed for 
the change (usually before you start working on it): 
[CAMEL-26083](https://issues.apache.org/jira/browse/CAMEL-26083).
   
   # 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 camel-support, camel-http-common, camel-netty-http, 
camel-servlet, camel-jetty and camel-undertow and ran their tests, 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