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/test_tools/random_decoder_test_base.h"
6
7 #include <stddef.h>
8
9 #include <algorithm>
10 #include <memory>
11
12 #include "quiche/http2/decoder/decode_buffer.h"
13 #include "quiche/http2/decoder/decode_status.h"
14 #include "quiche/http2/http2_constants.h"
15 #include "quiche/http2/test_tools/verify_macros.h"
16 #include "quiche/common/platform/api/quiche_logging.h"
17 #include "quiche/common/platform/api/quiche_test.h"
18
19 using ::testing::AssertionResult;
20
21 namespace http2 {
22 namespace test {
23
24 RandomDecoderTest::RandomDecoderTest() = default;
25
StopDecodeOnDone()26 bool RandomDecoderTest::StopDecodeOnDone() { return stop_decode_on_done_; }
27
DecodeSegments(DecodeBuffer * original,const SelectSize & select_size)28 DecodeStatus RandomDecoderTest::DecodeSegments(DecodeBuffer* original,
29 const SelectSize& select_size) {
30 DecodeStatus status = DecodeStatus::kDecodeInProgress;
31 bool first = true;
32 QUICHE_VLOG(2) << "DecodeSegments: input size=" << original->Remaining();
33 while (first || original->HasData()) {
34 size_t remaining = original->Remaining();
35 size_t size =
36 std::min(remaining, select_size(first, original->Offset(), remaining));
37 DecodeBuffer db(original->cursor(), size);
38 QUICHE_VLOG(2) << "Decoding " << size << " bytes of " << remaining
39 << " remaining";
40 if (first) {
41 first = false;
42 status = StartDecoding(&db);
43 } else {
44 status = ResumeDecoding(&db);
45 }
46 // A decoder MUST consume some input (if any is available), else we could
47 // get stuck in infinite loops.
48 if (db.Offset() == 0 && db.HasData() &&
49 status != DecodeStatus::kDecodeError) {
50 ADD_FAILURE() << "Decoder didn't make any progress; db.FullSize="
51 << db.FullSize()
52 << " original.Offset=" << original->Offset();
53 return DecodeStatus::kDecodeError;
54 }
55 original->AdvanceCursor(db.Offset());
56 switch (status) {
57 case DecodeStatus::kDecodeDone:
58 if (original->Empty() || StopDecodeOnDone()) {
59 return DecodeStatus::kDecodeDone;
60 }
61 continue;
62 case DecodeStatus::kDecodeInProgress:
63 continue;
64 case DecodeStatus::kDecodeError:
65 return DecodeStatus::kDecodeError;
66 }
67 }
68 return status;
69 }
70
71 // Decode |original| multiple times, with different segmentations, validating
72 // after each decode, returning on the first failure.
DecodeAndValidateSeveralWays(DecodeBuffer * original,bool return_non_zero_on_first,const Validator & validator)73 AssertionResult RandomDecoderTest::DecodeAndValidateSeveralWays(
74 DecodeBuffer* original, bool return_non_zero_on_first,
75 const Validator& validator) {
76 const uint32_t original_remaining = original->Remaining();
77 QUICHE_VLOG(1) << "DecodeAndValidateSeveralWays - Start, remaining = "
78 << original_remaining;
79 uint32_t first_consumed;
80 {
81 // Fast decode (no stopping unless decoder does so).
82 DecodeBuffer input(original->cursor(), original_remaining);
83 QUICHE_VLOG(2) << "DecodeSegmentsAndValidate with SelectRemaining";
84 HTTP2_VERIFY_SUCCESS(
85 DecodeSegmentsAndValidate(&input, SelectRemaining(), validator))
86 << "\nFailed with SelectRemaining; input.Offset=" << input.Offset()
87 << "; input.Remaining=" << input.Remaining();
88 first_consumed = input.Offset();
89 }
90 if (original_remaining <= 30) {
91 // Decode again, one byte at a time.
92 DecodeBuffer input(original->cursor(), original_remaining);
93 QUICHE_VLOG(2) << "DecodeSegmentsAndValidate with SelectOne";
94 HTTP2_VERIFY_SUCCESS(
95 DecodeSegmentsAndValidate(&input, SelectOne(), validator))
96 << "\nFailed with SelectOne; input.Offset=" << input.Offset()
97 << "; input.Remaining=" << input.Remaining();
98 HTTP2_VERIFY_EQ(first_consumed, input.Offset())
99 << "\nFailed with SelectOne";
100 }
101 if (original_remaining <= 20) {
102 // Decode again, one or zero bytes at a time.
103 DecodeBuffer input(original->cursor(), original_remaining);
104 QUICHE_VLOG(2) << "DecodeSegmentsAndValidate with SelectZeroAndOne";
105 HTTP2_VERIFY_SUCCESS(DecodeSegmentsAndValidate(
106 &input, SelectZeroAndOne(return_non_zero_on_first), validator))
107 << "\nFailed with SelectZeroAndOne";
108 HTTP2_VERIFY_EQ(first_consumed, input.Offset())
109 << "\nFailed with SelectZeroAndOne; input.Offset=" << input.Offset()
110 << "; input.Remaining=" << input.Remaining();
111 }
112 {
113 // Decode again, with randomly selected segment sizes.
114 DecodeBuffer input(original->cursor(), original_remaining);
115 QUICHE_VLOG(2) << "DecodeSegmentsAndValidate with SelectRandom";
116 HTTP2_VERIFY_SUCCESS(DecodeSegmentsAndValidate(
117 &input, SelectRandom(return_non_zero_on_first), validator))
118 << "\nFailed with SelectRandom; input.Offset=" << input.Offset()
119 << "; input.Remaining=" << input.Remaining();
120 HTTP2_VERIFY_EQ(first_consumed, input.Offset())
121 << "\nFailed with SelectRandom";
122 }
123 HTTP2_VERIFY_EQ(original_remaining, original->Remaining());
124 original->AdvanceCursor(first_consumed);
125 QUICHE_VLOG(1) << "DecodeAndValidateSeveralWays - SUCCESS";
126 return ::testing::AssertionSuccess();
127 }
128
129 // static
SelectZeroAndOne(bool return_non_zero_on_first)130 RandomDecoderTest::SelectSize RandomDecoderTest::SelectZeroAndOne(
131 bool return_non_zero_on_first) {
132 std::shared_ptr<bool> zero_next(new bool);
133 *zero_next = !return_non_zero_on_first;
134 return [zero_next](bool /*first*/, size_t /*offset*/,
135 size_t /*remaining*/) -> size_t {
136 if (*zero_next) {
137 *zero_next = false;
138 return 0;
139 } else {
140 *zero_next = true;
141 return 1;
142 }
143 };
144 }
145
SelectRandom(bool return_non_zero_on_first)146 RandomDecoderTest::SelectSize RandomDecoderTest::SelectRandom(
147 bool return_non_zero_on_first) {
148 return [this, return_non_zero_on_first](bool first, size_t /*offset*/,
149 size_t remaining) -> size_t {
150 uint32_t r = random_.Rand32();
151 if (first && return_non_zero_on_first) {
152 QUICHE_CHECK_LT(0u, remaining);
153 if (remaining == 1) {
154 return 1;
155 }
156 return 1 + (r % remaining); // size in range [1, remaining).
157 }
158 return r % (remaining + 1); // size in range [0, remaining].
159 };
160 }
161
RandStreamId()162 uint32_t RandomDecoderTest::RandStreamId() {
163 return random_.Rand32() & StreamIdMask();
164 }
165
166 } // namespace test
167 } // namespace http2
168