Skip to content

Commit

Permalink
Improve test coverage for vertx-websocket
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesnetherton authored and ppalaga committed May 9, 2023
1 parent ed13ab0 commit 7a56797
Show file tree
Hide file tree
Showing 19 changed files with 1,032 additions and 36 deletions.
30 changes: 27 additions & 3 deletions docs/modules/ROOT/pages/reference/extensions/vertx-websocket.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,46 @@ endif::[]
When you create a Vert.x WebSocket consumer (E.g with `from("vertx-websocket")`), the host and port configuration in the URI are redundant since the WebSocket will always be hosted on
the Quarkus HTTP server.

The configuration of the consumer can be simplified to only include the resource path of the WebSocket (prefixed with two forward slashes `//`). For example.
The configuration of the consumer can be simplified to only include the resource path of the WebSocket. For example.

[source,java]
----
from("vertx-websocket:///my-websocket-path")
from("vertx-websocket:/my-websocket-path")
.setBody().constant("Hello World");
----

Or alternatively, you can refer to the full host & port configuration for the Quarkus HTTP server.

[source,java]
----
from("vertx-websocket:localhost:{{quarkus.http.port}}/my-websocket-path")
from("vertx-websocket:{{quarkus.http.host}}:{{quarkus.http.port}}/my-websocket-path")
.setBody().constant("Hello World");
----

[id="extensions-vertx-websocket-usage-vert-x-websocket-producers"]
=== Vert.x WebSocket producers

Similar to above, if you want to produce messages to the internal Vert.x WebSocket consumer, then you can omit the host and port from the endpoint URI.

[source,java]
----
from("vertx-websocket:/my-websocket-path")
.log("Got body: ${body}");
from("direct:sendToWebSocket")
.log("vertx-websocket:/my-websocket-path");
----

Or alternatively, you can refer to the full host & port configuration for the Quarkus HTTP server.

[source,java]
----
from("direct:sendToWebSocket")
.log("vertx-websocket:{{quarkus.http.host}}:{{quarkus.http.port}}/my-websocket-path");
----

When producing messages to an external WebSocket server, then you must provide the host name and port (if required).


[id="extensions-vertx-websocket-additional-camel-quarkus-configuration"]
== Additional Camel Quarkus configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.vertx.deployment.VertxBuildItem;
import io.quarkus.vertx.http.deployment.VertxWebRouterBuildItem;
import io.quarkus.vertx.http.runtime.HttpConfiguration;
import org.apache.camel.component.vertx.websocket.VertxWebsocketComponent;
import org.apache.camel.quarkus.component.vertx.websocket.VertxWebsocketRecorder;
import org.apache.camel.quarkus.core.deployment.spi.CamelRuntimeBeanBuildItem;
Expand All @@ -37,9 +39,14 @@ FeatureBuildItem feature() {

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
CamelRuntimeBeanBuildItem configureVertxWebsocketComponent(VertxBuildItem vertx, VertxWebRouterBuildItem router,
CamelRuntimeBeanBuildItem configureVertxWebsocketComponent(
VertxBuildItem vertx,
VertxWebRouterBuildItem router,
LaunchModeBuildItem launchMode,
HttpConfiguration httpConfig,
VertxWebsocketRecorder recorder) {
return new CamelRuntimeBeanBuildItem("vertx-websocket", VertxWebsocketComponent.class.getName(),
recorder.createVertxWebsocketComponent(vertx.getVertx(), router.getHttpRouter()));
recorder.createVertxWebsocketComponent(vertx.getVertx(), router.getHttpRouter(), launchMode.getLaunchMode(),
httpConfig));
}
}
29 changes: 26 additions & 3 deletions extensions/vertx-websocket/runtime/src/main/doc/usage.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,41 @@
When you create a Vert.x WebSocket consumer (E.g with `from("vertx-websocket")`), the host and port configuration in the URI are redundant since the WebSocket will always be hosted on
the Quarkus HTTP server.

The configuration of the consumer can be simplified to only include the resource path of the WebSocket (prefixed with two forward slashes `//`). For example.
The configuration of the consumer can be simplified to only include the resource path of the WebSocket. For example.

[source,java]
----
from("vertx-websocket:///my-websocket-path")
from("vertx-websocket:/my-websocket-path")
.setBody().constant("Hello World");
----

Or alternatively, you can refer to the full host & port configuration for the Quarkus HTTP server.

[source,java]
----
from("vertx-websocket:localhost:{{quarkus.http.port}}/my-websocket-path")
from("vertx-websocket:{{quarkus.http.host}}:{{quarkus.http.port}}/my-websocket-path")
.setBody().constant("Hello World");
----

=== Vert.x WebSocket producers

Similar to above, if you want to produce messages to the internal Vert.x WebSocket consumer, then you can omit the host and port from the endpoint URI.

[source,java]
----
from("vertx-websocket:/my-websocket-path")
.log("Got body: ${body}");
from("direct:sendToWebSocket")
.log("vertx-websocket:/my-websocket-path");
----

Or alternatively, you can refer to the full host & port configuration for the Quarkus HTTP server.

[source,java]
----
from("direct:sendToWebSocket")
.log("vertx-websocket:{{quarkus.http.host}}:{{quarkus.http.port}}/my-websocket-path");
----

When producing messages to an external WebSocket server, then you must provide the host name and port (if required).
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,67 @@
*/
package org.apache.camel.quarkus.component.vertx.websocket;

import java.net.URI;
import java.util.concurrent.ExecutionException;

import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.RuntimeValue;
import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.vertx.http.runtime.CertificateConfig;
import io.quarkus.vertx.http.runtime.HttpConfiguration;
import io.quarkus.vertx.http.runtime.ServerSslConfig;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.WebSocketConnectOptions;
import io.vertx.ext.web.Router;
import org.apache.camel.CamelContext;
import org.apache.camel.component.vertx.websocket.VertxWebsocketComponent;
import org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration;
import org.apache.camel.component.vertx.websocket.VertxWebsocketConstants;
import org.apache.camel.component.vertx.websocket.VertxWebsocketEndpoint;
import org.apache.camel.component.vertx.websocket.VertxWebsocketHost;
import org.apache.camel.component.vertx.websocket.VertxWebsocketHostConfiguration;
import org.apache.camel.component.vertx.websocket.VertxWebsocketHostKey;
import org.apache.camel.spi.annotations.Component;
import org.apache.camel.util.ObjectHelper;

@Recorder
public class VertxWebsocketRecorder {
private static volatile int PORT;
private static volatile String HOST;

public RuntimeValue<VertxWebsocketComponent> createVertxWebsocketComponent(
RuntimeValue<Vertx> vertx,
RuntimeValue<Router> router,
LaunchMode launchMode,
HttpConfiguration httpConfig) {

boolean sslEnabled = false;
int httpPort = httpConfig.determinePort(launchMode);
int httpsPort = httpConfig.determineSslPort(launchMode);

ServerSslConfig ssl = httpConfig.ssl;
if (ssl != null) {
CertificateConfig certificate = ssl.certificate;
if (certificate != null) {
if (certificate.files.isPresent() && certificate.keyFiles.isPresent()) {
sslEnabled = true;
}

if (certificate.keyStoreFile.isPresent() && certificate.keyStorePassword.isPresent()) {
sslEnabled = true;
}
}
}

HOST = httpConfig.host;
PORT = sslEnabled ? httpsPort : httpPort;

public RuntimeValue<VertxWebsocketComponent> createVertxWebsocketComponent(RuntimeValue<Vertx> vertx,
RuntimeValue<Router> router) {
QuarkusVertxWebsocketComponent component = new QuarkusVertxWebsocketComponent();
component.setVertx(vertx.getValue());
component.setRouter(router.getValue());
component.setDefaultHost(HOST);
component.setDefaultPort(PORT);
return new RuntimeValue<>(component);
}

Expand All @@ -47,6 +87,29 @@ protected VertxWebsocketHost createVertxWebsocketHost(VertxWebsocketHostConfigur
VertxWebsocketHostKey hostKey) {
return new QuarkusVertxWebsocketHost(getCamelContext(), hostConfiguration, hostKey);
}

@Override
protected VertxWebsocketEndpoint createEndpointInstance(String uri, VertxWebsocketConfiguration configuration) {
return new QuarkusVertxWebsocketEndpoint(uri, this, configuration);
}
}

public static final class QuarkusVertxWebsocketEndpoint extends VertxWebsocketEndpoint {
public QuarkusVertxWebsocketEndpoint(String uri, VertxWebsocketComponent component,
VertxWebsocketConfiguration configuration) {
super(uri, component, configuration);
}

@Override
public WebSocketConnectOptions getWebSocketConnectOptions(HttpClientOptions options) {
WebSocketConnectOptions connectOptions = super.getWebSocketConnectOptions(options);
URI uri = URI.create(getEndpointUri().replaceFirst("wss?:/*", ""));
if (ObjectHelper.isNotEmpty(uri.getHost()) && uri.getPort() == -1) {
connectOptions.setPort(connectOptions.isSsl() ? VertxWebsocketConstants.DEFAULT_VERTX_CLIENT_WSS_PORT
: VertxWebsocketConstants.DEFAULT_VERTX_CLIENT_WS_PORT);
}
return connectOptions;
}
}

static final class QuarkusVertxWebsocketHost extends VertxWebsocketHost {
Expand All @@ -59,5 +122,20 @@ public QuarkusVertxWebsocketHost(CamelContext camelContext, VertxWebsocketHostCo
public void start() throws InterruptedException, ExecutionException {
// Noop as quarkus-vertx-http handles the server lifecycle
}

@Override
public int getPort() {
return PORT;
}

@Override
public boolean isManagedHost(String host) {
return HOST.equals(host);
}

@Override
public boolean isManagedPort(int port) {
return port == getPort();
}
}
}
15 changes: 15 additions & 0 deletions integration-tests/vertx-websocket/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Generating server SSL certificates and trust store

1. Generate the certificate keypair
When prompted for the 'Common Name', use localhost. The other prompts can be skipped.

```
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout src/main/resources/server-key.pem -out src/main/resources/server-cert.pem
```

2. Generate the PKCS12 trust store
```
cat src/main/resources/server-key.pem src/main/resources/server-cert.pem | openssl pkcs12 -export -out src/test/resources/truststore.p12 -passout pass:s3cr3t
```
54 changes: 50 additions & 4 deletions integration-tests/vertx-websocket/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,41 @@
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-vertx-websocket</artifactId>
</dependency>

<!-- test dependencies -->
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-direct</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-seda</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets</artifactId>
<scope>test</scope>
</dependency>

<!-- test dependencies -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


<profiles>
<profile>
<id>native</id>
Expand Down Expand Up @@ -87,6 +107,32 @@
</activation>
<dependencies>
<!-- The following dependencies guarantee that this module is built after them. You can update them by running `mvn process-resources -Pformat -N` from the source tree root directory -->
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-direct-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-seda-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-vertx-websocket-deployment</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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.vertx.websocket.it;

import java.util.concurrent.LinkedBlockingDeque;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.websocket.OnMessage;
import jakarta.websocket.server.ServerEndpoint;

@ServerEndpoint("/managed/by/quarkus/websockets")
@ApplicationScoped
public class QuarkusWebsocketResource {

private static final LinkedBlockingDeque<String> MESSAGES = new LinkedBlockingDeque<>();

@OnMessage
public void onMessage(String message) {
MESSAGES.add("Received message: " + message);
}

public static LinkedBlockingDeque<String> getMessages() {
return MESSAGES;
}

public static void clearMessages() {
MESSAGES.clear();
}
}
Loading

0 comments on commit 7a56797

Please sign in to comment.