|  | // Copyright 2016 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "http2/decoder/decode_buffer.h" | 
|  |  | 
|  | #include <functional> | 
|  |  | 
|  | #include "http2/platform/api/http2_logging.h" | 
|  | #include "http2/platform/api/http2_test_helpers.h" | 
|  | #include "http2/test_tools/http2_random.h" | 
|  | #include "common/platform/api/quiche_test.h" | 
|  |  | 
|  | namespace http2 { | 
|  | namespace test { | 
|  | namespace { | 
|  |  | 
|  | enum class TestEnumClass32 { | 
|  | kValue1 = 1, | 
|  | kValue99 = 99, | 
|  | kValue1M = 1000000, | 
|  | }; | 
|  |  | 
|  | enum class TestEnumClass8 { | 
|  | kValue1 = 1, | 
|  | kValue2 = 1, | 
|  | kValue99 = 99, | 
|  | kValue255 = 255, | 
|  | }; | 
|  |  | 
|  | enum TestEnum8 { | 
|  | kMaskLo = 0x01, | 
|  | kMaskHi = 0x80, | 
|  | }; | 
|  |  | 
|  | struct TestStruct { | 
|  | uint8_t f1; | 
|  | uint16_t f2; | 
|  | uint32_t f3;  // Decoded as a uint24 | 
|  | uint32_t f4; | 
|  | uint32_t f5;  // Decoded as if uint31 | 
|  | TestEnumClass32 f6; | 
|  | TestEnumClass8 f7; | 
|  | TestEnum8 f8; | 
|  | }; | 
|  |  | 
|  | class DecodeBufferTest : public QuicheTest { | 
|  | protected: | 
|  | Http2Random random_; | 
|  | uint32_t decode_offset_; | 
|  | }; | 
|  |  | 
|  | TEST_F(DecodeBufferTest, DecodesFixedInts) { | 
|  | const char data[] = "\x01\x12\x23\x34\x45\x56\x67\x78\x89\x9a"; | 
|  | DecodeBuffer b1(data, strlen(data)); | 
|  | EXPECT_EQ(1, b1.DecodeUInt8()); | 
|  | EXPECT_EQ(0x1223u, b1.DecodeUInt16()); | 
|  | EXPECT_EQ(0x344556u, b1.DecodeUInt24()); | 
|  | EXPECT_EQ(0x6778899Au, b1.DecodeUInt32()); | 
|  | } | 
|  |  | 
|  | // Make sure that DecodeBuffer is not copying input, just pointing into | 
|  | // provided input buffer. | 
|  | TEST_F(DecodeBufferTest, HasNotCopiedInput) { | 
|  | const char data[] = "ab"; | 
|  | DecodeBuffer b1(data, 2); | 
|  |  | 
|  | EXPECT_EQ(2u, b1.Remaining()); | 
|  | EXPECT_EQ(0u, b1.Offset()); | 
|  | EXPECT_FALSE(b1.Empty()); | 
|  | EXPECT_EQ(data, b1.cursor());  // cursor points to input buffer | 
|  | EXPECT_TRUE(b1.HasData()); | 
|  |  | 
|  | b1.AdvanceCursor(1); | 
|  |  | 
|  | EXPECT_EQ(1u, b1.Remaining()); | 
|  | EXPECT_EQ(1u, b1.Offset()); | 
|  | EXPECT_FALSE(b1.Empty()); | 
|  | EXPECT_EQ(&data[1], b1.cursor()); | 
|  | EXPECT_TRUE(b1.HasData()); | 
|  |  | 
|  | b1.AdvanceCursor(1); | 
|  |  | 
|  | EXPECT_EQ(0u, b1.Remaining()); | 
|  | EXPECT_EQ(2u, b1.Offset()); | 
|  | EXPECT_TRUE(b1.Empty()); | 
|  | EXPECT_EQ(&data[2], b1.cursor()); | 
|  | EXPECT_FALSE(b1.HasData()); | 
|  |  | 
|  | DecodeBuffer b2(data, 0); | 
|  |  | 
|  | EXPECT_EQ(0u, b2.Remaining()); | 
|  | EXPECT_EQ(0u, b2.Offset()); | 
|  | EXPECT_TRUE(b2.Empty()); | 
|  | EXPECT_EQ(data, b2.cursor()); | 
|  | EXPECT_FALSE(b2.HasData()); | 
|  | } | 
|  |  | 
|  | // DecodeBufferSubset can't go beyond the end of the base buffer. | 
|  | TEST_F(DecodeBufferTest, DecodeBufferSubsetLimited) { | 
|  | const char data[] = "abc"; | 
|  | DecodeBuffer base(data, 3); | 
|  | base.AdvanceCursor(1); | 
|  | DecodeBufferSubset subset(&base, 100); | 
|  | EXPECT_EQ(2u, subset.FullSize()); | 
|  | } | 
|  |  | 
|  | // DecodeBufferSubset advances the cursor of its base upon destruction. | 
|  | TEST_F(DecodeBufferTest, DecodeBufferSubsetAdvancesCursor) { | 
|  | const char data[] = "abc"; | 
|  | const size_t size = sizeof(data) - 1; | 
|  | EXPECT_EQ(3u, size); | 
|  | DecodeBuffer base(data, size); | 
|  | { | 
|  | // First no change to the cursor. | 
|  | DecodeBufferSubset subset(&base, size + 100); | 
|  | EXPECT_EQ(size, subset.FullSize()); | 
|  | EXPECT_EQ(base.FullSize(), subset.FullSize()); | 
|  | EXPECT_EQ(0u, subset.Offset()); | 
|  | } | 
|  | EXPECT_EQ(0u, base.Offset()); | 
|  | EXPECT_EQ(size, base.Remaining()); | 
|  | } | 
|  |  | 
|  | // Make sure that DecodeBuffer ctor complains about bad args. | 
|  | #if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) | 
|  | TEST(DecodeBufferDeathTest, NonNullBufferRequired) { | 
|  | EXPECT_DEBUG_DEATH({ DecodeBuffer b(nullptr, 3); }, "nullptr"); | 
|  | } | 
|  |  | 
|  | // Make sure that DecodeBuffer ctor complains about bad args. | 
|  | TEST(DecodeBufferDeathTest, ModestBufferSizeRequired) { | 
|  | EXPECT_DEBUG_DEATH( | 
|  | { | 
|  | // This depends on being able to allocate a fairly large array on the | 
|  | // stack. If that fails, we can instead do this: | 
|  | // | 
|  | //   std::string data(DecodeBuffer::kMaxDecodeBufferLength + 1, ' '); | 
|  | //   DecodeBuffer b(data.data(), data.size()); | 
|  |  | 
|  | const char data[DecodeBuffer::kMaxDecodeBufferLength + 1] = {}; | 
|  | DecodeBuffer b(data, sizeof data); | 
|  | }, | 
|  | "Max.*Length"); | 
|  | } | 
|  |  | 
|  | // Make sure that DecodeBuffer detects advance beyond end, in debug mode. | 
|  | TEST(DecodeBufferDeathTest, LimitedAdvance) { | 
|  | { | 
|  | // Advance right up to end is OK. | 
|  | const char data[] = "abc"; | 
|  | DecodeBuffer b(data, 3); | 
|  | b.AdvanceCursor(3);  // OK | 
|  | EXPECT_TRUE(b.Empty()); | 
|  | } | 
|  | EXPECT_DEBUG_DEATH( | 
|  | { | 
|  | // Going beyond is not OK. | 
|  | const char data[] = "abc"; | 
|  | DecodeBuffer b(data, 3); | 
|  | b.AdvanceCursor(4); | 
|  | }, | 
|  | "4 vs. 3"); | 
|  | } | 
|  |  | 
|  | // Make sure that DecodeBuffer detects decode beyond end, in debug mode. | 
|  | TEST(DecodeBufferDeathTest, DecodeUInt8PastEnd) { | 
|  | const char data[] = {0x12, 0x23}; | 
|  | DecodeBuffer b(data, sizeof data); | 
|  | EXPECT_EQ(2u, b.FullSize()); | 
|  | EXPECT_EQ(0x1223, b.DecodeUInt16()); | 
|  | EXPECT_DEBUG_DEATH({ b.DecodeUInt8(); }, "1 vs. 0"); | 
|  | } | 
|  |  | 
|  | // Make sure that DecodeBuffer detects decode beyond end, in debug mode. | 
|  | TEST(DecodeBufferDeathTest, DecodeUInt16OverEnd) { | 
|  | const char data[] = {0x12, 0x23, 0x34}; | 
|  | DecodeBuffer b(data, sizeof data); | 
|  | EXPECT_EQ(3u, b.FullSize()); | 
|  | EXPECT_EQ(0x1223, b.DecodeUInt16()); | 
|  | EXPECT_DEBUG_DEATH({ b.DecodeUInt16(); }, "2 vs. 1"); | 
|  | } | 
|  |  | 
|  | // Make sure that DecodeBuffer doesn't agree with having two subsets. | 
|  | TEST(DecodeBufferSubsetDeathTest, TwoSubsets) { | 
|  | const char data[] = "abc"; | 
|  | DecodeBuffer base(data, 3); | 
|  | DecodeBufferSubset subset1(&base, 1); | 
|  | EXPECT_DEBUG_DEATH({ DecodeBufferSubset subset2(&base, 1); }, | 
|  | "There is already a subset"); | 
|  | } | 
|  |  | 
|  | // Make sure that DecodeBufferSubset notices when the base's cursor has moved. | 
|  | TEST(DecodeBufferSubsetDeathTest, BaseCursorAdvanced) { | 
|  | const char data[] = "abc"; | 
|  | DecodeBuffer base(data, 3); | 
|  | base.AdvanceCursor(1); | 
|  | EXPECT_DEBUG_DEATH( | 
|  | { | 
|  | DecodeBufferSubset subset1(&base, 2); | 
|  | base.AdvanceCursor(1); | 
|  | }, | 
|  | "Access via subset only when present"); | 
|  | } | 
|  | #endif  // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace test | 
|  | }  // namespace http2 |