|  | // 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 "quiche/http2/decoder/http2_frame_decoder.h" | 
|  |  | 
|  | // Tests of Http2FrameDecoder. | 
|  |  | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "quiche/http2/http2_constants.h" | 
|  | #include "quiche/http2/test_tools/frame_parts.h" | 
|  | #include "quiche/http2/test_tools/frame_parts_collector_listener.h" | 
|  | #include "quiche/http2/test_tools/http2_random.h" | 
|  | #include "quiche/http2/test_tools/random_decoder_test_base.h" | 
|  | #include "quiche/http2/test_tools/verify_macros.h" | 
|  | #include "quiche/common/platform/api/quiche_logging.h" | 
|  |  | 
|  | using ::testing::AssertionSuccess; | 
|  |  | 
|  | namespace http2 { | 
|  | namespace test { | 
|  | class Http2FrameDecoderPeer { | 
|  | public: | 
|  | static size_t remaining_total_payload(Http2FrameDecoder* decoder) { | 
|  | return decoder->frame_decoder_state_.remaining_total_payload(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class Http2FrameDecoderTest : public RandomDecoderTest { | 
|  | protected: | 
|  | DecodeStatus StartDecoding(DecodeBuffer* db) override { | 
|  | QUICHE_DVLOG(2) << "StartDecoding, db->Remaining=" << db->Remaining(); | 
|  | collector_.Reset(); | 
|  | PrepareDecoder(); | 
|  |  | 
|  | DecodeStatus status = decoder_->DecodeFrame(db); | 
|  | if (status != DecodeStatus::kDecodeInProgress) { | 
|  | // Keep track of this so that a concrete test can verify that both fast | 
|  | // and slow decoding paths have been tested. | 
|  | ++fast_decode_count_; | 
|  | if (status == DecodeStatus::kDecodeError) { | 
|  | ConfirmDiscardsRemainingPayload(); | 
|  | } | 
|  | } | 
|  | return status; | 
|  | } | 
|  |  | 
|  | DecodeStatus ResumeDecoding(DecodeBuffer* db) override { | 
|  | QUICHE_DVLOG(2) << "ResumeDecoding, db->Remaining=" << db->Remaining(); | 
|  | DecodeStatus status = decoder_->DecodeFrame(db); | 
|  | if (status != DecodeStatus::kDecodeInProgress) { | 
|  | // Keep track of this so that a concrete test can verify that both fast | 
|  | // and slow decoding paths have been tested. | 
|  | ++slow_decode_count_; | 
|  | if (status == DecodeStatus::kDecodeError) { | 
|  | ConfirmDiscardsRemainingPayload(); | 
|  | } | 
|  | } | 
|  | return status; | 
|  | } | 
|  |  | 
|  | // When an error is returned, the decoder is in state kDiscardPayload, and | 
|  | // stays there until the remaining bytes of the frame's payload have been | 
|  | // skipped over. There are no callbacks for this situation. | 
|  | void ConfirmDiscardsRemainingPayload() { | 
|  | ASSERT_TRUE(decoder_->IsDiscardingPayload()); | 
|  | size_t remaining = | 
|  | Http2FrameDecoderPeer::remaining_total_payload(decoder_.get()); | 
|  | // The decoder will discard the remaining bytes, but not go beyond that, | 
|  | // which these conditions verify. | 
|  | size_t extra = 10; | 
|  | std::string junk(remaining + extra, '0'); | 
|  | DecodeBuffer tmp(junk); | 
|  | EXPECT_EQ(DecodeStatus::kDecodeDone, decoder_->DecodeFrame(&tmp)); | 
|  | EXPECT_EQ(remaining, tmp.Offset()); | 
|  | EXPECT_EQ(extra, tmp.Remaining()); | 
|  | EXPECT_FALSE(decoder_->IsDiscardingPayload()); | 
|  | } | 
|  |  | 
|  | void PrepareDecoder() { | 
|  | decoder_ = std::make_unique<Http2FrameDecoder>(&collector_); | 
|  | decoder_->set_maximum_payload_size(maximum_payload_size_); | 
|  | } | 
|  |  | 
|  | void ResetDecodeSpeedCounters() { | 
|  | fast_decode_count_ = 0; | 
|  | slow_decode_count_ = 0; | 
|  | } | 
|  |  | 
|  | AssertionResult VerifyCollected(const FrameParts& expected) { | 
|  | HTTP2_VERIFY_FALSE(collector_.IsInProgress()); | 
|  | HTTP2_VERIFY_EQ(1u, collector_.size()); | 
|  | return expected.VerifyEquals(*collector_.frame(0)); | 
|  | } | 
|  |  | 
|  | AssertionResult DecodePayloadAndValidateSeveralWays(absl::string_view payload, | 
|  | Validator validator) { | 
|  | DecodeBuffer db(payload); | 
|  | bool start_decoding_requires_non_empty = false; | 
|  | return DecodeAndValidateSeveralWays(&db, start_decoding_requires_non_empty, | 
|  | validator); | 
|  | } | 
|  |  | 
|  | // Decode one frame's payload and confirm that the listener recorded the | 
|  | // expected FrameParts instance, and only one FrameParts instance. The | 
|  | // payload will be decoded several times with different partitionings | 
|  | // of the payload, and after each the validator will be called. | 
|  | AssertionResult DecodePayloadAndValidateSeveralWays( | 
|  | absl::string_view payload, const FrameParts& expected) { | 
|  | auto validator = [&expected, this](const DecodeBuffer& /*input*/, | 
|  | DecodeStatus status) -> AssertionResult { | 
|  | HTTP2_VERIFY_EQ(status, DecodeStatus::kDecodeDone); | 
|  | return VerifyCollected(expected); | 
|  | }; | 
|  | ResetDecodeSpeedCounters(); | 
|  | HTTP2_VERIFY_SUCCESS(DecodePayloadAndValidateSeveralWays( | 
|  | payload, ValidateDoneAndEmpty(validator))); | 
|  | HTTP2_VERIFY_GT(fast_decode_count_, 0u); | 
|  | HTTP2_VERIFY_GT(slow_decode_count_, 0u); | 
|  |  | 
|  | // Repeat with more input; it should stop without reading that input. | 
|  | std::string next_frame = Random().RandString(10); | 
|  | std::string input(payload.data(), payload.size()); | 
|  | input += next_frame; | 
|  |  | 
|  | ResetDecodeSpeedCounters(); | 
|  | HTTP2_VERIFY_SUCCESS(DecodePayloadAndValidateSeveralWays( | 
|  | payload, ValidateDoneAndOffset(payload.size(), validator))); | 
|  | HTTP2_VERIFY_GT(fast_decode_count_, 0u); | 
|  | HTTP2_VERIFY_GT(slow_decode_count_, 0u); | 
|  |  | 
|  | return AssertionSuccess(); | 
|  | } | 
|  |  | 
|  | template <size_t N> | 
|  | AssertionResult DecodePayloadAndValidateSeveralWays( | 
|  | const char (&buf)[N], const FrameParts& expected) { | 
|  | return DecodePayloadAndValidateSeveralWays(absl::string_view(buf, N), | 
|  | expected); | 
|  | } | 
|  |  | 
|  | template <size_t N> | 
|  | AssertionResult DecodePayloadAndValidateSeveralWays( | 
|  | const char (&buf)[N], const Http2FrameHeader& header) { | 
|  | return DecodePayloadAndValidateSeveralWays(absl::string_view(buf, N), | 
|  | FrameParts(header)); | 
|  | } | 
|  |  | 
|  | template <size_t N> | 
|  | AssertionResult DecodePayloadExpectingError(const char (&buf)[N], | 
|  | const FrameParts& expected) { | 
|  | auto validator = [&expected, this](const DecodeBuffer& /*input*/, | 
|  | DecodeStatus status) -> AssertionResult { | 
|  | HTTP2_VERIFY_EQ(status, DecodeStatus::kDecodeError); | 
|  | return VerifyCollected(expected); | 
|  | }; | 
|  | ResetDecodeSpeedCounters(); | 
|  | EXPECT_TRUE( | 
|  | DecodePayloadAndValidateSeveralWays(ToStringPiece(buf), validator)); | 
|  | EXPECT_GT(fast_decode_count_, 0u); | 
|  | EXPECT_GT(slow_decode_count_, 0u); | 
|  | return AssertionSuccess(); | 
|  | } | 
|  |  | 
|  | template <size_t N> | 
|  | AssertionResult DecodePayloadExpectingFrameSizeError(const char (&buf)[N], | 
|  | FrameParts expected) { | 
|  | expected.SetHasFrameSizeError(true); | 
|  | return DecodePayloadExpectingError(buf, expected); | 
|  | } | 
|  |  | 
|  | template <size_t N> | 
|  | AssertionResult DecodePayloadExpectingFrameSizeError( | 
|  | const char (&buf)[N], const Http2FrameHeader& header) { | 
|  | return DecodePayloadExpectingFrameSizeError(buf, FrameParts(header)); | 
|  | } | 
|  |  | 
|  | // Count of payloads that are fully decoded by StartDecodingPayload or for | 
|  | // which an error was detected by StartDecodingPayload. | 
|  | size_t fast_decode_count_ = 0; | 
|  |  | 
|  | // Count of payloads that required calling ResumeDecodingPayload in order to | 
|  | // decode completely, or for which an error was detected by | 
|  | // ResumeDecodingPayload. | 
|  | size_t slow_decode_count_ = 0; | 
|  |  | 
|  | uint32_t maximum_payload_size_ = Http2SettingsInfo::DefaultMaxFrameSize(); | 
|  | FramePartsCollectorListener collector_; | 
|  | std::unique_ptr<Http2FrameDecoder> decoder_; | 
|  | }; | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | // Tests that pass the minimum allowed size for the frame type, which is often | 
|  | // empty. The tests are in order by frame type value (i.e. 0 for DATA frames). | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, DataEmpty) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x00',  // Payload length: 0 | 
|  | '\x00',                  // DATA | 
|  | '\x00',                  // Flags: none | 
|  | '\x00', '\x00', '\x00', | 
|  | '\x00',  // Stream ID: 0 (invalid but unchecked here) | 
|  | }; | 
|  | Http2FrameHeader header(0, Http2FrameType::DATA, 0, 0); | 
|  | FrameParts expected(header, ""); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, HeadersEmpty) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x00',          // Payload length: 0 | 
|  | '\x01',                          // HEADERS | 
|  | '\x00',                          // Flags: none | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream ID: 0  (REQUIRES ID) | 
|  | }; | 
|  | Http2FrameHeader header(0, Http2FrameType::HEADERS, 0, 1); | 
|  | FrameParts expected(header, ""); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, Priority) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x05',          // Length: 5 | 
|  | '\x02',                          //   Type: PRIORITY | 
|  | '\x00',                          //  Flags: none | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream: 2 | 
|  | '\x80', '\x00', '\x00', '\x01',  // Parent: 1 (Exclusive) | 
|  | '\x10',                          // Weight: 17 | 
|  | }; | 
|  | Http2FrameHeader header(5, Http2FrameType::PRIORITY, 0, 2); | 
|  | FrameParts expected(header); | 
|  | expected.SetOptPriority(Http2PriorityFields(1, 17, true)); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, RstStream) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x04',          // Length: 4 | 
|  | '\x03',                          //   Type: RST_STREAM | 
|  | '\x00',                          //  Flags: none | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream: 1 | 
|  | '\x00', '\x00', '\x00', '\x01',  //  Error: PROTOCOL_ERROR | 
|  | }; | 
|  | Http2FrameHeader header(4, Http2FrameType::RST_STREAM, 0, 1); | 
|  | FrameParts expected(header); | 
|  | expected.SetOptRstStreamErrorCode(Http2ErrorCode::PROTOCOL_ERROR); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, SettingsEmpty) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x00',          // Length: 0 | 
|  | '\x04',                          //   Type: SETTINGS | 
|  | '\x00',                          //  Flags: none | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream: 1 (invalid but unchecked here) | 
|  | }; | 
|  | Http2FrameHeader header(0, Http2FrameType::SETTINGS, 0, 1); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, SettingsAck) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x00',          //   Length: 6 | 
|  | '\x04',                          //     Type: SETTINGS | 
|  | '\x01',                          //    Flags: ACK | 
|  | '\x00', '\x00', '\x00', '\x00',  //   Stream: 0 | 
|  | }; | 
|  | Http2FrameHeader header(0, Http2FrameType::SETTINGS, Http2FrameFlag::ACK, 0); | 
|  | FrameParts expected(header); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, PushPromiseMinimal) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x04',  // Payload length: 4 | 
|  | '\x05',                  // PUSH_PROMISE | 
|  | '\x04',                  // Flags: END_HEADERS | 
|  | '\x00', '\x00', '\x00', | 
|  | '\x02',  //   Stream: 2 (invalid but unchecked here) | 
|  | '\x00', '\x00', '\x00', | 
|  | '\x01',  // Promised: 1 (invalid but unchecked here) | 
|  | }; | 
|  | Http2FrameHeader header(4, Http2FrameType::PUSH_PROMISE, | 
|  | Http2FrameFlag::END_HEADERS, 2); | 
|  | FrameParts expected(header, ""); | 
|  | expected.SetOptPushPromise(Http2PushPromiseFields{1}); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, Ping) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x08',          //   Length: 8 | 
|  | '\x06',                          //     Type: PING | 
|  | '\xfe',                          //    Flags: no valid flags | 
|  | '\x00', '\x00', '\x00', '\x00',  //   Stream: 0 | 
|  | 's',    'o',    'm',    'e',     // "some" | 
|  | 'd',    'a',    't',    'a',     // "data" | 
|  | }; | 
|  | Http2FrameHeader header(8, Http2FrameType::PING, 0, 0); | 
|  | FrameParts expected(header); | 
|  | expected.SetOptPing( | 
|  | Http2PingFields{{'s', 'o', 'm', 'e', 'd', 'a', 't', 'a'}}); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, PingAck) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x08',          //   Length: 8 | 
|  | '\x06',                          //     Type: PING | 
|  | '\xff',                          //    Flags: ACK (plus all invalid flags) | 
|  | '\x00', '\x00', '\x00', '\x00',  //   Stream: 0 | 
|  | 's',    'o',    'm',    'e',     // "some" | 
|  | 'd',    'a',    't',    'a',     // "data" | 
|  | }; | 
|  | Http2FrameHeader header(8, Http2FrameType::PING, Http2FrameFlag::ACK, 0); | 
|  | FrameParts expected(header); | 
|  | expected.SetOptPing( | 
|  | Http2PingFields{{'s', 'o', 'm', 'e', 'd', 'a', 't', 'a'}}); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, GoAwayMinimal) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x08',          // Length: 8 (no opaque data) | 
|  | '\x07',                          //   Type: GOAWAY | 
|  | '\xff',                          //  Flags: 0xff (no valid flags) | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream: 1 (invalid but unchecked here) | 
|  | '\x80', '\x00', '\x00', '\xff',  //   Last: 255 (plus R bit) | 
|  | '\x00', '\x00', '\x00', '\x09',  //  Error: COMPRESSION_ERROR | 
|  | }; | 
|  | Http2FrameHeader header(8, Http2FrameType::GOAWAY, 0, 1); | 
|  | FrameParts expected(header); | 
|  | expected.SetOptGoaway( | 
|  | Http2GoAwayFields(255, Http2ErrorCode::COMPRESSION_ERROR)); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, WindowUpdate) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x04',          // Length: 4 | 
|  | '\x08',                          //   Type: WINDOW_UPDATE | 
|  | '\x0f',                          //  Flags: 0xff (no valid flags) | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream: 1 | 
|  | '\x80', '\x00', '\x04', '\x00',  //   Incr: 1024 (plus R bit) | 
|  | }; | 
|  | Http2FrameHeader header(4, Http2FrameType::WINDOW_UPDATE, 0, 1); | 
|  | FrameParts expected(header); | 
|  | expected.SetOptWindowUpdateIncrement(1024); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, ContinuationEmpty) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x00',  // Payload length: 0 | 
|  | '\x09',                  // CONTINUATION | 
|  | '\x00',                  // Flags: none | 
|  | '\x00', '\x00', '\x00', | 
|  | '\x00',  // Stream ID: 0 (invalid but unchecked here) | 
|  | }; | 
|  | Http2FrameHeader header(0, Http2FrameType::CONTINUATION, 0, 0); | 
|  | FrameParts expected(header); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, AltSvcMinimal) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x02',  // Payload length: 2 | 
|  | '\x0a',                  // ALTSVC | 
|  | '\xff',                  // Flags: none (plus 0xff) | 
|  | '\x00', '\x00', '\x00', | 
|  | '\x00',          // Stream ID: 0 (invalid but unchecked here) | 
|  | '\x00', '\x00',  // Origin Length: 0 | 
|  | }; | 
|  | Http2FrameHeader header(2, Http2FrameType::ALTSVC, 0, 0); | 
|  | FrameParts expected(header); | 
|  | expected.SetOptAltsvcOriginLength(0); | 
|  | expected.SetOptAltsvcValueLength(0); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, UnknownEmpty) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x00',          // Payload length: 0 | 
|  | '\x20',                          // 32 (unknown) | 
|  | '\xff',                          // Flags: all | 
|  | '\x00', '\x00', '\x00', '\x00',  // Stream ID: 0 | 
|  | }; | 
|  | Http2FrameHeader header(0, static_cast<Http2FrameType>(32), 0xff, 0); | 
|  | FrameParts expected(header); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | // Tests of longer payloads, for those frame types that allow longer payloads. | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, DataPayload) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x03',          // Payload length: 7 | 
|  | '\x00',                          // DATA | 
|  | '\x80',                          // Flags: 0x80 | 
|  | '\x00', '\x00', '\x02', '\x02',  // Stream ID: 514 | 
|  | 'a',    'b',    'c',             // Data | 
|  | }; | 
|  | Http2FrameHeader header(3, Http2FrameType::DATA, 0, 514); | 
|  | FrameParts expected(header, "abc"); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, HeadersPayload) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x03',          // Payload length: 3 | 
|  | '\x01',                          // HEADERS | 
|  | '\x05',                          // Flags: END_STREAM | END_HEADERS | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream ID: 0  (REQUIRES ID) | 
|  | 'a',    'b',    'c',  // HPACK fragment (doesn't have to be valid) | 
|  | }; | 
|  | Http2FrameHeader header( | 
|  | 3, Http2FrameType::HEADERS, | 
|  | Http2FrameFlag::END_STREAM | Http2FrameFlag::END_HEADERS, 2); | 
|  | FrameParts expected(header, "abc"); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, HeadersPriority) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x05',          // Payload length: 5 | 
|  | '\x01',                          // HEADERS | 
|  | '\x20',                          // Flags: PRIORITY | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream ID: 0  (REQUIRES ID) | 
|  | '\x00', '\x00', '\x00', '\x01',  // Parent: 1 (Not Exclusive) | 
|  | '\xff',                          // Weight: 256 | 
|  | }; | 
|  | Http2FrameHeader header(5, Http2FrameType::HEADERS, Http2FrameFlag::PRIORITY, | 
|  | 2); | 
|  | FrameParts expected(header); | 
|  | expected.SetOptPriority(Http2PriorityFields(1, 256, false)); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, Settings) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x0c',          // Length: 12 | 
|  | '\x04',                          //   Type: SETTINGS | 
|  | '\x00',                          //  Flags: none | 
|  | '\x00', '\x00', '\x00', '\x00',  // Stream: 0 | 
|  | '\x00', '\x04',                  //  Param: INITIAL_WINDOW_SIZE | 
|  | '\x0a', '\x0b', '\x0c', '\x0d',  //  Value: 168496141 | 
|  | '\x00', '\x02',                  //  Param: ENABLE_PUSH | 
|  | '\x00', '\x00', '\x00', '\x03',  //  Value: 3 (invalid but unchecked here) | 
|  | }; | 
|  | Http2FrameHeader header(12, Http2FrameType::SETTINGS, 0, 0); | 
|  | FrameParts expected(header); | 
|  | expected.AppendSetting(Http2SettingFields( | 
|  | Http2SettingsParameter::INITIAL_WINDOW_SIZE, 168496141)); | 
|  | expected.AppendSetting( | 
|  | Http2SettingFields(Http2SettingsParameter::ENABLE_PUSH, 3)); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, PushPromisePayload) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', 7,               // Payload length: 7 | 
|  | '\x05',                          // PUSH_PROMISE | 
|  | '\x04',                          // Flags: END_HEADERS | 
|  | '\x00', '\x00', '\x00', '\xff',  // Stream ID: 255 | 
|  | '\x00', '\x00', '\x01', '\x00',  // Promised: 256 | 
|  | 'a',    'b',    'c',  // HPACK fragment (doesn't have to be valid) | 
|  | }; | 
|  | Http2FrameHeader header(7, Http2FrameType::PUSH_PROMISE, | 
|  | Http2FrameFlag::END_HEADERS, 255); | 
|  | FrameParts expected(header, "abc"); | 
|  | expected.SetOptPushPromise(Http2PushPromiseFields{256}); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, GoAwayOpaqueData) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x0e',          // Length: 14 | 
|  | '\x07',                          //   Type: GOAWAY | 
|  | '\xff',                          //  Flags: 0xff (no valid flags) | 
|  | '\x80', '\x00', '\x00', '\x00',  // Stream: 0 (plus R bit) | 
|  | '\x00', '\x00', '\x01', '\x00',  //   Last: 256 | 
|  | '\x00', '\x00', '\x00', '\x03',  //  Error: FLOW_CONTROL_ERROR | 
|  | 'o',    'p',    'a',    'q',    'u', 'e', | 
|  | }; | 
|  | Http2FrameHeader header(14, Http2FrameType::GOAWAY, 0, 0); | 
|  | FrameParts expected(header, "opaque"); | 
|  | expected.SetOptGoaway( | 
|  | Http2GoAwayFields(256, Http2ErrorCode::FLOW_CONTROL_ERROR)); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, ContinuationPayload) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x03',          // Payload length: 3 | 
|  | '\x09',                          // CONTINUATION | 
|  | '\xff',                          // Flags: END_HEADERS | 0xfb | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream ID: 2 | 
|  | 'a',    'b',    'c',             // Data | 
|  | }; | 
|  | Http2FrameHeader header(3, Http2FrameType::CONTINUATION, | 
|  | Http2FrameFlag::END_HEADERS, 2); | 
|  | FrameParts expected(header, "abc"); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, AltSvcPayload) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x08',          // Payload length: 3 | 
|  | '\x0a',                          // ALTSVC | 
|  | '\x00',                          // Flags: none | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream ID: 2 | 
|  | '\x00', '\x03',                  // Origin Length: 0 | 
|  | 'a',    'b',    'c',             // Origin | 
|  | 'd',    'e',    'f',             // Value | 
|  | }; | 
|  | Http2FrameHeader header(8, Http2FrameType::ALTSVC, 0, 2); | 
|  | FrameParts expected(header); | 
|  | expected.SetAltSvcExpected("abc", "def"); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, PriorityUpdatePayload) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x07',          // Payload length: 7 | 
|  | '\x10',                          // PRIORITY_UPDATE | 
|  | '\x00',                          // Flags: none | 
|  | '\x00', '\x00', '\x00', '\x00',  // Stream ID: 0 | 
|  | '\x00', '\x00', '\x00', '\x05',  // Prioritized Stream ID: 5 | 
|  | 'a',    'b',    'c',             // Priority Field Value | 
|  | }; | 
|  | Http2FrameHeader header(7, Http2FrameType::PRIORITY_UPDATE, 0, 0); | 
|  |  | 
|  | FrameParts expected(header, "abc"); | 
|  | expected.SetOptPriorityUpdate(Http2PriorityUpdateFields{5}); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, UnknownPayload) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x03',          // Payload length: 3 | 
|  | '\x30',                          // 48 (unknown) | 
|  | '\x00',                          // Flags: none | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream ID: 2 | 
|  | 'a',    'b',    'c',             // Payload | 
|  | }; | 
|  | Http2FrameHeader header(3, static_cast<Http2FrameType>(48), 0, 2); | 
|  | FrameParts expected(header, "abc"); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | // Tests of padded payloads, for those frame types that allow padding. | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, DataPayloadAndPadding) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x07',          // Payload length: 7 | 
|  | '\x00',                          // DATA | 
|  | '\x09',                          // Flags: END_STREAM | PADDED | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream ID: 0  (REQUIRES ID) | 
|  | '\x03',                          // Pad Len | 
|  | 'a',    'b',    'c',             // Data | 
|  | '\x00', '\x00', '\x00',          // Padding | 
|  | }; | 
|  | Http2FrameHeader header(7, Http2FrameType::DATA, | 
|  | Http2FrameFlag::END_STREAM | Http2FrameFlag::PADDED, | 
|  | 2); | 
|  | size_t total_pad_length = 4;  // Including the Pad Length field. | 
|  | FrameParts expected(header, "abc", total_pad_length); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, HeadersPayloadAndPadding) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x07',          // Payload length: 7 | 
|  | '\x01',                          // HEADERS | 
|  | '\x08',                          // Flags: PADDED | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream ID: 0  (REQUIRES ID) | 
|  | '\x03',                          // Pad Len | 
|  | 'a',    'b',    'c',     // HPACK fragment (doesn't have to be valid) | 
|  | '\x00', '\x00', '\x00',  // Padding | 
|  | }; | 
|  | Http2FrameHeader header(7, Http2FrameType::HEADERS, Http2FrameFlag::PADDED, | 
|  | 2); | 
|  | size_t total_pad_length = 4;  // Including the Pad Length field. | 
|  | FrameParts expected(header, "abc", total_pad_length); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, HeadersPayloadPriorityAndPadding) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x0c',          // Payload length: 12 | 
|  | '\x01',                          // HEADERS | 
|  | '\xff',                          // Flags: all, including undefined | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream ID: 0  (REQUIRES ID) | 
|  | '\x03',                          // Pad Len | 
|  | '\x80', '\x00', '\x00', '\x01',  // Parent: 1 (Exclusive) | 
|  | '\x10',                          // Weight: 17 | 
|  | 'a',    'b',    'c',     // HPACK fragment (doesn't have to be valid) | 
|  | '\x00', '\x00', '\x00',  // Padding | 
|  | }; | 
|  | Http2FrameHeader header(12, Http2FrameType::HEADERS, | 
|  | Http2FrameFlag::END_STREAM | | 
|  | Http2FrameFlag::END_HEADERS | | 
|  | Http2FrameFlag::PADDED | Http2FrameFlag::PRIORITY, | 
|  | 2); | 
|  | size_t total_pad_length = 4;  // Including the Pad Length field. | 
|  | FrameParts expected(header, "abc", total_pad_length); | 
|  | expected.SetOptPriority(Http2PriorityFields(1, 17, true)); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, PushPromisePayloadAndPadding) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', 11,              // Payload length: 11 | 
|  | '\x05',                          // PUSH_PROMISE | 
|  | '\xff',                          // Flags: END_HEADERS | PADDED | 0xf3 | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream ID: 1 | 
|  | '\x03',                          // Pad Len | 
|  | '\x00', '\x00', '\x00', '\x02',  // Promised: 2 | 
|  | 'a',    'b',    'c',     // HPACK fragment (doesn't have to be valid) | 
|  | '\x00', '\x00', '\x00',  // Padding | 
|  | }; | 
|  | Http2FrameHeader header(11, Http2FrameType::PUSH_PROMISE, | 
|  | Http2FrameFlag::END_HEADERS | Http2FrameFlag::PADDED, | 
|  | 1); | 
|  | size_t total_pad_length = 4;  // Including the Pad Length field. | 
|  | FrameParts expected(header, "abc", total_pad_length); | 
|  | expected.SetOptPushPromise(Http2PushPromiseFields{2}); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | // Payload too short errors. | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, DataMissingPadLengthField) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x00',          // Payload length: 0 | 
|  | '\x00',                          // DATA | 
|  | '\x08',                          // Flags: PADDED | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream ID: 1 | 
|  | }; | 
|  | Http2FrameHeader header(0, Http2FrameType::DATA, Http2FrameFlag::PADDED, 1); | 
|  | FrameParts expected(header); | 
|  | expected.SetOptMissingLength(1); | 
|  | EXPECT_TRUE(DecodePayloadExpectingError(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, HeaderPaddingTooLong) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x02',          // Payload length: 0 | 
|  | '\x01',                          // HEADERS | 
|  | '\x08',                          // Flags: PADDED | 
|  | '\x00', '\x01', '\x00', '\x00',  // Stream ID: 65536 | 
|  | '\xff',                          // Pad Len: 255 | 
|  | '\x00',                          // Only one byte of padding | 
|  | }; | 
|  | Http2FrameHeader header(2, Http2FrameType::HEADERS, Http2FrameFlag::PADDED, | 
|  | 65536); | 
|  | FrameParts expected(header); | 
|  | expected.SetOptMissingLength(254); | 
|  | EXPECT_TRUE(DecodePayloadExpectingError(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, HeaderMissingPriority) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x04',          // Payload length: 0 | 
|  | '\x01',                          // HEADERS | 
|  | '\x20',                          // Flags: PRIORITY | 
|  | '\x00', '\x01', '\x00', '\x00',  // Stream ID: 65536 | 
|  | '\x00', '\x00', '\x00', '\x00',  // Priority (truncated) | 
|  | }; | 
|  | Http2FrameHeader header(4, Http2FrameType::HEADERS, Http2FrameFlag::PRIORITY, | 
|  | 65536); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, PriorityTooShort) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x04',          // Length: 5 | 
|  | '\x02',                          //   Type: PRIORITY | 
|  | '\x00',                          //  Flags: none | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream: 2 | 
|  | '\x80', '\x00', '\x00', '\x01',  // Parent: 1 (Exclusive) | 
|  | }; | 
|  | Http2FrameHeader header(4, Http2FrameType::PRIORITY, 0, 2); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, RstStreamTooShort) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x03',          // Length: 4 | 
|  | '\x03',                          //   Type: RST_STREAM | 
|  | '\x00',                          //  Flags: none | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream: 1 | 
|  | '\x00', '\x00', '\x00',          //  Truncated | 
|  | }; | 
|  | Http2FrameHeader header(3, Http2FrameType::RST_STREAM, 0, 1); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | // SETTINGS frames must a multiple of 6 bytes long, so an 9 byte payload is | 
|  | // invalid. | 
|  | TEST_F(Http2FrameDecoderTest, SettingsWrongSize) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x09',          // Length: 2 | 
|  | '\x04',                          //   Type: SETTINGS | 
|  | '\x00',                          //  Flags: none | 
|  | '\x00', '\x00', '\x00', '\x00',  // Stream: 0 | 
|  | '\x00', '\x02',                  //  Param: ENABLE_PUSH | 
|  | '\x00', '\x00', '\x00', '\x03',  //  Value: 1 | 
|  | '\x00', '\x04',                  //  Param: INITIAL_WINDOW_SIZE | 
|  | '\x00',                          //  Value: Truncated | 
|  | }; | 
|  | Http2FrameHeader header(9, Http2FrameType::SETTINGS, 0, 0); | 
|  | FrameParts expected(header); | 
|  | expected.AppendSetting( | 
|  | Http2SettingFields(Http2SettingsParameter::ENABLE_PUSH, 3)); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, expected)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, PushPromiseTooShort) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', 3,               // Payload length: 3 | 
|  | '\x05',                          // PUSH_PROMISE | 
|  | '\x00',                          // Flags: none | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream ID: 1 | 
|  | '\x00', '\x00', '\x00',          // Truncated promise id | 
|  | }; | 
|  | Http2FrameHeader header(3, Http2FrameType::PUSH_PROMISE, 0, 1); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, PushPromisePaddedTruncatedPromise) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', 4,               // Payload length: 4 | 
|  | '\x05',                          // PUSH_PROMISE | 
|  | '\x08',                          // Flags: PADDED | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream ID: 1 | 
|  | '\x00',                          // Pad Len | 
|  | '\x00', '\x00', '\x00',          // Truncated promise id | 
|  | }; | 
|  | Http2FrameHeader header(4, Http2FrameType::PUSH_PROMISE, | 
|  | Http2FrameFlag::PADDED, 1); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, PingTooShort) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x07',          //   Length: 8 | 
|  | '\x06',                          //     Type: PING | 
|  | '\xfe',                          //    Flags: no valid flags | 
|  | '\x00', '\x00', '\x00', '\x00',  //   Stream: 0 | 
|  | 's',    'o',    'm',    'e',     // "some" | 
|  | 'd',    'a',    't',             // Too little | 
|  | }; | 
|  | Http2FrameHeader header(7, Http2FrameType::PING, 0, 0); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, GoAwayTooShort) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x00',          // Length: 0 | 
|  | '\x07',                          //   Type: GOAWAY | 
|  | '\xff',                          //  Flags: 0xff (no valid flags) | 
|  | '\x00', '\x00', '\x00', '\x00',  // Stream: 0 | 
|  | }; | 
|  | Http2FrameHeader header(0, Http2FrameType::GOAWAY, 0, 0); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, WindowUpdateTooShort) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x03',          // Length: 3 | 
|  | '\x08',                          //   Type: WINDOW_UPDATE | 
|  | '\x0f',                          //  Flags: 0xff (no valid flags) | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream: 1 | 
|  | '\x80', '\x00', '\x04',          // Truncated | 
|  | }; | 
|  | Http2FrameHeader header(3, Http2FrameType::WINDOW_UPDATE, 0, 1); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, AltSvcTruncatedOriginLength) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x01',          // Payload length: 3 | 
|  | '\x0a',                          // ALTSVC | 
|  | '\x00',                          // Flags: none | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream ID: 2 | 
|  | '\x00',                          // Origin Length: truncated | 
|  | }; | 
|  | Http2FrameHeader header(1, Http2FrameType::ALTSVC, 0, 2); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, AltSvcTruncatedOrigin) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x05',          // Payload length: 3 | 
|  | '\x0a',                          // ALTSVC | 
|  | '\x00',                          // Flags: none | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream ID: 2 | 
|  | '\x00', '\x04',                  // Origin Length: 4 (too long) | 
|  | 'a',    'b',    'c',             // Origin | 
|  | }; | 
|  | Http2FrameHeader header(5, Http2FrameType::ALTSVC, 0, 2); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | // Payload too long errors. | 
|  |  | 
|  | // The decoder calls the listener's OnFrameSizeError method if the frame's | 
|  | // payload is longer than the currently configured maximum payload size. | 
|  | TEST_F(Http2FrameDecoderTest, BeyondMaximum) { | 
|  | maximum_payload_size_ = 2; | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x07',          // Payload length: 7 | 
|  | '\x00',                          // DATA | 
|  | '\x09',                          // Flags: END_STREAM | PADDED | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream ID: 0  (REQUIRES ID) | 
|  | '\x03',                          // Pad Len | 
|  | 'a',    'b',    'c',             // Data | 
|  | '\x00', '\x00', '\x00',          // Padding | 
|  | }; | 
|  | Http2FrameHeader header(7, Http2FrameType::DATA, | 
|  | Http2FrameFlag::END_STREAM | Http2FrameFlag::PADDED, | 
|  | 2); | 
|  | FrameParts expected(header); | 
|  | expected.SetHasFrameSizeError(true); | 
|  | auto validator = [&expected, this](const DecodeBuffer& input, | 
|  | DecodeStatus status) -> AssertionResult { | 
|  | HTTP2_VERIFY_EQ(status, DecodeStatus::kDecodeError); | 
|  | // The decoder detects this error after decoding the header, and without | 
|  | // trying to decode the payload. | 
|  | HTTP2_VERIFY_EQ(input.Offset(), Http2FrameHeader::EncodedSize()); | 
|  | return VerifyCollected(expected); | 
|  | }; | 
|  | ResetDecodeSpeedCounters(); | 
|  | EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(ToStringPiece(kFrameData), | 
|  | validator)); | 
|  | EXPECT_GT(fast_decode_count_, 0u); | 
|  | EXPECT_GT(slow_decode_count_, 0u); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, PriorityTooLong) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x06',          // Length: 5 | 
|  | '\x02',                          //   Type: PRIORITY | 
|  | '\x00',                          //  Flags: none | 
|  | '\x00', '\x00', '\x00', '\x02',  // Stream: 2 | 
|  | '\x80', '\x00', '\x00', '\x01',  // Parent: 1 (Exclusive) | 
|  | '\x10',                          // Weight: 17 | 
|  | '\x00',                          // Too much | 
|  | }; | 
|  | Http2FrameHeader header(6, Http2FrameType::PRIORITY, 0, 2); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, RstStreamTooLong) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x05',          // Length: 4 | 
|  | '\x03',                          //   Type: RST_STREAM | 
|  | '\x00',                          //  Flags: none | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream: 1 | 
|  | '\x00', '\x00', '\x00', '\x01',  //  Error: PROTOCOL_ERROR | 
|  | '\x00',                          // Too much | 
|  | }; | 
|  | Http2FrameHeader header(5, Http2FrameType::RST_STREAM, 0, 1); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, SettingsAckTooLong) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x06',          //   Length: 6 | 
|  | '\x04',                          //     Type: SETTINGS | 
|  | '\x01',                          //    Flags: ACK | 
|  | '\x00', '\x00', '\x00', '\x00',  //   Stream: 0 | 
|  | '\x00', '\x00',                  //   Extra | 
|  | '\x00', '\x00', '\x00', '\x00',  //   Extra | 
|  | }; | 
|  | Http2FrameHeader header(6, Http2FrameType::SETTINGS, Http2FrameFlag::ACK, 0); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, PingAckTooLong) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x09',          //   Length: 8 | 
|  | '\x06',                          //     Type: PING | 
|  | '\xff',                          //    Flags: ACK | 0xfe | 
|  | '\x00', '\x00', '\x00', '\x00',  //   Stream: 0 | 
|  | 's',    'o',    'm',    'e',     // "some" | 
|  | 'd',    'a',    't',    'a',     // "data" | 
|  | '\x00',                          // Too much | 
|  | }; | 
|  | Http2FrameHeader header(9, Http2FrameType::PING, Http2FrameFlag::ACK, 0); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | TEST_F(Http2FrameDecoderTest, WindowUpdateTooLong) { | 
|  | const char kFrameData[] = { | 
|  | '\x00', '\x00', '\x05',          // Length: 5 | 
|  | '\x08',                          //   Type: WINDOW_UPDATE | 
|  | '\x0f',                          //  Flags: 0xff (no valid flags) | 
|  | '\x00', '\x00', '\x00', '\x01',  // Stream: 1 | 
|  | '\x80', '\x00', '\x04', '\x00',  //   Incr: 1024 (plus R bit) | 
|  | '\x00',                          // Too much | 
|  | }; | 
|  | Http2FrameHeader header(5, Http2FrameType::WINDOW_UPDATE, 0, 1); | 
|  | EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header)); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace test | 
|  | }  // namespace http2 |