This is an automated email from the ASF dual-hosted git repository.

hulk pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git


The following commit(s) were added to refs/heads/unstable by this push:
     new 7fe2bc2e Add support of RESP3 verbatim string (#2102)
7fe2bc2e is described below

commit 7fe2bc2e072ab0a9b1643a4852ea43889b38f0b6
Author: Leslie <[email protected]>
AuthorDate: Sun Feb 18 15:45:24 2024 +0800

    Add support of RESP3 verbatim string (#2102)
---
 src/commands/cmd_cluster.cc                 |  4 +--
 src/commands/cmd_server.cc                  | 12 ++++----
 src/server/redis_connection.h               | 11 +++++++
 tests/gocase/unit/protocol/protocol_test.go | 46 +++++++++++++++--------------
 4 files changed, 44 insertions(+), 29 deletions(-)

diff --git a/src/commands/cmd_cluster.cc b/src/commands/cmd_cluster.cc
index 4554a327..96ecfbde 100644
--- a/src/commands/cmd_cluster.cc
+++ b/src/commands/cmd_cluster.cc
@@ -85,7 +85,7 @@ class CommandCluster : public Commander {
       std::string nodes_desc;
       Status s = srv->cluster->GetClusterNodes(&nodes_desc);
       if (s.IsOK()) {
-        *output = redis::BulkString(nodes_desc);
+        *output = conn->VerbatimString("txt", nodes_desc);
       } else {
         return {Status::RedisExecErr, s.Msg()};
       }
@@ -93,7 +93,7 @@ class CommandCluster : public Commander {
       std::string cluster_info;
       Status s = srv->cluster->GetClusterInfo(&cluster_info);
       if (s.IsOK()) {
-        *output = redis::BulkString(cluster_info);
+        *output = conn->VerbatimString("txt", cluster_info);
       } else {
         return {Status::RedisExecErr, s.Msg()};
       }
diff --git a/src/commands/cmd_server.cc b/src/commands/cmd_server.cc
index 141883b6..0463cf8f 100644
--- a/src/commands/cmd_server.cc
+++ b/src/commands/cmd_server.cc
@@ -278,7 +278,7 @@ class CommandInfo : public Commander {
     }
     std::string info;
     srv->GetInfo(conn->GetNamespace(), section, &info);
-    *output = redis::BulkString(info);
+    *output = conn->VerbatimString("txt", info);
     return Status::OK();
   }
 };
@@ -503,10 +503,10 @@ class CommandClient : public Commander {
 
   Status Execute(Server *srv, Connection *conn, std::string *output) override {
     if (subcommand_ == "list") {
-      *output = redis::BulkString(srv->GetClientsStr());
+      *output = conn->VerbatimString("txt", srv->GetClientsStr());
       return Status::OK();
     } else if (subcommand_ == "info") {
-      *output = redis::BulkString(conn->ToString());
+      *output = conn->VerbatimString("txt", conn->ToString());
       return Status::OK();
     } else if (subcommand_ == "setname") {
       conn->SetName(conn_name_);
@@ -648,16 +648,18 @@ class CommandDebug : public Commander {
             redis::BulkString("key:123"),
             redis::Integer(90),
         });
+      } else if (protocol_type_ == "verbatim") {  // verbatim string
+        *output = conn->VerbatimString("txt", "verbatim string");
       } else {
         *output = redis::Error(
             "Wrong protocol type name. Please use one of the following: "
-            "string|integer|double|array|set|bignum|true|false|null|attrib");
+            
"string|integer|double|array|set|bignum|true|false|null|attrib|verbatim");
       }
     } else if (subcommand_ == "dbsize-limit") {
       srv->storage->SetDBSizeLimit(dbsize_limit_);
       *output = redis::SimpleString("OK");
     } else {
-      return {Status::RedisInvalidCmd, "Unknown subcommand, should be DEBUG, 
PROTOCOL or DBSIZE-LIMIT"};
+      return {Status::RedisInvalidCmd, "Unknown subcommand, should be SLEEP, 
PROTOCOL or DBSIZE-LIMIT"};
     }
     return Status::OK();
   }
diff --git a/src/server/redis_connection.h b/src/server/redis_connection.h
index 2211e2e1..9faf0c27 100644
--- a/src/server/redis_connection.h
+++ b/src/server/redis_connection.h
@@ -71,6 +71,17 @@ class Connection : public EvbufCallbackBase<Connection> {
   std::string Double(double d) const {
     return protocol_version_ == RESP::v3 ? "," + util::Float2String(d) + CRLF 
: BulkString(util::Float2String(d));
   }
+  // ext is the extension of file to send, 'txt' for text file, 'md ' for 
markdown file
+  // at most 3 chars, padded with space
+  // if RESP is V2, treat verbatim string as blob string
+  // https://github.com/redis/redis/blob/7.2/src/networking.c#L1099
+  std::string VerbatimString(std::string ext, const std::string &data) const {
+    CHECK(ext.size() <= 3);
+    size_t padded_len = 3 - ext.size();
+    ext = ext + std::string(padded_len, ' ');
+    return protocol_version_ == RESP::v3 ? "=" + std::to_string(3 + 1 + 
data.size()) + CRLF + ext + ":" + data + CRLF
+                                         : BulkString(data);
+  }
   std::string NilString() const { return redis::NilString(protocol_version_); }
   std::string NilArray() const { return protocol_version_ == RESP::v3 ? "_" 
CRLF : "*-1" CRLF; }
   std::string MultiBulkString(const std::vector<std::string> &values) const;
diff --git a/tests/gocase/unit/protocol/protocol_test.go 
b/tests/gocase/unit/protocol/protocol_test.go
index f2a3432d..39914a33 100644
--- a/tests/gocase/unit/protocol/protocol_test.go
+++ b/tests/gocase/unit/protocol/protocol_test.go
@@ -151,17 +151,18 @@ func TestProtocolRESP2(t *testing.T) {
 
        t.Run("debug protocol string", func(t *testing.T) {
                types := map[string][]string{
-                       "string":  {"$11", "Hello World"},
-                       "integer": {":12345"},
-                       "double":  {"$5", "3.141"},
-                       "array":   {"*3", ":0", ":1", ":2"},
-                       "set":     {"*3", ":0", ":1", ":2"},
-                       "map":     {"*6", ":0", ":0", ":1", ":1", ":2", ":0"},
-                       "bignum":  {"$37", 
"1234567999999999999999999999999999999"},
-                       "true":    {":1"},
-                       "false":   {":0"},
-                       "null":    {"$-1"},
-                       "attrib":  {"|1", "$14", "key-popularity", "*2", "$7", 
"key:123", ":90"},
+                       "string":   {"$11", "Hello World"},
+                       "integer":  {":12345"},
+                       "double":   {"$5", "3.141"},
+                       "array":    {"*3", ":0", ":1", ":2"},
+                       "set":      {"*3", ":0", ":1", ":2"},
+                       "map":      {"*6", ":0", ":0", ":1", ":1", ":2", ":0"},
+                       "bignum":   {"$37", 
"1234567999999999999999999999999999999"},
+                       "true":     {":1"},
+                       "false":    {":0"},
+                       "null":     {"$-1"},
+                       "attrib":   {"|1", "$14", "key-popularity", "*2", "$7", 
"key:123", ":90"},
+                       "verbatim": {"$15", "verbatim string"},
                }
                for typ, expected := range types {
                        args := []string{"DEBUG", "PROTOCOL", typ}
@@ -208,17 +209,18 @@ func TestProtocolRESP3(t *testing.T) {
                }
 
                types := map[string][]string{
-                       "string":  {"$11", "Hello World"},
-                       "integer": {":12345"},
-                       "double":  {",3.141"},
-                       "array":   {"*3", ":0", ":1", ":2"},
-                       "set":     {"~3", ":0", ":1", ":2"},
-                       "map":     {"%3", ":0", "#f", ":1", "#t", ":2", "#f"},
-                       "bignum":  {"(1234567999999999999999999999999999999"},
-                       "true":    {"#t"},
-                       "false":   {"#f"},
-                       "null":    {"_"},
-                       "attrib":  {"|1", "$14", "key-popularity", "*2", "$7", 
"key:123", ":90"},
+                       "string":   {"$11", "Hello World"},
+                       "integer":  {":12345"},
+                       "double":   {",3.141"},
+                       "array":    {"*3", ":0", ":1", ":2"},
+                       "set":      {"~3", ":0", ":1", ":2"},
+                       "map":      {"%3", ":0", "#f", ":1", "#t", ":2", "#f"},
+                       "bignum":   {"(1234567999999999999999999999999999999"},
+                       "true":     {"#t"},
+                       "false":    {"#f"},
+                       "null":     {"_"},
+                       "attrib":   {"|1", "$14", "key-popularity", "*2", "$7", 
"key:123", ":90"},
+                       "verbatim": {"=19", "txt:verbatim string"},
                }
                for typ, expected := range types {
                        args := []string{"DEBUG", "PROTOCOL", typ}

Reply via email to