From 74fe6551c13ac12eaf30f09f06cb883efd128607 Mon Sep 17 00:00:00 2001 From: Daniel Norberg Date: Tue, 13 Feb 2024 15:50:58 +0100 Subject: [PATCH] redis-proxy: support echo command (#32296) Signed-off-by: Daniel Norberg --- changelogs/current.yaml | 3 +++ .../arch_overview/other_protocols/redis.rst | 8 +++++--- .../network/common/redis/supported_commands.h | 5 +++++ .../network/redis_proxy/command_splitter_impl.cc | 13 +++++++++++++ .../redis_proxy/command_splitter_impl_test.cc | 16 ++++++++++++++++ 5 files changed, 42 insertions(+), 3 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 62b3e12c9ffb..292d36bcdca6 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -170,5 +170,8 @@ new_features: Envoy will select the host with the fewest active requests from the entire host set rather than :ref:`choice_count ` random choices. +- area: redis + change: | + Added support for the ``ECHO`` command. deprecated: diff --git a/docs/root/intro/arch_overview/other_protocols/redis.rst b/docs/root/intro/arch_overview/other_protocols/redis.rst index 1b38fd8c887a..d7c4480528e6 100644 --- a/docs/root/intro/arch_overview/other_protocols/redis.rst +++ b/docs/root/intro/arch_overview/other_protocols/redis.rst @@ -127,13 +127,14 @@ Supported commands At the protocol level, pipelines are supported. Use pipelining wherever possible for the best performance. -At the command level, Envoy only supports commands that can be reliably hashed to a server. AUTH and PING +At the command level, Envoy only supports commands that can be reliably hashed to a server. AUTH, PING and ECHO are the only exceptions. AUTH is processed locally by Envoy if a downstream password has been configured, and no other commands will be processed until authentication is successful when a password has been configured. Envoy will transparently issue AUTH commands upon connecting to upstream servers, if upstream authentication passwords are configured for the cluster. Envoy responds to PING immediately with PONG. -Arguments to PING are not allowed. All other supported commands must contain a key. Supported commands are -functionally identical to the original Redis command except possibly in failure scenarios. +Arguments to PING are not allowed. Envoy responds to ECHO immediately with the command argument. +All other supported commands must contain a key. Supported commands are functionally identical to the +original Redis command except possibly in failure scenarios. For details on each command's usage see the official `Redis command reference `_. @@ -143,6 +144,7 @@ For details on each command's usage see the official :widths: 1, 1 AUTH, Authentication + ECHO, Connection PING, Connection QUIT, Connection DEL, Generic diff --git a/source/extensions/filters/network/common/redis/supported_commands.h b/source/extensions/filters/network/common/redis/supported_commands.h index 560689eba715..907eb2d6e238 100644 --- a/source/extensions/filters/network/common/redis/supported_commands.h +++ b/source/extensions/filters/network/common/redis/supported_commands.h @@ -61,6 +61,11 @@ struct SupportedCommands { */ static const std::string& auth() { CONSTRUCT_ON_FIRST_USE(std::string, "auth"); } + /** + * @return auth command + */ + static const std::string& echo() { CONSTRUCT_ON_FIRST_USE(std::string, "echo"); } + /** * @return mget command */ diff --git a/source/extensions/filters/network/redis_proxy/command_splitter_impl.cc b/source/extensions/filters/network/redis_proxy/command_splitter_impl.cc index 692b5e073243..606d7c9ec202 100644 --- a/source/extensions/filters/network/redis_proxy/command_splitter_impl.cc +++ b/source/extensions/filters/network/redis_proxy/command_splitter_impl.cc @@ -629,6 +629,19 @@ SplitRequestPtr InstanceImpl::makeRequest(Common::Redis::RespValuePtr&& request, return nullptr; } + if (command_name == Common::Redis::SupportedCommands::echo()) { + // Respond to ECHO locally. + if (request->asArray().size() != 2) { + onInvalidRequest(callbacks); + return nullptr; + } + Common::Redis::RespValuePtr echo_resp(new Common::Redis::RespValue()); + echo_resp->type(Common::Redis::RespType::BulkString); + echo_resp->asString() = request->asArray()[1].asString(); + callbacks.onResponse(std::move(echo_resp)); + return nullptr; + } + if (command_name == Common::Redis::SupportedCommands::time()) { // Respond to TIME locally. Common::Redis::RespValuePtr time_resp(new Common::Redis::RespValue()); diff --git a/test/extensions/filters/network/redis_proxy/command_splitter_impl_test.cc b/test/extensions/filters/network/redis_proxy/command_splitter_impl_test.cc index b433468d170a..7a24ed7416ba 100644 --- a/test/extensions/filters/network/redis_proxy/command_splitter_impl_test.cc +++ b/test/extensions/filters/network/redis_proxy/command_splitter_impl_test.cc @@ -406,6 +406,22 @@ TEST_F(RedisSingleServerRequestTest, PingSuccess) { EXPECT_EQ(nullptr, handle_); }; +TEST_F(RedisSingleServerRequestTest, EchoSuccess) { + InSequence s; + + Common::Redis::RespValuePtr request{new Common::Redis::RespValue()}; + makeBulkStringArray(*request, {"echo", "foobar"}); + + Common::Redis::RespValue response; + response.type(Common::Redis::RespType::BulkString); + response.asString() = "foobar"; + + EXPECT_CALL(callbacks_, connectionAllowed()).WillOnce(Return(true)); + EXPECT_CALL(callbacks_, onResponse_(PointeesEq(&response))); + handle_ = splitter_.makeRequest(std::move(request), callbacks_, dispatcher_, stream_info_); + EXPECT_EQ(nullptr, handle_); +}; + TEST_F(RedisSingleServerRequestTest, Time) { InSequence s;