Croway commented on code in PR #9264:
URL: https://github.com/apache/camel-quarkus/pull/9264#discussion_r4157525426


##########
extensions-jvm/cli-connector/deployment/src/main/java/org/apache/camel/quarkus/component/cli/connector/deployment/CliConnectorProcessor.java:
##########
@@ -51,6 +59,29 @@ CamelBeanBuildItem 
camelBeanBuildItem(CamelCliConnectorRecorder recorder) {
                 recorder.createCliConnectorFactory(Version.getVersion()));
     }
 
+    /**
+     * The WebSocket transport uses the WebSockets Next client when the 
application has it, the JDK client otherwise.
+     */
+    @BuildStep
+    void webSocketClient(
+            Capabilities capabilities,
+            BuildProducer<AdditionalBeanBuildItem> additionalBeans,
+            BuildProducer<RunTimeConfigurationDefaultBuildItem> 
configDefaults) {
+        if (capabilities.isPresent(Capability.WEBSOCKETS_NEXT)) {
+            // the back-pressure of the WebSockets Next client (since Quarkus 
3.40) fetches one frame per message
+            // received, so every message received in several frames (over 64 
KB) leaves fewer frames to fetch, until
+            // the connection stops reading: no back-pressure by default, as 
before Quarkus 3.40
+            // TODO: Remove when 
https://github.com/quarkusio/quarkus/issues/57079 is fixed
+            configDefaults.produce(new RunTimeConfigurationDefaultBuildItem(
+                    "quarkus.websockets-next.client.max-pending-messages", 
"0"));

Review Comment:
   _Claude Code on behalf of Croway_
   
   Agreed. Fixed in fa4f77ac3c by dropping WebSockets Next (see the thread on 
the Vert.x client): the extension no longer touches any 
`quarkus.websockets-next` setting, so the application's own clients keep their 
back-pressure.



##########
extensions-jvm/cli-connector/runtime/src/main/doc/usage.adoc:
##########
@@ -0,0 +1,38 @@
+=== WebSocket transport
+
+With `camel.cli.transport=websocket`, the application dials out to a developer 
tool over a WebSocket instead of
+exchanging files with the Camel CLI (see the
+xref:{cq-camel-components}:others:cli-connector.adoc[CLI Connector] 
documentation for the protocol and the options).
+
+[source,properties]
+----
+camel.cli.transport = websocket
+camel.cli.websocket.url = ws://127.0.0.1:8000/connect?executionId=run-1
+----
+
+The tool gets full control of the application: the transport refuses to start 
with the `prod` profile
+(`camel.main.profile=prod`).

Review Comment:
   _Claude Code on behalf of Croway_
   
   Right, and that was already the case with the JDK client: 
`CamelMainDevModeProcessor` only sets the Camel profile in dev mode. Fixed in 
fa4f77ac3c: `QuarkusLocalCliConnector.createTransport()` refuses `websocket` 
when the Quarkus `prod` profile is active, with a message like Camel's. 
`CliConnectorWebSocketProdProfileTest` (`QuarkusProdModeTest`) checks that a 
packaged application refuses it and exits. The docs now scope the example to 
`%dev` and say that a packaged application, e.g. on a remote dev cluster, needs 
another profile. I went with the profile rather than `LaunchMode.NORMAL` so 
that this remote dev cluster case stays possible.



##########
extensions-jvm/cli-connector/runtime/src/main/java/org/apache/camel/quarkus/component/cli/connector/QuarkusCliWebSocketClient.java:
##########
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.cli.connector;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.OptionalInt;
+import java.util.concurrent.CompletionStage;
+
+import io.quarkus.arc.Arc;
+import io.quarkus.arc.InstanceHandle;
+import io.quarkus.websockets.next.BasicWebSocketConnector;
+import io.quarkus.websockets.next.BasicWebSocketConnector.ExecutionModel;
+import io.quarkus.websockets.next.CloseReason;
+import io.quarkus.websockets.next.WebSocketClientConnection;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.http.UpgradeRejectedException;
+import jakarta.annotation.PostConstruct;
+import jakarta.inject.Inject;
+import org.apache.camel.cli.connector.CliWebSocketClient;
+import org.apache.camel.cli.connector.CliWebSocketHandshakeException;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.jboss.logging.Logger;
+
+/**
+ * The {@link CliWebSocketClient} of the Camel CLI connector WebSocket 
transport, with the Quarkus WebSockets Next
+ * client: registered as a bean only when the application has {@code 
quarkus-websockets-next}.
+ * <p/>
+ * The callbacks run on the Vert.x event loop: they only hand over to the 
transport, which parses and sends on its own
+ * threads.
+ */
+public class QuarkusCliWebSocketClient implements CliWebSocketClient {
+
+    static final String NAME = "quarkus-websockets-next";
+    private static final Logger LOG = 
Logger.getLogger(QuarkusCliWebSocketClient.class);
+    private static final String MAX_MESSAGE_SIZE_KEY = 
"quarkus.websockets-next.client.max-message-size";
+    private static final int GOING_AWAY = 1001;
+
+    @Inject
+    CamelCliConnectorRunTimeConfig config;
+
+    @PostConstruct
+    void checkMaxMessageSize() {
+        // set globally, it replaces the size of every connector (see 
customizeOptions)
+        OptionalInt size = 
ConfigProvider.getConfig().getOptionalValue(MAX_MESSAGE_SIZE_KEY, Integer.class)
+                .map(OptionalInt::of).orElse(OptionalInt.empty());
+        if (size.isPresent() && size.getAsInt() < MAX_MESSAGE_SIZE) {
+            LOG.warnf("%s=%d: the Camel CLI connector cannot receive actions 
larger than that (up to %d supported)",
+                    MAX_MESSAGE_SIZE_KEY, size.getAsInt(), MAX_MESSAGE_SIZE);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public CompletionStage<Channel> connect(URI url, Map<String, String> 
headers, Listener listener) {
+        // a new connector for each connection, released when it is closed
+        InstanceHandle<BasicWebSocketConnector> handle = 
Arc.container().instance(BasicWebSocketConnector.class);
+        String path = url.getRawPath() != null ? url.getRawPath() : "";
+        int last = path.lastIndexOf('/') + 1;
+        BasicWebSocketConnector connector = handle.get()
+                .baseUri(baseUri(url, path.substring(0, last)))
+                // the last segment as the path: WebSockets Next adds a '/' 
after the base uri path otherwise
+                // TODO: Remove when 
https://github.com/quarkusio/quarkus/issues/57081 is fixed
+                .path(path.substring(last))
+                .executionModel(ExecutionModel.NON_BLOCKING)
+                // not the frame size: Vert.x also splits the messages it 
sends into frames of that size, and servers
+                // commonly refuse frames over 64 KB
+                .customizeOptions((connect, client) -> 
client.setMaxMessageSize(MAX_MESSAGE_SIZE))

Review Comment:
   _Claude Code on behalf of Croway_
   
   Good catch, it would have stayed stuck. Fixed in fa4f77ac3c: 10 s TCP 
connect timeout in the client options and a 10 s handshake deadline (a Vert.x 
timer, closing the client), as the JDK client. Not 
`WebSocketConnectOptions.setTimeout`: in Vert.x 4.5 it also sets an idle 
timeout. `reconnectsWhenTheToolDoesNotAnswerTheHandshake` uses a tool that 
never answers the upgrade, and `staysConnected` checks that an open connection 
outlives the timeouts.



##########
extensions-jvm/cli-connector/runtime/src/main/java/org/apache/camel/quarkus/component/cli/connector/QuarkusCliWebSocketClient.java:
##########
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.cli.connector;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.OptionalInt;
+import java.util.concurrent.CompletionStage;
+
+import io.quarkus.arc.Arc;
+import io.quarkus.arc.InstanceHandle;
+import io.quarkus.websockets.next.BasicWebSocketConnector;
+import io.quarkus.websockets.next.BasicWebSocketConnector.ExecutionModel;
+import io.quarkus.websockets.next.CloseReason;
+import io.quarkus.websockets.next.WebSocketClientConnection;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.http.UpgradeRejectedException;
+import jakarta.annotation.PostConstruct;
+import jakarta.inject.Inject;
+import org.apache.camel.cli.connector.CliWebSocketClient;
+import org.apache.camel.cli.connector.CliWebSocketHandshakeException;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.jboss.logging.Logger;
+
+/**
+ * The {@link CliWebSocketClient} of the Camel CLI connector WebSocket 
transport, with the Quarkus WebSockets Next
+ * client: registered as a bean only when the application has {@code 
quarkus-websockets-next}.
+ * <p/>
+ * The callbacks run on the Vert.x event loop: they only hand over to the 
transport, which parses and sends on its own
+ * threads.
+ */
+public class QuarkusCliWebSocketClient implements CliWebSocketClient {
+
+    static final String NAME = "quarkus-websockets-next";
+    private static final Logger LOG = 
Logger.getLogger(QuarkusCliWebSocketClient.class);
+    private static final String MAX_MESSAGE_SIZE_KEY = 
"quarkus.websockets-next.client.max-message-size";
+    private static final int GOING_AWAY = 1001;
+
+    @Inject
+    CamelCliConnectorRunTimeConfig config;
+
+    @PostConstruct
+    void checkMaxMessageSize() {
+        // set globally, it replaces the size of every connector (see 
customizeOptions)
+        OptionalInt size = 
ConfigProvider.getConfig().getOptionalValue(MAX_MESSAGE_SIZE_KEY, Integer.class)
+                .map(OptionalInt::of).orElse(OptionalInt.empty());
+        if (size.isPresent() && size.getAsInt() < MAX_MESSAGE_SIZE) {
+            LOG.warnf("%s=%d: the Camel CLI connector cannot receive actions 
larger than that (up to %d supported)",
+                    MAX_MESSAGE_SIZE_KEY, size.getAsInt(), MAX_MESSAGE_SIZE);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public CompletionStage<Channel> connect(URI url, Map<String, String> 
headers, Listener listener) {
+        // a new connector for each connection, released when it is closed
+        InstanceHandle<BasicWebSocketConnector> handle = 
Arc.container().instance(BasicWebSocketConnector.class);
+        String path = url.getRawPath() != null ? url.getRawPath() : "";
+        int last = path.lastIndexOf('/') + 1;
+        BasicWebSocketConnector connector = handle.get()
+                .baseUri(baseUri(url, path.substring(0, last)))
+                // the last segment as the path: WebSockets Next adds a '/' 
after the base uri path otherwise
+                // TODO: Remove when 
https://github.com/quarkusio/quarkus/issues/57081 is fixed
+                .path(path.substring(last))
+                .executionModel(ExecutionModel.NON_BLOCKING)
+                // not the frame size: Vert.x also splits the messages it 
sends into frames of that size, and servers
+                // commonly refuse frames over 64 KB

Review Comment:
   _Claude Code on behalf of Croway_
   
   Not acceptable, you're right: tools sending one frame per message would 
break. With the plain Vert.x client the two directions can differ, fixed in 
fa4f77ac3c: incoming frames and messages are accepted up to 3 x 16 M bytes (so 
a 16 M chars message fits whatever its characters, which also addresses bytes 
vs chars), and outgoing messages are split by the client into frames of 16 K 
chars (at most 48 KB in UTF-8, never splitting a surrogate pair) written with 
`writeFrame`. `receivesLargeActionsInASingleFrame` sends a 1 MB action in a 
single frame.



##########
extensions-jvm/cli-connector/runtime/src/main/java/org/apache/camel/quarkus/component/cli/connector/QuarkusCliWebSocketClient.java:
##########
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.cli.connector;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.OptionalInt;
+import java.util.concurrent.CompletionStage;
+
+import io.quarkus.arc.Arc;
+import io.quarkus.arc.InstanceHandle;
+import io.quarkus.websockets.next.BasicWebSocketConnector;
+import io.quarkus.websockets.next.BasicWebSocketConnector.ExecutionModel;
+import io.quarkus.websockets.next.CloseReason;
+import io.quarkus.websockets.next.WebSocketClientConnection;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.http.UpgradeRejectedException;
+import jakarta.annotation.PostConstruct;
+import jakarta.inject.Inject;
+import org.apache.camel.cli.connector.CliWebSocketClient;
+import org.apache.camel.cli.connector.CliWebSocketHandshakeException;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.jboss.logging.Logger;
+
+/**
+ * The {@link CliWebSocketClient} of the Camel CLI connector WebSocket 
transport, with the Quarkus WebSockets Next
+ * client: registered as a bean only when the application has {@code 
quarkus-websockets-next}.
+ * <p/>
+ * The callbacks run on the Vert.x event loop: they only hand over to the 
transport, which parses and sends on its own
+ * threads.
+ */
+public class QuarkusCliWebSocketClient implements CliWebSocketClient {

Review Comment:
   _Claude Code on behalf of Croway_
   
   Yes, thanks for the suggestion: done in fa4f77ac3c, and it is now the 
default client on Camel Quarkus (`camel.cli.websocket.client=jdk` still selects 
the JDK client). `VertxCliWebSocketClient` uses a `WebSocketClient` per 
connection from the application's `Vertx`: per-client options, the close status 
code, the raw request URI, and `abort()` closes the client (with a 1 s closing 
timeout, so the socket is gone right away). TLS comes from the TLS registry 
(`quarkus.camel.cli.websocket.tls-configuration-name`), otherwise the JVM 
default trust store. The optional dependency, the capability check and the 
by-name bean are gone. The three Quarkus issues stay open upstream but no 
longer affect this extension. Soak test (240 s, with disruptions) green with 
the Vert.x client.



##########
extensions-jvm/cli-connector/runtime/src/main/java/org/apache/camel/quarkus/component/cli/connector/QuarkusCliWebSocketClient.java:
##########
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.cli.connector;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.OptionalInt;
+import java.util.concurrent.CompletionStage;
+
+import io.quarkus.arc.Arc;
+import io.quarkus.arc.InstanceHandle;
+import io.quarkus.websockets.next.BasicWebSocketConnector;
+import io.quarkus.websockets.next.BasicWebSocketConnector.ExecutionModel;
+import io.quarkus.websockets.next.CloseReason;
+import io.quarkus.websockets.next.WebSocketClientConnection;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.http.UpgradeRejectedException;
+import jakarta.annotation.PostConstruct;
+import jakarta.inject.Inject;
+import org.apache.camel.cli.connector.CliWebSocketClient;
+import org.apache.camel.cli.connector.CliWebSocketHandshakeException;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.jboss.logging.Logger;
+
+/**
+ * The {@link CliWebSocketClient} of the Camel CLI connector WebSocket 
transport, with the Quarkus WebSockets Next
+ * client: registered as a bean only when the application has {@code 
quarkus-websockets-next}.
+ * <p/>
+ * The callbacks run on the Vert.x event loop: they only hand over to the 
transport, which parses and sends on its own
+ * threads.
+ */
+public class QuarkusCliWebSocketClient implements CliWebSocketClient {
+
+    static final String NAME = "quarkus-websockets-next";
+    private static final Logger LOG = 
Logger.getLogger(QuarkusCliWebSocketClient.class);
+    private static final String MAX_MESSAGE_SIZE_KEY = 
"quarkus.websockets-next.client.max-message-size";
+    private static final int GOING_AWAY = 1001;
+
+    @Inject
+    CamelCliConnectorRunTimeConfig config;
+
+    @PostConstruct
+    void checkMaxMessageSize() {
+        // set globally, it replaces the size of every connector (see 
customizeOptions)
+        OptionalInt size = 
ConfigProvider.getConfig().getOptionalValue(MAX_MESSAGE_SIZE_KEY, Integer.class)
+                .map(OptionalInt::of).orElse(OptionalInt.empty());

Review Comment:
   _Claude Code on behalf of Croway_
   
   Gone with the WebSockets Next client in fa4f77ac3c.



##########
integration-tests-jvm/cli-connector-websockets-next/pom.xml:
##########
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.camel.quarkus</groupId>
+        <artifactId>camel-quarkus-build-parent-it</artifactId>
+        <version>4.0.0-SNAPSHOT</version>
+        <relativePath>../../poms/build-parent-it/pom.xml</relativePath>
+    </parent>
+
+    
<artifactId>camel-quarkus-integration-test-cli-connector-websockets-next</artifactId>
+    <name>Camel Quarkus :: Integration Tests :: CLI Connector :: WebSockets 
Next</name>
+    <description>Integration tests for Camel Quarkus CLI Connector 
extension</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-cli-connector</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-websockets-next</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-management</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-direct</artifactId>
+        </dependency>
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.rest-assured</groupId>
+            <artifactId>rest-assured</artifactId>
+            <scope>test</scope>
+        </dependency>

Review Comment:
   _Claude Code on behalf of Croway_
   
   Removed in fa4f77ac3c (the `cli-connector-websockets-next` module is gone 
too).



-- 
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