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]
