Skip to content

Commit 9af0f74

Browse files
authored
Merge pull request #43454 from mcruzdev/issue-43167
Add client parameter when creating Vertx Redis client
2 parents e2c1d31 + d6264ad commit 9af0f74

File tree

5 files changed

+191
-7
lines changed

5 files changed

+191
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package io.quarkus.redis.deployment.client;
2+
3+
import jakarta.inject.Inject;
4+
5+
import org.assertj.core.api.Assertions;
6+
import org.jboss.shrinkwrap.api.ShrinkWrap;
7+
import org.jboss.shrinkwrap.api.spec.JavaArchive;
8+
import org.junit.jupiter.api.Test;
9+
import org.junit.jupiter.api.extension.RegisterExtension;
10+
11+
import io.quarkus.redis.client.RedisClientName;
12+
import io.quarkus.redis.datasource.RedisDataSource;
13+
import io.quarkus.test.QuarkusUnitTest;
14+
import io.quarkus.test.common.QuarkusTestResource;
15+
import io.vertx.mutiny.redis.client.Response;
16+
import io.vertx.redis.client.Command;
17+
18+
@QuarkusTestResource(RedisTestResource.class)
19+
public class RedisConfigureClientNameTest {
20+
21+
@RegisterExtension
22+
static final QuarkusUnitTest unitTest = new QuarkusUnitTest()
23+
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class))
24+
.overrideConfigKey("quarkus.redis.my-redis.hosts", "${quarkus.redis.tr}/1")
25+
.overrideConfigKey("quarkus.redis.my-redis.configure-client-name", "true")
26+
.overrideConfigKey("quarkus.redis.my-redis.client-name", "perfect-client-name")
27+
.overrideConfigKey("quarkus.redis.no-configure.hosts", "${quarkus.redis.tr}/2")
28+
.overrideConfigKey("quarkus.redis.no-configure.client-name", "i-am-not-applicable")
29+
.overrideConfigKey("quarkus.redis.no-configure.configure-client-name", "false")
30+
.overrideConfigKey("quarkus.redis.from-annotation.configure-client-name", "true")
31+
.overrideConfigKey("quarkus.redis.from-annotation.hosts", "${quarkus.redis.tr}/3");
32+
33+
@Inject
34+
@RedisClientName("my-redis")
35+
RedisDataSource myRedis;
36+
37+
@Inject
38+
@RedisClientName("no-configure")
39+
RedisDataSource noConfigure;
40+
41+
@Inject
42+
@RedisClientName("from-annotation")
43+
RedisDataSource fromAnnotation;
44+
45+
@Test
46+
void shouldConfigureClientNameCorrectly() {
47+
Response executed = myRedis.execute(Command.CLIENT, "GETNAME");
48+
Assertions.assertThat(executed).isNotNull();
49+
Assertions.assertThat(executed.toString()).isEqualTo("perfect-client-name");
50+
}
51+
52+
@Test
53+
void shouldConfigureFromRedisClientNameAnnotation() {
54+
Response executed = fromAnnotation.execute(Command.CLIENT, "GETNAME");
55+
Assertions.assertThat(executed).isNotNull();
56+
Assertions.assertThat(executed.toString()).isEqualTo("from-annotation");
57+
}
58+
59+
@Test
60+
void shouldNotConfigureClientName() {
61+
Response executed = noConfigure.execute(Command.CLIENT, "GETNAME");
62+
Assertions.assertThat(executed).isNull();
63+
}
64+
}

extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/RedisClientRecorder.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
public class RedisClientRecorder {
3939

4040
// Split client and DS recorders
41-
4241
private final RedisConfig config;
4342
private static final Map<String, RedisClientAndApi> clients = new HashMap<>();
4443
private static final Map<String, ReactiveRedisDataSourceImpl> dataSources = new HashMap<>();

extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configurePfxTrustOptions;
99

1010
import java.net.URI;
11+
import java.net.URISyntaxException;
1112
import java.util.ArrayList;
1213
import java.util.List;
1314
import java.util.Optional;
1415
import java.util.Set;
16+
import java.util.function.Consumer;
1517

1618
import org.jboss.logging.Logger;
1719

@@ -41,6 +43,7 @@
4143
public class VertxRedisClientFactory {
4244

4345
public static final String DEFAULT_CLIENT = "<default>";
46+
public static String NON_RESERVED_URI_PATTERN = "[^a-zA-Z0-9\\-_.~]";
4447

4548
private static final Logger LOGGER = Logger.getLogger(VertxRedisClientFactory.class);
4649

@@ -51,19 +54,30 @@ private VertxRedisClientFactory() {
5154
public static Redis create(String name, Vertx vertx, RedisClientConfig config, TlsConfigurationRegistry tlsRegistry) {
5255
RedisOptions options = new RedisOptions();
5356

57+
Consumer<Set<URI>> configureOptions = new Consumer<Set<URI>>() {
58+
@Override
59+
public void accept(Set<URI> uris) {
60+
for (URI uri : uris) {
61+
if (config.configureClientName()) {
62+
String client = config.clientName().orElse(name);
63+
String newURI = applyClientQueryParam(client, uri);
64+
options.addConnectionString(newURI);
65+
} else {
66+
options.addConnectionString(uri.toString().trim());
67+
}
68+
}
69+
}
70+
};
71+
5472
List<URI> hosts = new ArrayList<>();
5573
if (config.hosts().isPresent()) {
5674
hosts.addAll(config.hosts().get());
57-
for (URI uri : config.hosts().get()) {
58-
options.addConnectionString(uri.toString().trim());
59-
}
75+
configureOptions.accept(config.hosts().get());
6076
} else if (config.hostsProviderName().isPresent()) {
6177
RedisHostsProvider hostsProvider = findProvider(config.hostsProviderName().get());
6278
Set<URI> computedHosts = hostsProvider.getHosts();
6379
hosts.addAll(computedHosts);
64-
for (URI uri : computedHosts) {
65-
options.addConnectionString(uri.toString());
66-
}
80+
configureOptions.accept(computedHosts);
6781
} else {
6882
throw new ConfigurationException("Redis host not configured - you must either configure 'quarkus.redis.hosts` or" +
6983
" 'quarkus.redis.host-provider-name' and have a bean providing the hosts programmatically.");
@@ -109,6 +123,48 @@ public static Redis create(String name, Vertx vertx, RedisClientConfig config, T
109123
return Redis.createClient(vertx, options);
110124
}
111125

126+
public static String applyClientQueryParam(String client, URI uri) {
127+
128+
if (client.matches(".*" + NON_RESERVED_URI_PATTERN + ".*")) {
129+
LOGGER.warn("The client query parameter contains reserved URI characters. " +
130+
"This may result in an incorrect client name after URI encoding.");
131+
}
132+
133+
String query = uri.getQuery();
134+
135+
boolean hasClient = hasRedisClientParameter(query);
136+
137+
if (hasClient) {
138+
LOGGER.warnf("Your host already has a client name. The client name %s will be disregarded.", client);
139+
return uri.toString().trim();
140+
}
141+
142+
query = query == null ? "client=" + client
143+
: uri.getQuery() + "&client=" + client;
144+
145+
try {
146+
return new URI(
147+
uri.getScheme(), uri.getAuthority(), uri.getPath(), query, uri.getFragment()).toString().trim();
148+
} catch (URISyntaxException e) {
149+
LOGGER.warnf("Was not possible to generate a new Redis URL with client query parameter, " +
150+
"the value is: %s", client);
151+
return uri.toString().trim();
152+
}
153+
}
154+
155+
private static boolean hasRedisClientParameter(String query) {
156+
if (query != null) {
157+
String[] pairs = query.split("&");
158+
for (String pair : pairs) {
159+
String[] keyValue = pair.split("=");
160+
if (keyValue.length == 2 && keyValue[0].equals("client")) {
161+
return true;
162+
}
163+
}
164+
}
165+
return false;
166+
}
167+
112168
private static void customize(String name, RedisOptions options) {
113169
if (Arc.container() != null) {
114170
List<InstanceHandle<RedisOptionsCustomizer>> customizers = Arc.container().listAll(RedisOptionsCustomizer.class);

extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/RedisClientConfig.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.Optional;
66
import java.util.Set;
77

8+
import io.quarkus.redis.client.RedisClientName;
89
import io.quarkus.runtime.annotations.ConfigDocDefault;
910
import io.quarkus.runtime.annotations.ConfigDocSection;
1011
import io.quarkus.runtime.annotations.ConfigGroup;
@@ -197,6 +198,29 @@ public interface RedisClientConfig {
197198
@ConfigDocSection
198199
TlsConfig tls();
199200

201+
/**
202+
* The client name used to identify the connection.
203+
* <p>
204+
* If the {@link RedisClientConfig#configureClientName()} is enabled, and this property is not set
205+
* it will attempt to extract the value from the {@link RedisClientName#value()} annotation.
206+
* <p>
207+
* If the {@link RedisClientConfig#configureClientName()} is enabled, both this property and the
208+
* {@link RedisClientName#value()} must adhere to the pattern '[a-zA-Z0-9\\-_.~]*'; if not,
209+
* this may result in an incorrect client name after URI encoding.
210+
*/
211+
Optional<String> clientName();
212+
213+
/**
214+
* Whether it should set the client name while connecting with Redis.
215+
* <p>
216+
* This is necessary because Redis only accepts {@code client=my-client-name} query parameter in version 6+.
217+
* <p>
218+
* This property can be used with {@link RedisClientConfig#clientName()} configuration.
219+
*
220+
*/
221+
@WithDefault("false")
222+
Boolean configureClientName();
223+
200224
/**
201225
* The name of the TLS configuration to use.
202226
* <p>
@@ -232,6 +256,8 @@ default String toDebugString() {
232256
", hashSlotCacheTtl=" + hashSlotCacheTtl() +
233257
", tcp=" + tcp() +
234258
", tls=" + tls() +
259+
", clientName=" + clientName() +
260+
", configureClientName=" + configureClientName() +
235261
'}';
236262
}
237263

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.quarkus.redis.runtime.client;
2+
3+
import java.net.URI;
4+
5+
import org.assertj.core.api.Assertions;
6+
import org.junit.jupiter.api.Test;
7+
8+
class VertxRedisClientFactoryTest {
9+
10+
@Test
11+
void shouldApplyQuery() {
12+
13+
String applied = VertxRedisClientFactory.applyClientQueryParam(
14+
"quarkus-app", URI.create("redis://localhost:6379"));
15+
Assertions.assertThat(applied).isEqualTo("redis://localhost:6379?client=quarkus-app");
16+
}
17+
18+
@Test
19+
void shouldNotApplyQuery() {
20+
String applied = VertxRedisClientFactory.applyClientQueryParam(
21+
"quarkus-app", URI.create("redis://localhost:6379?client=quarkiverse-app"));
22+
Assertions.assertThat(applied).isEqualTo("redis://localhost:6379?client=quarkiverse-app");
23+
}
24+
25+
@Test
26+
void shouldApplyWithReservedURICharacters() {
27+
String applied = VertxRedisClientFactory.applyClientQueryParam(
28+
"quarkus&%$ app", URI.create("redis://localhost:6379"));
29+
Assertions.assertThat(applied).isEqualTo("redis://localhost:6379?client=quarkus&%25$%20app");
30+
}
31+
32+
@Test
33+
void shouldApplyWithQueryParams() {
34+
String applied = VertxRedisClientFactory.applyClientQueryParam(
35+
"quarkus-app", URI.create("redis://localhost:6379?someQueryParam=123456789"));
36+
Assertions.assertThat(applied).isEqualTo("redis://localhost:6379?someQueryParam=123456789&client=quarkus-app");
37+
}
38+
39+
}

0 commit comments

Comments
 (0)