Skip to content

Commit

Permalink
Add TLS Registry configuration to WebSockets Next Client
Browse files Browse the repository at this point in the history
Resolves: quarkusio#41004
  • Loading branch information
geoand committed Jun 11, 2024
1 parent 169608b commit bd334f9
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 31 deletions.
4 changes: 4 additions & 0 deletions extensions/websockets-next/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-tls-registry</artifactId>
</dependency>
<!-- Quarkus Security API -->
<dependency>
<groupId>io.quarkus.security</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,14 @@ public interface WebSocketsClientRuntimeConfig {
@WithDefault("close")
UnhandledFailureStrategy unhandledFailureStrategy();

/**
* The name of the TLS configuration to use.
* <p>
* If a name is configured, it uses the configuration from {@code quarkus.tls.<name>.*}
* If a name is configured, but no TLS configuration is found with that name then an error will be thrown.
* <p>
* The default TLS configuration is <strong>not</strong> used by default.
*/
Optional<String> tlsConfigurationName();

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -54,8 +54,8 @@ public class BasicWebSocketConnectorImpl extends WebSocketConnectorBase<BasicWeb
private BiConsumer<WebSocketClientConnection, Throwable> 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
Expand Down Expand Up @@ -115,18 +115,7 @@ public Uni<WebSocketClientConnection> 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"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<THIS extends WebSocketConnectorBase<THIS>> {

Expand Down Expand Up @@ -45,15 +51,19 @@ abstract class WebSocketConnectorBase<THIS extends WebSocketConnectorBase<THIS>>

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<>();
this.vertx = vertx;
this.codecs = codecs;
this.connectionManager = connectionManager;
this.config = config;
this.tlsConfigurationRegistry = tlsConfigurationRegistry;
this.path = "";
this.pathParamNames = Set.of();
}
Expand Down Expand Up @@ -129,4 +139,58 @@ 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<TlsConfiguration> maybeTlsConfiguration = TlsConfiguration.from(tlsConfigurationRegistry,
config.tlsConfigurationName());
if (maybeTlsConfiguration.isPresent()) {
clientOptions.setSsl(true);

TlsConfiguration tlsConfiguration = maybeTlsConfiguration.get();
clientOptions.setTrustOptions(tlsConfiguration.getTrustStoreOptions());

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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -41,8 +41,9 @@ public class WebSocketConnectorImpl<CLIENT> extends WebSocketConnectorBase<WebSo
private final ClientEndpoint clientEndpoint;

WebSocketConnectorImpl(InjectionPoint injectionPoint, Codecs codecs, Vertx vertx, ClientConnectionManager connectionManager,
ClientEndpointsContext endpointsContext, WebSocketsClientRuntimeConfig config) {
super(vertx, codecs, connectionManager, config);
ClientEndpointsContext endpointsContext, WebSocketsClientRuntimeConfig config,
TlsConfigurationRegistry tlsConfigurationRegistry) {
super(vertx, codecs, connectionManager, config, tlsConfigurationRegistry);
this.clientEndpoint = Objects.requireNonNull(endpointsContext.endpoint(getEndpointClass(injectionPoint)));
setPath(clientEndpoint.path);
}
Expand All @@ -52,18 +53,7 @@ public Uni<WebSocketClientConnection> 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) {
Expand Down

0 comments on commit bd334f9

Please sign in to comment.