Skip to content

Commit

Permalink
redis-proxy: support echo command (envoyproxy#32296)
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Norberg <dano@modal.com>
  • Loading branch information
danielnorberg authored Feb 13, 2024
1 parent a1887ea commit 74fe655
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 3 deletions.
3 changes: 3 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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 <envoy_v3_api_msg_extensions.load_balancing_policies.least_request.v3.LeastRequest>`
random choices.
- area: redis
change: |
Added support for the ``ECHO`` command.
deprecated:
8 changes: 5 additions & 3 deletions docs/root/intro/arch_overview/other_protocols/redis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://redis.io/commands>`_.
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

0 comments on commit 74fe655

Please sign in to comment.