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

Side by Side Diff: net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h

Issue 2554683003: Revert of Add new HTTP/2 and HPACK decoder in net/http2/. (Closed)
Patch Set: 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 unified diff | Download patch
OLDNEW
(Empty)
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 #ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_
6 #define NET_HTTP2_DECODER_PAYLOAD_DECODERS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_
7
8 // Base class for testing concrete payload decoder classes.
9
10 #include <stddef.h>
11
12 #include <string>
13
14 #include "base/bind.h"
15 #include "base/bind_helpers.h"
16 #include "base/callback.h"
17 #include "base/logging.h"
18 #include "base/strings/string_piece.h"
19 #include "net/http2/decoder/decode_buffer.h"
20 #include "net/http2/decoder/decode_status.h"
21 #include "net/http2/decoder/frame_decoder_state.h"
22 #include "net/http2/decoder/frame_parts.h"
23 #include "net/http2/decoder/http2_frame_decoder_listener.h"
24 #include "net/http2/http2_constants.h"
25 #include "net/http2/http2_constants_test_util.h"
26 #include "net/http2/http2_structures.h"
27 #include "net/http2/tools/http2_frame_builder.h"
28 #include "net/http2/tools/random_decoder_test.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30
31 namespace net {
32 namespace test {
33
34 // Base class for tests of payload decoders. Below this there is a templated
35 // sub-class that adds a bunch of type specific features.
36 class PayloadDecoderBaseTest : public RandomDecoderTest {
37 protected:
38 PayloadDecoderBaseTest();
39
40 // Virtual functions to be implemented by the test classes for the individual
41 // payload decoders...
42
43 // Start decoding the payload.
44 virtual DecodeStatus StartDecodingPayload(DecodeBuffer* db) = 0;
45
46 // Resume decoding the payload.
47 virtual DecodeStatus ResumeDecodingPayload(DecodeBuffer* db) = 0;
48
49 // In support of ensuring that we're really accessing and updating the
50 // decoder, prepare the decoder by, for example, overwriting the decoder.
51 virtual void PreparePayloadDecoder() = 0;
52
53 // Get the listener to be inserted into the FrameDecoderState, ready for
54 // listening (e.g. reset if it is a FramePartsCollector).
55 virtual Http2FrameDecoderListener* PrepareListener() = 0;
56
57 // Record a frame header for use on each call to StartDecoding.
58 void set_frame_header(const Http2FrameHeader& header) {
59 EXPECT_EQ(0, InvalidFlagMaskForFrameType(header.type) & header.flags);
60 if (!frame_header_is_set_ || frame_header_ != header) {
61 VLOG(2) << "set_frame_header: " << frame_header_;
62 }
63 frame_header_ = header;
64 frame_header_is_set_ = true;
65 }
66
67 const Http2FrameHeader& frame_header() const {
68 CHECK(frame_header_is_set_);
69 return frame_header_;
70 }
71
72 FrameDecoderState* mutable_state() { return &frame_decoder_state_; }
73
74 // Randomize the payload decoder, sets the payload decoder's frame_header_,
75 // then start decoding the payload. Called by RandomDecoderTest. This method
76 // is final so that we can always perform certain actions when
77 // RandomDecoderTest starts the decoding of a payload, such as randomizing the
78 // the payload decoder, injecting the frame header and counting fast decoding
79 // cases. Sub-classes must implement StartDecodingPayload to perform their
80 // initial decoding of a frame's payload.
81 DecodeStatus StartDecoding(DecodeBuffer* db) final;
82
83 // Called by RandomDecoderTest. This method is final so that we can always
84 // perform certain actions when RandomDecoderTest calls it, such as counting
85 // slow decode cases. Sub-classes must implement ResumeDecodingPayload to
86 // continue decoding the frame's payload, which must not all be in one buffer.
87 DecodeStatus ResumeDecoding(DecodeBuffer* db) final;
88
89 // Given the specified payload (without the common frame header), decode
90 // it with several partitionings of the payload.
91 ::testing::AssertionResult DecodePayloadAndValidateSeveralWays(
92 base::StringPiece payload,
93 Validator validator);
94
95 // TODO(jamessynge): Add helper method for verifying these are both non-zero,
96 // and call the new method from tests that expect successful decoding.
97 void ResetDecodeSpeedCounters() {
98 fast_decode_count_ = 0;
99 slow_decode_count_ = 0;
100 }
101
102 // Count of payloads that are full decoded by StartDecodingPayload, or that
103 // an error was detected by StartDecodingPayload.
104 size_t fast_decode_count_ = 0;
105
106 // Count of payloads that require calling ResumeDecodingPayload in order to
107 // decode them completely (or to detect an error during decoding).
108 size_t slow_decode_count_ = 0;
109
110 private:
111 bool frame_header_is_set_ = false;
112 Http2FrameHeader frame_header_;
113 FrameDecoderState frame_decoder_state_;
114 };
115
116 // Base class for payload decoders of type Decoder, with corresponding test
117 // peer of type DecoderPeer, and using class Listener as the implementation
118 // of Http2FrameDecoderListenerInterface to be used during decoding.
119 // Typically Listener is a sub-class of FramePartsCollector.
120 // SupportedFrameType is set to false only for UnknownPayloadDecoder.
121 template <class Decoder,
122 class DecoderPeer,
123 class Listener,
124 bool SupportedFrameType = true>
125 class AbstractPayloadDecoderTest : public PayloadDecoderBaseTest {
126 public:
127 static bool SucceedingApproveSize(size_t size) { return true; }
128
129 protected:
130 // An ApproveSize function returns true to approve decoding the specified
131 // size of payload, else false to skip that size. Typically used for negative
132 // tests; for example, decoding a SETTINGS frame at all sizes except for
133 // multiples of 6.
134 typedef base::Callback<bool(size_t size)> ApproveSize;
135
136 AbstractPayloadDecoderTest() {}
137
138 // These tests are in setup rather than the constructor for two reasons:
139 // 1) Constructors are not allowed to fail, so gUnit documents that EXPECT_*
140 // and ASSERT_* are not allowed in constructors, and should instead be in
141 // SetUp if they are needed before the body of the test is executed.
142 // 2) To allow the sub-class constructor to make any desired modifications to
143 // the DecoderPeer before these tests are executed; in particular,
144 // UnknownPayloadDecoderPeer has not got a fixed frame type, but it is
145 // instead set during the test's constructor.
146 void SetUp() override {
147 PayloadDecoderBaseTest::SetUp();
148
149 // Confirm that DecoderPeer et al returns sensible values. Using auto as the
150 // variable type so that no (narrowing) conversions take place that hide
151 // problems; i.e. if someone changes KnownFlagsMaskForFrameType so that it
152 // doesn't return a uint8, and has bits above the low-order 8 bits set, this
153 // bit of paranoia should detect the problem before we get too far.
154 auto frame_type = DecoderPeer::FrameType();
155 if (SupportedFrameType) {
156 EXPECT_TRUE(IsSupportedHttp2FrameType(frame_type)) << frame_type;
157 } else {
158 EXPECT_FALSE(IsSupportedHttp2FrameType(frame_type)) << frame_type;
159 }
160
161 auto known_flags = KnownFlagsMaskForFrameType(frame_type);
162 EXPECT_EQ(known_flags, known_flags & 0xff);
163
164 auto flags_to_avoid = DecoderPeer::FlagsAffectingPayloadDecoding();
165 EXPECT_EQ(flags_to_avoid, flags_to_avoid & known_flags);
166 }
167
168 void PreparePayloadDecoder() override {
169 payload_decoder_.~Decoder();
170 new (&payload_decoder_) Decoder;
171 }
172
173 Http2FrameDecoderListener* PrepareListener() override {
174 listener_.Reset();
175 return &listener_;
176 }
177
178 // Returns random flags, but only those valid for the frame type, yet not
179 // those that the DecoderPeer says will affect the decoding of the payload
180 // (e.g. the PRIORTY flag on a HEADERS frame or PADDED on DATA frames).
181 uint8_t RandFlags() {
182 return Random().Rand8() &
183 KnownFlagsMaskForFrameType(DecoderPeer::FrameType()) &
184 ~DecoderPeer::FlagsAffectingPayloadDecoding();
185 }
186
187 // Start decoding the payload.
188 DecodeStatus StartDecodingPayload(DecodeBuffer* db) override {
189 DVLOG(2) << "StartDecodingPayload, db->Remaining=" << db->Remaining();
190 return payload_decoder_.StartDecodingPayload(mutable_state(), db);
191 }
192
193 // Resume decoding the payload.
194 DecodeStatus ResumeDecodingPayload(DecodeBuffer* db) override {
195 DVLOG(2) << "ResumeDecodingPayload, db->Remaining=" << db->Remaining();
196 return payload_decoder_.ResumeDecodingPayload(mutable_state(), db);
197 }
198
199 // Wrap |validator| in another one which will check that we've reached the
200 // expected state of kDecodeError with OnFrameSizeError having been called by
201 AssertionResult ValidatorForDecodePayloadAndValidateSeveralWays(
202 const FrameParts& expected) {
203 VERIFY_FALSE(listener_.IsInProgress());
204 VERIFY_EQ(1u, listener_.size());
205 VERIFY_AND_RETURN_SUCCESS(expected.VerifyEquals(*listener_.frame(0)));
206 }
207
208 // Decode one frame's payload and confirm that the listener recorded the
209 // expected FrameParts instance, and only FrameParts instance. The payload
210 // will be decoded several times with different partitionings of the payload,
211 // and after each the validator will be called.
212 AssertionResult DecodePayloadAndValidateSeveralWays(
213 base::StringPiece payload,
214 const FrameParts& expected) {
215 return PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays(
216 payload, this->ValidateDoneAndEmpty(base::Bind(
217 &AbstractPayloadDecoderTest::
218 ValidatorForDecodePayloadAndValidateSeveralWays,
219 base::Unretained(this), base::ConstRef(expected))));
220 }
221
222 // Wrap |validator| in another one which will check that we've reached the
223 // expected state of kDecodeError with OnFrameSizeError having been called by
224 // the payload decoder.
225 AssertionResult ValidatorForVerifyDetectsFrameSizeError(
226 const Http2FrameHeader& header,
227 const Validator& validator,
228 const DecodeBuffer& input,
229 DecodeStatus status) {
230 DVLOG(2) << "VerifyDetectsFrameSizeError validator; status=" << status
231 << "; input.Remaining=" << input.Remaining();
232 VERIFY_EQ(DecodeStatus::kDecodeError, status);
233 VERIFY_FALSE(listener_.IsInProgress());
234 VERIFY_EQ(1u, listener_.size());
235 const FrameParts* frame = listener_.frame(0);
236 VERIFY_EQ(header, frame->frame_header);
237 VERIFY_TRUE(frame->has_frame_size_error);
238 // Verify did not get OnPaddingTooLong, as we should only ever produce
239 // one of these two errors for a single frame.
240 VERIFY_FALSE(frame->opt_missing_length);
241 return validator.Run(input, status);
242 }
243
244 // Decode one frame's payload, expecting that the final status will be
245 // kDecodeError, and that OnFrameSizeError will have been called on the
246 // listener. The payload will be decoded several times with different
247 // partitionings of the payload. The type WrappedValidator is either
248 // RandomDecoderTest::Validator, RandomDecoderTest::NoArgValidator or
249 // std::nullptr_t (not extra validation).
250 template <typename WrappedValidator>
251 ::testing::AssertionResult VerifyDetectsFrameSizeError(
252 base::StringPiece payload,
253 const Http2FrameHeader& header,
254 WrappedValidator wrapped_validator) {
255 set_frame_header(header);
256 // If wrapped_validator is not a RandomDecoderTest::Validator, make it so.
257 Validator validator = this->ToValidator(wrapped_validator);
258 VERIFY_AND_RETURN_SUCCESS(
259 PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays(
260 payload, base::Bind(&AbstractPayloadDecoderTest::
261 ValidatorForVerifyDetectsFrameSizeError,
262 base::Unretained(this), base::ConstRef(header),
263 base::ConstRef(validator))));
264 }
265
266 // Confirm that we get OnFrameSizeError when trying to decode unpadded_payload
267 // at all sizes from zero to unpadded_payload.size(), except those sizes not
268 // approved by approve_size.
269 // If total_pad_length is greater than zero, then that amount of padding
270 // is added to the payload (including the Pad Length field).
271 // The flags will be required_flags, PADDED if total_pad_length > 0, and some
272 // randomly selected flag bits not excluded by FlagsAffectingPayloadDecoding.
273 ::testing::AssertionResult VerifyDetectsMultipleFrameSizeErrors(
274 uint8_t required_flags,
275 base::StringPiece unpadded_payload,
276 ApproveSize approve_size,
277 int total_pad_length) {
278 // required_flags should come from those that are defined for the frame
279 // type AND are those that affect the decoding of the payload (otherwise,
280 // the flag shouldn't be required).
281 Http2FrameType frame_type = DecoderPeer::FrameType();
282 VERIFY_EQ(required_flags,
283 required_flags & KnownFlagsMaskForFrameType(frame_type));
284 VERIFY_EQ(required_flags,
285 required_flags & DecoderPeer::FlagsAffectingPayloadDecoding());
286
287 if (0 != (Http2FrameFlag::FLAG_PADDED &
288 KnownFlagsMaskForFrameType(frame_type))) {
289 // Frame type supports padding.
290 if (total_pad_length == 0) {
291 required_flags &= ~Http2FrameFlag::FLAG_PADDED;
292 } else {
293 required_flags |= Http2FrameFlag::FLAG_PADDED;
294 }
295 } else {
296 VERIFY_EQ(0, total_pad_length);
297 }
298
299 bool validated = false;
300 for (size_t real_payload_size = 0;
301 real_payload_size <= unpadded_payload.size(); ++real_payload_size) {
302 if (!approve_size.Run(real_payload_size)) {
303 continue;
304 }
305 VLOG(1) << "real_payload_size=" << real_payload_size;
306 uint8_t flags = required_flags | RandFlags();
307 Http2FrameBuilder fb;
308 if (total_pad_length > 0) {
309 // total_pad_length_ includes the size of the Pad Length field, and thus
310 // ranges from 0 (no PADDED flag) to 256 (Pad Length == 255).
311 fb.AppendUInt8(total_pad_length - 1);
312 }
313 // Append a subset of the unpadded_payload, which the decoder should
314 // determine is not a valid amount.
315 fb.Append(unpadded_payload.substr(0, real_payload_size));
316 if (total_pad_length > 0) {
317 fb.AppendZeroes(total_pad_length - 1);
318 }
319 // We choose a random stream id because the payload decoders aren't
320 // checking stream ids.
321 uint32_t stream_id = RandStreamId();
322 Http2FrameHeader header(fb.size(), frame_type, flags, stream_id);
323 VERIFY_SUCCESS(VerifyDetectsFrameSizeError(
324 fb.buffer(), header, base::Bind(&SucceedingValidator)));
325 validated = true;
326 }
327 VERIFY_TRUE(validated);
328 return ::testing::AssertionSuccess();
329 }
330
331 // As above, but for frames without padding.
332 ::testing::AssertionResult VerifyDetectsFrameSizeError(
333 uint8_t required_flags,
334 base::StringPiece unpadded_payload,
335 ApproveSize approve_size) {
336 Http2FrameType frame_type = DecoderPeer::FrameType();
337 uint8_t known_flags = KnownFlagsMaskForFrameType(frame_type);
338 VERIFY_EQ(0, known_flags & Http2FrameFlag::FLAG_PADDED);
339 VERIFY_EQ(0, required_flags & Http2FrameFlag::FLAG_PADDED);
340 VERIFY_AND_RETURN_SUCCESS(VerifyDetectsMultipleFrameSizeErrors(
341 required_flags, unpadded_payload, approve_size, 0));
342 }
343
344 Listener listener_;
345 union {
346 // Confirm at compile time that Decoder can be in an anonymous union,
347 // i.e. complain loudly if Decoder has members that prevent this, as it
348 // becomes annoying and possibly difficult to deal with non-anonymous
349 // unions and such union members.
350 Decoder payload_decoder_;
351 };
352 };
353
354 // A base class for tests parameterized by the total number of bytes of
355 // padding, including the Pad Length field (i.e. a total_pad_length of 0
356 // means unpadded as there is then no room for the Pad Length field).
357 // The frame type must support padding.
358 template <class Decoder, class DecoderPeer, class Listener>
359 class AbstractPaddablePayloadDecoderTest
360 : public AbstractPayloadDecoderTest<Decoder, DecoderPeer, Listener>,
361 public ::testing::WithParamInterface<int> {
362 typedef AbstractPayloadDecoderTest<Decoder, DecoderPeer, Listener> Base;
363
364 protected:
365 using Base::Random;
366 using Base::RandStreamId;
367 using Base::set_frame_header;
368 using Base::listener_;
369 typedef typename Base::Validator Validator;
370
371 AbstractPaddablePayloadDecoderTest() : total_pad_length_(GetParam()) {
372 LOG(INFO) << "total_pad_length_ = " << total_pad_length_;
373 }
374
375 // Note that total_pad_length_ includes the size of the Pad Length field,
376 // and thus ranges from 0 (no PADDED flag) to 256 (Pad Length == 255).
377 bool IsPadded() const { return total_pad_length_ > 0; }
378
379 // Value of the Pad Length field. Only call if IsPadded.
380 size_t pad_length() const {
381 EXPECT_TRUE(IsPadded());
382 return total_pad_length_ - 1;
383 }
384
385 // Clear the frame builder and add the Pad Length field if appropriate.
386 void Reset() {
387 frame_builder_ = Http2FrameBuilder();
388 if (IsPadded()) {
389 frame_builder_.AppendUInt8(pad_length());
390 }
391 }
392
393 void MaybeAppendTrailingPadding() {
394 if (IsPadded()) {
395 frame_builder_.AppendZeroes(pad_length());
396 }
397 }
398
399 uint8_t RandFlags() {
400 uint8_t flags = Base::RandFlags();
401 if (IsPadded()) {
402 flags |= Http2FrameFlag::FLAG_PADDED;
403 } else {
404 flags &= ~Http2FrameFlag::FLAG_PADDED;
405 }
406 return flags;
407 }
408
409 static ::testing::AssertionResult ValidatorForVerifyDetectsPaddingTooLong(
410 const Http2FrameHeader& header,
411 int expected_missing_length,
412 const Listener& listener,
413 const DecodeBuffer& input,
414 DecodeStatus status) {
415 VERIFY_EQ(DecodeStatus::kDecodeError, status);
416 VERIFY_FALSE(listener.IsInProgress());
417 VERIFY_EQ(1u, listener.size());
418 const FrameParts* frame = listener.frame(0);
419 VERIFY_EQ(header, frame->frame_header);
420 VERIFY_TRUE(frame->opt_missing_length);
421 VERIFY_EQ(expected_missing_length, frame->opt_missing_length.value());
422 // Verify did not get OnFrameSizeError.
423 VERIFY_FALSE(frame->has_frame_size_error);
424 return ::testing::AssertionSuccess();
425 }
426
427 // Verify that we get OnPaddingTooLong when decoding payload, and that the
428 // amount of missing padding is as specified. header.IsPadded must be true,
429 // and the payload must be empty or the PadLength field must be too large.
430 ::testing::AssertionResult VerifyDetectsPaddingTooLong(
431 base::StringPiece payload,
432 const Http2FrameHeader& header,
433 int expected_missing_length) {
434 set_frame_header(header);
435 auto& listener = listener_;
436 VERIFY_AND_RETURN_SUCCESS(
437 PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays(
438 payload, base::Bind(&AbstractPaddablePayloadDecoderTest::
439 ValidatorForVerifyDetectsPaddingTooLong,
440 header, expected_missing_length,
441 base::ConstRef(listener))));
442 }
443
444 // Verifies that we get OnPaddingTooLong for a padded frame payload whose
445 // (randomly selected) payload length is less than total_pad_length_.
446 // Flags will be selected at random, except PADDED will be set and
447 // flags_to_avoid will not be set. The stream id is selected at random.
448 ::testing::AssertionResult VerifyDetectsPaddingTooLong() {
449 uint8_t flags = RandFlags() | Http2FrameFlag::FLAG_PADDED;
450
451 // Create an all padding payload for total_pad_length_.
452 int payload_length = 0;
453 Http2FrameBuilder fb;
454 if (IsPadded()) {
455 fb.AppendUInt8(pad_length());
456 fb.AppendZeroes(pad_length());
457 VLOG(1) << "fb.size=" << fb.size();
458 // Pick a random length for the payload that is shorter than neccesary.
459 payload_length = Random().Rand32() % fb.size();
460 }
461
462 VLOG(1) << "payload_length=" << payload_length;
463 std::string payload = fb.buffer().substr(0, payload_length);
464
465 // The missing length is the amount we cut off the end, unless
466 // payload_length is zero, in which case the decoder knows only that 1
467 // byte, the Pad Length field, is missing.
468 int missing_length = payload_length == 0 ? 1 : fb.size() - payload_length;
469 VLOG(1) << "missing_length=" << missing_length;
470
471 const Http2FrameHeader header(payload_length, DecoderPeer::FrameType(),
472 flags, RandStreamId());
473 VERIFY_AND_RETURN_SUCCESS(
474 VerifyDetectsPaddingTooLong(payload, header, missing_length));
475 }
476
477 // total_pad_length_ includes the size of the Pad Length field, and thus
478 // ranges from 0 (no PADDED flag) to 256 (Pad Length == 255).
479 const size_t total_pad_length_;
480 Http2FrameBuilder frame_builder_;
481 };
482
483 } // namespace test
484 } // namespace net
485
486 #endif // NET_HTTP2_DECODER_PAYLOAD_DECODERS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698