blob: a837180877398fff83406736634aab41f8728dd0 [file] [log] [blame]
QUICHE teamfd50a402018-12-07 22:54:05 -05001// 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 "net/third_party/quiche/src/http2/http2_structures.h"
6
7// Tests are focused on Http2FrameHeader because it has by far the most
8// methods of any of the structures.
9// Note that EXPECT.*DEATH tests are slow (a fork is probably involved).
10
11// And in case you're wondering, yes, these are ridiculously thorough tests,
12// but believe it or not, I've found stupid bugs this way.
13
14#include <memory>
15#include <ostream>
16#include <sstream>
bnc47904002019-08-16 11:49:48 -070017#include <string>
QUICHE teamfd50a402018-12-07 22:54:05 -050018#include <tuple>
19#include <type_traits>
20#include <vector>
21
QUICHE teamfd50a402018-12-07 22:54:05 -050022#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
23#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
24#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
25#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
bnc252403d2020-01-15 08:39:53 -080026#include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h"
QUICHE teamfd50a402018-12-07 22:54:05 -050027
28using ::testing::AssertionResult;
29using ::testing::AssertionSuccess;
30using ::testing::Combine;
31using ::testing::HasSubstr;
32using ::testing::MatchesRegex;
33using ::testing::Not;
34using ::testing::Values;
35using ::testing::ValuesIn;
36
37namespace http2 {
38namespace test {
39namespace {
40
41template <typename E>
42E IncrementEnum(E e) {
43 using I = typename std::underlying_type<E>::type;
44 return static_cast<E>(1 + static_cast<I>(e));
45}
46
47template <class T>
48AssertionResult VerifyRandomCalls() {
49 T t1;
50 Http2Random seq1;
51 Randomize(&t1, &seq1);
52
53 T t2;
54 Http2Random seq2(seq1.Key());
55 Randomize(&t2, &seq2);
56
57 // The two Randomize calls should have made the same number of calls into
58 // the Http2Random implementations.
59 VERIFY_EQ(seq1.Rand64(), seq2.Rand64());
60
61 // And because Http2Random implementation is returning the same sequence, and
62 // Randomize should have been consistent in applying those results, the two
63 // Ts should have the same value.
64 VERIFY_EQ(t1, t2);
65
66 Randomize(&t2, &seq2);
67 VERIFY_NE(t1, t2);
68
69 Randomize(&t1, &seq1);
70 VERIFY_EQ(t1, t2);
71
72 VERIFY_EQ(seq1.Rand64(), seq2.Rand64());
73
74 return AssertionSuccess();
75}
76
77#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
78std::vector<Http2FrameType> ValidFrameTypes() {
79 std::vector<Http2FrameType> valid_types{Http2FrameType::DATA};
80 while (valid_types.back() != Http2FrameType::ALTSVC) {
81 valid_types.push_back(IncrementEnum(valid_types.back()));
82 }
83 return valid_types;
84}
85#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
86
87TEST(Http2FrameHeaderTest, Constructor) {
88 Http2Random random;
89 uint8_t frame_type = 0;
90 do {
91 // Only the payload length is DCHECK'd in the constructor, so we need to
92 // make sure it is a "uint24".
93 uint32_t payload_length = random.Rand32() & 0xffffff;
94 Http2FrameType type = static_cast<Http2FrameType>(frame_type);
95 uint8_t flags = random.Rand8();
96 uint32_t stream_id = random.Rand32();
97
98 Http2FrameHeader v(payload_length, type, flags, stream_id);
99
100 EXPECT_EQ(payload_length, v.payload_length);
101 EXPECT_EQ(type, v.type);
102 EXPECT_EQ(flags, v.flags);
103 EXPECT_EQ(stream_id, v.stream_id);
104 } while (frame_type++ == 255);
105
106#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
107 EXPECT_DEBUG_DEATH(Http2FrameHeader(0x01000000, Http2FrameType::DATA, 0, 1),
108 "payload_length");
109#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
110}
111
112TEST(Http2FrameHeaderTest, Eq) {
113 Http2Random random;
114 uint32_t payload_length = random.Rand32() & 0xffffff;
115 Http2FrameType type = static_cast<Http2FrameType>(random.Rand8());
116
117 uint8_t flags = random.Rand8();
118 uint32_t stream_id = random.Rand32();
119
120 Http2FrameHeader v(payload_length, type, flags, stream_id);
121
122 EXPECT_EQ(payload_length, v.payload_length);
123 EXPECT_EQ(type, v.type);
124 EXPECT_EQ(flags, v.flags);
125 EXPECT_EQ(stream_id, v.stream_id);
126
127 Http2FrameHeader u(0, type, ~flags, stream_id);
128
129 EXPECT_NE(u, v);
130 EXPECT_NE(v, u);
131 EXPECT_FALSE(u == v);
132 EXPECT_FALSE(v == u);
133 EXPECT_TRUE(u != v);
134 EXPECT_TRUE(v != u);
135
136 u = v;
137
138 EXPECT_EQ(u, v);
139 EXPECT_EQ(v, u);
140 EXPECT_TRUE(u == v);
141 EXPECT_TRUE(v == u);
142 EXPECT_FALSE(u != v);
143 EXPECT_FALSE(v != u);
144
145 EXPECT_TRUE(VerifyRandomCalls<Http2FrameHeader>());
146}
147
148#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
149// The tests of the valid frame types include EXPECT_DEBUG_DEATH, which is
150// quite slow, so using value parameterized tests in order to allow sharding.
151class Http2FrameHeaderTypeAndFlagTest
152 : public ::testing::TestWithParam<
153 std::tuple<Http2FrameType, Http2FrameFlag>> {
154 protected:
155 Http2FrameHeaderTypeAndFlagTest()
156 : type_(std::get<0>(GetParam())), flags_(std::get<1>(GetParam())) {
QUICHE team61940b42019-03-07 23:32:27 -0500157 HTTP2_LOG(INFO) << "Frame type: " << type_;
158 HTTP2_LOG(INFO) << "Frame flags: "
159 << Http2FrameFlagsToString(type_, flags_);
QUICHE teamfd50a402018-12-07 22:54:05 -0500160 }
161
162 const Http2FrameType type_;
163 const Http2FrameFlag flags_;
164};
165
166class IsEndStreamTest : public Http2FrameHeaderTypeAndFlagTest {};
QUICHE team61940b42019-03-07 23:32:27 -0500167INSTANTIATE_TEST_SUITE_P(IsEndStream,
168 IsEndStreamTest,
QUICHE team3cab5a92019-01-30 21:10:16 -0500169 Combine(ValuesIn(ValidFrameTypes()),
170 Values(~Http2FrameFlag::END_STREAM, 0xff)));
QUICHE teamfd50a402018-12-07 22:54:05 -0500171TEST_P(IsEndStreamTest, IsEndStream) {
172 const bool is_set =
173 (flags_ & Http2FrameFlag::END_STREAM) == Http2FrameFlag::END_STREAM;
bnc47904002019-08-16 11:49:48 -0700174 std::string flags_string;
QUICHE teamfd50a402018-12-07 22:54:05 -0500175 Http2FrameHeader v(0, type_, flags_, 0);
176 switch (type_) {
177 case Http2FrameType::DATA:
178 case Http2FrameType::HEADERS:
179 EXPECT_EQ(is_set, v.IsEndStream()) << v;
180 flags_string = v.FlagsToString();
181 if (is_set) {
182 EXPECT_THAT(flags_string, MatchesRegex(".*\\|?END_STREAM\\|.*"));
183 } else {
184 EXPECT_THAT(flags_string, Not(HasSubstr("END_STREAM")));
185 }
186 v.RetainFlags(Http2FrameFlag::END_STREAM);
187 EXPECT_EQ(is_set, v.IsEndStream()) << v;
188 {
189 std::stringstream s;
190 s << v;
191 EXPECT_EQ(v.ToString(), s.str());
192 if (is_set) {
193 EXPECT_THAT(s.str(), HasSubstr("flags=END_STREAM,"));
194 } else {
195 EXPECT_THAT(s.str(), HasSubstr("flags=,"));
196 }
197 }
198 break;
199 default:
200 EXPECT_DEBUG_DEATH(v.IsEndStream(), "DATA.*HEADERS") << v;
201 }
202}
203
204class IsACKTest : public Http2FrameHeaderTypeAndFlagTest {};
QUICHE team61940b42019-03-07 23:32:27 -0500205INSTANTIATE_TEST_SUITE_P(IsAck,
206 IsACKTest,
QUICHE team3cab5a92019-01-30 21:10:16 -0500207 Combine(ValuesIn(ValidFrameTypes()),
208 Values(~Http2FrameFlag::ACK, 0xff)));
QUICHE teamfd50a402018-12-07 22:54:05 -0500209TEST_P(IsACKTest, IsAck) {
210 const bool is_set = (flags_ & Http2FrameFlag::ACK) == Http2FrameFlag::ACK;
bnc47904002019-08-16 11:49:48 -0700211 std::string flags_string;
QUICHE teamfd50a402018-12-07 22:54:05 -0500212 Http2FrameHeader v(0, type_, flags_, 0);
213 switch (type_) {
214 case Http2FrameType::SETTINGS:
215 case Http2FrameType::PING:
216 EXPECT_EQ(is_set, v.IsAck()) << v;
217 flags_string = v.FlagsToString();
218 if (is_set) {
219 EXPECT_THAT(flags_string, MatchesRegex(".*\\|?ACK\\|.*"));
220 } else {
221 EXPECT_THAT(flags_string, Not(HasSubstr("ACK")));
222 }
223 v.RetainFlags(Http2FrameFlag::ACK);
224 EXPECT_EQ(is_set, v.IsAck()) << v;
225 {
226 std::stringstream s;
227 s << v;
228 EXPECT_EQ(v.ToString(), s.str());
229 if (is_set) {
230 EXPECT_THAT(s.str(), HasSubstr("flags=ACK,"));
231 } else {
232 EXPECT_THAT(s.str(), HasSubstr("flags=,"));
233 }
234 }
235 break;
236 default:
237 EXPECT_DEBUG_DEATH(v.IsAck(), "SETTINGS.*PING") << v;
238 }
239}
240
241class IsEndHeadersTest : public Http2FrameHeaderTypeAndFlagTest {};
QUICHE team61940b42019-03-07 23:32:27 -0500242INSTANTIATE_TEST_SUITE_P(IsEndHeaders,
243 IsEndHeadersTest,
QUICHE team3cab5a92019-01-30 21:10:16 -0500244 Combine(ValuesIn(ValidFrameTypes()),
245 Values(~Http2FrameFlag::END_HEADERS, 0xff)));
QUICHE teamfd50a402018-12-07 22:54:05 -0500246TEST_P(IsEndHeadersTest, IsEndHeaders) {
247 const bool is_set =
248 (flags_ & Http2FrameFlag::END_HEADERS) == Http2FrameFlag::END_HEADERS;
bnc47904002019-08-16 11:49:48 -0700249 std::string flags_string;
QUICHE teamfd50a402018-12-07 22:54:05 -0500250 Http2FrameHeader v(0, type_, flags_, 0);
251 switch (type_) {
252 case Http2FrameType::HEADERS:
253 case Http2FrameType::PUSH_PROMISE:
254 case Http2FrameType::CONTINUATION:
255 EXPECT_EQ(is_set, v.IsEndHeaders()) << v;
256 flags_string = v.FlagsToString();
257 if (is_set) {
258 EXPECT_THAT(flags_string, MatchesRegex(".*\\|?END_HEADERS\\|.*"));
259 } else {
260 EXPECT_THAT(flags_string, Not(HasSubstr("END_HEADERS")));
261 }
262 v.RetainFlags(Http2FrameFlag::END_HEADERS);
263 EXPECT_EQ(is_set, v.IsEndHeaders()) << v;
264 {
265 std::stringstream s;
266 s << v;
267 EXPECT_EQ(v.ToString(), s.str());
268 if (is_set) {
269 EXPECT_THAT(s.str(), HasSubstr("flags=END_HEADERS,"));
270 } else {
271 EXPECT_THAT(s.str(), HasSubstr("flags=,"));
272 }
273 }
274 break;
275 default:
276 EXPECT_DEBUG_DEATH(v.IsEndHeaders(),
277 "HEADERS.*PUSH_PROMISE.*CONTINUATION")
278 << v;
279 }
280}
281
282class IsPaddedTest : public Http2FrameHeaderTypeAndFlagTest {};
QUICHE team61940b42019-03-07 23:32:27 -0500283INSTANTIATE_TEST_SUITE_P(IsPadded,
284 IsPaddedTest,
QUICHE team3cab5a92019-01-30 21:10:16 -0500285 Combine(ValuesIn(ValidFrameTypes()),
286 Values(~Http2FrameFlag::PADDED, 0xff)));
QUICHE teamfd50a402018-12-07 22:54:05 -0500287TEST_P(IsPaddedTest, IsPadded) {
288 const bool is_set =
289 (flags_ & Http2FrameFlag::PADDED) == Http2FrameFlag::PADDED;
bnc47904002019-08-16 11:49:48 -0700290 std::string flags_string;
QUICHE teamfd50a402018-12-07 22:54:05 -0500291 Http2FrameHeader v(0, type_, flags_, 0);
292 switch (type_) {
293 case Http2FrameType::DATA:
294 case Http2FrameType::HEADERS:
295 case Http2FrameType::PUSH_PROMISE:
296 EXPECT_EQ(is_set, v.IsPadded()) << v;
297 flags_string = v.FlagsToString();
298 if (is_set) {
299 EXPECT_THAT(flags_string, MatchesRegex(".*\\|?PADDED\\|.*"));
300 } else {
301 EXPECT_THAT(flags_string, Not(HasSubstr("PADDED")));
302 }
303 v.RetainFlags(Http2FrameFlag::PADDED);
304 EXPECT_EQ(is_set, v.IsPadded()) << v;
305 {
306 std::stringstream s;
307 s << v;
308 EXPECT_EQ(v.ToString(), s.str());
309 if (is_set) {
310 EXPECT_THAT(s.str(), HasSubstr("flags=PADDED,"));
311 } else {
312 EXPECT_THAT(s.str(), HasSubstr("flags=,"));
313 }
314 }
315 break;
316 default:
317 EXPECT_DEBUG_DEATH(v.IsPadded(), "DATA.*HEADERS.*PUSH_PROMISE") << v;
318 }
319}
320
321class HasPriorityTest : public Http2FrameHeaderTypeAndFlagTest {};
QUICHE team61940b42019-03-07 23:32:27 -0500322INSTANTIATE_TEST_SUITE_P(HasPriority,
323 HasPriorityTest,
QUICHE team3cab5a92019-01-30 21:10:16 -0500324 Combine(ValuesIn(ValidFrameTypes()),
325 Values(~Http2FrameFlag::PRIORITY, 0xff)));
QUICHE teamfd50a402018-12-07 22:54:05 -0500326TEST_P(HasPriorityTest, HasPriority) {
327 const bool is_set =
328 (flags_ & Http2FrameFlag::PRIORITY) == Http2FrameFlag::PRIORITY;
bnc47904002019-08-16 11:49:48 -0700329 std::string flags_string;
QUICHE teamfd50a402018-12-07 22:54:05 -0500330 Http2FrameHeader v(0, type_, flags_, 0);
331 switch (type_) {
332 case Http2FrameType::HEADERS:
333 EXPECT_EQ(is_set, v.HasPriority()) << v;
334 flags_string = v.FlagsToString();
335 if (is_set) {
336 EXPECT_THAT(flags_string, MatchesRegex(".*\\|?PRIORITY\\|.*"));
337 } else {
338 EXPECT_THAT(flags_string, Not(HasSubstr("PRIORITY")));
339 }
340 v.RetainFlags(Http2FrameFlag::PRIORITY);
341 EXPECT_EQ(is_set, v.HasPriority()) << v;
342 {
343 std::stringstream s;
344 s << v;
345 EXPECT_EQ(v.ToString(), s.str());
346 if (is_set) {
347 EXPECT_THAT(s.str(), HasSubstr("flags=PRIORITY,"));
348 } else {
349 EXPECT_THAT(s.str(), HasSubstr("flags=,"));
350 }
351 }
352 break;
353 default:
354 EXPECT_DEBUG_DEATH(v.HasPriority(), "HEADERS") << v;
355 }
356}
357
358TEST(Http2PriorityFieldsTest, Constructor) {
359 Http2Random random;
360 uint32_t stream_dependency = random.Rand32() & StreamIdMask();
361 uint32_t weight = 1 + random.Rand8();
362 bool is_exclusive = random.OneIn(2);
363
364 Http2PriorityFields v(stream_dependency, weight, is_exclusive);
365
366 EXPECT_EQ(stream_dependency, v.stream_dependency);
367 EXPECT_EQ(weight, v.weight);
368 EXPECT_EQ(is_exclusive, v.is_exclusive);
369
370 // The high-bit must not be set on the stream id.
371 EXPECT_DEBUG_DEATH(
372 Http2PriorityFields(stream_dependency | 0x80000000, weight, is_exclusive),
373 "31-bit");
374
375 // The weight must be in the range 1-256.
376 EXPECT_DEBUG_DEATH(Http2PriorityFields(stream_dependency, 0, is_exclusive),
377 "too small");
378 EXPECT_DEBUG_DEATH(
379 Http2PriorityFields(stream_dependency, weight + 256, is_exclusive),
380 "too large");
381
382 EXPECT_TRUE(VerifyRandomCalls<Http2PriorityFields>());
383}
384#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
385
386TEST(Http2RstStreamFieldsTest, IsSupported) {
387 Http2RstStreamFields v{Http2ErrorCode::HTTP2_NO_ERROR};
388 EXPECT_TRUE(v.IsSupportedErrorCode()) << v;
389
390 Http2RstStreamFields u{static_cast<Http2ErrorCode>(~0)};
391 EXPECT_FALSE(u.IsSupportedErrorCode()) << v;
392
393 EXPECT_TRUE(VerifyRandomCalls<Http2RstStreamFields>());
394}
395
396TEST(Http2SettingFieldsTest, Misc) {
397 Http2Random random;
398 Http2SettingsParameter parameter =
399 static_cast<Http2SettingsParameter>(random.Rand16());
400 uint32_t value = random.Rand32();
401
402 Http2SettingFields v(parameter, value);
403
404 EXPECT_EQ(v, v);
405 EXPECT_EQ(parameter, v.parameter);
406 EXPECT_EQ(value, v.value);
407
408 if (static_cast<uint16_t>(parameter) < 7) {
409 EXPECT_TRUE(v.IsSupportedParameter()) << v;
410 } else {
411 EXPECT_FALSE(v.IsSupportedParameter()) << v;
412 }
413
414 Http2SettingFields u(parameter, ~value);
415 EXPECT_NE(v, u);
416 EXPECT_EQ(v.parameter, u.parameter);
417 EXPECT_NE(v.value, u.value);
418
419 Http2SettingFields w(IncrementEnum(parameter), value);
420 EXPECT_NE(v, w);
421 EXPECT_NE(v.parameter, w.parameter);
422 EXPECT_EQ(v.value, w.value);
423
424 Http2SettingFields x(Http2SettingsParameter::MAX_FRAME_SIZE, 123);
425 std::stringstream s;
426 s << x;
427 EXPECT_EQ("parameter=MAX_FRAME_SIZE, value=123", s.str());
428
429 EXPECT_TRUE(VerifyRandomCalls<Http2SettingFields>());
430}
431
432TEST(Http2PushPromiseTest, Misc) {
433 Http2Random random;
434 uint32_t promised_stream_id = random.Rand32() & StreamIdMask();
435
436 Http2PushPromiseFields v{promised_stream_id};
437 EXPECT_EQ(promised_stream_id, v.promised_stream_id);
438 EXPECT_EQ(v, v);
439
440 std::stringstream s;
441 s << v;
bnc252403d2020-01-15 08:39:53 -0800442 EXPECT_EQ(quiche::QuicheStrCat("promised_stream_id=", promised_stream_id),
443 s.str());
QUICHE teamfd50a402018-12-07 22:54:05 -0500444
445 // High-bit is reserved, but not used, so we can set it.
446 promised_stream_id |= 0x80000000;
447 Http2PushPromiseFields w{promised_stream_id};
448 EXPECT_EQ(w, w);
449 EXPECT_NE(v, w);
450
451 v.promised_stream_id = promised_stream_id;
452 EXPECT_EQ(v, w);
453
454 EXPECT_TRUE(VerifyRandomCalls<Http2PushPromiseFields>());
455}
456
457TEST(Http2PingFieldsTest, Misc) {
458 Http2PingFields v{{'8', ' ', 'b', 'y', 't', 'e', 's', '\0'}};
459 std::stringstream s;
460 s << v;
461 EXPECT_EQ("opaque_bytes=0x3820627974657300", s.str());
462
463 EXPECT_TRUE(VerifyRandomCalls<Http2PingFields>());
464}
465
466TEST(Http2GoAwayFieldsTest, Misc) {
467 Http2Random random;
468 uint32_t last_stream_id = random.Rand32() & StreamIdMask();
469 Http2ErrorCode error_code = static_cast<Http2ErrorCode>(random.Rand32());
470
471 Http2GoAwayFields v(last_stream_id, error_code);
472 EXPECT_EQ(v, v);
473 EXPECT_EQ(last_stream_id, v.last_stream_id);
474 EXPECT_EQ(error_code, v.error_code);
475
476 if (static_cast<uint32_t>(error_code) < 14) {
477 EXPECT_TRUE(v.IsSupportedErrorCode()) << v;
478 } else {
479 EXPECT_FALSE(v.IsSupportedErrorCode()) << v;
480 }
481
482 Http2GoAwayFields u(~last_stream_id, error_code);
483 EXPECT_NE(v, u);
484 EXPECT_NE(v.last_stream_id, u.last_stream_id);
485 EXPECT_EQ(v.error_code, u.error_code);
486
487 EXPECT_TRUE(VerifyRandomCalls<Http2GoAwayFields>());
488}
489
490TEST(Http2WindowUpdateTest, Misc) {
491 Http2Random random;
492 uint32_t window_size_increment = random.Rand32() & UInt31Mask();
493
494 Http2WindowUpdateFields v{window_size_increment};
495 EXPECT_EQ(window_size_increment, v.window_size_increment);
496 EXPECT_EQ(v, v);
497
498 std::stringstream s;
499 s << v;
bnc252403d2020-01-15 08:39:53 -0800500 EXPECT_EQ(
501 quiche::QuicheStrCat("window_size_increment=", window_size_increment),
502 s.str());
QUICHE teamfd50a402018-12-07 22:54:05 -0500503
504 // High-bit is reserved, but not used, so we can set it.
505 window_size_increment |= 0x80000000;
506 Http2WindowUpdateFields w{window_size_increment};
507 EXPECT_EQ(w, w);
508 EXPECT_NE(v, w);
509
510 v.window_size_increment = window_size_increment;
511 EXPECT_EQ(v, w);
512
513 EXPECT_TRUE(VerifyRandomCalls<Http2WindowUpdateFields>());
514}
515
516TEST(Http2AltSvcTest, Misc) {
517 Http2Random random;
518 uint16_t origin_length = random.Rand16();
519
520 Http2AltSvcFields v{origin_length};
521 EXPECT_EQ(origin_length, v.origin_length);
522 EXPECT_EQ(v, v);
523
524 std::stringstream s;
525 s << v;
bnc252403d2020-01-15 08:39:53 -0800526 EXPECT_EQ(quiche::QuicheStrCat("origin_length=", origin_length), s.str());
QUICHE teamfd50a402018-12-07 22:54:05 -0500527
528 Http2AltSvcFields w{++origin_length};
529 EXPECT_EQ(w, w);
530 EXPECT_NE(v, w);
531
532 v.origin_length = w.origin_length;
533 EXPECT_EQ(v, w);
534
535 EXPECT_TRUE(VerifyRandomCalls<Http2AltSvcFields>());
536}
537
538} // namespace
539} // namespace test
540} // namespace http2