From 8bdaf6ea87f1a1c41ed8c7cc51fb8debaa1765b9 Mon Sep 17 00:00:00 2001 From: Tihomir Mateev Date: Thu, 12 Dec 2024 16:57:13 +0200 Subject: [PATCH] Handle UTF-8 characters in command arguments --- .../io/lettuce/core/protocol/CommandArgs.java | 27 ++++++------------- .../core/AuthenticationIntegrationTests.java | 15 +++++++++++ .../core/RedisCommandBuilderUnitTests.java | 11 ++++++++ .../commands/AclCommandIntegrationTests.java | 4 +-- 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/lettuce/core/protocol/CommandArgs.java b/src/main/java/io/lettuce/core/protocol/CommandArgs.java index da5f897852..addc78a8db 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandArgs.java +++ b/src/main/java/io/lettuce/core/protocol/CommandArgs.java @@ -20,6 +20,7 @@ package io.lettuce.core.protocol; import java.nio.ByteBuffer; +import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Base64; @@ -53,7 +54,7 @@ */ public class CommandArgs { - static final byte[] CRLF = "\r\n".getBytes(StandardCharsets.US_ASCII); + static final byte[] CRLF = "\r\n".getBytes(StandardCharsets.UTF_8); protected final RedisCodec codec; @@ -609,16 +610,9 @@ void encode(ByteBuf target) { } static void writeString(ByteBuf target, String value) { + byte[] output = value.getBytes(StandardCharsets.UTF_8); - target.writeByte('$'); - - IntegerArgument.writeInteger(target, value.length()); - target.writeBytes(CRLF); - - for (int i = 0; i < value.length(); i++) { - target.writeByte((byte) value.charAt(i)); - } - target.writeBytes(CRLF); + BytesArgument.writeBytes(target, output); } @Override @@ -646,16 +640,11 @@ void encode(ByteBuf target) { } static void writeString(ByteBuf target, char[] value) { + final ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(CharBuffer.wrap(value)); + final byte[] output = new byte[byteBuffer.remaining()]; + byteBuffer.get(output); - target.writeByte('$'); - - IntegerArgument.writeInteger(target, value.length); - target.writeBytes(CRLF); - - for (char c : value) { - target.writeByte((byte) c); - } - target.writeBytes(CRLF); + BytesArgument.writeBytes(target, output); } @Override diff --git a/src/test/java/io/lettuce/core/AuthenticationIntegrationTests.java b/src/test/java/io/lettuce/core/AuthenticationIntegrationTests.java index 864a2103b0..77d3f8edd8 100644 --- a/src/test/java/io/lettuce/core/AuthenticationIntegrationTests.java +++ b/src/test/java/io/lettuce/core/AuthenticationIntegrationTests.java @@ -37,6 +37,9 @@ void setUp(StatefulRedisConnection connection) { connection.sync().dispatch(CommandType.ACL, new StatusOutput<>(StringCodec.UTF8), new CommandArgs<>(StringCodec.UTF8).add("SETUSER").add("john").add("on").add(">foobared").add("-@all")); + + connection.sync().dispatch(CommandType.ACL, new StatusOutput<>(StringCodec.UTF8), + new CommandArgs<>(StringCodec.UTF8).add("SETUSER").add("日本語").add("on").add(">日本語").add("+@all")); } @Test @@ -71,4 +74,16 @@ void ownCredentialProvider(RedisClient client) { }); } + @Test + @Inject + void authAsJapanese(RedisClient client) { + + RedisURI uri = RedisURI.builder().withHost(TestSettings.host()).withPort(TestSettings.port()) + .withAuthentication("日本語", "日本語".toCharArray()).build(); + + StatefulRedisConnection connection = client.connect(uri); + assertThat(connection.sync().ping()).isEqualTo("PONG"); + connection.close(); + } + } diff --git a/src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java index d8e71cffb3..2d4f1cf405 100644 --- a/src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java +++ b/src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java @@ -32,6 +32,17 @@ class RedisCommandBuilderUnitTests { RedisCommandBuilder sut = new RedisCommandBuilder<>(StringCodec.UTF8); + @Test() + void shouldCorrectlyConstructHello() { + + Command command = sut.hello(3, "日本語", "日本語".toCharArray(), null); + ByteBuf buf = Unpooled.directBuffer(); + command.encode(buf); + + assertThat(buf.toString(StandardCharsets.UTF_8)).isEqualTo("*5\r\n" + "$5\r\n" + "HELLO\r\n" + "$1\r\n" + "3\r\n" + + "$4\r\n" + "AUTH\r\n" + "$9\r\n" + "日本語\r\n" + "$9\r\n" + "日本語\r\n"); + } + @Test void shouldCorrectlyConstructXreadgroup() { diff --git a/src/test/java/io/lettuce/core/commands/AclCommandIntegrationTests.java b/src/test/java/io/lettuce/core/commands/AclCommandIntegrationTests.java index 7fbeb7cf9f..4d154dc0f7 100644 --- a/src/test/java/io/lettuce/core/commands/AclCommandIntegrationTests.java +++ b/src/test/java/io/lettuce/core/commands/AclCommandIntegrationTests.java @@ -131,7 +131,7 @@ void aclLog() { @Test void aclList() { - assertThat(redis.aclList()).hasSize(1).first().asString().contains("user default"); + assertThat(redis.aclList()).hasSize(2).first().asString().contains("user default"); } @Test @@ -161,7 +161,7 @@ void aclSetuserWithCategories() { @Test void aclUsers() { - assertThat(redis.aclUsers()).hasSize(1).first().isEqualTo("default"); + assertThat(redis.aclUsers()).hasSize(2).first().isEqualTo("default"); } @Test