Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(228)

Unified Diff: net/http2/decoder/http2_structure_decoder_test.cc

Issue 2293613002: Add new HTTP/2 and HPACK decoder in net/http2/. (Closed)
Patch Set: Replace LOG(INFO) by VLOG(2) in DecodeBufferTest.SlowDecodeTestStruct so that trybots do not fail. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/http2/decoder/http2_structure_decoder.cc ('k') | net/http2/decoder/http2_structure_decoder_test_util.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/http2/decoder/http2_structure_decoder_test.cc
diff --git a/net/http2/decoder/http2_structure_decoder_test.cc b/net/http2/decoder/http2_structure_decoder_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..68479a167dd40d51f9133264a634927a2430326f
--- /dev/null
+++ b/net/http2/decoder/http2_structure_decoder_test.cc
@@ -0,0 +1,512 @@
+// 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 "net/http2/decoder/http2_structure_decoder.h"
+
+// Tests decoding all of the fixed size HTTP/2 structures (i.e. those defined in
+// net/http2/http2_structures.h) using Http2StructureDecoder, which handles
+// buffering of structures split across input buffer boundaries, and in turn
+// uses DoDecode when it has all of a structure in a contiguous buffer.
+
+// NOTE: This tests the first pair of Start and Resume, which don't take
+// a remaining_payload parameter. The other pair are well tested via the
+// payload decoder tests, though...
+// TODO(jamessynge): Create type parameterized tests for Http2StructureDecoder
+// where the type is the type of structure, and with testing of both pairs of
+// Start and Resume methods; note that it appears that the first pair will be
+// used only for Http2FrameHeader, and the other pair only for structures in the
+// frame payload.
+
+#include <stddef.h>
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+const bool kMayReturnZeroOnFirst = false;
+
+template <class S>
+string SerializeStructure(const S& s) {
+ Http2FrameBuilder fb;
+ fb.Append(s);
+ EXPECT_EQ(S::EncodedSize(), fb.size());
+ return fb.buffer();
+}
+
+template <class S>
+class Http2StructureDecoderTest : public RandomDecoderTest {
+ protected:
+ typedef S Structure;
+
+ Http2StructureDecoderTest() {
+ // IF the test adds more data after the encoded structure, stop as
+ // soon as the structure is decoded.
+ stop_decode_on_done_ = true;
+ }
+
+ DecodeStatus StartDecoding(DecodeBuffer* b) override {
+ // Overwrite the current contents of |structure_|, in to which we'll
+ // decode the buffer, so that we can be confident that we really decoded
+ // the structure every time.
+ structure_.~S();
+ new (&structure_) S;
+ uint32_t old_remaining = b->Remaining();
+ if (structure_decoder_.Start(&structure_, b)) {
+ EXPECT_EQ(old_remaining - S::EncodedSize(), b->Remaining());
+ ++fast_decode_count_;
+ return DecodeStatus::kDecodeDone;
+ } else {
+ EXPECT_LT(structure_decoder_.offset(), S::EncodedSize());
+ EXPECT_EQ(0u, b->Remaining());
+ EXPECT_EQ(old_remaining - structure_decoder_.offset(), b->Remaining());
+ ++incomplete_start_count_;
+ return DecodeStatus::kDecodeInProgress;
+ }
+ }
+
+ DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+ uint32_t old_offset = structure_decoder_.offset();
+ EXPECT_LT(old_offset, S::EncodedSize());
+ uint32_t avail = b->Remaining();
+ if (structure_decoder_.Resume(&structure_, b)) {
+ EXPECT_LE(S::EncodedSize(), old_offset + avail);
+ EXPECT_EQ(b->Remaining(), avail - (S::EncodedSize() - old_offset));
+ ++slow_decode_count_;
+ return DecodeStatus::kDecodeDone;
+ } else {
+ EXPECT_LT(structure_decoder_.offset(), S::EncodedSize());
+ EXPECT_EQ(0u, b->Remaining());
+ EXPECT_GT(S::EncodedSize(), old_offset + avail);
+ ++incomplete_resume_count_;
+ return DecodeStatus::kDecodeInProgress;
+ }
+ }
+
+ AssertionResult ValidatorForDecodeLeadingStructure(const S* expected,
+ const DecodeBuffer& db,
+ DecodeStatus status) {
+ VERIFY_EQ(*expected, structure_);
+ return AssertionSuccess();
+ }
+
+ // Fully decodes the Structure at the start of data, and confirms it matches
+ // *expected (if provided).
+ AssertionResult DecodeLeadingStructure(const S* expected, StringPiece data) {
+ VERIFY_LE(S::EncodedSize(), data.size());
+ DecodeBuffer original(data);
+
+ // The validator is called after each of the several times that the input
+ // DecodeBuffer is decoded, each with a different segmentation of the input.
+ // Validate that structure_ matches the expected value, if provided.
+ Validator validator =
+ (expected == nullptr)
+ ? base::Bind(&SucceedingValidator)
+ : base::Bind(&Http2StructureDecoderTest::
+ ValidatorForDecodeLeadingStructure,
+ base::Unretained(this), expected);
+
+ // Before that, validate that decoding is done and that we've advanced
+ // the cursor the expected amount.
+ Validator wrapped_validator =
+ ValidateDoneAndOffset(S::EncodedSize(), validator);
+
+ // Decode several times, with several segmentations of the input buffer.
+ fast_decode_count_ = 0;
+ slow_decode_count_ = 0;
+ incomplete_start_count_ = 0;
+ incomplete_resume_count_ = 0;
+ VERIFY_SUCCESS(DecodeAndValidateSeveralWays(
+ &original, kMayReturnZeroOnFirst, wrapped_validator));
+ VERIFY_FALSE(HasFailure());
+ VERIFY_EQ(S::EncodedSize(), structure_decoder_.offset());
+ VERIFY_EQ(S::EncodedSize(), original.Offset());
+ VERIFY_LT(0u, fast_decode_count_);
+ VERIFY_LT(0u, slow_decode_count_);
+ VERIFY_LT(0u, incomplete_start_count_);
+
+ // If the structure is large enough so that SelectZeroOrOne will have
+ // caused Resume to return false, check that occurred.
+ if (S::EncodedSize() >= 2) {
+ VERIFY_LE(0u, incomplete_resume_count_);
+ } else {
+ VERIFY_EQ(0u, incomplete_resume_count_);
+ }
+ if (expected != nullptr) {
+ DVLOG(1) << "DecodeLeadingStructure expected: " << *expected;
+ DVLOG(1) << "DecodeLeadingStructure actual: " << structure_;
+ VERIFY_EQ(*expected, structure_);
+ }
+ return AssertionSuccess();
+ }
+
+ template <size_t N>
+ AssertionResult DecodeLeadingStructure(const char (&data)[N]) {
+ VERIFY_AND_RETURN_SUCCESS(
+ DecodeLeadingStructure(nullptr, StringPiece(data, N)));
+ }
+
+ // Encode the structure |in_s| into bytes, then decode the bytes
+ // and validate that the decoder produced the same field values.
+ AssertionResult EncodeThenDecode(const S& in_s) {
+ string bytes = SerializeStructure(in_s);
+ VERIFY_EQ(S::EncodedSize(), bytes.size());
+ VERIFY_AND_RETURN_SUCCESS(DecodeLeadingStructure(&in_s, bytes));
+ }
+
+ // Repeatedly fill a structure with random but valid contents, encode it, then
+ // decode it, and finally validate that the decoded structure matches the
+ // random input. Lather-rinse-and-repeat.
+ AssertionResult TestDecodingRandomizedStructures(size_t count) {
+ for (size_t i = 0; i < count; ++i) {
+ Structure input;
+ Randomize(&input, RandomPtr());
+ VERIFY_SUCCESS(EncodeThenDecode(input));
+ }
+ return AssertionSuccess();
+ }
+
+ AssertionResult TestDecodingRandomizedStructures() {
+ VERIFY_SUCCESS(TestDecodingRandomizedStructures(100));
+ return AssertionSuccess();
+ }
+
+ uint32_t decode_offset_ = 0;
+ S structure_;
+ Http2StructureDecoder structure_decoder_;
+ size_t fast_decode_count_ = 0;
+ size_t slow_decode_count_ = 0;
+ size_t incomplete_start_count_ = 0;
+ size_t incomplete_resume_count_ = 0;
+};
+
+class Http2FrameHeaderDecoderTest
+ : public Http2StructureDecoderTest<Http2FrameHeader> {};
+
+TEST_F(Http2FrameHeaderDecoderTest, DecodesLiteral) {
+ {
+ // Realistic input.
+ const char kData[] = {
+ 0x00, 0x00, 0x05, // Payload length: 5
+ 0x01, // Frame type: HEADERS
+ 0x08, // Flags: PADDED
+ 0x00, 0x00, 0x00, 0x01, // Stream ID: 1
+ 0x04, // Padding length: 4
+ 0x00, 0x00, 0x00, 0x00, // Padding bytes
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(5u, structure_.payload_length);
+ EXPECT_EQ(Http2FrameType::HEADERS, structure_.type);
+ EXPECT_EQ(Http2FrameFlag::FLAG_PADDED, structure_.flags);
+ EXPECT_EQ(1u, structure_.stream_id);
+ }
+ {
+ // Unlikely input.
+ const char kData[] = {
+ 0xffu, 0xffu, 0xffu, // Payload length: uint24 max
+ 0xffu, // Frame type: Unknown
+ 0xffu, // Flags: Unknown/All
+ 0xffu, 0xffu, 0xffu, 0xffu, // Stream ID: uint31 max, plus R-bit
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ((1u << 24) - 1u, structure_.payload_length);
+ EXPECT_EQ(static_cast<Http2FrameType>(255), structure_.type);
+ EXPECT_EQ(255, structure_.flags);
+ EXPECT_EQ(0x7FFFFFFFu, structure_.stream_id);
+ }
+}
+
+TEST_F(Http2FrameHeaderDecoderTest, DecodesRandomized) {
+ TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2PriorityFieldsDecoderTest
+ : public Http2StructureDecoderTest<Http2PriorityFields> {};
+
+TEST_F(Http2PriorityFieldsDecoderTest, DecodesLiteral) {
+ {
+ const char kData[] = {
+ 0x80u, 0x00, 0x00, 0x05, // Exclusive (yes) and Dependency (5)
+ 0xffu, // Weight: 256 (after adding 1)
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(5u, structure_.stream_dependency);
+ EXPECT_EQ(256u, structure_.weight);
+ EXPECT_EQ(true, structure_.is_exclusive);
+ }
+ {
+ const char kData[] = {
+ 0x7fu, 0xffu,
+ 0xffu, 0xffu, // Exclusive (no) and Dependency (0x7fffffff)
+ 0x00u, // Weight: 1 (after adding 1)
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(StreamIdMask(), structure_.stream_dependency);
+ EXPECT_EQ(1u, structure_.weight);
+ EXPECT_FALSE(structure_.is_exclusive);
+ }
+}
+
+TEST_F(Http2PriorityFieldsDecoderTest, DecodesRandomized) {
+ TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2RstStreamFieldsDecoderTest
+ : public Http2StructureDecoderTest<Http2RstStreamFields> {};
+
+TEST_F(Http2RstStreamFieldsDecoderTest, DecodesLiteral) {
+ {
+ const char kData[] = {
+ 0x00, 0x00, 0x00, 0x01, // Error: PROTOCOL_ERROR
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_TRUE(structure_.IsSupportedErrorCode());
+ EXPECT_EQ(Http2ErrorCode::PROTOCOL_ERROR, structure_.error_code);
+ }
+ {
+ const char kData[] = {
+ 0xffu, 0xffu, 0xffu, 0xffu, // Error: max uint32 (Unknown error code)
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_FALSE(structure_.IsSupportedErrorCode());
+ EXPECT_EQ(static_cast<Http2ErrorCode>(0xffffffff), structure_.error_code);
+ }
+}
+
+TEST_F(Http2RstStreamFieldsDecoderTest, DecodesRandomized) {
+ TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2SettingFieldsDecoderTest
+ : public Http2StructureDecoderTest<Http2SettingFields> {};
+
+TEST_F(Http2SettingFieldsDecoderTest, DecodesLiteral) {
+ {
+ const char kData[] = {
+ 0x00, 0x01, // Setting: HEADER_TABLE_SIZE
+ 0x00, 0x00, 0x40, 0x00, // Value: 16K
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_TRUE(structure_.IsSupportedParameter());
+ EXPECT_EQ(Http2SettingsParameter::HEADER_TABLE_SIZE, structure_.parameter);
+ EXPECT_EQ(1u << 14, structure_.value);
+ }
+ {
+ const char kData[] = {
+ 0x00, 0x00, // Setting: Unknown (0)
+ 0xffu, 0xffu, 0xffu, 0xffu, // Value: max uint32
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_FALSE(structure_.IsSupportedParameter());
+ EXPECT_EQ(static_cast<Http2SettingsParameter>(0), structure_.parameter);
+ }
+}
+
+TEST_F(Http2SettingFieldsDecoderTest, DecodesRandomized) {
+ TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2PushPromiseFieldsDecoderTest
+ : public Http2StructureDecoderTest<Http2PushPromiseFields> {};
+
+TEST_F(Http2PushPromiseFieldsDecoderTest, DecodesLiteral) {
+ {
+ const char kData[] = {
+ 0x00, 0x01, 0x8au, 0x92u, // Promised Stream ID: 101010
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(101010u, structure_.promised_stream_id);
+ }
+ {
+ // Promised stream id has R-bit (reserved for future use) set, which
+ // should be cleared by the decoder.
+ const char kData[] = {
+ 0xffu, 0xffu, 0xffu, 0xffu, // Promised Stream ID: max uint31 and R-bit
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(StreamIdMask(), structure_.promised_stream_id);
+ }
+}
+
+TEST_F(Http2PushPromiseFieldsDecoderTest, DecodesRandomized) {
+ TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2PingFieldsDecoderTest
+ : public Http2StructureDecoderTest<Http2PingFields> {};
+
+TEST_F(Http2PingFieldsDecoderTest, DecodesLiteral) {
+ {
+ // Each byte is different, so can detect if order changed.
+ const char kData[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+ }
+ {
+ // All zeros, detect problems handling NULs.
+ const char kData[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+ }
+ {
+ const char kData[] = {
+ 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+ }
+}
+
+TEST_F(Http2PingFieldsDecoderTest, DecodesRandomized) {
+ TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2GoAwayFieldsDecoderTest
+ : public Http2StructureDecoderTest<Http2GoAwayFields> {};
+
+TEST_F(Http2GoAwayFieldsDecoderTest, DecodesLiteral) {
+ {
+ const char kData[] = {
+ 0x00, 0x00, 0x00, 0x00, // Last Stream ID: 0
+ 0x00, 0x00, 0x00, 0x00, // Error: NO_ERROR (0)
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(0u, structure_.last_stream_id);
+ EXPECT_TRUE(structure_.IsSupportedErrorCode());
+ EXPECT_EQ(Http2ErrorCode::HTTP2_NO_ERROR, structure_.error_code);
+ }
+ {
+ const char kData[] = {
+ 0x00, 0x00, 0x00, 0x01, // Last Stream ID: 1
+ 0x00, 0x00, 0x00, 0x0d, // Error: HTTP_1_1_REQUIRED
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(1u, structure_.last_stream_id);
+ EXPECT_TRUE(structure_.IsSupportedErrorCode());
+ EXPECT_EQ(Http2ErrorCode::HTTP_1_1_REQUIRED, structure_.error_code);
+ }
+ {
+ const char kData[] = {
+ 0xffu, 0xffu, 0xffu, 0xffu, // Last Stream ID: max uint31 and R-bit
+ 0xffu, 0xffu, 0xffu, 0xffu, // Error: max uint32 (Unknown error code)
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(StreamIdMask(), structure_.last_stream_id); // No high-bit.
+ EXPECT_FALSE(structure_.IsSupportedErrorCode());
+ EXPECT_EQ(static_cast<Http2ErrorCode>(0xffffffff), structure_.error_code);
+ }
+}
+
+TEST_F(Http2GoAwayFieldsDecoderTest, DecodesRandomized) {
+ TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2WindowUpdateFieldsDecoderTest
+ : public Http2StructureDecoderTest<Http2WindowUpdateFields> {};
+
+TEST_F(Http2WindowUpdateFieldsDecoderTest, DecodesLiteral) {
+ {
+ const char kData[] = {
+ 0x00, 0x01, 0x00, 0x00, // Window Size Increment: 2 ^ 16
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(1u << 16, structure_.window_size_increment);
+ }
+ {
+ // Increment must be non-zero, but we need to be able to decode the invalid
+ // zero to detect it.
+ const char kData[] = {
+ 0x00, 0x00, 0x00, 0x00, // Window Size Increment: 0
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(0u, structure_.window_size_increment);
+ }
+ {
+ // Increment has R-bit (reserved for future use) set, which
+ // should be cleared by the decoder.
+ const char kData[] = {
+ 0xffu, 0xffu, 0xffu,
+ 0xffu, // Window Size Increment: max uint31 and R-bit
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(StreamIdMask(), structure_.window_size_increment);
+ }
+}
+
+TEST_F(Http2WindowUpdateFieldsDecoderTest, DecodesRandomized) {
+ TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2AltSvcFieldsDecoderTest
+ : public Http2StructureDecoderTest<Http2AltSvcFields> {};
+
+TEST_F(Http2AltSvcFieldsDecoderTest, DecodesLiteral) {
+ {
+ const char kData[] = {
+ 0x00, 0x00, // Origin Length: 0
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(0, structure_.origin_length);
+ }
+ {
+ const char kData[] = {
+ 0x00, 0x14, // Origin Length: 20
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(20, structure_.origin_length);
+ }
+ {
+ const char kData[] = {
+ 0xffu, 0xffu, // Origin Length: uint16 max
+ };
+ ASSERT_TRUE(DecodeLeadingStructure(kData));
+ EXPECT_EQ(65535, structure_.origin_length);
+ }
+}
+
+TEST_F(Http2AltSvcFieldsDecoderTest, DecodesRandomized) {
+ TestDecodingRandomizedStructures();
+}
+
+} // namespace
+} // namespace test
+} // namespace net
« no previous file with comments | « net/http2/decoder/http2_structure_decoder.cc ('k') | net/http2/decoder/http2_structure_decoder_test_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698