| Index: net/http2/decoder/http2_frame_decoder_test.cc
 | 
| diff --git a/net/http2/decoder/http2_frame_decoder_test.cc b/net/http2/decoder/http2_frame_decoder_test.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..c2ec0a0dbc2650b97306d49afd1228c63de49afe
 | 
| --- /dev/null
 | 
| +++ b/net/http2/decoder/http2_frame_decoder_test.cc
 | 
| @@ -0,0 +1,946 @@
 | 
| +// 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_frame_decoder.h"
 | 
| +
 | 
| +// Tests of Http2FrameDecoder.
 | 
| +
 | 
| +#include <string>
 | 
| +#include <vector>
 | 
| +
 | 
| +#include "base/bind.h"
 | 
| +#include "base/bind_helpers.h"
 | 
| +#include "base/logging.h"
 | 
| +#include "net/http2/decoder/frame_parts.h"
 | 
| +#include "net/http2/decoder/frame_parts_collector_listener.h"
 | 
| +#include "net/http2/http2_constants.h"
 | 
| +#include "net/http2/tools/failure.h"
 | 
| +#include "net/http2/tools/http2_random.h"
 | 
| +#include "net/http2/tools/random_decoder_test.h"
 | 
| +#include "testing/gtest/include/gtest/gtest.h"
 | 
| +
 | 
| +using base::StringPiece;
 | 
| +using std::string;
 | 
| +using ::testing::AssertionResult;
 | 
| +using ::testing::AssertionSuccess;
 | 
| +
 | 
| +namespace net {
 | 
| +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 {
 | 
| + public:
 | 
| +  AssertionResult ValidatorForDecodePayloadExpectingError(
 | 
| +      const FrameParts& expected,
 | 
| +      const DecodeBuffer& input,
 | 
| +      DecodeStatus status) {
 | 
| +    VERIFY_EQ(status, DecodeStatus::kDecodeError);
 | 
| +    VERIFY_AND_RETURN_SUCCESS(VerifyCollected(expected));
 | 
| +  }
 | 
| +
 | 
| +  AssertionResult ValidatorForBeyondMaximum(const FrameParts& expected,
 | 
| +                                            const DecodeBuffer& input,
 | 
| +                                            DecodeStatus status) {
 | 
| +    VERIFY_EQ(status, DecodeStatus::kDecodeError);
 | 
| +    // The decoder detects this error after decoding the header, and without
 | 
| +    // trying to decode the payload.
 | 
| +    VERIFY_EQ(input.Offset(), Http2FrameHeader::EncodedSize());
 | 
| +    VERIFY_AND_RETURN_SUCCESS(VerifyCollected(expected));
 | 
| +  }
 | 
| +
 | 
| + protected:
 | 
| +  void SetUp() override {
 | 
| +    // On any one run of this suite, we'll always choose the same value for
 | 
| +    // use_default_reconstruct_ because the random seed is the same for each
 | 
| +    // test case, but across runs the random seed changes.
 | 
| +    use_default_reconstruct_ = Random().OneIn(2);
 | 
| +  }
 | 
| +
 | 
| +  DecodeStatus StartDecoding(DecodeBuffer* db) override {
 | 
| +    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 {
 | 
| +    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_);
 | 
| +    // The decoder will discard the remaining bytes, but not go beyond that,
 | 
| +    // which these conditions verify.
 | 
| +    size_t extra = 10;
 | 
| +    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() {
 | 
| +    // Save and restore the maximum_payload_size when reconstructing
 | 
| +    // the decoder.
 | 
| +    size_t maximum_payload_size = decoder_.maximum_payload_size();
 | 
| +
 | 
| +    // Alternate which constructor is used.
 | 
| +    if (use_default_reconstruct_) {
 | 
| +      decoder_.~Http2FrameDecoder();
 | 
| +      new (&decoder_) Http2FrameDecoder;
 | 
| +      decoder_.set_listener(&collector_);
 | 
| +    } else {
 | 
| +      decoder_.~Http2FrameDecoder();
 | 
| +      new (&decoder_) Http2FrameDecoder(&collector_);
 | 
| +    }
 | 
| +    decoder_.set_maximum_payload_size(maximum_payload_size);
 | 
| +
 | 
| +    use_default_reconstruct_ = !use_default_reconstruct_;
 | 
| +  }
 | 
| +
 | 
| +  void ResetDecodeSpeedCounters() {
 | 
| +    fast_decode_count_ = 0;
 | 
| +    slow_decode_count_ = 0;
 | 
| +  }
 | 
| +
 | 
| +  AssertionResult VerifyCollected(const FrameParts& expected) {
 | 
| +    VERIFY_FALSE(collector_.IsInProgress());
 | 
| +    VERIFY_EQ(1u, collector_.size());
 | 
| +    VERIFY_AND_RETURN_SUCCESS(expected.VerifyEquals(*collector_.frame(0)));
 | 
| +  }
 | 
| +
 | 
| +  AssertionResult DecodePayloadAndValidateSeveralWays(StringPiece payload,
 | 
| +                                                      Validator validator) {
 | 
| +    DecodeBuffer db(payload);
 | 
| +    bool start_decoding_requires_non_empty = false;
 | 
| +    return DecodeAndValidateSeveralWays(&db, start_decoding_requires_non_empty,
 | 
| +                                        validator);
 | 
| +  }
 | 
| +
 | 
| +  AssertionResult ValidatorForDecodePayloadAndValidateSeveralWays(
 | 
| +      const FrameParts& expected,
 | 
| +      const DecodeBuffer& input,
 | 
| +      DecodeStatus status) {
 | 
| +    VERIFY_EQ(status, DecodeStatus::kDecodeDone);
 | 
| +    VERIFY_AND_RETURN_SUCCESS(VerifyCollected(expected));
 | 
| +  }
 | 
| +
 | 
| +  // 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(
 | 
| +      StringPiece payload,
 | 
| +      const FrameParts& expected) {
 | 
| +    Validator validator = base::Bind(
 | 
| +        &Http2FrameDecoderTest::ValidatorForDecodePayloadAndValidateSeveralWays,
 | 
| +        base::Unretained(this), base::ConstRef(expected));
 | 
| +    ResetDecodeSpeedCounters();
 | 
| +    VERIFY_SUCCESS(DecodePayloadAndValidateSeveralWays(
 | 
| +        payload, ValidateDoneAndEmpty(validator)));
 | 
| +    VERIFY_GT(fast_decode_count_, 0u);
 | 
| +    VERIFY_GT(slow_decode_count_, 0u);
 | 
| +
 | 
| +    // Repeat with more input; it should stop without reading that input.
 | 
| +    string next_frame = Random().RandString(10);
 | 
| +    string input;
 | 
| +    payload.AppendToString(&input);
 | 
| +    input += next_frame;
 | 
| +
 | 
| +    ResetDecodeSpeedCounters();
 | 
| +    VERIFY_SUCCESS(DecodePayloadAndValidateSeveralWays(
 | 
| +        payload, ValidateDoneAndOffset(payload.size(), validator)));
 | 
| +    VERIFY_GT(fast_decode_count_, 0u);
 | 
| +    VERIFY_GT(slow_decode_count_, 0u);
 | 
| +
 | 
| +    return AssertionSuccess();
 | 
| +  }
 | 
| +
 | 
| +  template <size_t N>
 | 
| +  AssertionResult DecodePayloadAndValidateSeveralWays(
 | 
| +      const char (&buf)[N],
 | 
| +      const FrameParts& expected) {
 | 
| +    return DecodePayloadAndValidateSeveralWays(StringPiece(buf, N), expected);
 | 
| +  }
 | 
| +
 | 
| +  template <size_t N>
 | 
| +  AssertionResult DecodePayloadAndValidateSeveralWays(
 | 
| +      const char (&buf)[N],
 | 
| +      const Http2FrameHeader& header) {
 | 
| +    return DecodePayloadAndValidateSeveralWays(StringPiece(buf, N),
 | 
| +                                               FrameParts(header));
 | 
| +  }
 | 
| +
 | 
| +  template <size_t N>
 | 
| +  AssertionResult DecodePayloadExpectingError(const char (&buf)[N],
 | 
| +                                              const FrameParts& expected) {
 | 
| +    ResetDecodeSpeedCounters();
 | 
| +    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(
 | 
| +        ToStringPiece(buf),
 | 
| +        base::Bind(
 | 
| +            &Http2FrameDecoderTest::ValidatorForDecodePayloadExpectingError,
 | 
| +            base::Unretained(this), expected)));
 | 
| +    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.has_frame_size_error = true;
 | 
| +    VERIFY_AND_RETURN_SUCCESS(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;
 | 
| +
 | 
| +  FramePartsCollectorListener collector_;
 | 
| +  Http2FrameDecoder decoder_;
 | 
| +  bool use_default_reconstruct_;
 | 
| +};
 | 
| +
 | 
| +////////////////////////////////////////////////////////////////////////////////
 | 
| +// 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[] = {
 | 
| +      0x00, 0x00, 0x00,        // Payload length: 0
 | 
| +      0x00,                    // DATA
 | 
| +      0x00,                    // Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x00,  // 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[] = {
 | 
| +      0x00, 0x00, 0x00,        // Payload length: 0
 | 
| +      0x01,                    // HEADERS
 | 
| +      0x00,                    // Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x01,  // 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[] = {
 | 
| +      0x00,  0x00, 0x05,        // Length: 5
 | 
| +      0x02,                     //   Type: PRIORITY
 | 
| +      0x00,                     //  Flags: none
 | 
| +      0x00,  0x00, 0x00, 0x02,  // Stream: 2
 | 
| +      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
 | 
| +      0x10,                     // Weight: 17
 | 
| +  };
 | 
| +  Http2FrameHeader header(5, Http2FrameType::PRIORITY, 0, 2);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.opt_priority = Http2PriorityFields(1, 17, true);
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, RstStream) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x04,        // Length: 4
 | 
| +      0x03,                    //   Type: RST_STREAM
 | 
| +      0x00,                    //  Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x01,  // Stream: 1
 | 
| +      0x00, 0x00, 0x00, 0x01,  //  Error: PROTOCOL_ERROR
 | 
| +  };
 | 
| +  Http2FrameHeader header(4, Http2FrameType::RST_STREAM, 0, 1);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.opt_rst_stream_error_code = Http2ErrorCode::PROTOCOL_ERROR;
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, SettingsEmpty) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x00,        // Length: 0
 | 
| +      0x04,                    //   Type: SETTINGS
 | 
| +      0x00,                    //  Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x01,  // 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[] = {
 | 
| +      0x00, 0x00, 0x00,        //   Length: 6
 | 
| +      0x04,                    //     Type: SETTINGS
 | 
| +      0x01,                    //    Flags: ACK
 | 
| +      0x00, 0x00, 0x00, 0x00,  //   Stream: 0
 | 
| +  };
 | 
| +  Http2FrameHeader header(0, Http2FrameType::SETTINGS, Http2FrameFlag::FLAG_ACK,
 | 
| +                          0);
 | 
| +  FrameParts expected(header);
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, PushPromiseMinimal) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x04,        // Payload length: 4
 | 
| +      0x05,                    // PUSH_PROMISE
 | 
| +      0x04,                    // Flags: END_HEADERS
 | 
| +      0x00, 0x00, 0x00, 0x02,  //   Stream: 2 (invalid but unchecked here)
 | 
| +      0x00, 0x00, 0x00, 0x01,  // Promised: 1 (invalid but unchecked here)
 | 
| +  };
 | 
| +  Http2FrameHeader header(4, Http2FrameType::PUSH_PROMISE,
 | 
| +                          Http2FrameFlag::FLAG_END_HEADERS, 2);
 | 
| +  FrameParts expected(header, "");
 | 
| +  expected.opt_push_promise = Http2PushPromiseFields{1};
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, Ping) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x08,        //   Length: 8
 | 
| +      0x06,                     //     Type: PING
 | 
| +      0xfeu,                    //    Flags: no valid flags
 | 
| +      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
 | 
| +      's',   'o',  'm',  'e',   // "some"
 | 
| +      'd',   'a',  't',  'a',   // "data"
 | 
| +  };
 | 
| +  Http2FrameHeader header(8, Http2FrameType::PING, 0, 0);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.opt_ping = Http2PingFields{{'s', 'o', 'm', 'e', 'd', 'a', 't', 'a'}};
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, PingAck) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x08,        //   Length: 8
 | 
| +      0x06,                     //     Type: PING
 | 
| +      0xffu,                    //    Flags: ACK (plus all invalid flags)
 | 
| +      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
 | 
| +      's',   'o',  'm',  'e',   // "some"
 | 
| +      'd',   'a',  't',  'a',   // "data"
 | 
| +  };
 | 
| +  Http2FrameHeader header(8, Http2FrameType::PING, Http2FrameFlag::FLAG_ACK, 0);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.opt_ping = Http2PingFields{{'s', 'o', 'm', 'e', 'd', 'a', 't', 'a'}};
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, GoAwayMinimal) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x08,         // Length: 8 (no opaque data)
 | 
| +      0x07,                      //   Type: GOAWAY
 | 
| +      0xffu,                     //  Flags: 0xff (no valid flags)
 | 
| +      0x00,  0x00, 0x00, 0x01,   // Stream: 1 (invalid but unchecked here)
 | 
| +      0x80u, 0x00, 0x00, 0xffu,  //   Last: 255 (plus R bit)
 | 
| +      0x00,  0x00, 0x00, 0x09,   //  Error: COMPRESSION_ERROR
 | 
| +  };
 | 
| +  Http2FrameHeader header(8, Http2FrameType::GOAWAY, 0, 1);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.opt_goaway =
 | 
| +      Http2GoAwayFields(255, Http2ErrorCode::COMPRESSION_ERROR);
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, WindowUpdate) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x04,        // Length: 4
 | 
| +      0x08,                     //   Type: WINDOW_UPDATE
 | 
| +      0x0f,                     //  Flags: 0xff (no valid flags)
 | 
| +      0x00,  0x00, 0x00, 0x01,  // Stream: 1
 | 
| +      0x80u, 0x00, 0x04, 0x00,  //   Incr: 1024 (plus R bit)
 | 
| +  };
 | 
| +  Http2FrameHeader header(4, Http2FrameType::WINDOW_UPDATE, 0, 1);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.opt_window_update_increment = 1024;
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, ContinuationEmpty) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x00,        // Payload length: 0
 | 
| +      0x09,                    // CONTINUATION
 | 
| +      0x00,                    // Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x00,  // 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[] = {
 | 
| +      0x00,  0x00, 0x02,        // Payload length: 2
 | 
| +      0x0a,                     // ALTSVC
 | 
| +      0xffu,                    // Flags: none (plus 0xff)
 | 
| +      0x00,  0x00, 0x00, 0x00,  // Stream ID: 0 (invalid but unchecked here)
 | 
| +      0x00,  0x00,              // Origin Length: 0
 | 
| +  };
 | 
| +  Http2FrameHeader header(2, Http2FrameType::ALTSVC, 0, 0);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.opt_altsvc_origin_length = 0;
 | 
| +  expected.opt_altsvc_value_length = 0;
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, UnknownEmpty) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x00,        // Payload length: 0
 | 
| +      0x20,                     // 32 (unknown)
 | 
| +      0xffu,                    // Flags: all
 | 
| +      0x00,  0x00, 0x00, 0x00,  // 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[] = {
 | 
| +      0x00,  0x00, 0x03,        // Payload length: 7
 | 
| +      0x00,                     // DATA
 | 
| +      0x80u,                    // Flags: 0x80
 | 
| +      0x00,  0x00, 0x02, 0x02,  // 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[] = {
 | 
| +      0x00, 0x00, 0x03,        // Payload length: 3
 | 
| +      0x01,                    // HEADERS
 | 
| +      0x05,                    // Flags: END_STREAM | END_HEADERS
 | 
| +      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
 | 
| +      'a',  'b',  'c',         // HPACK fragment (doesn't have to be valid)
 | 
| +  };
 | 
| +  Http2FrameHeader header(
 | 
| +      3, Http2FrameType::HEADERS,
 | 
| +      Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_END_HEADERS, 2);
 | 
| +  FrameParts expected(header, "abc");
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, HeadersPriority) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x05,        // Payload length: 5
 | 
| +      0x01,                     // HEADERS
 | 
| +      0x20,                     // Flags: PRIORITY
 | 
| +      0x00,  0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
 | 
| +      0x00,  0x00, 0x00, 0x01,  // Parent: 1 (Not Exclusive)
 | 
| +      0xffu,                    // Weight: 256
 | 
| +  };
 | 
| +  Http2FrameHeader header(5, Http2FrameType::HEADERS,
 | 
| +                          Http2FrameFlag::FLAG_PRIORITY, 2);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.opt_priority = Http2PriorityFields(1, 256, false);
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, Settings) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x0c,        // Length: 12
 | 
| +      0x04,                    //   Type: SETTINGS
 | 
| +      0x00,                    //  Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x00,  // Stream: 0
 | 
| +      0x00, 0x04,              //  Param: INITIAL_WINDOW_SIZE
 | 
| +      0x0a, 0x0b, 0x0c, 0x0d,  //  Value: 168496141
 | 
| +      0x00, 0x02,              //  Param: ENABLE_PUSH
 | 
| +      0x00, 0x00, 0x00, 0x03,  //  Value: 3 (invalid but unchecked here)
 | 
| +  };
 | 
| +  Http2FrameHeader header(12, Http2FrameType::SETTINGS, 0, 0);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.settings.push_back(Http2SettingFields(
 | 
| +      Http2SettingsParameter::INITIAL_WINDOW_SIZE, 168496141));
 | 
| +  expected.settings.push_back(
 | 
| +      Http2SettingFields(Http2SettingsParameter::ENABLE_PUSH, 3));
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, PushPromisePayload) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 7,            // Payload length: 7
 | 
| +      0x05,                     // PUSH_PROMISE
 | 
| +      0x04,                     // Flags: END_HEADERS
 | 
| +      0x00, 0x00, 0x00, 0xffu,  // Stream ID: 255
 | 
| +      0x00, 0x00, 0x01, 0x00,   // Promised: 256
 | 
| +      'a',  'b',  'c',          // HPACK fragment (doesn't have to be valid)
 | 
| +  };
 | 
| +  Http2FrameHeader header(7, Http2FrameType::PUSH_PROMISE,
 | 
| +                          Http2FrameFlag::FLAG_END_HEADERS, 255);
 | 
| +  FrameParts expected(header, "abc");
 | 
| +  expected.opt_push_promise = Http2PushPromiseFields{256};
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, GoAwayOpaqueData) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x0e,        // Length: 14
 | 
| +      0x07,                     //   Type: GOAWAY
 | 
| +      0xffu,                    //  Flags: 0xff (no valid flags)
 | 
| +      0x80u, 0x00, 0x00, 0x00,  // Stream: 0 (plus R bit)
 | 
| +      0x00,  0x00, 0x01, 0x00,  //   Last: 256
 | 
| +      0x00,  0x00, 0x00, 0x03,  //  Error: FLOW_CONTROL_ERROR
 | 
| +      'o',   'p',  'a',  'q',  'u', 'e',
 | 
| +  };
 | 
| +  Http2FrameHeader header(14, Http2FrameType::GOAWAY, 0, 0);
 | 
| +  FrameParts expected(header, "opaque");
 | 
| +  expected.opt_goaway =
 | 
| +      Http2GoAwayFields(256, Http2ErrorCode::FLOW_CONTROL_ERROR);
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, ContinuationPayload) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x03,        // Payload length: 3
 | 
| +      0x09,                     // CONTINUATION
 | 
| +      0xffu,                    // Flags: END_HEADERS | 0xfb
 | 
| +      0x00,  0x00, 0x00, 0x02,  // Stream ID: 2
 | 
| +      'a',   'b',  'c',         // Data
 | 
| +  };
 | 
| +  Http2FrameHeader header(3, Http2FrameType::CONTINUATION,
 | 
| +                          Http2FrameFlag::FLAG_END_HEADERS, 2);
 | 
| +  FrameParts expected(header, "abc");
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, AltSvcPayload) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x08,        // Payload length: 3
 | 
| +      0x0a,                    // ALTSVC
 | 
| +      0x00,                    // Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
 | 
| +      0x00, 0x03,              // 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, UnknownPayload) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x03,        // Payload length: 3
 | 
| +      0x30,                    // 48 (unknown)
 | 
| +      0x00,                    // Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x02,  // 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[] = {
 | 
| +      0x00, 0x00, 0x07,        // Payload length: 7
 | 
| +      0x00,                    // DATA
 | 
| +      0x09,                    // Flags: END_STREAM | PADDED
 | 
| +      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
 | 
| +      0x03,                    // Pad Len
 | 
| +      'a',  'b',  'c',         // Data
 | 
| +      0x00, 0x00, 0x00,        // Padding
 | 
| +  };
 | 
| +  Http2FrameHeader header(
 | 
| +      7, Http2FrameType::DATA,
 | 
| +      Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_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[] = {
 | 
| +      0x00, 0x00, 0x07,        // Payload length: 7
 | 
| +      0x01,                    // HEADERS
 | 
| +      0x08,                    // Flags: PADDED
 | 
| +      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
 | 
| +      0x03,                    // Pad Len
 | 
| +      'a',  'b',  'c',         // HPACK fragment (doesn't have to be valid)
 | 
| +      0x00, 0x00, 0x00,        // Padding
 | 
| +  };
 | 
| +  Http2FrameHeader header(7, Http2FrameType::HEADERS,
 | 
| +                          Http2FrameFlag::FLAG_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[] = {
 | 
| +      0x00,  0x00, 0x0c,        // Payload length: 12
 | 
| +      0x01,                     // HEADERS
 | 
| +      0xffu,                    // Flags: all, including undefined
 | 
| +      0x00,  0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
 | 
| +      0x03,                     // Pad Len
 | 
| +      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
 | 
| +      0x10,                     // Weight: 17
 | 
| +      'a',   'b',  'c',         // HPACK fragment (doesn't have to be valid)
 | 
| +      0x00,  0x00, 0x00,        // Padding
 | 
| +  };
 | 
| +  Http2FrameHeader header(
 | 
| +      12, Http2FrameType::HEADERS,
 | 
| +      Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_END_HEADERS |
 | 
| +          Http2FrameFlag::FLAG_PADDED | Http2FrameFlag::FLAG_PRIORITY,
 | 
| +      2);
 | 
| +  size_t total_pad_length = 4;  // Including the Pad Length field.
 | 
| +  FrameParts expected(header, "abc", total_pad_length);
 | 
| +  expected.opt_priority = Http2PriorityFields(1, 17, true);
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, PushPromisePayloadAndPadding) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 11,          // Payload length: 11
 | 
| +      0x05,                     // PUSH_PROMISE
 | 
| +      0xffu,                    // Flags: END_HEADERS | PADDED | 0xf3
 | 
| +      0x00,  0x00, 0x00, 0x01,  // Stream ID: 1
 | 
| +      0x03,                     // Pad Len
 | 
| +      0x00,  0x00, 0x00, 0x02,  // Promised: 2
 | 
| +      'a',   'b',  'c',         // HPACK fragment (doesn't have to be valid)
 | 
| +      0x00,  0x00, 0x00,        // Padding
 | 
| +  };
 | 
| +  Http2FrameHeader header(
 | 
| +      11, Http2FrameType::PUSH_PROMISE,
 | 
| +      Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED, 1);
 | 
| +  size_t total_pad_length = 4;  // Including the Pad Length field.
 | 
| +  FrameParts expected(header, "abc", total_pad_length);
 | 
| +  expected.opt_push_promise = Http2PushPromiseFields{2};
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +////////////////////////////////////////////////////////////////////////////////
 | 
| +// Payload too short errors.
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, DataMissingPadLengthField) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x00,        // Payload length: 0
 | 
| +      0x00,                    // DATA
 | 
| +      0x08,                    // Flags: PADDED
 | 
| +      0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
 | 
| +  };
 | 
| +  Http2FrameHeader header(0, Http2FrameType::DATA, Http2FrameFlag::FLAG_PADDED,
 | 
| +                          1);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.opt_missing_length = 1;
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingError(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, HeaderPaddingTooLong) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x02,        // Payload length: 0
 | 
| +      0x01,                     // HEADERS
 | 
| +      0x08,                     // Flags: PADDED
 | 
| +      0x00,  0x01, 0x00, 0x00,  // Stream ID: 65536
 | 
| +      0xffu,                    // Pad Len: 255
 | 
| +      0x00,                     // Only one byte of padding
 | 
| +  };
 | 
| +  Http2FrameHeader header(2, Http2FrameType::HEADERS,
 | 
| +                          Http2FrameFlag::FLAG_PADDED, 65536);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.opt_missing_length = 254;
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingError(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, HeaderMissingPriority) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x04,        // Payload length: 0
 | 
| +      0x01,                    // HEADERS
 | 
| +      0x20,                    // Flags: PRIORITY
 | 
| +      0x00, 0x01, 0x00, 0x00,  // Stream ID: 65536
 | 
| +      0x00, 0x00, 0x00, 0x00,  // Priority (truncated)
 | 
| +  };
 | 
| +  Http2FrameHeader header(4, Http2FrameType::HEADERS,
 | 
| +                          Http2FrameFlag::FLAG_PRIORITY, 65536);
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, PriorityTooShort) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x04,        // Length: 5
 | 
| +      0x02,                     //   Type: PRIORITY
 | 
| +      0x00,                     //  Flags: none
 | 
| +      0x00,  0x00, 0x00, 0x02,  // Stream: 2
 | 
| +      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
 | 
| +  };
 | 
| +  Http2FrameHeader header(4, Http2FrameType::PRIORITY, 0, 2);
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, RstStreamTooShort) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x03,        // Length: 4
 | 
| +      0x03,                    //   Type: RST_STREAM
 | 
| +      0x00,                    //  Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x01,  // Stream: 1
 | 
| +      0x00, 0x00, 0x00,        //  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[] = {
 | 
| +      0x00, 0x00, 0x09,        // Length: 2
 | 
| +      0x04,                    //   Type: SETTINGS
 | 
| +      0x00,                    //  Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x00,  // Stream: 0
 | 
| +      0x00, 0x02,              //  Param: ENABLE_PUSH
 | 
| +      0x00, 0x00, 0x00, 0x03,  //  Value: 1
 | 
| +      0x00, 0x04,              //  Param: INITIAL_WINDOW_SIZE
 | 
| +      0x00,                    //  Value: Truncated
 | 
| +  };
 | 
| +  Http2FrameHeader header(9, Http2FrameType::SETTINGS, 0, 0);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.settings.push_back(
 | 
| +      Http2SettingFields(Http2SettingsParameter::ENABLE_PUSH, 3));
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, expected));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, PushPromiseTooShort) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 3,           // Payload length: 3
 | 
| +      0x05,                    // PUSH_PROMISE
 | 
| +      0x00,                    // Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
 | 
| +      0x00, 0x00, 0x00,        // Truncated promise id
 | 
| +  };
 | 
| +  Http2FrameHeader header(3, Http2FrameType::PUSH_PROMISE, 0, 1);
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, PushPromisePaddedTruncatedPromise) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 4,           // Payload length: 4
 | 
| +      0x05,                    // PUSH_PROMISE
 | 
| +      0x08,                    // Flags: PADDED
 | 
| +      0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
 | 
| +      0x00,                    // Pad Len
 | 
| +      0x00, 0x00, 0x00,        // Truncated promise id
 | 
| +  };
 | 
| +  Http2FrameHeader header(4, Http2FrameType::PUSH_PROMISE,
 | 
| +                          Http2FrameFlag::FLAG_PADDED, 1);
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, PingTooShort) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x07,        //   Length: 8
 | 
| +      0x06,                     //     Type: PING
 | 
| +      0xfeu,                    //    Flags: no valid flags
 | 
| +      0x00,  0x00, 0x00, 0x00,  //   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[] = {
 | 
| +      0x00,  0x00, 0x00,        // Length: 0
 | 
| +      0x07,                     //   Type: GOAWAY
 | 
| +      0xffu,                    //  Flags: 0xff (no valid flags)
 | 
| +      0x00,  0x00, 0x00, 0x00,  // Stream: 0
 | 
| +  };
 | 
| +  Http2FrameHeader header(0, Http2FrameType::GOAWAY, 0, 0);
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, WindowUpdateTooShort) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x03,        // Length: 3
 | 
| +      0x08,                     //   Type: WINDOW_UPDATE
 | 
| +      0x0f,                     //  Flags: 0xff (no valid flags)
 | 
| +      0x00,  0x00, 0x00, 0x01,  // Stream: 1
 | 
| +      0x80u, 0x00, 0x04,        // Truncated
 | 
| +  };
 | 
| +  Http2FrameHeader header(3, Http2FrameType::WINDOW_UPDATE, 0, 1);
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, AltSvcTruncatedOriginLength) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x01,        // Payload length: 3
 | 
| +      0x0a,                    // ALTSVC
 | 
| +      0x00,                    // Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
 | 
| +      0x00,                    // Origin Length: truncated
 | 
| +  };
 | 
| +  Http2FrameHeader header(1, Http2FrameType::ALTSVC, 0, 2);
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, AltSvcTruncatedOrigin) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x05,        // Payload length: 3
 | 
| +      0x0a,                    // ALTSVC
 | 
| +      0x00,                    // Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
 | 
| +      0x00, 0x04,              // 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) {
 | 
| +  decoder_.set_maximum_payload_size(2);
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x07,        // Payload length: 7
 | 
| +      0x00,                    // DATA
 | 
| +      0x09,                    // Flags: END_STREAM | PADDED
 | 
| +      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
 | 
| +      0x03,                    // Pad Len
 | 
| +      'a',  'b',  'c',         // Data
 | 
| +      0x00, 0x00, 0x00,        // Padding
 | 
| +  };
 | 
| +  Http2FrameHeader header(
 | 
| +      7, Http2FrameType::DATA,
 | 
| +      Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_PADDED, 2);
 | 
| +  FrameParts expected(header);
 | 
| +  expected.has_frame_size_error = true;
 | 
| +  ResetDecodeSpeedCounters();
 | 
| +  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(
 | 
| +      ToStringPiece(kFrameData),
 | 
| +      base::Bind(&Http2FrameDecoderTest::ValidatorForBeyondMaximum,
 | 
| +                 base::Unretained(this), expected)));
 | 
| +  EXPECT_GT(fast_decode_count_, 0u);
 | 
| +  EXPECT_GT(slow_decode_count_, 0u);
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, PriorityTooLong) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x06,        // Length: 5
 | 
| +      0x02,                     //   Type: PRIORITY
 | 
| +      0x00,                     //  Flags: none
 | 
| +      0x00,  0x00, 0x00, 0x02,  // Stream: 2
 | 
| +      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
 | 
| +      0x10,                     // Weight: 17
 | 
| +      0x00,                     // Too much
 | 
| +  };
 | 
| +  Http2FrameHeader header(6, Http2FrameType::PRIORITY, 0, 2);
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, RstStreamTooLong) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x05,        // Length: 4
 | 
| +      0x03,                    //   Type: RST_STREAM
 | 
| +      0x00,                    //  Flags: none
 | 
| +      0x00, 0x00, 0x00, 0x01,  // Stream: 1
 | 
| +      0x00, 0x00, 0x00, 0x01,  //  Error: PROTOCOL_ERROR
 | 
| +      0x00,                    // Too much
 | 
| +  };
 | 
| +  Http2FrameHeader header(5, Http2FrameType::RST_STREAM, 0, 1);
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, SettingsAckTooLong) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00, 0x00, 0x06,        //   Length: 6
 | 
| +      0x04,                    //     Type: SETTINGS
 | 
| +      0x01,                    //    Flags: ACK
 | 
| +      0x00, 0x00, 0x00, 0x00,  //   Stream: 0
 | 
| +      0x00, 0x00,              //   Extra
 | 
| +      0x00, 0x00, 0x00, 0x00,  //   Extra
 | 
| +  };
 | 
| +  Http2FrameHeader header(6, Http2FrameType::SETTINGS, Http2FrameFlag::FLAG_ACK,
 | 
| +                          0);
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, PingAckTooLong) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x09,        //   Length: 8
 | 
| +      0x06,                     //     Type: PING
 | 
| +      0xffu,                    //    Flags: ACK | 0xfe
 | 
| +      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
 | 
| +      's',   'o',  'm',  'e',   // "some"
 | 
| +      'd',   'a',  't',  'a',   // "data"
 | 
| +      0x00,                     // Too much
 | 
| +  };
 | 
| +  Http2FrameHeader header(9, Http2FrameType::PING, Http2FrameFlag::FLAG_ACK, 0);
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
 | 
| +}
 | 
| +
 | 
| +TEST_F(Http2FrameDecoderTest, WindowUpdateTooLong) {
 | 
| +  const char kFrameData[] = {
 | 
| +      0x00,  0x00, 0x05,        // Length: 5
 | 
| +      0x08,                     //   Type: WINDOW_UPDATE
 | 
| +      0x0f,                     //  Flags: 0xff (no valid flags)
 | 
| +      0x00,  0x00, 0x00, 0x01,  // Stream: 1
 | 
| +      0x80u, 0x00, 0x04, 0x00,  //   Incr: 1024 (plus R bit)
 | 
| +      0x00,                     // Too much
 | 
| +  };
 | 
| +  Http2FrameHeader header(5, Http2FrameType::WINDOW_UPDATE, 0, 1);
 | 
| +  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +}  // namespace test
 | 
| +}  // namespace net
 | 
| 
 |