blob: 3ded5d7882bc2db99bc4784a34c0454e0f8c66d1 [file] [log] [blame]
// 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 "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 char kData[] = {
0x00, 0x00, 0x00, // Payload length: 0 (unset)
0x00, // Frame type: DATA
0x00, // Flags: none
0x00, 0x00, 0x00, 0x7b, // Stream ID: 123
};
EXPECT_EQ(absl::string_view(kData, sizeof 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 char kData[] = {
0xff, 0xff, 0xff, // Payload length: 2^24 - 1 (max uint24)
0x01, // Frame type: HEADER
0x04, // Flags: END_HEADERS
0x7f, 0xff, 0xff, 0xff, // Stream ID: stream id mask
};
EXPECT_EQ(absl::string_view(kData, sizeof 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());
// clang-format off
const char kData[] = {
0x00, 0x00, 0x3d, // Payload length: 61
0x00, // Frame type: DATA
0x08, // Flags: PADDED
0x00, 0x00, 0x4e, 0x20, // Stream ID: 20000
0x32, // Padding Length: 50
't', 'e', 'n', ' ', 'b', // "ten b"
'y', 't', 'e', 's', '.', // "ytes."
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Padding bytes
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Padding bytes
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Padding bytes
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Padding bytes
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Padding bytes
};
// clang-format on
EXPECT_EQ(absl::string_view(kData, sizeof 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);
// clang-format off
const char kData[] = {
0x00, 0x00, 0x24, // Payload length: 36
0x04, // Frame type: SETTINGS
0x00, // Flags: none
0x00, 0x00, 0x00, 0x00, // Stream ID: 0
0x00, 0x01, // HEADER_TABLE_SIZE
0x00, 0x00, 0x10, 0x00, // 4096
0x00, 0x02, // ENABLE_PUSH
0x00, 0x00, 0x00, 0x00, // 0
0x00, 0x03, // MAX_CONCURRENT_STREAMS
0xff, 0xff, 0xff, 0xff, // 0xffffffff (max uint32)
0x00, 0x04, // INITIAL_WINDOW_SIZE
0x00, 0x01, 0x00, 0x00, // 4096
0x00, 0x05, // MAX_FRAME_SIZE
0x00, 0x00, 0x40, 0x00, // 4096
0x00, 0x06, // MAX_HEADER_LIST_SIZE
0x00, 0x00, 0x04, 0x00, // 1024
};
// clang-format on
EXPECT_EQ(absl::string_view(kData, 9), fb.buffer().substr(0, 9));
for (int n = 0; n < 6; ++n) {
int offset = 9 + n * 6;
EXPECT_EQ(absl::string_view(kData + offset, 6),
fb.buffer().substr(offset, 6))
<< "Setting #" << n;
}
EXPECT_EQ(absl::string_view(kData, sizeof kData), fb.buffer());
}
TEST(Http2FrameBuilderTest, EnhanceYourCalm) {
const char kData[] = {0x00, 0x00, 0x00, 0x0b};
const absl::string_view expected(kData, sizeof kData);
{
Http2FrameBuilder fb;
fb.Append(Http2ErrorCode::ENHANCE_YOUR_CALM);
EXPECT_EQ(expected, fb.buffer());
}
{
Http2FrameBuilder fb;
Http2RstStreamFields rsp;
rsp.error_code = Http2ErrorCode::ENHANCE_YOUR_CALM;
fb.Append(rsp);
EXPECT_EQ(expected, fb.buffer());
}
}
TEST(Http2FrameBuilderTest, PushPromise) {
const char kData[] = {0x7f, 0xff, 0xff, 0xff};
{
Http2FrameBuilder fb;
fb.Append(Http2PushPromiseFields{0x7fffffff});
EXPECT_EQ(absl::string_view(kData, sizeof 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(absl::string_view(kData, sizeof kData), fb.buffer());
}
}
TEST(Http2FrameBuilderTest, Ping) {
Http2FrameBuilder fb;
Http2PingFields ping{"8 bytes"};
fb.Append(ping);
const char kData[] = {'8', ' ', 'b', 'y', 't', 'e', 's', '\0'};
EXPECT_EQ(sizeof kData, Http2PingFields::EncodedSize());
EXPECT_EQ(absl::string_view(kData, sizeof kData), fb.buffer());
}
TEST(Http2FrameBuilderTest, GoAway) {
const char kData[] = {
0x12, 0x34, 0x56, 0x78, // Last Stream Id
0x00, 0x00, 0x00, 0x01, // Error code
};
EXPECT_EQ(sizeof kData, Http2GoAwayFields::EncodedSize());
{
Http2FrameBuilder fb;
Http2GoAwayFields ga(0x12345678, Http2ErrorCode::PROTOCOL_ERROR);
fb.Append(ga);
EXPECT_EQ(absl::string_view(kData, sizeof 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(absl::string_view(kData, sizeof 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 char kData[] = {
0x00, 0x01, 0xe2, 0x40, // Valid Window Size Increment
0x00, 0x00, 0x00, 0x01, // High-bit cleared
0x00, 0x00, 0x00, 0x00, // Invalid Window Size Increment
};
EXPECT_EQ(sizeof kData, 3 * Http2WindowUpdateFields::EncodedSize());
EXPECT_EQ(absl::string_view(kData, sizeof kData), fb.buffer());
}
TEST(Http2FrameBuilderTest, AltSvc) {
Http2FrameBuilder fb;
fb.Append(Http2AltSvcFields{99});
fb.Append(Http2AltSvcFields{0}); // No optional origin
const char kData[] = {
0x00, 0x63, // Has origin.
0x00, 0x00, // Doesn't have origin.
};
EXPECT_EQ(sizeof kData, 2 * Http2AltSvcFields::EncodedSize());
EXPECT_EQ(absl::string_view(kData, sizeof kData), fb.buffer());
}
} // namespace
} // namespace test
} // namespace http2