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