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

chenBright pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new 68d18eec Fix inline redis protocol consuming HTTP/2 connection preface 
(#3338)
68d18eec is described below

commit 68d18eecf7878018a171ef660ffdba25b683c01a
Author: rajvarun77 <[email protected]>
AuthorDate: Sun Jun 14 00:32:43 2026 -0400

    Fix inline redis protocol consuming HTTP/2 connection preface (#3338)
    
    Inline redis protocol support (#3024) accepts any alpha-leading line as
    an inline command. The HTTP/2 client connection preface
    ("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") is alpha-leading, so it was parsed
    as an inline command instead of returning PARSE_ERROR_TRY_OTHERS. This
    prevented protocol auto-detection from falling through to HTTP/2, and
    gRPC clients calling a server with redis_service enabled failed with
    "connection closed before server preface received".
    
    Detect the HTTP/2 preface (fully, or as a not-yet-complete prefix) in
    the inline branch of RedisCommandParser::ConsumeImpl and defer to other
    protocols. A command-name blacklist is not viable because some HTTP
    methods are also valid redis commands (e.g. GET), whereas the preface
    is a fixed magic string that no redis command begins with, making the
    check unambiguous.
    
    Add RedisTest.inline_does_not_eat_h2_preface covering the full preface,
    a partial prefix, and a genuine inline command sharing the leading byte.
    
    Co-authored-by: rajvarun77 <[email protected]>
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
 src/brpc/redis_command.cpp   | 17 +++++++++++++++++
 test/brpc_redis_unittest.cpp | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/src/brpc/redis_command.cpp b/src/brpc/redis_command.cpp
index 4532b3c1..cf1507b8 100644
--- a/src/brpc/redis_command.cpp
+++ b/src/brpc/redis_command.cpp
@@ -408,6 +408,23 @@ RedisCommandConsumeState 
RedisCommandParser::ConsumeImpl(butil::IOBuf& buf,
             *err = PARSE_ERROR_TRY_OTHERS;
             return CONSUME_STATE_ERROR;
         }
+        // The HTTP/2 connection preface ("PRI * HTTP/2.0\r\n...") is 
alpha-leading
+        // and would otherwise be consumed as an inline redis command (first 
token
+        // "PRI"), preventing protocol auto-detection from falling through to
+        // HTTP/2 (e.g. gRPC clients). Defer to other protocols when the input
+        // matches the preface, either fully or as a not-yet-complete prefix. 
No
+        // valid redis command begins with these bytes, so this is unambiguous.
+        // See issue #3109.
+        static const char h2_preface[] = "PRI * HTTP/2.0\r\n";
+        const size_t h2_preface_len = sizeof(h2_preface) - 1;
+        if (*pfc == h2_preface[0]) {
+            char head[h2_preface_len];
+            const size_t n = buf.copy_to(head, h2_preface_len);
+            if (memcmp(head, h2_preface, n) == 0) {
+                *err = PARSE_ERROR_TRY_OTHERS;
+                return CONSUME_STATE_ERROR;
+            }
+        }
         const size_t buf_size = buf.size();
         const auto copy_str = static_cast<char *>(arena->allocate(buf_size + 
1));
         // arena->allocate() may return NULL on allocation failure
diff --git a/test/brpc_redis_unittest.cpp b/test/brpc_redis_unittest.cpp
index 4dffd55e..43775d85 100644
--- a/test/brpc_redis_unittest.cpp
+++ b/test/brpc_redis_unittest.cpp
@@ -658,6 +658,42 @@ TEST_F(RedisTest, command_parser) {
     }
 }
 
+// Regression test for issue #3109: the inline redis protocol must not consume
+// the HTTP/2 connection preface as a command, otherwise protocol 
auto-detection
+// never falls through to HTTP/2 and gRPC clients fail with "connection closed
+// before server preface received".
+TEST_F(RedisTest, inline_does_not_eat_h2_preface) {
+    brpc::RedisCommandParser parser;
+    butil::IOBuf buf;
+    std::vector<butil::StringPiece> command_out;
+    butil::Arena arena;
+    {
+        // Full HTTP/2 client connection preface: must defer to other 
protocols.
+        buf.append("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
+        ASSERT_EQ(brpc::PARSE_ERROR_TRY_OTHERS,
+                  parser.Consume(buf, &command_out, &arena));
+        buf.clear();
+        parser.Reset();
+    }
+    {
+        // A not-yet-complete prefix of the preface must also defer, leaving 
the
+        // bytes intact for the HTTP/2 parser instead of being misparsed.
+        buf.append("PRI * HT");
+        ASSERT_EQ(brpc::PARSE_ERROR_TRY_OTHERS,
+                  parser.Consume(buf, &command_out, &arena));
+        buf.clear();
+        parser.Reset();
+    }
+    {
+        // A genuine inline command sharing the leading 'P' must still parse.
+        buf.append("PING\r\n");
+        ASSERT_EQ(brpc::PARSE_OK, parser.Consume(buf, &command_out, &arena));
+        ASSERT_EQ("ping", GetCompleteCommand(command_out));
+        buf.clear();
+        parser.Reset();
+    }
+}
+
 TEST_F(RedisTest, redis_reply_codec) {
     butil::Arena arena;
     // status


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to