Index: net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h |
diff --git a/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h |
deleted file mode 100644 |
index b49b15efbd0e874582434a283a4c4f3b0941b98b..0000000000000000000000000000000000000000 |
--- a/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h |
+++ /dev/null |
@@ -1,486 +0,0 @@ |
-// 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. |
- |
-#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_ |
-#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_ |
- |
-// Base class for testing concrete payload decoder classes. |
- |
-#include <stddef.h> |
- |
-#include <string> |
- |
-#include "base/bind.h" |
-#include "base/bind_helpers.h" |
-#include "base/callback.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/decoder/frame_decoder_state.h" |
-#include "net/http2/decoder/frame_parts.h" |
-#include "net/http2/decoder/http2_frame_decoder_listener.h" |
-#include "net/http2/http2_constants.h" |
-#include "net/http2/http2_constants_test_util.h" |
-#include "net/http2/http2_structures.h" |
-#include "net/http2/tools/http2_frame_builder.h" |
-#include "net/http2/tools/random_decoder_test.h" |
-#include "testing/gtest/include/gtest/gtest.h" |
- |
-namespace net { |
-namespace test { |
- |
-// Base class for tests of payload decoders. Below this there is a templated |
-// sub-class that adds a bunch of type specific features. |
-class PayloadDecoderBaseTest : public RandomDecoderTest { |
- protected: |
- PayloadDecoderBaseTest(); |
- |
- // Virtual functions to be implemented by the test classes for the individual |
- // payload decoders... |
- |
- // Start decoding the payload. |
- virtual DecodeStatus StartDecodingPayload(DecodeBuffer* db) = 0; |
- |
- // Resume decoding the payload. |
- virtual DecodeStatus ResumeDecodingPayload(DecodeBuffer* db) = 0; |
- |
- // In support of ensuring that we're really accessing and updating the |
- // decoder, prepare the decoder by, for example, overwriting the decoder. |
- virtual void PreparePayloadDecoder() = 0; |
- |
- // Get the listener to be inserted into the FrameDecoderState, ready for |
- // listening (e.g. reset if it is a FramePartsCollector). |
- virtual Http2FrameDecoderListener* PrepareListener() = 0; |
- |
- // Record a frame header for use on each call to StartDecoding. |
- void set_frame_header(const Http2FrameHeader& header) { |
- EXPECT_EQ(0, InvalidFlagMaskForFrameType(header.type) & header.flags); |
- if (!frame_header_is_set_ || frame_header_ != header) { |
- VLOG(2) << "set_frame_header: " << frame_header_; |
- } |
- frame_header_ = header; |
- frame_header_is_set_ = true; |
- } |
- |
- const Http2FrameHeader& frame_header() const { |
- CHECK(frame_header_is_set_); |
- return frame_header_; |
- } |
- |
- FrameDecoderState* mutable_state() { return &frame_decoder_state_; } |
- |
- // Randomize the payload decoder, sets the payload decoder's frame_header_, |
- // then start decoding the payload. Called by RandomDecoderTest. This method |
- // is final so that we can always perform certain actions when |
- // RandomDecoderTest starts the decoding of a payload, such as randomizing the |
- // the payload decoder, injecting the frame header and counting fast decoding |
- // cases. Sub-classes must implement StartDecodingPayload to perform their |
- // initial decoding of a frame's payload. |
- DecodeStatus StartDecoding(DecodeBuffer* db) final; |
- |
- // Called by RandomDecoderTest. This method is final so that we can always |
- // perform certain actions when RandomDecoderTest calls it, such as counting |
- // slow decode cases. Sub-classes must implement ResumeDecodingPayload to |
- // continue decoding the frame's payload, which must not all be in one buffer. |
- DecodeStatus ResumeDecoding(DecodeBuffer* db) final; |
- |
- // Given the specified payload (without the common frame header), decode |
- // it with several partitionings of the payload. |
- ::testing::AssertionResult DecodePayloadAndValidateSeveralWays( |
- base::StringPiece payload, |
- Validator validator); |
- |
- // TODO(jamessynge): Add helper method for verifying these are both non-zero, |
- // and call the new method from tests that expect successful decoding. |
- void ResetDecodeSpeedCounters() { |
- fast_decode_count_ = 0; |
- slow_decode_count_ = 0; |
- } |
- |
- // Count of payloads that are full decoded by StartDecodingPayload, or that |
- // an error was detected by StartDecodingPayload. |
- size_t fast_decode_count_ = 0; |
- |
- // Count of payloads that require calling ResumeDecodingPayload in order to |
- // decode them completely (or to detect an error during decoding). |
- size_t slow_decode_count_ = 0; |
- |
- private: |
- bool frame_header_is_set_ = false; |
- Http2FrameHeader frame_header_; |
- FrameDecoderState frame_decoder_state_; |
-}; |
- |
-// Base class for payload decoders of type Decoder, with corresponding test |
-// peer of type DecoderPeer, and using class Listener as the implementation |
-// of Http2FrameDecoderListenerInterface to be used during decoding. |
-// Typically Listener is a sub-class of FramePartsCollector. |
-// SupportedFrameType is set to false only for UnknownPayloadDecoder. |
-template <class Decoder, |
- class DecoderPeer, |
- class Listener, |
- bool SupportedFrameType = true> |
-class AbstractPayloadDecoderTest : public PayloadDecoderBaseTest { |
- public: |
- static bool SucceedingApproveSize(size_t size) { return true; } |
- |
- protected: |
- // An ApproveSize function returns true to approve decoding the specified |
- // size of payload, else false to skip that size. Typically used for negative |
- // tests; for example, decoding a SETTINGS frame at all sizes except for |
- // multiples of 6. |
- typedef base::Callback<bool(size_t size)> ApproveSize; |
- |
- AbstractPayloadDecoderTest() {} |
- |
- // These tests are in setup rather than the constructor for two reasons: |
- // 1) Constructors are not allowed to fail, so gUnit documents that EXPECT_* |
- // and ASSERT_* are not allowed in constructors, and should instead be in |
- // SetUp if they are needed before the body of the test is executed. |
- // 2) To allow the sub-class constructor to make any desired modifications to |
- // the DecoderPeer before these tests are executed; in particular, |
- // UnknownPayloadDecoderPeer has not got a fixed frame type, but it is |
- // instead set during the test's constructor. |
- void SetUp() override { |
- PayloadDecoderBaseTest::SetUp(); |
- |
- // Confirm that DecoderPeer et al returns sensible values. Using auto as the |
- // variable type so that no (narrowing) conversions take place that hide |
- // problems; i.e. if someone changes KnownFlagsMaskForFrameType so that it |
- // doesn't return a uint8, and has bits above the low-order 8 bits set, this |
- // bit of paranoia should detect the problem before we get too far. |
- auto frame_type = DecoderPeer::FrameType(); |
- if (SupportedFrameType) { |
- EXPECT_TRUE(IsSupportedHttp2FrameType(frame_type)) << frame_type; |
- } else { |
- EXPECT_FALSE(IsSupportedHttp2FrameType(frame_type)) << frame_type; |
- } |
- |
- auto known_flags = KnownFlagsMaskForFrameType(frame_type); |
- EXPECT_EQ(known_flags, known_flags & 0xff); |
- |
- auto flags_to_avoid = DecoderPeer::FlagsAffectingPayloadDecoding(); |
- EXPECT_EQ(flags_to_avoid, flags_to_avoid & known_flags); |
- } |
- |
- void PreparePayloadDecoder() override { |
- payload_decoder_.~Decoder(); |
- new (&payload_decoder_) Decoder; |
- } |
- |
- Http2FrameDecoderListener* PrepareListener() override { |
- listener_.Reset(); |
- return &listener_; |
- } |
- |
- // Returns random flags, but only those valid for the frame type, yet not |
- // those that the DecoderPeer says will affect the decoding of the payload |
- // (e.g. the PRIORTY flag on a HEADERS frame or PADDED on DATA frames). |
- uint8_t RandFlags() { |
- return Random().Rand8() & |
- KnownFlagsMaskForFrameType(DecoderPeer::FrameType()) & |
- ~DecoderPeer::FlagsAffectingPayloadDecoding(); |
- } |
- |
- // Start decoding the payload. |
- DecodeStatus StartDecodingPayload(DecodeBuffer* db) override { |
- DVLOG(2) << "StartDecodingPayload, db->Remaining=" << db->Remaining(); |
- return payload_decoder_.StartDecodingPayload(mutable_state(), db); |
- } |
- |
- // Resume decoding the payload. |
- DecodeStatus ResumeDecodingPayload(DecodeBuffer* db) override { |
- DVLOG(2) << "ResumeDecodingPayload, db->Remaining=" << db->Remaining(); |
- return payload_decoder_.ResumeDecodingPayload(mutable_state(), db); |
- } |
- |
- // Wrap |validator| in another one which will check that we've reached the |
- // expected state of kDecodeError with OnFrameSizeError having been called by |
- AssertionResult ValidatorForDecodePayloadAndValidateSeveralWays( |
- const FrameParts& expected) { |
- VERIFY_FALSE(listener_.IsInProgress()); |
- VERIFY_EQ(1u, listener_.size()); |
- VERIFY_AND_RETURN_SUCCESS(expected.VerifyEquals(*listener_.frame(0))); |
- } |
- |
- // Decode one frame's payload and confirm that the listener recorded the |
- // expected FrameParts instance, and only 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( |
- base::StringPiece payload, |
- const FrameParts& expected) { |
- return PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays( |
- payload, this->ValidateDoneAndEmpty(base::Bind( |
- &AbstractPayloadDecoderTest:: |
- ValidatorForDecodePayloadAndValidateSeveralWays, |
- base::Unretained(this), base::ConstRef(expected)))); |
- } |
- |
- // Wrap |validator| in another one which will check that we've reached the |
- // expected state of kDecodeError with OnFrameSizeError having been called by |
- // the payload decoder. |
- AssertionResult ValidatorForVerifyDetectsFrameSizeError( |
- const Http2FrameHeader& header, |
- const Validator& validator, |
- const DecodeBuffer& input, |
- DecodeStatus status) { |
- DVLOG(2) << "VerifyDetectsFrameSizeError validator; status=" << status |
- << "; input.Remaining=" << input.Remaining(); |
- VERIFY_EQ(DecodeStatus::kDecodeError, status); |
- VERIFY_FALSE(listener_.IsInProgress()); |
- VERIFY_EQ(1u, listener_.size()); |
- const FrameParts* frame = listener_.frame(0); |
- VERIFY_EQ(header, frame->frame_header); |
- VERIFY_TRUE(frame->has_frame_size_error); |
- // Verify did not get OnPaddingTooLong, as we should only ever produce |
- // one of these two errors for a single frame. |
- VERIFY_FALSE(frame->opt_missing_length); |
- return validator.Run(input, status); |
- } |
- |
- // Decode one frame's payload, expecting that the final status will be |
- // kDecodeError, and that OnFrameSizeError will have been called on the |
- // listener. The payload will be decoded several times with different |
- // partitionings of the payload. The type WrappedValidator is either |
- // RandomDecoderTest::Validator, RandomDecoderTest::NoArgValidator or |
- // std::nullptr_t (not extra validation). |
- template <typename WrappedValidator> |
- ::testing::AssertionResult VerifyDetectsFrameSizeError( |
- base::StringPiece payload, |
- const Http2FrameHeader& header, |
- WrappedValidator wrapped_validator) { |
- set_frame_header(header); |
- // If wrapped_validator is not a RandomDecoderTest::Validator, make it so. |
- Validator validator = this->ToValidator(wrapped_validator); |
- VERIFY_AND_RETURN_SUCCESS( |
- PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays( |
- payload, base::Bind(&AbstractPayloadDecoderTest:: |
- ValidatorForVerifyDetectsFrameSizeError, |
- base::Unretained(this), base::ConstRef(header), |
- base::ConstRef(validator)))); |
- } |
- |
- // Confirm that we get OnFrameSizeError when trying to decode unpadded_payload |
- // at all sizes from zero to unpadded_payload.size(), except those sizes not |
- // approved by approve_size. |
- // If total_pad_length is greater than zero, then that amount of padding |
- // is added to the payload (including the Pad Length field). |
- // The flags will be required_flags, PADDED if total_pad_length > 0, and some |
- // randomly selected flag bits not excluded by FlagsAffectingPayloadDecoding. |
- ::testing::AssertionResult VerifyDetectsMultipleFrameSizeErrors( |
- uint8_t required_flags, |
- base::StringPiece unpadded_payload, |
- ApproveSize approve_size, |
- int total_pad_length) { |
- // required_flags should come from those that are defined for the frame |
- // type AND are those that affect the decoding of the payload (otherwise, |
- // the flag shouldn't be required). |
- Http2FrameType frame_type = DecoderPeer::FrameType(); |
- VERIFY_EQ(required_flags, |
- required_flags & KnownFlagsMaskForFrameType(frame_type)); |
- VERIFY_EQ(required_flags, |
- required_flags & DecoderPeer::FlagsAffectingPayloadDecoding()); |
- |
- if (0 != (Http2FrameFlag::FLAG_PADDED & |
- KnownFlagsMaskForFrameType(frame_type))) { |
- // Frame type supports padding. |
- if (total_pad_length == 0) { |
- required_flags &= ~Http2FrameFlag::FLAG_PADDED; |
- } else { |
- required_flags |= Http2FrameFlag::FLAG_PADDED; |
- } |
- } else { |
- VERIFY_EQ(0, total_pad_length); |
- } |
- |
- bool validated = false; |
- for (size_t real_payload_size = 0; |
- real_payload_size <= unpadded_payload.size(); ++real_payload_size) { |
- if (!approve_size.Run(real_payload_size)) { |
- continue; |
- } |
- VLOG(1) << "real_payload_size=" << real_payload_size; |
- uint8_t flags = required_flags | RandFlags(); |
- Http2FrameBuilder fb; |
- if (total_pad_length > 0) { |
- // total_pad_length_ includes the size of the Pad Length field, and thus |
- // ranges from 0 (no PADDED flag) to 256 (Pad Length == 255). |
- fb.AppendUInt8(total_pad_length - 1); |
- } |
- // Append a subset of the unpadded_payload, which the decoder should |
- // determine is not a valid amount. |
- fb.Append(unpadded_payload.substr(0, real_payload_size)); |
- if (total_pad_length > 0) { |
- fb.AppendZeroes(total_pad_length - 1); |
- } |
- // We choose a random stream id because the payload decoders aren't |
- // checking stream ids. |
- uint32_t stream_id = RandStreamId(); |
- Http2FrameHeader header(fb.size(), frame_type, flags, stream_id); |
- VERIFY_SUCCESS(VerifyDetectsFrameSizeError( |
- fb.buffer(), header, base::Bind(&SucceedingValidator))); |
- validated = true; |
- } |
- VERIFY_TRUE(validated); |
- return ::testing::AssertionSuccess(); |
- } |
- |
- // As above, but for frames without padding. |
- ::testing::AssertionResult VerifyDetectsFrameSizeError( |
- uint8_t required_flags, |
- base::StringPiece unpadded_payload, |
- ApproveSize approve_size) { |
- Http2FrameType frame_type = DecoderPeer::FrameType(); |
- uint8_t known_flags = KnownFlagsMaskForFrameType(frame_type); |
- VERIFY_EQ(0, known_flags & Http2FrameFlag::FLAG_PADDED); |
- VERIFY_EQ(0, required_flags & Http2FrameFlag::FLAG_PADDED); |
- VERIFY_AND_RETURN_SUCCESS(VerifyDetectsMultipleFrameSizeErrors( |
- required_flags, unpadded_payload, approve_size, 0)); |
- } |
- |
- Listener listener_; |
- union { |
- // Confirm at compile time that Decoder can be in an anonymous union, |
- // i.e. complain loudly if Decoder has members that prevent this, as it |
- // becomes annoying and possibly difficult to deal with non-anonymous |
- // unions and such union members. |
- Decoder payload_decoder_; |
- }; |
-}; |
- |
-// A base class for tests parameterized by the total number of bytes of |
-// padding, including the Pad Length field (i.e. a total_pad_length of 0 |
-// means unpadded as there is then no room for the Pad Length field). |
-// The frame type must support padding. |
-template <class Decoder, class DecoderPeer, class Listener> |
-class AbstractPaddablePayloadDecoderTest |
- : public AbstractPayloadDecoderTest<Decoder, DecoderPeer, Listener>, |
- public ::testing::WithParamInterface<int> { |
- typedef AbstractPayloadDecoderTest<Decoder, DecoderPeer, Listener> Base; |
- |
- protected: |
- using Base::Random; |
- using Base::RandStreamId; |
- using Base::set_frame_header; |
- using Base::listener_; |
- typedef typename Base::Validator Validator; |
- |
- AbstractPaddablePayloadDecoderTest() : total_pad_length_(GetParam()) { |
- LOG(INFO) << "total_pad_length_ = " << total_pad_length_; |
- } |
- |
- // Note that total_pad_length_ includes the size of the Pad Length field, |
- // and thus ranges from 0 (no PADDED flag) to 256 (Pad Length == 255). |
- bool IsPadded() const { return total_pad_length_ > 0; } |
- |
- // Value of the Pad Length field. Only call if IsPadded. |
- size_t pad_length() const { |
- EXPECT_TRUE(IsPadded()); |
- return total_pad_length_ - 1; |
- } |
- |
- // Clear the frame builder and add the Pad Length field if appropriate. |
- void Reset() { |
- frame_builder_ = Http2FrameBuilder(); |
- if (IsPadded()) { |
- frame_builder_.AppendUInt8(pad_length()); |
- } |
- } |
- |
- void MaybeAppendTrailingPadding() { |
- if (IsPadded()) { |
- frame_builder_.AppendZeroes(pad_length()); |
- } |
- } |
- |
- uint8_t RandFlags() { |
- uint8_t flags = Base::RandFlags(); |
- if (IsPadded()) { |
- flags |= Http2FrameFlag::FLAG_PADDED; |
- } else { |
- flags &= ~Http2FrameFlag::FLAG_PADDED; |
- } |
- return flags; |
- } |
- |
- static ::testing::AssertionResult ValidatorForVerifyDetectsPaddingTooLong( |
- const Http2FrameHeader& header, |
- int expected_missing_length, |
- const Listener& listener, |
- const DecodeBuffer& input, |
- DecodeStatus status) { |
- VERIFY_EQ(DecodeStatus::kDecodeError, status); |
- VERIFY_FALSE(listener.IsInProgress()); |
- VERIFY_EQ(1u, listener.size()); |
- const FrameParts* frame = listener.frame(0); |
- VERIFY_EQ(header, frame->frame_header); |
- VERIFY_TRUE(frame->opt_missing_length); |
- VERIFY_EQ(expected_missing_length, frame->opt_missing_length.value()); |
- // Verify did not get OnFrameSizeError. |
- VERIFY_FALSE(frame->has_frame_size_error); |
- return ::testing::AssertionSuccess(); |
- } |
- |
- // Verify that we get OnPaddingTooLong when decoding payload, and that the |
- // amount of missing padding is as specified. header.IsPadded must be true, |
- // and the payload must be empty or the PadLength field must be too large. |
- ::testing::AssertionResult VerifyDetectsPaddingTooLong( |
- base::StringPiece payload, |
- const Http2FrameHeader& header, |
- int expected_missing_length) { |
- set_frame_header(header); |
- auto& listener = listener_; |
- VERIFY_AND_RETURN_SUCCESS( |
- PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays( |
- payload, base::Bind(&AbstractPaddablePayloadDecoderTest:: |
- ValidatorForVerifyDetectsPaddingTooLong, |
- header, expected_missing_length, |
- base::ConstRef(listener)))); |
- } |
- |
- // Verifies that we get OnPaddingTooLong for a padded frame payload whose |
- // (randomly selected) payload length is less than total_pad_length_. |
- // Flags will be selected at random, except PADDED will be set and |
- // flags_to_avoid will not be set. The stream id is selected at random. |
- ::testing::AssertionResult VerifyDetectsPaddingTooLong() { |
- uint8_t flags = RandFlags() | Http2FrameFlag::FLAG_PADDED; |
- |
- // Create an all padding payload for total_pad_length_. |
- int payload_length = 0; |
- Http2FrameBuilder fb; |
- if (IsPadded()) { |
- fb.AppendUInt8(pad_length()); |
- fb.AppendZeroes(pad_length()); |
- VLOG(1) << "fb.size=" << fb.size(); |
- // Pick a random length for the payload that is shorter than neccesary. |
- payload_length = Random().Rand32() % fb.size(); |
- } |
- |
- VLOG(1) << "payload_length=" << payload_length; |
- std::string payload = fb.buffer().substr(0, payload_length); |
- |
- // The missing length is the amount we cut off the end, unless |
- // payload_length is zero, in which case the decoder knows only that 1 |
- // byte, the Pad Length field, is missing. |
- int missing_length = payload_length == 0 ? 1 : fb.size() - payload_length; |
- VLOG(1) << "missing_length=" << missing_length; |
- |
- const Http2FrameHeader header(payload_length, DecoderPeer::FrameType(), |
- flags, RandStreamId()); |
- VERIFY_AND_RETURN_SUCCESS( |
- VerifyDetectsPaddingTooLong(payload, header, missing_length)); |
- } |
- |
- // total_pad_length_ includes the size of the Pad Length field, and thus |
- // ranges from 0 (no PADDED flag) to 256 (Pad Length == 255). |
- const size_t total_pad_length_; |
- Http2FrameBuilder frame_builder_; |
-}; |
- |
-} // namespace test |
-} // namespace net |
- |
-#endif // NET_HTTP2_DECODER_PAYLOAD_DECODERS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_ |