| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #include <gtest/gtest.h> |
| |
| #include <string> |
| |
| #include "brpc/policy/mysql/mysql_auth_packet.h" |
| #include "butil/strings/string_piece.h" |
| |
| namespace { |
| |
| using brpc::policy::mysql::DecodeLengthEncodedInt; |
| using brpc::policy::mysql::DecodeLengthEncodedString; |
| using brpc::policy::mysql::DecodeNullTerminatedString; |
| using brpc::policy::mysql::DecodePacketHeader; |
| using brpc::policy::mysql::EncodeLengthEncodedInt; |
| using brpc::policy::mysql::EncodeLengthEncodedString; |
| using brpc::policy::mysql::EncodePacketHeader; |
| using brpc::policy::mysql::PacketHeader; |
| using brpc::policy::mysql::kMaxPayloadLen; |
| using brpc::policy::mysql::kPacketHeaderLen; |
| |
| // ---------------------------------------------------------------------- |
| // length-encoded integer |
| // ---------------------------------------------------------------------- |
| |
| TEST(LenencIntTest, Decode_1Byte_Zero) { |
| const char buf[] = {0x00}; |
| uint64_t v = 0xdead; |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 1), &v), 1u); |
| EXPECT_EQ(v, 0u); |
| } |
| |
| TEST(LenencIntTest, Decode_1Byte_Max250) { |
| const char buf[] = {static_cast<char>(0xfa)}; |
| uint64_t v = 0; |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 1), &v), 1u); |
| EXPECT_EQ(v, 0xfau); |
| } |
| |
| TEST(LenencIntTest, Decode_2Byte_251) { |
| const char buf[] = {static_cast<char>(0xfc), static_cast<char>(0xfb), 0x00}; |
| uint64_t v = 0; |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 3), &v), 3u); |
| EXPECT_EQ(v, 251u); |
| } |
| |
| TEST(LenencIntTest, Decode_2Byte_Max65535) { |
| const char buf[] = {static_cast<char>(0xfc), |
| static_cast<char>(0xff), |
| static_cast<char>(0xff)}; |
| uint64_t v = 0; |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 3), &v), 3u); |
| EXPECT_EQ(v, 0xffffu); |
| } |
| |
| TEST(LenencIntTest, Decode_3Byte) { |
| const char buf[] = {static_cast<char>(0xfd), 0x01, 0x02, 0x03}; |
| uint64_t v = 0; |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 4), &v), 4u); |
| EXPECT_EQ(v, 0x030201u); |
| } |
| |
| TEST(LenencIntTest, Decode_8Byte) { |
| const char buf[] = {static_cast<char>(0xfe), |
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; |
| uint64_t v = 0; |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 9), &v), 9u); |
| EXPECT_EQ(v, 0x0807060504030201ULL); |
| } |
| |
| TEST(LenencIntTest, Decode_ReservedFF_ReturnsZero) { |
| const char buf[] = {static_cast<char>(0xff)}; |
| uint64_t v = 0; |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 1), &v), 0u); |
| } |
| |
| TEST(LenencIntTest, Decode_Truncated_ReturnsZero) { |
| const char buf[] = {static_cast<char>(0xfc), 0x01}; // missing 1 byte |
| uint64_t v = 0; |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 2), &v), 0u); |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 0), &v), 0u); |
| } |
| |
| TEST(LenencIntTest, Decode_NullMarkerFB_ReportsNull) { |
| const char buf[] = {static_cast<char>(0xfb)}; |
| uint64_t v = 0xdead; |
| bool is_null = false; |
| // 0xFB is the NULL marker: 1 byte consumed, value NULL, *out defined to 0. |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 1), &v, &is_null), |
| 1u); |
| EXPECT_TRUE(is_null); |
| EXPECT_EQ(v, 0u); |
| } |
| |
| TEST(LenencIntTest, Decode_NonNull_SetsIsNullFalse) { |
| const char buf[] = {0x05}; |
| uint64_t v = 0; |
| bool is_null = true; |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 1), &v, &is_null), |
| 1u); |
| EXPECT_FALSE(is_null); |
| EXPECT_EQ(v, 5u); |
| } |
| |
| TEST(LenencIntTest, Decode_Failure_DefinesOutAndIsNull) { |
| // Reserved 0xFF marker -> failure; *out reset to 0, *is_null to false even |
| // though both held stale values, so a careless caller can't read garbage. |
| const char buf[] = {static_cast<char>(0xff)}; |
| uint64_t v = 0xdead; |
| bool is_null = true; |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 1), &v, &is_null), |
| 0u); |
| EXPECT_FALSE(is_null); |
| EXPECT_EQ(v, 0u); |
| } |
| |
| TEST(LenencIntTest, Decode_NullMarker_WithoutIsNullArg) { |
| // |is_null| is optional; 0xFB without it must not crash and still |
| // consumes the single marker byte. |
| const char buf[] = {static_cast<char>(0xfb)}; |
| uint64_t v = 0xdead; |
| EXPECT_EQ(DecodeLengthEncodedInt(butil::StringPiece(buf, 1), &v), 1u); |
| EXPECT_EQ(v, 0u); |
| } |
| |
| TEST(LenencIntTest, Encode_RoundTrip_AllRanges) { |
| const uint64_t values[] = { |
| 0, 1, 250, 251, 0xffff, 0x10000, 0xffffff, 0x1000000, 0xffffffffULL |
| }; |
| for (uint64_t v : values) { |
| std::string buf; |
| EncodeLengthEncodedInt(v, &buf); |
| uint64_t decoded = 0; |
| EXPECT_GT(DecodeLengthEncodedInt(buf, &decoded), 0u); |
| EXPECT_EQ(decoded, v); |
| } |
| } |
| |
| // ---------------------------------------------------------------------- |
| // length-encoded string |
| // ---------------------------------------------------------------------- |
| |
| TEST(LenencStringTest, Empty) { |
| std::string buf; |
| EncodeLengthEncodedString(butil::StringPiece(""), &buf); |
| EXPECT_EQ(buf, std::string("\0", 1)); |
| std::string out; |
| EXPECT_EQ(DecodeLengthEncodedString(buf, &out), 1u); |
| EXPECT_TRUE(out.empty()); |
| } |
| |
| TEST(LenencStringTest, ShortString_RoundTrip) { |
| std::string buf; |
| EncodeLengthEncodedString(butil::StringPiece("hello"), &buf); |
| EXPECT_EQ(buf.size(), 6u); |
| std::string out; |
| EXPECT_EQ(DecodeLengthEncodedString(buf, &out), 6u); |
| EXPECT_EQ(out, "hello"); |
| } |
| |
| TEST(LenencStringTest, ContainsNul_RoundTrip) { |
| std::string buf; |
| const std::string value("a\0b\0c", 5); |
| EncodeLengthEncodedString(butil::StringPiece(value), &buf); |
| std::string out; |
| EXPECT_EQ(DecodeLengthEncodedString(buf, &out), 6u); |
| EXPECT_EQ(out, value); |
| } |
| |
| TEST(LenencStringTest, TruncatedPayload_ReturnsZero) { |
| // Encoded length says 10 but only 3 bytes available. |
| std::string buf; |
| buf.push_back(0x0a); |
| buf.append("abc"); |
| std::string out; |
| EXPECT_EQ(DecodeLengthEncodedString(buf, &out), 0u); |
| } |
| |
| TEST(LenencStringTest, NullMarkerFB_ReportsNull) { |
| // A length-encoded string whose leading lenenc-int is 0xFB is NULL, |
| // distinct from the empty string (lenenc 0x00). Only the marker byte is |
| // consumed and out_value is cleared. |
| const char buf[] = {static_cast<char>(0xfb), 'x', 'y'}; |
| std::string out = "stale"; |
| bool is_null = false; |
| EXPECT_EQ(DecodeLengthEncodedString(butil::StringPiece(buf, 3), &out, |
| &is_null), |
| 1u); |
| EXPECT_TRUE(is_null); |
| EXPECT_TRUE(out.empty()); |
| } |
| |
| TEST(LenencStringTest, NonNull_SetsIsNullFalse) { |
| std::string buf; |
| EncodeLengthEncodedString(butil::StringPiece("hi"), &buf); |
| std::string out; |
| bool is_null = true; |
| EXPECT_EQ(DecodeLengthEncodedString(buf, &out, &is_null), 3u); |
| EXPECT_FALSE(is_null); |
| EXPECT_EQ(out, "hi"); |
| } |
| |
| TEST(LenencStringTest, EmptyIsNotNull) { |
| // Empty string (lenenc 0x00) must NOT be reported as NULL. |
| std::string buf; |
| EncodeLengthEncodedString(butil::StringPiece(""), &buf); |
| std::string out = "stale"; |
| bool is_null = true; |
| EXPECT_EQ(DecodeLengthEncodedString(buf, &out, &is_null), 1u); |
| EXPECT_FALSE(is_null); |
| EXPECT_TRUE(out.empty()); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // packet header |
| // ---------------------------------------------------------------------- |
| |
| TEST(PacketHeaderTest, RoundTrip_TypicalSizes) { |
| const uint32_t sizes[] = {0u, 1u, 0xffu, 0x100u, 0xffffu, 0x10000u, 0x123456u}; |
| for (uint32_t s : sizes) { |
| PacketHeader in = {s, 7}; |
| std::string buf; |
| EncodePacketHeader(in, &buf); |
| ASSERT_EQ(buf.size(), kPacketHeaderLen); |
| PacketHeader out; |
| ASSERT_TRUE(DecodePacketHeader(buf, &out)); |
| EXPECT_EQ(out.payload_len, s); |
| EXPECT_EQ(out.seq, 7u); |
| } |
| } |
| |
| TEST(PacketHeaderTest, MaxPayloadLength) { |
| PacketHeader in = {kMaxPayloadLen, 0}; |
| std::string buf; |
| EncodePacketHeader(in, &buf); |
| PacketHeader out; |
| ASSERT_TRUE(DecodePacketHeader(buf, &out)); |
| EXPECT_EQ(out.payload_len, kMaxPayloadLen); |
| } |
| |
| TEST(PacketHeaderTest, SequenceWraparound) { |
| PacketHeader in = {0, 255}; |
| std::string buf; |
| EncodePacketHeader(in, &buf); |
| PacketHeader out; |
| ASSERT_TRUE(DecodePacketHeader(buf, &out)); |
| EXPECT_EQ(out.seq, 255u); |
| } |
| |
| TEST(PacketHeaderTest, Decode_TruncatedReturnsFalse) { |
| PacketHeader out; |
| EXPECT_FALSE(DecodePacketHeader(butil::StringPiece("\x00\x00\x00", 3), &out)); |
| EXPECT_FALSE(DecodePacketHeader(butil::StringPiece("", 0), &out)); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // NUL-terminated string |
| // ---------------------------------------------------------------------- |
| |
| TEST(NullTermStringTest, HappyPath) { |
| const char buf[] = "hello\0extra"; |
| std::string out; |
| EXPECT_EQ(DecodeNullTerminatedString( |
| butil::StringPiece(buf, sizeof(buf) - 1), &out), |
| 6u); |
| EXPECT_EQ(out, "hello"); |
| } |
| |
| TEST(NullTermStringTest, EmptyString) { |
| const char buf[] = "\0rest"; |
| std::string out; |
| EXPECT_EQ(DecodeNullTerminatedString( |
| butil::StringPiece(buf, sizeof(buf) - 1), &out), |
| 1u); |
| EXPECT_TRUE(out.empty()); |
| } |
| |
| TEST(NullTermStringTest, NoNul_ReturnsZero) { |
| std::string out; |
| EXPECT_EQ(DecodeNullTerminatedString(butil::StringPiece("abc"), &out), 0u); |
| } |
| |
| } // namespace |