1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/http2/decoder/payload_decoders/push_promise_payload_decoder.h"
6
7 #include <stddef.h>
8
9 #include <string>
10
11 #include "quiche/http2/decoder/http2_frame_decoder_listener.h"
12 #include "quiche/http2/http2_constants.h"
13 #include "quiche/http2/test_tools/frame_parts.h"
14 #include "quiche/http2/test_tools/frame_parts_collector.h"
15 #include "quiche/http2/test_tools/http2_frame_builder.h"
16 #include "quiche/http2/test_tools/http2_random.h"
17 #include "quiche/http2/test_tools/http2_structures_test_util.h"
18 #include "quiche/http2/test_tools/payload_decoder_base_test_util.h"
19 #include "quiche/http2/test_tools/random_decoder_test_base.h"
20 #include "quiche/common/platform/api/quiche_logging.h"
21 #include "quiche/common/platform/api/quiche_test.h"
22
23 namespace http2 {
24 namespace test {
25
26 // Provides friend access to an instance of the payload decoder, and also
27 // provides info to aid in testing.
28 class PushPromisePayloadDecoderPeer {
29 public:
FrameType()30 static constexpr Http2FrameType FrameType() {
31 return Http2FrameType::PUSH_PROMISE;
32 }
33
34 // Returns the mask of flags that affect the decoding of the payload (i.e.
35 // flags that that indicate the presence of certain fields or padding).
FlagsAffectingPayloadDecoding()36 static constexpr uint8_t FlagsAffectingPayloadDecoding() {
37 return Http2FrameFlag::PADDED;
38 }
39 };
40
41 namespace {
42
43 // Listener listens for only those methods expected by the payload decoder
44 // under test, and forwards them onto the FrameParts instance for the current
45 // frame.
46 struct Listener : public FramePartsCollector {
OnPushPromiseStarthttp2::test::__anon2d96e7d90111::Listener47 void OnPushPromiseStart(const Http2FrameHeader& header,
48 const Http2PushPromiseFields& promise,
49 size_t total_padding_length) override {
50 QUICHE_VLOG(1) << "OnPushPromiseStart header: " << header
51 << " promise: " << promise
52 << " total_padding_length: " << total_padding_length;
53 EXPECT_EQ(Http2FrameType::PUSH_PROMISE, header.type);
54 StartFrame(header)->OnPushPromiseStart(header, promise,
55 total_padding_length);
56 }
57
OnHpackFragmenthttp2::test::__anon2d96e7d90111::Listener58 void OnHpackFragment(const char* data, size_t len) override {
59 QUICHE_VLOG(1) << "OnHpackFragment: len=" << len;
60 CurrentFrame()->OnHpackFragment(data, len);
61 }
62
OnPushPromiseEndhttp2::test::__anon2d96e7d90111::Listener63 void OnPushPromiseEnd() override {
64 QUICHE_VLOG(1) << "OnPushPromiseEnd";
65 EndFrame()->OnPushPromiseEnd();
66 }
67
OnPaddinghttp2::test::__anon2d96e7d90111::Listener68 void OnPadding(const char* padding, size_t skipped_length) override {
69 QUICHE_VLOG(1) << "OnPadding: " << skipped_length;
70 CurrentFrame()->OnPadding(padding, skipped_length);
71 }
72
OnPaddingTooLonghttp2::test::__anon2d96e7d90111::Listener73 void OnPaddingTooLong(const Http2FrameHeader& header,
74 size_t missing_length) override {
75 QUICHE_VLOG(1) << "OnPaddingTooLong: " << header
76 << "; missing_length: " << missing_length;
77 FrameError(header)->OnPaddingTooLong(header, missing_length);
78 }
79
OnFrameSizeErrorhttp2::test::__anon2d96e7d90111::Listener80 void OnFrameSizeError(const Http2FrameHeader& header) override {
81 QUICHE_VLOG(1) << "OnFrameSizeError: " << header;
82 FrameError(header)->OnFrameSizeError(header);
83 }
84 };
85
86 class PushPromisePayloadDecoderTest
87 : public AbstractPaddablePayloadDecoderTest<
88 PushPromisePayloadDecoder, PushPromisePayloadDecoderPeer, Listener> {
89 };
90
91 INSTANTIATE_TEST_SUITE_P(VariousPadLengths, PushPromisePayloadDecoderTest,
92 ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
93
94 // Payload contains the required Http2PushPromiseFields, followed by some
95 // (fake) HPACK payload.
TEST_P(PushPromisePayloadDecoderTest,VariousHpackPayloadSizes)96 TEST_P(PushPromisePayloadDecoderTest, VariousHpackPayloadSizes) {
97 for (size_t hpack_size : {0, 1, 2, 3, 255, 256, 1024}) {
98 QUICHE_LOG(INFO) << "########### hpack_size = " << hpack_size
99 << " ###########";
100 Reset();
101 std::string hpack_payload = Random().RandString(hpack_size);
102 Http2PushPromiseFields push_promise{RandStreamId()};
103 frame_builder_.Append(push_promise);
104 frame_builder_.Append(hpack_payload);
105 MaybeAppendTrailingPadding();
106 Http2FrameHeader frame_header(frame_builder_.size(),
107 Http2FrameType::PUSH_PROMISE, RandFlags(),
108 RandStreamId());
109 set_frame_header(frame_header);
110 FrameParts expected(frame_header, hpack_payload, total_pad_length_);
111 expected.SetOptPushPromise(push_promise);
112 EXPECT_TRUE(
113 DecodePayloadAndValidateSeveralWays(frame_builder_.buffer(), expected));
114 }
115 }
116
117 // Confirm we get an error if the payload is not long enough for the required
118 // portion of the payload, regardless of the amount of (valid) padding.
TEST_P(PushPromisePayloadDecoderTest,Truncated)119 TEST_P(PushPromisePayloadDecoderTest, Truncated) {
120 auto approve_size = [](size_t size) {
121 return size != Http2PushPromiseFields::EncodedSize();
122 };
123 Http2PushPromiseFields push_promise{RandStreamId()};
124 Http2FrameBuilder fb;
125 fb.Append(push_promise);
126 EXPECT_TRUE(VerifyDetectsMultipleFrameSizeErrors(0, fb.buffer(), approve_size,
127 total_pad_length_));
128 }
129
130 // Confirm we get an error if the PADDED flag is set but the payload is not
131 // long enough to hold even the Pad Length amount of padding.
TEST_P(PushPromisePayloadDecoderTest,PaddingTooLong)132 TEST_P(PushPromisePayloadDecoderTest, PaddingTooLong) {
133 EXPECT_TRUE(VerifyDetectsPaddingTooLong());
134 }
135
136 } // namespace
137 } // namespace test
138 } // namespace http2
139