| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "quiche/http2/test_tools/http2_frame_builder.h" |
| |
| #include "absl/strings/escaping.h" |
| #include "quiche/common/platform/api/quiche_test.h" |
| |
| namespace http2 { |
| namespace test { |
| namespace { |
| |
| const char kHighBitSetMsg[] = "High-bit of uint32_t should be clear"; |
| |
| TEST(Http2FrameBuilderTest, Constructors) { |
| { |
| Http2FrameBuilder fb; |
| EXPECT_EQ(0u, fb.size()); |
| } |
| { |
| Http2FrameBuilder fb(Http2FrameType::DATA, 0, 123); |
| EXPECT_EQ(9u, fb.size()); |
| |
| const std::string kData = absl::HexStringToBytes( |
| "000000" // Payload length: 0 (unset) |
| "00" // Frame type: DATA |
| "00" // Flags: none |
| "0000007b"); // Stream ID: 123 |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| { |
| Http2FrameHeader header; |
| header.payload_length = (1 << 24) - 1; |
| header.type = Http2FrameType::HEADERS; |
| header.flags = Http2FrameFlag::END_HEADERS; |
| header.stream_id = StreamIdMask(); |
| Http2FrameBuilder fb(header); |
| EXPECT_EQ(9u, fb.size()); |
| |
| const std::string kData = absl::HexStringToBytes( |
| "ffffff" // Payload length: 2^24 - 1 (max uint24) |
| "01" // Frame type: HEADER |
| "04" // Flags: END_HEADERS |
| "7fffffff"); // Stream ID: stream id mask |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| } |
| |
| TEST(Http2FrameBuilderTest, SetPayloadLength) { |
| Http2FrameBuilder fb(Http2FrameType::DATA, PADDED, 20000); |
| EXPECT_EQ(9u, fb.size()); |
| |
| fb.AppendUInt8(50); // Trailing payload length |
| EXPECT_EQ(10u, fb.size()); |
| |
| fb.Append("ten bytes."); |
| EXPECT_EQ(20u, fb.size()); |
| |
| fb.AppendZeroes(50); |
| EXPECT_EQ(70u, fb.size()); |
| |
| fb.SetPayloadLength(); |
| EXPECT_EQ(70u, fb.size()); |
| |
| const std::string kData = absl::HexStringToBytes( |
| "00003d" // Payload length: 61 |
| "00" // Frame type: DATA |
| "08" // Flags: PADDED |
| "00004e20" // Stream ID: 20000 |
| "32" // Padding Length: 50 |
| "74656e2062797465732e" // "ten bytes." |
| "00000000000000000000" // Padding bytes |
| "00000000000000000000" // Padding bytes |
| "00000000000000000000" // Padding bytes |
| "00000000000000000000" // Padding bytes |
| "00000000000000000000"); // Padding bytes |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| |
| TEST(Http2FrameBuilderTest, Settings) { |
| Http2FrameBuilder fb(Http2FrameType::SETTINGS, 0, 0); |
| Http2SettingFields sf; |
| |
| sf.parameter = Http2SettingsParameter::HEADER_TABLE_SIZE; |
| sf.value = 1 << 12; |
| fb.Append(sf); |
| |
| sf.parameter = Http2SettingsParameter::ENABLE_PUSH; |
| sf.value = 0; |
| fb.Append(sf); |
| |
| sf.parameter = Http2SettingsParameter::MAX_CONCURRENT_STREAMS; |
| sf.value = ~0; |
| fb.Append(sf); |
| |
| sf.parameter = Http2SettingsParameter::INITIAL_WINDOW_SIZE; |
| sf.value = 1 << 16; |
| fb.Append(sf); |
| |
| sf.parameter = Http2SettingsParameter::MAX_FRAME_SIZE; |
| sf.value = 1 << 14; |
| fb.Append(sf); |
| |
| sf.parameter = Http2SettingsParameter::MAX_HEADER_LIST_SIZE; |
| sf.value = 1 << 10; |
| fb.Append(sf); |
| |
| size_t payload_size = 6 * Http2SettingFields::EncodedSize(); |
| EXPECT_EQ(Http2FrameHeader::EncodedSize() + payload_size, fb.size()); |
| |
| fb.SetPayloadLength(payload_size); |
| |
| const std::string kData = absl::HexStringToBytes( |
| "000024" // Payload length: 36 |
| "04" // Frame type: SETTINGS |
| "00" // Flags: none |
| "00000000" // Stream ID: 0 |
| "0001" // HEADER_TABLE_SIZE |
| "00001000" // 4096 |
| "0002" // ENABLE_PUSH |
| "00000000" // 0 |
| "0003" // MAX_CONCURRENT_STREAMS |
| "ffffffff" // 0xffffffff (max uint32) |
| "0004" // INITIAL_WINDOW_SIZE |
| "00010000" // 4096 |
| "0005" // MAX_FRAME_SIZE |
| "00004000" // 4096 |
| "0006" // MAX_HEADER_LIST_SIZE |
| "00000400"); // 1024 |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| |
| TEST(Http2FrameBuilderTest, EnhanceYourCalm) { |
| const std::string kData = absl::HexStringToBytes("0000000b"); |
| { |
| Http2FrameBuilder fb; |
| fb.Append(Http2ErrorCode::ENHANCE_YOUR_CALM); |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| { |
| Http2FrameBuilder fb; |
| Http2RstStreamFields rsp; |
| rsp.error_code = Http2ErrorCode::ENHANCE_YOUR_CALM; |
| fb.Append(rsp); |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| } |
| |
| TEST(Http2FrameBuilderTest, PushPromise) { |
| const std::string kData = absl::HexStringToBytes("7fffffff"); |
| { |
| Http2FrameBuilder fb; |
| fb.Append(Http2PushPromiseFields{0x7fffffff}); |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| { |
| Http2FrameBuilder fb; |
| // Will generate an error if the high-bit of the stream id is set. |
| EXPECT_NONFATAL_FAILURE(fb.Append(Http2PushPromiseFields{0xffffffff}), |
| kHighBitSetMsg); |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| } |
| |
| TEST(Http2FrameBuilderTest, Ping) { |
| Http2FrameBuilder fb; |
| Http2PingFields ping{"8 bytes"}; |
| fb.Append(ping); |
| |
| const absl::string_view kData{"8 bytes\0", 8}; |
| EXPECT_EQ(kData.size(), Http2PingFields::EncodedSize()); |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| |
| TEST(Http2FrameBuilderTest, GoAway) { |
| const std::string kData = absl::HexStringToBytes( |
| "12345678" // Last Stream Id |
| "00000001"); // Error code |
| EXPECT_EQ(kData.size(), Http2GoAwayFields::EncodedSize()); |
| { |
| Http2FrameBuilder fb; |
| Http2GoAwayFields ga(0x12345678, Http2ErrorCode::PROTOCOL_ERROR); |
| fb.Append(ga); |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| { |
| Http2FrameBuilder fb; |
| // Will generate a test failure if the high-bit of the stream id is set. |
| Http2GoAwayFields ga(0x92345678, Http2ErrorCode::PROTOCOL_ERROR); |
| EXPECT_NONFATAL_FAILURE(fb.Append(ga), kHighBitSetMsg); |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| } |
| |
| TEST(Http2FrameBuilderTest, WindowUpdate) { |
| Http2FrameBuilder fb; |
| fb.Append(Http2WindowUpdateFields{123456}); |
| |
| // Will generate a test failure if the high-bit of the increment is set. |
| EXPECT_NONFATAL_FAILURE(fb.Append(Http2WindowUpdateFields{0x80000001}), |
| kHighBitSetMsg); |
| |
| // Will generate a test failure if the increment is zero. |
| EXPECT_NONFATAL_FAILURE(fb.Append(Http2WindowUpdateFields{0}), "non-zero"); |
| |
| const std::string kData = absl::HexStringToBytes( |
| "0001e240" // Valid Window Size Increment |
| "00000001" // High-bit cleared |
| "00000000"); // Invalid Window Size Increment |
| EXPECT_EQ(kData.size(), 3 * Http2WindowUpdateFields::EncodedSize()); |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| |
| TEST(Http2FrameBuilderTest, AltSvc) { |
| Http2FrameBuilder fb; |
| fb.Append(Http2AltSvcFields{99}); |
| fb.Append(Http2AltSvcFields{0}); // No optional origin |
| const std::string kData = absl::HexStringToBytes( |
| "0063" // Has origin. |
| "0000"); // Doesn't have origin. |
| EXPECT_EQ(kData.size(), 2 * Http2AltSvcFields::EncodedSize()); |
| EXPECT_EQ(kData, fb.buffer()); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace http2 |