io.quarkus.security
diff --git a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/WebSocketsClientRuntimeConfig.java b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/WebSocketsClientRuntimeConfig.java
index ecaf0bb169d0d..b79a9de857853 100644
--- a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/WebSocketsClientRuntimeConfig.java
+++ b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/WebSocketsClientRuntimeConfig.java
@@ -48,4 +48,14 @@ public interface WebSocketsClientRuntimeConfig {
@WithDefault("close")
UnhandledFailureStrategy unhandledFailureStrategy();
+ /**
+ * The name of the TLS configuration to use.
+ *
+ * If a name is configured, it uses the configuration from {@code quarkus.tls..*}
+ * If a name is configured, but no TLS configuration is found with that name then an error will be thrown.
+ *
+ * The default TLS configuration is not used by default.
+ */
+ Optional tlsConfigurationName();
+
}
diff --git a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/BasicWebSocketConnectorImpl.java b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/BasicWebSocketConnectorImpl.java
index 46eca5bd0b36e..d47df577837d3 100644
--- a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/BasicWebSocketConnectorImpl.java
+++ b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/BasicWebSocketConnectorImpl.java
@@ -14,6 +14,7 @@
import org.jboss.logging.Logger;
+import io.quarkus.tls.TlsConfigurationRegistry;
import io.quarkus.virtual.threads.VirtualThreadsRecorder;
import io.quarkus.websockets.next.BasicWebSocketConnector;
import io.quarkus.websockets.next.CloseReason;
@@ -27,7 +28,6 @@
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.WebSocketClient;
-import io.vertx.core.http.WebSocketClientOptions;
import io.vertx.core.http.WebSocketConnectOptions;
@Typed(BasicWebSocketConnector.class)
@@ -54,8 +54,8 @@ public class BasicWebSocketConnectorImpl extends WebSocketConnectorBase errorHandler;
BasicWebSocketConnectorImpl(Vertx vertx, Codecs codecs, ClientConnectionManager connectionManager,
- WebSocketsClientRuntimeConfig config) {
- super(vertx, codecs, connectionManager, config);
+ WebSocketsClientRuntimeConfig config, TlsConfigurationRegistry tlsConfigurationRegistry) {
+ super(vertx, codecs, connectionManager, config, tlsConfigurationRegistry);
}
@Override
@@ -115,18 +115,7 @@ public Uni connect() {
// Currently we create a new client for each connection
// The client is closed when the connection is closed
// TODO would it make sense to share clients?
- WebSocketClientOptions clientOptions = new WebSocketClientOptions();
- if (config.offerPerMessageCompression()) {
- clientOptions.setTryUsePerMessageCompression(true);
- if (config.compressionLevel().isPresent()) {
- clientOptions.setCompressionLevel(config.compressionLevel().getAsInt());
- }
- }
- if (config.maxMessageSize().isPresent()) {
- clientOptions.setMaxMessageSize(config.maxMessageSize().getAsInt());
- }
-
- WebSocketClient client = vertx.createWebSocketClient();
+ WebSocketClient client = vertx.createWebSocketClient(populateClientOptions());
WebSocketConnectOptions connectOptions = new WebSocketConnectOptions()
.setSsl(baseUri.getScheme().equals("https"))
diff --git a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorBase.java b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorBase.java
index 728850f3083fd..ee098d6a43d16 100644
--- a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorBase.java
+++ b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorBase.java
@@ -9,13 +9,19 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import io.quarkus.tls.TlsConfiguration;
+import io.quarkus.tls.TlsConfigurationRegistry;
import io.quarkus.websockets.next.WebSocketClientException;
import io.quarkus.websockets.next.WebSocketsClientRuntimeConfig;
import io.vertx.core.Vertx;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.http.WebSocketClientOptions;
+import io.vertx.core.net.SSLOptions;
abstract class WebSocketConnectorBase> {
@@ -45,8 +51,11 @@ abstract class WebSocketConnectorBase>
protected final WebSocketsClientRuntimeConfig config;
+ protected final TlsConfigurationRegistry tlsConfigurationRegistry;
+
WebSocketConnectorBase(Vertx vertx, Codecs codecs,
- ClientConnectionManager connectionManager, WebSocketsClientRuntimeConfig config) {
+ ClientConnectionManager connectionManager, WebSocketsClientRuntimeConfig config,
+ TlsConfigurationRegistry tlsConfigurationRegistry) {
this.headers = new HashMap<>();
this.subprotocols = new HashSet<>();
this.pathParams = new HashMap<>();
@@ -54,6 +63,7 @@ abstract class WebSocketConnectorBase>
this.codecs = codecs;
this.connectionManager = connectionManager;
this.config = config;
+ this.tlsConfigurationRegistry = tlsConfigurationRegistry;
this.path = "";
this.pathParamNames = Set.of();
}
@@ -129,4 +139,56 @@ String replacePathParameters(String path) {
return path.startsWith("/") ? sb.toString() : "/" + sb.toString();
}
+ protected WebSocketClientOptions populateClientOptions() {
+ WebSocketClientOptions clientOptions = new WebSocketClientOptions();
+ if (config.offerPerMessageCompression()) {
+ clientOptions.setTryUsePerMessageCompression(true);
+ if (config.compressionLevel().isPresent()) {
+ clientOptions.setCompressionLevel(config.compressionLevel().getAsInt());
+ }
+ }
+ if (config.maxMessageSize().isPresent()) {
+ clientOptions.setMaxMessageSize(config.maxMessageSize().getAsInt());
+ }
+
+ Optional maybeTlsConfiguration = TlsConfiguration.from(tlsConfigurationRegistry,
+ config.tlsConfigurationName());
+ if (maybeTlsConfiguration.isPresent()) {
+ clientOptions.setSsl(true);
+
+ TlsConfiguration tlsConfiguration = maybeTlsConfiguration.get();
+ if (tlsConfiguration.getTrustStoreOptions() != null) {
+ clientOptions.setTrustOptions(tlsConfiguration.getTrustStoreOptions());
+ }
+
+ // For mTLS:
+ if (tlsConfiguration.getKeyStoreOptions() != null) {
+ clientOptions.setKeyCertOptions(tlsConfiguration.getKeyStoreOptions());
+ }
+
+ if (tlsConfiguration.isTrustAll()) {
+ clientOptions.setTrustAll(true);
+ }
+ if (tlsConfiguration.getHostnameVerificationAlgorithm().isPresent()
+ && tlsConfiguration.getHostnameVerificationAlgorithm().get().equals("NONE")) {
+ // Only disable hostname verification if the algorithm is explicitly set to NONE
+ clientOptions.setVerifyHost(false);
+ }
+
+ SSLOptions sslOptions = tlsConfiguration.getSSLOptions();
+ if (sslOptions != null) {
+ clientOptions.setSslHandshakeTimeout(sslOptions.getSslHandshakeTimeout());
+ clientOptions.setSslHandshakeTimeoutUnit(sslOptions.getSslHandshakeTimeoutUnit());
+ for (String suite : sslOptions.getEnabledCipherSuites()) {
+ clientOptions.addEnabledCipherSuite(suite);
+ }
+ for (Buffer buffer : sslOptions.getCrlValues()) {
+ clientOptions.addCrlValue(buffer);
+ }
+ clientOptions.setEnabledSecureTransportProtocols(sslOptions.getEnabledSecureTransportProtocols());
+ clientOptions.setUseAlpn(sslOptions.isUseAlpn());
+ }
+ }
+ return clientOptions;
+ }
}
diff --git a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorImpl.java b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorImpl.java
index ceaeab285dd80..be39e41799564 100644
--- a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorImpl.java
+++ b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorImpl.java
@@ -16,6 +16,7 @@
import org.jboss.logging.Logger;
import io.quarkus.arc.Arc;
+import io.quarkus.tls.TlsConfigurationRegistry;
import io.quarkus.websockets.next.WebSocketClientConnection;
import io.quarkus.websockets.next.WebSocketClientException;
import io.quarkus.websockets.next.WebSocketConnector;
@@ -26,7 +27,6 @@
import io.smallrye.mutiny.vertx.UniHelper;
import io.vertx.core.Vertx;
import io.vertx.core.http.WebSocketClient;
-import io.vertx.core.http.WebSocketClientOptions;
import io.vertx.core.http.WebSocketConnectOptions;
@Typed(WebSocketConnector.class)
@@ -41,8 +41,9 @@ public class WebSocketConnectorImpl extends WebSocketConnectorBase connect() {
// Currently we create a new client for each connection
// The client is closed when the connection is closed
// TODO would it make sense to share clients?
- WebSocketClientOptions clientOptions = new WebSocketClientOptions();
- if (config.offerPerMessageCompression()) {
- clientOptions.setTryUsePerMessageCompression(true);
- if (config.compressionLevel().isPresent()) {
- clientOptions.setCompressionLevel(config.compressionLevel().getAsInt());
- }
- }
- if (config.maxMessageSize().isPresent()) {
- clientOptions.setMaxMessageSize(config.maxMessageSize().getAsInt());
- }
-
- WebSocketClient client = vertx.createWebSocketClient();
+ WebSocketClient client = vertx.createWebSocketClient(populateClientOptions());
StringBuilder serverEndpoint = new StringBuilder();
if (baseUri != null) {