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

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

Issue 2293613002: Add new HTTP/2 and HPACK decoder in net/http2/. (Closed)
Patch Set: Replace LOG(INFO) by VLOG(2) in DecodeBufferTest.SlowDecodeTestStruct so that trybots do not fail. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « net/http2/decoder/http2_frame_decoder_listener_test_util.cc ('k') | net/http2/decoder/http2_structure_decoder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698