Andrea Cosentino created CAMEL-24788:
----------------------------------------
Summary: camel-vertx-websocket: a shared websocket host is
unregistered when the first of its consumers stops
Key: CAMEL-24788
URL: https://issues.apache.org/jira/browse/CAMEL-24788
Project: Camel
Issue Type: Bug
Components: camel-vertx-websocket
Reporter: Andrea Cosentino
Assignee: Andrea Cosentino
h3. Summary
All {{vertx-websocket}} consumers bound to the same host and port share one
{{VertxWebsocketHost}}, but the host is dropped from the component registry as
soon as the *first* of those consumers stops. Every consumer that stops
afterwards is silently ignored, its Vert.x route is never removed, and the HTTP
server it runs on is never stopped.
h3. Detail
{{VertxWebsocketHostKey}} is {{\{host, port\}}} only (lines 25-26), so
{{connectConsumer}} shares a single host across every path on that port:
{code:java}
VertxWebsocketHost host = vertxHostRegistry.computeIfAbsent(hostKey, key -> {
... });
host.connect(consumer);
{code}
({{VertxWebsocketComponent}} line 167)
but {{disconnectConsumer}} removes that shared host on the first stop:
{code:java}
VertxWebsocketHost vertxWebsocketHost = vertxHostRegistry.remove(hostKey);
if (vertxWebsocketHost != null) {
vertxWebsocketHost.disconnect(configuration.getWebsocketURI().getPath());
}
{code}
({{VertxWebsocketComponent}} line 205)
With two consumers, {{/a}} and {{/b}}, on the same port:
* stopping the {{/a}} route removes the shared host from the registry, then
{{disconnect("/a")}} removes only that route. {{VertxWebsocketHost.disconnect}}
(line 166) stops the server only once its own {{routeRegistry}} is empty, and
{{/b}} is still in it, so the server keeps running - correctly, at that point;
* stopping the {{/b}} route then finds *nothing* in the registry: {{remove}}
returns null, the null guard skips, and route {{/b}} is never removed. **The
server carries on listening after every Camel route is stopped.**
* starting a consumer on that host and port again builds a *second*
{{VertxWebsocketHost}} with a new Router and calls {{start()}} on a port the
first server still holds.
{{VertxWebsocketComponent.doStop}} (line 157) closes Vert.x only when Camel
created it, so when the application supplies its own {{Vertx}} or {{Router}} -
the usual arrangement under Spring Boot and Quarkus - nothing ever tears the
server down.
h3. Two smaller problems in the same lifecycle
* {{VertxWebsocketHost}} line 54 keeps {{routeRegistry}} in a plain
{{HashMap}}, mutated by {{connect}} and {{disconnect}}, i.e. on route start and
stop. The field right below it is a {{CopyOnWriteArrayList}} carrying an
explicit {{// thread-safe}} comment, so the difference looks accidental rather
than considered. Route start/stop is concurrent under the supervising route
controller.
* {{VertxWebsocketHost.disconnect}} lines 164-165 do
{{routeRegistry.remove(path)}} and then {{route.remove()}} with no null check,
so an unknown or repeated path throws a {{NullPointerException}}.
Found by a source audit of {{components/camel-vertx}} at be3dd651866f.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)