[chttp2] New framing layer (#33692)

Building out a new framing layer for chttp2.

The central idea here is to have the framing layer be solely responsible
for serialization of frames, and their deserialization - the framing
layer can reject frames that have invalid syntax - but the enacting of
what that frame means is left to a higher layer.

This class will become foundational for the promise conversion of chttp2
- by eliminating action from the parsing of frames we can reuse this
sensitive code.

Right now the new layer is inactive - there's a test that exercises it
relatively well, and not much more. In the next PRs I'll add an
experiments to enable using this layer or the existing code in the
writing and reading paths.

---------

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
pull/34179/head
Craig Tiller 1 year ago committed by GitHub
parent de98c1c9ad
commit 12c9748134
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      BUILD
  2. 1041
      CMakeLists.txt
  3. 2
      Package.swift
  4. 1578
      build_autogenerated.yaml
  5. 4
      gRPC-C++.podspec
  6. 4
      gRPC-Core.podspec
  7. 2
      grpc.gemspec
  8. 2
      package.xml
  9. 2
      src/core/BUILD
  10. 2
      src/core/ext/transport/chttp2/server/chttp2_server.cc
  11. 2
      src/core/ext/transport/chttp2/transport/chttp2_transport.cc
  12. 500
      src/core/ext/transport/chttp2/transport/frame.cc
  13. 213
      src/core/ext/transport/chttp2/transport/frame.h
  14. 2
      src/core/ext/transport/chttp2/transport/frame_data.h
  15. 2
      src/core/ext/transport/chttp2/transport/frame_goaway.h
  16. 2
      src/core/ext/transport/chttp2/transport/frame_ping.h
  17. 2
      src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
  18. 2
      src/core/ext/transport/chttp2/transport/frame_rst_stream.h
  19. 2
      src/core/ext/transport/chttp2/transport/frame_settings.cc
  20. 2
      src/core/ext/transport/chttp2/transport/frame_settings.h
  21. 2
      src/core/ext/transport/chttp2/transport/frame_window_update.h
  22. 2
      src/core/ext/transport/chttp2/transport/hpack_encoder.cc
  23. 2
      src/core/ext/transport/chttp2/transport/hpack_parser.h
  24. 2
      src/core/ext/transport/chttp2/transport/internal.h
  25. 43
      src/core/ext/transport/chttp2/transport/legacy_frame.h
  26. 2
      src/core/ext/transport/chttp2/transport/parsing.cc
  27. 2
      src/core/ext/transport/chttp2/transport/stream_lists.cc
  28. 2
      src/core/ext/transport/chttp2/transport/writing.cc
  29. 6
      src/core/lib/slice/slice.h
  30. 15
      src/core/lib/slice/slice_buffer.cc
  31. 20
      src/core/lib/slice/slice_buffer.h
  32. 13
      test/core/transport/chttp2/BUILD
  33. 393
      test/core/transport/chttp2/frame_test.cc
  34. 2
      test/core/transport/chttp2/hpack_encoder_test.cc
  35. 2
      test/core/transport/chttp2/ping_configuration_test.cc
  36. 2
      tools/doxygen/Doxyfile.c++.internal
  37. 2
      tools/doxygen/Doxyfile.core.internal
  38. 72
      tools/run_tests/generated/tests.json

28
BUILD

@ -3718,9 +3718,31 @@ grpc_cc_library(
grpc_cc_library(
name = "chttp2_frame",
srcs = [
"//src/core:ext/transport/chttp2/transport/frame.cc",
],
hdrs = [
"//src/core:ext/transport/chttp2/transport/frame.h",
],
external_deps = [
"absl/status",
"absl/status:statusor",
"absl/strings",
"absl/types:span",
"absl/types:variant",
],
deps = [
"gpr",
"//src/core:slice",
"//src/core:slice_buffer",
],
)
grpc_cc_library(
name = "chttp2_legacy_frame",
hdrs = [
"//src/core:ext/transport/chttp2/transport/legacy_frame.h",
],
deps = ["gpr"],
)
@ -3805,7 +3827,7 @@ grpc_cc_library(
"absl/types:variant",
],
deps = [
"chttp2_frame",
"chttp2_legacy_frame",
"gpr",
"gpr_platform",
"grpc_base",
@ -3836,7 +3858,7 @@ grpc_cc_library(
external_deps = ["absl/strings"],
deps = [
"chttp2_bin_encoder",
"chttp2_frame",
"chttp2_legacy_frame",
"chttp2_varint",
"gpr",
"gpr_platform",
@ -3931,7 +3953,7 @@ grpc_cc_library(
deps = [
"channel_arg_names",
"chttp2_context_list_entry",
"chttp2_frame",
"chttp2_legacy_frame",
"chttp2_varint",
"debug_location",
"exec_ctx",

1041
CMakeLists.txt generated

File diff suppressed because it is too large Load Diff

2
Package.swift generated

@ -278,7 +278,6 @@ let package = Package(
"src/core/ext/transport/chttp2/transport/decode_huff.h",
"src/core/ext/transport/chttp2/transport/flow_control.cc",
"src/core/ext/transport/chttp2/transport/flow_control.h",
"src/core/ext/transport/chttp2/transport/frame.h",
"src/core/ext/transport/chttp2/transport/frame_data.cc",
"src/core/ext/transport/chttp2/transport/frame_data.h",
"src/core/ext/transport/chttp2/transport/frame_goaway.cc",
@ -309,6 +308,7 @@ let package = Package(
"src/core/ext/transport/chttp2/transport/huffsyms.cc",
"src/core/ext/transport/chttp2/transport/huffsyms.h",
"src/core/ext/transport/chttp2/transport/internal.h",
"src/core/ext/transport/chttp2/transport/legacy_frame.h",
"src/core/ext/transport/chttp2/transport/parsing.cc",
"src/core/ext/transport/chttp2/transport/ping_abuse_policy.cc",
"src/core/ext/transport/chttp2/transport/ping_abuse_policy.h",

File diff suppressed because it is too large Load Diff

4
gRPC-C++.podspec generated

@ -360,7 +360,6 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/context_list_entry.h',
'src/core/ext/transport/chttp2/transport/decode_huff.h',
'src/core/ext/transport/chttp2/transport/flow_control.h',
'src/core/ext/transport/chttp2/transport/frame.h',
'src/core/ext/transport/chttp2/transport/frame_data.h',
'src/core/ext/transport/chttp2/transport/frame_goaway.h',
'src/core/ext/transport/chttp2/transport/frame_ping.h',
@ -377,6 +376,7 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/http_trace.h',
'src/core/ext/transport/chttp2/transport/huffsyms.h',
'src/core/ext/transport/chttp2/transport/internal.h',
'src/core/ext/transport/chttp2/transport/legacy_frame.h',
'src/core/ext/transport/chttp2/transport/ping_abuse_policy.h',
'src/core/ext/transport/chttp2/transport/ping_rate_policy.h',
'src/core/ext/transport/chttp2/transport/varint.h',
@ -1417,7 +1417,6 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/context_list_entry.h',
'src/core/ext/transport/chttp2/transport/decode_huff.h',
'src/core/ext/transport/chttp2/transport/flow_control.h',
'src/core/ext/transport/chttp2/transport/frame.h',
'src/core/ext/transport/chttp2/transport/frame_data.h',
'src/core/ext/transport/chttp2/transport/frame_goaway.h',
'src/core/ext/transport/chttp2/transport/frame_ping.h',
@ -1434,6 +1433,7 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/http_trace.h',
'src/core/ext/transport/chttp2/transport/huffsyms.h',
'src/core/ext/transport/chttp2/transport/internal.h',
'src/core/ext/transport/chttp2/transport/legacy_frame.h',
'src/core/ext/transport/chttp2/transport/ping_abuse_policy.h',
'src/core/ext/transport/chttp2/transport/ping_rate_policy.h',
'src/core/ext/transport/chttp2/transport/varint.h',

4
gRPC-Core.podspec generated

@ -379,7 +379,6 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/decode_huff.h',
'src/core/ext/transport/chttp2/transport/flow_control.cc',
'src/core/ext/transport/chttp2/transport/flow_control.h',
'src/core/ext/transport/chttp2/transport/frame.h',
'src/core/ext/transport/chttp2/transport/frame_data.cc',
'src/core/ext/transport/chttp2/transport/frame_data.h',
'src/core/ext/transport/chttp2/transport/frame_goaway.cc',
@ -410,6 +409,7 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/huffsyms.cc',
'src/core/ext/transport/chttp2/transport/huffsyms.h',
'src/core/ext/transport/chttp2/transport/internal.h',
'src/core/ext/transport/chttp2/transport/legacy_frame.h',
'src/core/ext/transport/chttp2/transport/parsing.cc',
'src/core/ext/transport/chttp2/transport/ping_abuse_policy.cc',
'src/core/ext/transport/chttp2/transport/ping_abuse_policy.h',
@ -2165,7 +2165,6 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/context_list_entry.h',
'src/core/ext/transport/chttp2/transport/decode_huff.h',
'src/core/ext/transport/chttp2/transport/flow_control.h',
'src/core/ext/transport/chttp2/transport/frame.h',
'src/core/ext/transport/chttp2/transport/frame_data.h',
'src/core/ext/transport/chttp2/transport/frame_goaway.h',
'src/core/ext/transport/chttp2/transport/frame_ping.h',
@ -2182,6 +2181,7 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/http_trace.h',
'src/core/ext/transport/chttp2/transport/huffsyms.h',
'src/core/ext/transport/chttp2/transport/internal.h',
'src/core/ext/transport/chttp2/transport/legacy_frame.h',
'src/core/ext/transport/chttp2/transport/ping_abuse_policy.h',
'src/core/ext/transport/chttp2/transport/ping_rate_policy.h',
'src/core/ext/transport/chttp2/transport/varint.h',

2
grpc.gemspec generated

@ -284,7 +284,6 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/transport/chttp2/transport/decode_huff.h )
s.files += %w( src/core/ext/transport/chttp2/transport/flow_control.cc )
s.files += %w( src/core/ext/transport/chttp2/transport/flow_control.h )
s.files += %w( src/core/ext/transport/chttp2/transport/frame.h )
s.files += %w( src/core/ext/transport/chttp2/transport/frame_data.cc )
s.files += %w( src/core/ext/transport/chttp2/transport/frame_data.h )
s.files += %w( src/core/ext/transport/chttp2/transport/frame_goaway.cc )
@ -315,6 +314,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/transport/chttp2/transport/huffsyms.cc )
s.files += %w( src/core/ext/transport/chttp2/transport/huffsyms.h )
s.files += %w( src/core/ext/transport/chttp2/transport/internal.h )
s.files += %w( src/core/ext/transport/chttp2/transport/legacy_frame.h )
s.files += %w( src/core/ext/transport/chttp2/transport/parsing.cc )
s.files += %w( src/core/ext/transport/chttp2/transport/ping_abuse_policy.cc )
s.files += %w( src/core/ext/transport/chttp2/transport/ping_abuse_policy.h )

2
package.xml generated

@ -266,7 +266,6 @@
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/decode_huff.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/flow_control.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/flow_control.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/frame.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/frame_data.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/frame_data.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/frame_goaway.cc" role="src" />
@ -297,6 +296,7 @@
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/huffsyms.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/huffsyms.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/internal.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/legacy_frame.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/parsing.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/ping_abuse_policy.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/ping_abuse_policy.h" role="src" />

@ -5744,7 +5744,7 @@ grpc_cc_library(
"transport_fwd",
"unique_type_name",
"//:channel_arg_names",
"//:chttp2_frame",
"//:chttp2_legacy_frame",
"//:config",
"//:debug_location",
"//:exec_ctx",

@ -49,8 +49,8 @@
#include <grpc/support/log.h>
#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/internal.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/address_utils/sockaddr_utils.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/channelz.h"

@ -55,7 +55,6 @@
#include "src/core/ext/transport/chttp2/transport/context_list_entry.h"
#include "src/core/ext/transport/chttp2/transport/flow_control.h"
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/frame_data.h"
#include "src/core/ext/transport/chttp2/transport/frame_goaway.h"
#include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h"
@ -63,6 +62,7 @@
#include "src/core/ext/transport/chttp2/transport/http2_settings.h"
#include "src/core/ext/transport/chttp2/transport/http_trace.h"
#include "src/core/ext/transport/chttp2/transport/internal.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/ext/transport/chttp2/transport/ping_abuse_policy.h"
#include "src/core/ext/transport/chttp2/transport/ping_rate_policy.h"
#include "src/core/ext/transport/chttp2/transport/varint.h"

@ -0,0 +1,500 @@
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <grpc/support/port_platform.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include <stddef.h>
#include <cstdint>
#include <utility>
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include <grpc/support/log.h>
#include "src/core/lib/gprpp/crash.h"
namespace grpc_core {
namespace {
constexpr uint8_t kFrameTypeData = 0;
constexpr uint8_t kFrameTypeHeader = 1;
constexpr uint8_t kFrameTypeContinuation = 9;
constexpr uint8_t kFrameTypeRstStream = 3;
constexpr uint8_t kFrameTypeSettings = 4;
constexpr uint8_t kFrameTypePing = 6;
constexpr uint8_t kFrameTypeGoaway = 7;
constexpr uint8_t kFrameTypeWindowUpdate = 8;
constexpr uint8_t kFlagEndStream = 1;
constexpr uint8_t kFlagAck = 1;
constexpr uint8_t kFlagEndHeaders = 4;
constexpr uint8_t kFlagPadded = 8;
constexpr uint8_t kFlagPriority = 0x20;
constexpr size_t kFrameHeaderSize = 9;
void Write2b(uint16_t x, uint8_t* output) {
output[0] = static_cast<uint8_t>(x >> 8);
output[1] = static_cast<uint8_t>(x);
}
uint16_t Read2b(const uint8_t* input) {
return static_cast<uint16_t>(input[0]) << 8 | static_cast<uint16_t>(input[1]);
}
void Write3b(uint32_t x, uint8_t* output) {
GPR_ASSERT(x < 16777216);
output[0] = static_cast<uint8_t>(x >> 16);
output[1] = static_cast<uint8_t>(x >> 8);
output[2] = static_cast<uint8_t>(x);
}
uint32_t Read3b(const uint8_t* input) {
return static_cast<uint32_t>(input[0]) << 16 |
static_cast<uint32_t>(input[1]) << 8 | static_cast<uint32_t>(input[2]);
}
void Write4b(uint32_t x, uint8_t* output) {
output[0] = static_cast<uint8_t>(x >> 24);
output[1] = static_cast<uint8_t>(x >> 16);
output[2] = static_cast<uint8_t>(x >> 8);
output[3] = static_cast<uint8_t>(x);
}
uint32_t Read4b(const uint8_t* input) {
return static_cast<uint32_t>(input[0]) << 24 |
static_cast<uint32_t>(input[1]) << 16 |
static_cast<uint32_t>(input[2]) << 8 | static_cast<uint32_t>(input[3]);
}
void Write8b(uint64_t x, uint8_t* output) {
output[0] = static_cast<uint8_t>(x >> 56);
output[1] = static_cast<uint8_t>(x >> 48);
output[2] = static_cast<uint8_t>(x >> 40);
output[3] = static_cast<uint8_t>(x >> 32);
output[4] = static_cast<uint8_t>(x >> 24);
output[5] = static_cast<uint8_t>(x >> 16);
output[6] = static_cast<uint8_t>(x >> 8);
output[7] = static_cast<uint8_t>(x);
}
uint64_t Read8b(const uint8_t* input) {
return static_cast<uint64_t>(input[0]) << 56 |
static_cast<uint64_t>(input[1]) << 48 |
static_cast<uint64_t>(input[2]) << 40 |
static_cast<uint64_t>(input[3]) << 32 |
static_cast<uint64_t>(input[4]) << 24 |
static_cast<uint64_t>(input[5]) << 16 |
static_cast<uint64_t>(input[6]) << 8 | static_cast<uint64_t>(input[7]);
}
uint8_t MaybeFlag(bool condition, uint8_t flag_mask) {
return condition ? flag_mask : 0;
}
bool ExtractFlag(uint8_t flags, uint8_t flag_mask) {
return (flags & flag_mask) != 0;
}
class SerializeExtraBytesRequired {
public:
size_t operator()(const Http2DataFrame&) { return 0; }
size_t operator()(const Http2HeaderFrame&) { return 0; }
size_t operator()(const Http2ContinuationFrame&) { return 0; }
size_t operator()(const Http2RstStreamFrame&) { return 4; }
size_t operator()(const Http2SettingsFrame& f) {
return 6 * f.settings.size();
}
size_t operator()(const Http2PingFrame&) { return 8; }
size_t operator()(const Http2GoawayFrame&) { return 8; }
size_t operator()(const Http2WindowUpdateFrame&) { return 4; }
size_t operator()(const Http2UnknownFrame&) { Crash("unreachable"); }
};
class SerializeHeaderAndPayload {
public:
SerializeHeaderAndPayload(size_t extra_bytes, SliceBuffer& out)
: out_(out),
extra_bytes_(MutableSlice::CreateUninitialized(extra_bytes)) {}
void operator()(Http2DataFrame& frame) {
auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize);
Http2FrameHeader{
static_cast<uint32_t>(frame.payload.Length()), kFrameTypeData,
MaybeFlag(frame.end_stream, kFlagEndStream), frame.stream_id}
.Serialize(hdr.begin());
out_.AppendIndexed(Slice(std::move(hdr)));
out_.TakeAndAppend(frame.payload);
}
void operator()(Http2HeaderFrame& frame) {
auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize);
Http2FrameHeader{
static_cast<uint32_t>(frame.payload.Length()), kFrameTypeHeader,
static_cast<uint8_t>(MaybeFlag(frame.end_headers, kFlagEndHeaders) |
MaybeFlag(frame.end_stream, kFlagEndStream)),
frame.stream_id}
.Serialize(hdr.begin());
out_.AppendIndexed(Slice(std::move(hdr)));
out_.TakeAndAppend(frame.payload);
}
void operator()(Http2ContinuationFrame& frame) {
auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize);
Http2FrameHeader{
static_cast<uint32_t>(frame.payload.Length()), kFrameTypeContinuation,
static_cast<uint8_t>(MaybeFlag(frame.end_headers, kFlagEndHeaders)),
frame.stream_id}
.Serialize(hdr.begin());
out_.AppendIndexed(Slice(std::move(hdr)));
out_.TakeAndAppend(frame.payload);
}
void operator()(Http2RstStreamFrame& frame) {
auto hdr_and_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 4);
Http2FrameHeader{4, kFrameTypeRstStream, 0, frame.stream_id}.Serialize(
hdr_and_payload.begin());
Write4b(frame.error_code, hdr_and_payload.begin() + kFrameHeaderSize);
out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
}
void operator()(Http2SettingsFrame& frame) {
const size_t payload_size = 6 * frame.settings.size();
auto hdr_and_payload =
extra_bytes_.TakeFirst(kFrameHeaderSize + payload_size);
Http2FrameHeader{static_cast<uint32_t>(payload_size), kFrameTypeSettings,
MaybeFlag(frame.ack, kFlagAck), 0}
.Serialize(hdr_and_payload.begin());
size_t offset = kFrameHeaderSize;
for (auto& setting : frame.settings) {
Write2b(setting.id, hdr_and_payload.begin() + offset);
Write4b(setting.value, hdr_and_payload.begin() + offset + 2);
offset += 6;
}
out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
}
void operator()(Http2PingFrame& frame) {
auto hdr_and_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 8);
Http2FrameHeader{8, kFrameTypePing, MaybeFlag(frame.ack, kFlagAck), 0}
.Serialize(hdr_and_payload.begin());
Write8b(frame.opaque, hdr_and_payload.begin() + kFrameHeaderSize);
out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
}
void operator()(Http2GoawayFrame& frame) {
auto hdr_and_fixed_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 8);
Http2FrameHeader{static_cast<uint32_t>(8 + frame.debug_data.length()),
kFrameTypeGoaway, 0, 0}
.Serialize(hdr_and_fixed_payload.begin());
Write4b(frame.last_stream_id,
hdr_and_fixed_payload.begin() + kFrameHeaderSize);
Write4b(frame.error_code,
hdr_and_fixed_payload.begin() + kFrameHeaderSize + 4);
out_.AppendIndexed(Slice(std::move(hdr_and_fixed_payload)));
out_.AppendIndexed(std::move(frame.debug_data));
}
void operator()(Http2WindowUpdateFrame& frame) {
auto hdr_and_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 4);
Http2FrameHeader{4, kFrameTypeWindowUpdate, 0, frame.stream_id}.Serialize(
hdr_and_payload.begin());
Write4b(frame.increment, hdr_and_payload.begin() + kFrameHeaderSize);
out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
}
void operator()(Http2UnknownFrame&) { Crash("unreachable"); }
private:
SliceBuffer& out_;
MutableSlice extra_bytes_;
};
absl::Status StripPadding(SliceBuffer& payload) {
if (payload.Length() < 1) {
return absl::InternalError("padding flag set but no padding byte");
}
uint8_t padding_bytes;
payload.MoveFirstNBytesIntoBuffer(1, &padding_bytes);
if (payload.Length() < padding_bytes) {
return absl::InternalError("padding flag set but not enough padding bytes");
}
payload.RemoveLastNBytes(padding_bytes);
return absl::OkStatus();
}
absl::StatusOr<Http2DataFrame> ParseDataFrame(const Http2FrameHeader& hdr,
SliceBuffer& payload) {
if (hdr.stream_id == 0) {
return absl::InternalError(
absl::StrCat("invalid stream id: ", hdr.ToString()));
}
if (hdr.flags & kFlagPadded) {
auto s = StripPadding(payload);
if (!s.ok()) return s;
}
return Http2DataFrame{hdr.stream_id, ExtractFlag(hdr.flags, kFlagEndStream),
std::move(payload)};
}
absl::StatusOr<Http2HeaderFrame> ParseHeaderFrame(const Http2FrameHeader& hdr,
SliceBuffer& payload) {
if (hdr.stream_id == 0) {
return absl::InternalError(
absl::StrCat("invalid stream id: ", hdr.ToString()));
}
if (hdr.flags & kFlagPadded) {
auto s = StripPadding(payload);
if (!s.ok()) return s;
}
if (hdr.flags & kFlagPriority) {
if (payload.Length() < 5) {
return absl::InternalError(
absl::StrCat("invalid priority payload: ", hdr.ToString()));
}
uint8_t trash[5];
payload.MoveFirstNBytesIntoBuffer(5, trash);
}
return Http2HeaderFrame{
hdr.stream_id, ExtractFlag(hdr.flags, kFlagEndHeaders),
ExtractFlag(hdr.flags, kFlagEndStream), std::move(payload)};
}
absl::StatusOr<Http2ContinuationFrame> ParseContinuationFrame(
const Http2FrameHeader& hdr, SliceBuffer& payload) {
if (hdr.stream_id == 0) {
return absl::InternalError(
absl::StrCat("invalid stream id: ", hdr.ToString()));
}
return Http2ContinuationFrame{hdr.stream_id,
ExtractFlag(hdr.flags, kFlagEndHeaders),
std::move(payload)};
}
absl::StatusOr<Http2RstStreamFrame> ParseRstStreamFrame(
const Http2FrameHeader& hdr, SliceBuffer& payload) {
if (payload.Length() != 4) {
return absl::InternalError(
absl::StrCat("invalid rst stream payload: ", hdr.ToString()));
}
if (hdr.stream_id == 0) {
return absl::InternalError(
absl::StrCat("invalid stream id: ", hdr.ToString()));
}
uint8_t buffer[4];
payload.CopyToBuffer(buffer);
return Http2RstStreamFrame{hdr.stream_id, Read4b(buffer)};
}
absl::StatusOr<Http2SettingsFrame> ParseSettingsFrame(
const Http2FrameHeader& hdr, SliceBuffer& payload) {
if (hdr.stream_id != 0) {
return absl::InternalError(
absl::StrCat("invalid stream id: ", hdr.ToString()));
}
if (hdr.flags == kFlagAck) {
if (payload.Length() != 0) {
return absl::InternalError(
absl::StrCat("invalid settings ack length: ", hdr.ToString()));
}
return Http2SettingsFrame{true, {}};
}
if (payload.Length() % 6 != 0) {
return absl::InternalError(
absl::StrCat("invalid settings payload: ", hdr.ToString(),
" -- settings must be multiples of 6 bytes long"));
}
Http2SettingsFrame frame{false, {}};
while (payload.Length() != 0) {
uint8_t buffer[6];
payload.MoveFirstNBytesIntoBuffer(6, buffer);
frame.settings.push_back({
Read2b(buffer),
Read4b(buffer + 2),
});
}
return std::move(frame);
}
absl::StatusOr<Http2PingFrame> ParsePingFrame(const Http2FrameHeader& hdr,
SliceBuffer& payload) {
if (payload.Length() != 8) {
return absl::InternalError(
absl::StrCat("invalid ping payload: ", hdr.ToString()));
}
if (hdr.stream_id != 0) {
return absl::InternalError(
absl::StrCat("invalid ping stream id: ", hdr.ToString()));
}
bool ack;
switch (hdr.flags) {
case 0:
ack = false;
break;
case kFlagAck:
ack = true;
break;
default:
return absl::InternalError(
absl::StrCat("invalid ping flags: ", hdr.ToString()));
}
uint8_t buffer[8];
payload.CopyToBuffer(buffer);
return Http2PingFrame{ack, Read8b(buffer)};
}
absl::StatusOr<Http2GoawayFrame> ParseGoawayFrame(const Http2FrameHeader& hdr,
SliceBuffer& payload) {
if (payload.Length() < 8) {
return absl::InternalError(
absl::StrCat("invalid goaway payload: ", hdr.ToString(),
" -- must be at least 8 bytes"));
}
if (hdr.stream_id != 0) {
return absl::InternalError(
absl::StrCat("invalid goaway stream id: ", hdr.ToString()));
}
if (hdr.flags != 0) {
return absl::InternalError(
absl::StrCat("invalid goaway flags: ", hdr.ToString()));
}
uint8_t buffer[8];
payload.MoveFirstNBytesIntoBuffer(8, buffer);
return Http2GoawayFrame{Read4b(buffer), Read4b(buffer + 4),
payload.JoinIntoSlice()};
}
absl::StatusOr<Http2WindowUpdateFrame> ParseWindowUpdateFrame(
const Http2FrameHeader& hdr, SliceBuffer& payload) {
if (payload.Length() != 4) {
return absl::InternalError(
absl::StrCat("invalid window update payload: ", hdr.ToString(),
" -- must be 4 bytes"));
}
if (hdr.flags != 0) {
return absl::InternalError(
absl::StrCat("invalid window update flags: ", hdr.ToString()));
}
uint8_t buffer[4];
payload.CopyToBuffer(buffer);
return Http2WindowUpdateFrame{hdr.stream_id, Read4b(buffer)};
}
} // namespace
void Http2FrameHeader::Serialize(uint8_t* output) const {
Write3b(length, output);
output[3] = type;
output[4] = flags;
Write4b(stream_id, output + 5);
}
Http2FrameHeader Http2FrameHeader::Parse(const uint8_t* input) {
return Http2FrameHeader{Read3b(input), input[3], input[4], Read4b(input + 5)};
}
namespace {
std::string Http2FrameTypeString(uint8_t frame_type) {
switch (frame_type) {
case kFrameTypeData:
return "DATA";
case kFrameTypeHeader:
return "HEADER";
case kFrameTypeContinuation:
return "CONTINUATION";
case kFrameTypeRstStream:
return "RST_STREAM";
case kFrameTypeSettings:
return "SETTINGS";
case kFrameTypeGoaway:
return "GOAWAY";
case kFrameTypeWindowUpdate:
return "WINDOW_UPDATE";
case kFrameTypePing:
return "PING";
}
return absl::StrCat("UNKNOWN(", frame_type, ")");
}
} // namespace
std::string Http2FrameHeader::ToString() const {
return absl::StrCat("{", Http2FrameTypeString(type), ": flags=", flags,
", stream_id=", stream_id, ", length=", length, "}");
}
void Serialize(absl::Span<Http2Frame> frames, SliceBuffer& out) {
size_t buffer_needed = 0;
for (auto& frame : frames) {
// Bytes needed for framing
buffer_needed += kFrameHeaderSize;
// Bytes needed for unserialized payload
buffer_needed += absl::visit(SerializeExtraBytesRequired(), frame);
}
SerializeHeaderAndPayload serialize(buffer_needed, out);
for (auto& frame : frames) {
absl::visit(serialize, frame);
}
}
absl::StatusOr<Http2Frame> ParseFramePayload(const Http2FrameHeader& hdr,
SliceBuffer payload) {
GPR_ASSERT(payload.Length() == hdr.length);
switch (hdr.type) {
case kFrameTypeData:
return ParseDataFrame(hdr, payload);
case kFrameTypeHeader:
return ParseHeaderFrame(hdr, payload);
case kFrameTypeContinuation:
return ParseContinuationFrame(hdr, payload);
case kFrameTypeRstStream:
return ParseRstStreamFrame(hdr, payload);
case kFrameTypeSettings:
return ParseSettingsFrame(hdr, payload);
case kFrameTypePing:
return ParsePingFrame(hdr, payload);
case kFrameTypeGoaway:
return ParseGoawayFrame(hdr, payload);
case kFrameTypeWindowUpdate:
return ParseWindowUpdateFrame(hdr, payload);
default:
return Http2UnknownFrame{};
}
}
} // namespace grpc_core

@ -1,5 +1,3 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
@ -13,31 +11,202 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H
#define GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H
#include <grpc/support/port_platform.h>
// defined in internal.h
typedef struct grpc_chttp2_stream grpc_chttp2_stream;
typedef struct grpc_chttp2_transport grpc_chttp2_transport;
#define GRPC_CHTTP2_FRAME_DATA 0
#define GRPC_CHTTP2_FRAME_HEADER 1
#define GRPC_CHTTP2_FRAME_CONTINUATION 9
#define GRPC_CHTTP2_FRAME_RST_STREAM 3
#define GRPC_CHTTP2_FRAME_SETTINGS 4
#define GRPC_CHTTP2_FRAME_PING 6
#define GRPC_CHTTP2_FRAME_GOAWAY 7
#define GRPC_CHTTP2_FRAME_WINDOW_UPDATE 8
#define GRPC_CHTTP2_DATA_FLAG_END_STREAM 1
#define GRPC_CHTTP2_FLAG_ACK 1
#define GRPC_CHTTP2_DATA_FLAG_END_HEADERS 4
#define GRPC_CHTTP2_DATA_FLAG_PADDED 8
#define GRPC_CHTTP2_FLAG_HAS_PRIORITY 0x20
#include <cstdint>
#include <string>
#include <vector>
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "absl/types/variant.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/slice/slice_buffer.h"
namespace grpc_core {
///////////////////////////////////////////////////////////////////////////////
// Frame types
//
// Define structs for each kind of frame that chttp2 reasons about.
//
// Each struct gets the members defined by the HTTP/2 spec for that frame type
// *that the semantic layers of chttp2 neead to reason about*.
//
// That means, for instance, that we drop padding and prioritization data from
// these structs, as they are handled by the HTTP/2 framing layer and are
// meaningless to the semantic layers above.
//
// If a frame type is associated with a stream, it has a stream_id member.
// If that frame type is only used at the channel layer it does not.
//
// Instead of carrying bitfields of flags like the wire format, we instead
// declare a bool per flag to make producing/consuming code easier to write.
//
// Equality operators are defined for use in unit tests.
// DATA frame
struct Http2DataFrame {
uint32_t stream_id = 0;
bool end_stream = false;
SliceBuffer payload;
bool operator==(const Http2DataFrame& other) const {
return stream_id == other.stream_id && end_stream == other.end_stream &&
payload.JoinIntoString() == other.payload.JoinIntoString();
}
};
// HEADER frame
struct Http2HeaderFrame {
uint32_t stream_id = 0;
bool end_headers = false;
bool end_stream = false;
SliceBuffer payload;
bool operator==(const Http2HeaderFrame& other) const {
return stream_id == other.stream_id && end_headers == other.end_headers &&
end_stream == other.end_stream &&
payload.JoinIntoString() == other.payload.JoinIntoString();
}
};
// CONTINUATION frame
struct Http2ContinuationFrame {
uint32_t stream_id = 0;
bool end_headers = false;
SliceBuffer payload;
bool operator==(const Http2ContinuationFrame& other) const {
return stream_id == other.stream_id && end_headers == other.end_headers &&
payload.JoinIntoString() == other.payload.JoinIntoString();
}
};
// RST_STREAM frame
struct Http2RstStreamFrame {
uint32_t stream_id = 0;
uint32_t error_code = 0;
bool operator==(const Http2RstStreamFrame& other) const {
return stream_id == other.stream_id && error_code == other.error_code;
}
};
// SETTINGS frame
struct Http2SettingsFrame {
struct Setting {
uint16_t id;
uint32_t value;
bool operator==(const Setting& other) const {
return id == other.id && value == other.value;
}
};
bool ack = false;
std::vector<Setting> settings;
bool operator==(const Http2SettingsFrame& other) const {
return ack == other.ack && settings == other.settings;
}
};
// PING frame
struct Http2PingFrame {
bool ack = false;
uint64_t opaque = 0;
bool operator==(const Http2PingFrame& other) const {
return ack == other.ack && opaque == other.opaque;
}
};
// GOAWAY frame
struct Http2GoawayFrame {
uint32_t last_stream_id = 0;
uint32_t error_code = 0;
Slice debug_data;
bool operator==(const Http2GoawayFrame& other) const {
return last_stream_id == other.last_stream_id &&
error_code == other.error_code &&
debug_data.as_string_view() == other.debug_data.as_string_view();
}
};
// WINDOW_UPDATE frame
struct Http2WindowUpdateFrame {
uint32_t stream_id;
uint32_t increment;
bool operator==(const Http2WindowUpdateFrame& other) const {
return stream_id == other.stream_id && increment == other.increment;
}
};
// Type of frame was unknown (and should be ignored)
struct Http2UnknownFrame {
bool operator==(const Http2UnknownFrame&) const { return true; }
};
///////////////////////////////////////////////////////////////////////////////
// Frame variant
//
// A union of all the frame types above, so that we may pass around an
// arbitrary frame between layers as appropriate.
using Http2Frame =
absl::variant<Http2DataFrame, Http2HeaderFrame, Http2ContinuationFrame,
Http2RstStreamFrame, Http2SettingsFrame, Http2PingFrame,
Http2GoawayFrame, Http2WindowUpdateFrame, Http2UnknownFrame>;
///////////////////////////////////////////////////////////////////////////////
// Frame header
//
// Define a struct for the frame header.
// Parsing this type is the first step in parsing a frame.
// No validation on the header is done during parsing - the fields should be
// instead interpreted by the frame type parser.
struct Http2FrameHeader {
uint32_t length;
uint8_t type;
uint8_t flags;
uint32_t stream_id;
// Serialize header to 9 byte long buffer output
// Crashes if length > 16777215 (as this is unencodable)
void Serialize(uint8_t* output) const;
// Parse header from 9 byte long buffer input
static Http2FrameHeader Parse(const uint8_t* input);
std::string ToString() const;
bool operator==(const Http2FrameHeader& other) const {
return length == other.length && type == other.type &&
flags == other.flags && stream_id == other.stream_id;
}
};
///////////////////////////////////////////////////////////////////////////////
// Parsing & serialization
// Given a frame header and a payload, parse the payload into a frame and
// return it.
// If this function returns an error, that should be considered a connection
// error.
// If a frame should simply be ignored, this function returns a
// Http2UnknownFrame.
// It is expected that hdr.length == payload.Length().
absl::StatusOr<Http2Frame> ParseFramePayload(const Http2FrameHeader& hdr,
SliceBuffer payload);
// Serialize frame and append to out, leaves frames in an unknown state (may
// move things out of frames)
void Serialize(absl::Span<Http2Frame> frames, SliceBuffer& out);
} // namespace grpc_core
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H

@ -29,7 +29,7 @@
#include <grpc/slice.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/promise/poll.h"
#include "src/core/lib/slice/slice_buffer.h"

@ -25,7 +25,7 @@
#include <grpc/slice.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/iomgr/error.h"
typedef enum {

@ -25,7 +25,7 @@
#include <grpc/slice.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/iomgr/error.h"
struct grpc_chttp2_ping_parser {

@ -31,9 +31,9 @@
#include <grpc/slice_buffer.h>
#include <grpc/support/log.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/http_trace.h"
#include "src/core/ext/transport/chttp2/transport/internal.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/transport/http2_errors.h"

@ -25,7 +25,7 @@
#include <grpc/slice.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/transport/transport.h"

@ -34,10 +34,10 @@
#include <grpc/support/log.h>
#include "src/core/ext/transport/chttp2/transport/flow_control.h"
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/frame_goaway.h"
#include "src/core/ext/transport/chttp2/transport/http_trace.h"
#include "src/core/ext/transport/chttp2/transport/internal.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/gprpp/debug_location.h"

@ -26,8 +26,8 @@
#include <grpc/slice.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/http2_settings.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/iomgr/error.h"
typedef enum {

@ -25,7 +25,7 @@
#include <grpc/slice.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/transport/transport.h"

@ -28,10 +28,10 @@
#include <grpc/support/log.h>
#include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/hpack_constants.h"
#include "src/core/ext/transport/chttp2/transport/hpack_encoder_table.h"
#include "src/core/ext/transport/chttp2/transport/http_trace.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/ext/transport/chttp2/transport/varint.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gprpp/crash.h"

@ -36,9 +36,9 @@
#include <grpc/slice.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/hpack_parse_result.h"
#include "src/core/ext/transport/chttp2/transport/hpack_parser_table.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/backoff/random_early_detection.h"
#include "src/core/lib/channel/call_tracer.h"
#include "src/core/lib/iomgr/error.h"

@ -39,7 +39,6 @@
#include "src/core/ext/transport/chttp2/transport/context_list_entry.h"
#include "src/core/ext/transport/chttp2/transport/flow_control.h"
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/frame_goaway.h"
#include "src/core/ext/transport/chttp2/transport/frame_ping.h"
#include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h"
@ -48,6 +47,7 @@
#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h"
#include "src/core/ext/transport/chttp2/transport/hpack_parser.h"
#include "src/core/ext/transport/chttp2/transport/http2_settings.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/ext/transport/chttp2/transport/ping_abuse_policy.h"
#include "src/core/ext/transport/chttp2/transport/ping_rate_policy.h"
#include "src/core/lib/channel/channel_args.h"

@ -0,0 +1,43 @@
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_LEGACY_FRAME_H
#define GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_LEGACY_FRAME_H
#include <grpc/support/port_platform.h>
// defined in internal.h
typedef struct grpc_chttp2_stream grpc_chttp2_stream;
typedef struct grpc_chttp2_transport grpc_chttp2_transport;
#define GRPC_CHTTP2_FRAME_DATA 0
#define GRPC_CHTTP2_FRAME_HEADER 1
#define GRPC_CHTTP2_FRAME_CONTINUATION 9
#define GRPC_CHTTP2_FRAME_RST_STREAM 3
#define GRPC_CHTTP2_FRAME_SETTINGS 4
#define GRPC_CHTTP2_FRAME_PING 6
#define GRPC_CHTTP2_FRAME_GOAWAY 7
#define GRPC_CHTTP2_FRAME_WINDOW_UPDATE 8
#define GRPC_CHTTP2_DATA_FLAG_END_STREAM 1
#define GRPC_CHTTP2_FLAG_ACK 1
#define GRPC_CHTTP2_DATA_FLAG_END_HEADERS 4
#define GRPC_CHTTP2_DATA_FLAG_PADDED 8
#define GRPC_CHTTP2_FLAG_HAS_PRIORITY 0x20
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_LEGACY_FRAME_H

@ -35,7 +35,6 @@
#include <grpc/support/log.h>
#include "src/core/ext/transport/chttp2/transport/flow_control.h"
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/frame_data.h"
#include "src/core/ext/transport/chttp2/transport/frame_goaway.h"
#include "src/core/ext/transport/chttp2/transport/frame_ping.h"
@ -47,6 +46,7 @@
#include "src/core/ext/transport/chttp2/transport/http2_settings.h"
#include "src/core/ext/transport/chttp2/transport/http_trace.h"
#include "src/core/ext/transport/chttp2/transport/internal.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/ext/transport/chttp2/transport/ping_rate_policy.h"
#include "src/core/lib/channel/call_tracer.h"
#include "src/core/lib/channel/channelz.h"

@ -20,8 +20,8 @@
#include <grpc/support/log.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/internal.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gprpp/bitset.h"

@ -39,7 +39,6 @@
#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
#include "src/core/ext/transport/chttp2/transport/context_list_entry.h"
#include "src/core/ext/transport/chttp2/transport/flow_control.h"
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/frame_data.h"
#include "src/core/ext/transport/chttp2/transport/frame_ping.h"
#include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h"
@ -49,6 +48,7 @@
#include "src/core/ext/transport/chttp2/transport/http2_settings.h"
#include "src/core/ext/transport/chttp2/transport/http_trace.h"
#include "src/core/ext/transport/chttp2/transport/internal.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/ext/transport/chttp2/transport/ping_rate_policy.h"
#include "src/core/lib/channel/channelz.h"
#include "src/core/lib/debug/stats.h"

@ -291,6 +291,12 @@ class GPR_MSVC_EMPTY_BASE_CLASS_WORKAROUND MutableSlice
return MutableSlice(grpc_slice_sub_no_ref(TakeCSlice(), pos, pos + n));
}
// Split this slice in two, returning the first n bytes and leaving the
// remainder.
MutableSlice TakeFirst(size_t n) {
return MutableSlice(grpc_slice_split_head(c_slice_ptr(), n));
}
// Iterator access to the underlying bytes
uint8_t* begin() { return mutable_data(); }
uint8_t* end() { return mutable_data() + size(); }

@ -70,6 +70,21 @@ std::string SliceBuffer::JoinIntoString() const {
return result;
}
Slice SliceBuffer::JoinIntoSlice() const {
if (slice_buffer_.count == 0) return Slice();
if (slice_buffer_.count == 1) return RefSlice(0);
grpc_slice slice = GRPC_SLICE_MALLOC(slice_buffer_.length);
size_t ofs = 0;
for (size_t i = 0; i < slice_buffer_.count; i++) {
memcpy(GRPC_SLICE_START_PTR(slice) + ofs,
GRPC_SLICE_START_PTR(slice_buffer_.slices[i]),
GRPC_SLICE_LENGTH(slice_buffer_.slices[i]));
ofs += GRPC_SLICE_LENGTH(slice_buffer_.slices[i]);
}
GPR_ASSERT(ofs == slice_buffer_.length);
return Slice(slice);
}
} // namespace grpc_core
// grow a buffer; requires GRPC_SLICE_BUFFER_INLINE_ELEMENTS > 1

@ -28,6 +28,10 @@
#include "src/core/lib/slice/slice.h"
// Copy the first n bytes of src into memory pointed to by dst.
void grpc_slice_buffer_copy_first_into_buffer(grpc_slice_buffer* src, size_t n,
void* dst);
namespace grpc_core {
/// A slice buffer holds the memory for a collection of slices.
@ -67,6 +71,9 @@ class SliceBuffer {
/// Appends a SliceBuffer into the SliceBuffer and makes an attempt to merge
/// this slice with the last slice in the SliceBuffer.
void Append(const SliceBuffer& other);
void TakeAndAppend(SliceBuffer& other) {
grpc_slice_buffer_move_into(&other.slice_buffer_, &slice_buffer_);
}
/// Adds a new slice into the SliceBuffer at the next available index.
/// Returns the index at which the new slice is added.
@ -75,6 +82,12 @@ class SliceBuffer {
/// Returns the number of slices held by the SliceBuffer.
size_t Count() const { return slice_buffer_.count; }
/// Copy the entire contents to a memory buffer.
void CopyToBuffer(uint8_t* dst) {
grpc_slice_buffer_copy_first_into_buffer(&slice_buffer_,
slice_buffer_.length, dst);
}
/// Removes/deletes the last n bytes in the SliceBuffer.
void RemoveLastNBytes(size_t n) {
grpc_slice_buffer_trim_end(&slice_buffer_, n, nullptr);
@ -125,6 +138,9 @@ class SliceBuffer {
/// Concatenate all slices and return the resulting string.
std::string JoinIntoString() const;
/// Concatenate all slices and return the resulting slice.
Slice JoinIntoSlice() const;
// Return a copy of the slice buffer
SliceBuffer Copy() const {
SliceBuffer copy;
@ -161,8 +177,4 @@ class SliceBuffer {
} // namespace grpc_core
// Copy the first n bytes of src into memory pointed to by dst.
void grpc_slice_buffer_copy_first_into_buffer(grpc_slice_buffer* src, size_t n,
void* dst);
#endif // GRPC_SRC_CORE_LIB_SLICE_SLICE_BUFFER_H

@ -145,6 +145,19 @@ grpc_cc_test(
],
)
grpc_cc_test(
name = "frame_test",
srcs = ["frame_test.cc"],
external_deps = ["gtest"],
language = "C++",
uses_event_engine = False,
uses_polling = False,
deps = [
"//:chttp2_frame",
"//src/core:http2_errors",
],
)
grpc_cc_test(
name = "bin_encoder_test",
srcs = ["bin_encoder_test.cc"],

@ -0,0 +1,393 @@
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include <algorithm>
#include <initializer_list>
#include <utility>
#include "absl/status/status.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "src/core/lib/transport/http2_errors.h"
namespace grpc_core {
namespace {
MATCHER_P2(StatusIs, code, message, "") {
return arg.code() == code && arg.message() == message;
}
void DoTheseThings(std::initializer_list<int>) {}
template <typename... Frames>
std::vector<uint8_t> Serialize(Frames... f) {
std::vector<Http2Frame> frames;
DoTheseThings({(frames.emplace_back(std::move(f)), 1)...});
SliceBuffer temp;
Serialize(absl::Span<Http2Frame>(frames), temp);
auto slice = temp.JoinIntoSlice();
return std::vector<uint8_t>(slice.begin(), slice.end());
}
template <typename... I>
std::vector<uint8_t> ByteVec(I... i) {
return std::vector<uint8_t>{static_cast<uint8_t>(i)...};
}
SliceBuffer SliceBufferFromString(absl::string_view s) {
SliceBuffer temp;
temp.Append(Slice::FromCopiedString(s));
return temp;
}
std::vector<uint8_t> Serialize(const Http2FrameHeader& header) {
uint8_t temp[9];
header.Serialize(temp);
return std::vector<uint8_t>(temp, temp + 9);
}
Http2FrameHeader ParseHeader(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3,
uint8_t b4, uint8_t b5, uint8_t b6, uint8_t b7,
uint8_t b8) {
uint8_t temp[9] = {b0, b1, b2, b3, b4, b5, b6, b7, b8};
return Http2FrameHeader::Parse(temp);
}
template <typename... I>
Http2Frame ParseFrame(I... i) {
SliceBuffer buffer;
buffer.Append(Slice::FromCopiedBuffer(ByteVec(i...)));
uint8_t hdr[9];
buffer.MoveFirstNBytesIntoBuffer(9, hdr);
auto frame_hdr = Http2FrameHeader::Parse(hdr);
EXPECT_EQ(frame_hdr.length, buffer.Length())
<< "frame_hdr=" << frame_hdr.ToString();
auto r = ParseFramePayload(frame_hdr, std::move(buffer));
EXPECT_TRUE(r.ok()) << r.status();
return std::move(r.value());
}
template <typename... I>
absl::Status ValidateFrame(I... i) {
SliceBuffer buffer;
buffer.Append(Slice::FromCopiedBuffer(ByteVec(i...)));
uint8_t hdr[9];
buffer.MoveFirstNBytesIntoBuffer(9, hdr);
auto frame_hdr = Http2FrameHeader::Parse(hdr);
EXPECT_EQ(frame_hdr.length, buffer.Length())
<< "frame_hdr=" << frame_hdr.ToString();
return ParseFramePayload(frame_hdr, std::move(buffer)).status();
}
TEST(Header, Serialization) {
EXPECT_EQ(Serialize(Http2FrameHeader{0, 0, 0, 0}),
ByteVec(0, 0, 0, 0, 0, 0, 0, 0, 0));
EXPECT_EQ(Serialize(Http2FrameHeader{0x123456, 0x9a, 0xbc, 0x12345678}),
ByteVec(0x12, 0x34, 0x56, 0x9a, 0xbc, 0x12, 0x34, 0x56, 0x78));
}
TEST(Header, Parse) {
EXPECT_EQ(ParseHeader(0, 0, 0, 0, 0, 0, 0, 0, 0),
(Http2FrameHeader{0, 0, 0, 0}));
EXPECT_EQ(ParseHeader(0x12, 0x34, 0x56, 0x9a, 0xbc, 0x12, 0x34, 0x56, 0x78),
(Http2FrameHeader{0x123456, 0x9a, 0xbc, 0x12345678}));
}
TEST(Header, ToString) {
EXPECT_EQ((Http2FrameHeader{0, 0, 0, 0}).ToString(),
"{DATA: flags=0, stream_id=0, length=0}");
EXPECT_EQ((Http2FrameHeader{0x123456, 0x9a, 0xbc, 0x12345678}).ToString(),
"{UNKNOWN(154): flags=188, stream_id=305419896, length=1193046}");
}
TEST(Frame, Serialization) {
EXPECT_EQ(Serialize(Http2DataFrame{1, false, SliceBufferFromString("hello")}),
ByteVec(0, 0, 5, 0, 0, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'));
EXPECT_EQ(Serialize(Http2DataFrame{0x98381822, true,
SliceBufferFromString("kids")}),
ByteVec(0, 0, 4, 0, 1, 0x98, 0x38, 0x18, 0x22, 'k', 'i', 'd', 's'));
EXPECT_EQ(Serialize(Http2HeaderFrame{1, false, false,
SliceBufferFromString("hello")}),
ByteVec(0, 0, 5, 1, 0, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'));
EXPECT_EQ(Serialize(Http2HeaderFrame{1, true, false,
SliceBufferFromString("hello")}),
ByteVec(0, 0, 5, 1, 4, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'));
EXPECT_EQ(Serialize(Http2HeaderFrame{1, false, true,
SliceBufferFromString("hello")}),
ByteVec(0, 0, 5, 1, 1, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'));
EXPECT_EQ(Serialize(Http2HeaderFrame{1, true, true,
SliceBufferFromString("hello")}),
ByteVec(0, 0, 5, 1, 5, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'));
EXPECT_EQ(Serialize(Http2ContinuationFrame{1, false,
SliceBufferFromString("hello")}),
ByteVec(0, 0, 5, 9, 0, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'));
EXPECT_EQ(Serialize(Http2ContinuationFrame{1, true,
SliceBufferFromString("hello")}),
ByteVec(0, 0, 5, 9, 4, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'));
EXPECT_EQ(Serialize(Http2RstStreamFrame{1, GRPC_HTTP2_CONNECT_ERROR}),
ByteVec(0, 0, 4, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0x0a));
EXPECT_EQ(Serialize(Http2SettingsFrame{}),
ByteVec(0, 0, 0, 4, 0, 0, 0, 0, 0));
EXPECT_EQ(
Serialize(Http2SettingsFrame{false, {{0x1234, 0x9abcdef0}}}),
ByteVec(0, 0, 6, 4, 0, 0, 0, 0, 0, 0x12, 0x34, 0x9a, 0xbc, 0xde, 0xf0));
EXPECT_EQ(Serialize(Http2SettingsFrame{
false, {{0x1234, 0x9abcdef0}, {0x4321, 0x12345678}}}),
ByteVec(0, 0, 12, 4, 0, 0, 0, 0, 0, 0x12, 0x34, 0x9a, 0xbc, 0xde,
0xf0, 0x43, 0x21, 0x12, 0x34, 0x56, 0x78));
EXPECT_EQ(Serialize(Http2SettingsFrame{true, {}}),
ByteVec(0, 0, 0, 4, 1, 0, 0, 0, 0));
EXPECT_EQ(Serialize(Http2PingFrame{false, 0x123456789abcdef0}),
ByteVec(0, 0, 8, 6, 0, 0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0x9a,
0xbc, 0xde, 0xf0));
EXPECT_EQ(Serialize(Http2PingFrame{true, 0x123456789abcdef0}),
ByteVec(0, 0, 8, 6, 1, 0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0x9a,
0xbc, 0xde, 0xf0));
EXPECT_EQ(Serialize(Http2GoawayFrame{0x12345678, GRPC_HTTP2_ENHANCE_YOUR_CALM,
Slice::FromCopiedString("hello")}),
ByteVec(0, 0, 13, 7, 0, 0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0, 0, 0,
0x0b, 'h', 'e', 'l', 'l', 'o'));
EXPECT_EQ(Serialize(Http2WindowUpdateFrame{1, 0x12345678}),
ByteVec(0, 0, 4, 8, 0, 0, 0, 0, 1, 0x12, 0x34, 0x56, 0x78));
}
TEST(Frame, Parse) {
EXPECT_EQ(
ParseFrame(0, 0, 5, 0, 0, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'),
Http2Frame(Http2DataFrame{1, false, SliceBufferFromString("hello")}));
EXPECT_EQ(
ParseFrame(0, 0, 4, 0, 1, 0x98, 0x38, 0x18, 0x22, 'k', 'i', 'd', 's'),
Http2Frame(
Http2DataFrame{0x98381822, true, SliceBufferFromString("kids")}));
EXPECT_EQ(ParseFrame(0, 0, 5, 1, 0, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'),
Http2Frame(Http2HeaderFrame{1, false, false,
SliceBufferFromString("hello")}));
EXPECT_EQ(
ParseFrame(0, 0, 4, 1, 4, 0x98, 0x38, 0x18, 0x22, 'k', 'i', 'd', 's'),
Http2Frame(Http2HeaderFrame{0x98381822, true, false,
SliceBufferFromString("kids")}));
EXPECT_EQ(
ParseFrame(0, 0, 4, 1, 1, 0x98, 0x38, 0x18, 0x22, 'k', 'i', 'd', 's'),
Http2Frame(Http2HeaderFrame{0x98381822, false, true,
SliceBufferFromString("kids")}));
EXPECT_EQ(ParseFrame(0, 0, 5, 9, 0, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'),
Http2Frame(Http2ContinuationFrame{1, false,
SliceBufferFromString("hello")}));
EXPECT_EQ(ParseFrame(0, 0, 5, 9, 4, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'),
Http2Frame(Http2ContinuationFrame{1, true,
SliceBufferFromString("hello")}));
EXPECT_EQ(ParseFrame(0, 0, 4, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0x0a),
Http2Frame(Http2RstStreamFrame{1, GRPC_HTTP2_CONNECT_ERROR}));
EXPECT_EQ(ParseFrame(0, 0, 0, 4, 0, 0, 0, 0, 0),
Http2Frame(Http2SettingsFrame{}));
EXPECT_EQ(
ParseFrame(0, 0, 6, 4, 0, 0, 0, 0, 0, 0x12, 0x34, 0x9a, 0xbc, 0xde, 0xf0),
Http2Frame(Http2SettingsFrame{false, {{0x1234, 0x9abcdef0}}}));
EXPECT_EQ(ParseFrame(0, 0, 12, 4, 0, 0, 0, 0, 0, 0x12, 0x34, 0x9a, 0xbc, 0xde,
0xf0, 0x43, 0x21, 0x12, 0x34, 0x56, 0x78),
Http2Frame(Http2SettingsFrame{
false, {{0x1234, 0x9abcdef0}, {0x4321, 0x12345678}}}));
EXPECT_EQ(ParseFrame(0, 0, 0, 4, 1, 0, 0, 0, 0),
Http2Frame(Http2SettingsFrame{true, {}}));
EXPECT_EQ(ParseFrame(0, 0, 8, 6, 0, 0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0x9a,
0xbc, 0xde, 0xf0),
Http2Frame(Http2PingFrame{false, 0x123456789abcdef0}));
EXPECT_EQ(ParseFrame(0, 0, 8, 6, 1, 0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0x9a,
0xbc, 0xde, 0xf0),
Http2Frame(Http2PingFrame{true, 0x123456789abcdef0}));
EXPECT_EQ(
ParseFrame(0, 0, 13, 7, 0, 0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0, 0, 0,
0x0b, 'h', 'e', 'l', 'l', 'o'),
Http2Frame(Http2GoawayFrame{0x12345678, GRPC_HTTP2_ENHANCE_YOUR_CALM,
Slice::FromCopiedString("hello")}));
EXPECT_EQ(ParseFrame(0, 0, 4, 8, 0, 0, 0, 0, 1, 0x12, 0x34, 0x56, 0x78),
Http2Frame(Http2WindowUpdateFrame{1, 0x12345678}));
}
TEST(Frame, ParsePadded) {
EXPECT_EQ(
ParseFrame(0, 0, 9, 0, 8, 0, 0, 0, 1, 3, 'h', 'e', 'l', 'l', 'o', 1, 2,
3),
Http2Frame(Http2DataFrame{1, false, SliceBufferFromString("hello")}));
EXPECT_EQ(
ParseFrame(0, 0, 8, 1, 8, 0, 0, 0, 1, 2, 'h', 'e', 'l', 'l', 'o', 1, 2),
Http2Frame(
Http2HeaderFrame{1, false, false, SliceBufferFromString("hello")}));
EXPECT_EQ(ParseFrame(0, 0, 10, 1, 32, 0, 0, 0, 1, 1, 2, 3, 4, 5, 'h', 'e',
'l', 'l', 'o'),
Http2Frame(Http2HeaderFrame{1, false, false,
SliceBufferFromString("hello")}));
EXPECT_EQ(ParseFrame(0, 0, 13, 1, 40, 0, 0, 0, 1, 2, 1, 2, 3, 4, 5, 'h', 'e',
'l', 'l', 'o', 1, 2),
Http2Frame(Http2HeaderFrame{1, false, false,
SliceBufferFromString("hello")}));
}
TEST(Frame, ParseRejects) {
EXPECT_THAT(ValidateFrame(0, 0, 0, 0, 0, 0, 0, 0, 0),
StatusIs(absl::StatusCode::kInternal,
"invalid stream id: {DATA: flags=0, "
"stream_id=0, length=0}"));
EXPECT_THAT(ValidateFrame(0, 0, 0, 1, 0, 0, 0, 0, 0),
StatusIs(absl::StatusCode::kInternal,
"invalid stream id: {HEADER: flags=0, "
"stream_id=0, length=0}"));
EXPECT_THAT(ValidateFrame(0, 0, 0, 9, 0, 0, 0, 0, 0),
StatusIs(absl::StatusCode::kInternal,
"invalid stream id: {CONTINUATION: flags=0, "
"stream_id=0, length=0}"));
EXPECT_THAT(ValidateFrame(0, 0, 3, 3, 0, 0, 0, 0, 1, 100, 100, 100),
StatusIs(absl::StatusCode::kInternal,
"invalid rst stream payload: {RST_STREAM: flags=0, "
"stream_id=1, length=3}"));
EXPECT_THAT(ValidateFrame(0, 0, 4, 3, 0, 0, 0, 0, 0, 100, 100, 100, 100),
StatusIs(absl::StatusCode::kInternal,
"invalid stream id: {RST_STREAM: flags=0, "
"stream_id=0, length=4}"));
EXPECT_THAT(ValidateFrame(0, 0, 1, 4, 1, 0, 0, 0, 0, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid settings ack length: {SETTINGS: flags=1, "
"stream_id=0, length=1}"));
EXPECT_THAT(ValidateFrame(0, 0, 1, 4, 0, 0, 0, 0, 0, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid settings payload: {SETTINGS: flags=0, "
"stream_id=0, length=1} -- settings must be multiples "
"of 6 bytes long"));
EXPECT_THAT(ValidateFrame(0, 0, 2, 4, 0, 0, 0, 0, 0, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid settings payload: {SETTINGS: flags=0, "
"stream_id=0, length=2} -- settings must be multiples "
"of 6 bytes long"));
EXPECT_THAT(ValidateFrame(0, 0, 3, 4, 0, 0, 0, 0, 0, 1, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid settings payload: {SETTINGS: flags=0, "
"stream_id=0, length=3} -- settings must be multiples "
"of 6 bytes long"));
EXPECT_THAT(ValidateFrame(0, 0, 4, 4, 0, 0, 0, 0, 0, 1, 1, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid settings payload: {SETTINGS: flags=0, "
"stream_id=0, length=4} -- settings must be multiples "
"of 6 bytes long"));
EXPECT_THAT(ValidateFrame(0, 0, 5, 4, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid settings payload: {SETTINGS: flags=0, "
"stream_id=0, length=5} -- settings must be multiples "
"of 6 bytes long"));
EXPECT_THAT(ValidateFrame(0, 0, 7, 4, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid settings payload: {SETTINGS: flags=0, "
"stream_id=0, length=7} -- settings must be multiples "
"of 6 bytes long"));
EXPECT_THAT(ValidateFrame(0, 0, 0, 4, 0, 0, 0, 0, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid stream id: {SETTINGS: flags=0, "
"stream_id=1, length=0}"));
EXPECT_THAT(ValidateFrame(0, 0, 0, 6, 0, 0, 0, 0, 0),
StatusIs(absl::StatusCode::kInternal,
"invalid ping payload: {PING: flags=0, "
"stream_id=0, length=0}"));
EXPECT_THAT(ValidateFrame(0, 0, 8, 6, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8),
StatusIs(absl::StatusCode::kInternal,
"invalid ping stream id: {PING: flags=0, "
"stream_id=1, length=8}"));
EXPECT_THAT(ValidateFrame(0, 0, 8, 6, 2, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8),
StatusIs(absl::StatusCode::kInternal,
"invalid ping flags: {PING: flags=2, "
"stream_id=0, length=8}"));
EXPECT_THAT(
ValidateFrame(0, 0, 8, 6, 255, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8),
StatusIs(absl::StatusCode::kInternal,
"invalid ping flags: {PING: flags=255, "
"stream_id=0, length=8}"));
EXPECT_THAT(ValidateFrame(0, 0, 0, 7, 0, 0, 0, 0, 0),
StatusIs(absl::StatusCode::kInternal,
"invalid goaway payload: {GOAWAY: flags=0, "
"stream_id=0, length=0} -- must be at least 8 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 1, 7, 0, 0, 0, 0, 0, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid goaway payload: {GOAWAY: flags=0, "
"stream_id=0, length=1} -- must be at least 8 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 2, 7, 0, 0, 0, 0, 0, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid goaway payload: {GOAWAY: flags=0, "
"stream_id=0, length=2} -- must be at least 8 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 3, 7, 0, 0, 0, 0, 0, 1, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid goaway payload: {GOAWAY: flags=0, "
"stream_id=0, length=3} -- must be at least 8 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 4, 7, 0, 0, 0, 0, 0, 1, 1, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid goaway payload: {GOAWAY: flags=0, "
"stream_id=0, length=4} -- must be at least 8 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 5, 7, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid goaway payload: {GOAWAY: flags=0, "
"stream_id=0, length=5} -- must be at least 8 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 6, 7, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid goaway payload: {GOAWAY: flags=0, "
"stream_id=0, length=6} -- must be at least 8 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 7, 7, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid goaway payload: {GOAWAY: flags=0, "
"stream_id=0, length=7} -- must be at least 8 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 8, 7, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8),
StatusIs(absl::StatusCode::kInternal,
"invalid goaway stream id: {GOAWAY: flags=0, "
"stream_id=1, length=8}"));
EXPECT_THAT(ValidateFrame(0, 0, 8, 7, 1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8),
StatusIs(absl::StatusCode::kInternal,
"invalid goaway flags: {GOAWAY: flags=1, "
"stream_id=0, length=8}"));
EXPECT_THAT(
ValidateFrame(0, 0, 8, 7, 255, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8),
StatusIs(absl::StatusCode::kInternal,
"invalid goaway flags: {GOAWAY: flags=255, "
"stream_id=0, length=8}"));
EXPECT_THAT(ValidateFrame(0, 0, 0, 8, 0, 0, 0, 0, 0),
StatusIs(absl::StatusCode::kInternal,
"invalid window update payload: {WINDOW_UPDATE: "
"flags=0, stream_id=0, length=0} -- must be 4 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 1, 8, 0, 0, 0, 0, 0, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid window update payload: {WINDOW_UPDATE: "
"flags=0, stream_id=0, length=1} -- must be 4 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 2, 8, 0, 0, 0, 0, 0, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid window update payload: {WINDOW_UPDATE: "
"flags=0, stream_id=0, length=2} -- must be 4 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 3, 8, 0, 0, 0, 0, 0, 1, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid window update payload: {WINDOW_UPDATE: "
"flags=0, stream_id=0, length=3} -- must be 4 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 5, 8, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid window update payload: {WINDOW_UPDATE: "
"flags=0, stream_id=0, length=5} -- must be 4 bytes"));
EXPECT_THAT(ValidateFrame(0, 0, 4, 8, 1, 0, 0, 0, 0, 1, 1, 1, 1),
StatusIs(absl::StatusCode::kInternal,
"invalid window update flags: {WINDOW_UPDATE: flags=1, "
"stream_id=0, length=4}"));
}
} // namespace
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -31,7 +31,7 @@
#include <grpc/slice_buffer.h>
#include <grpc/support/log.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/resource_quota/arena.h"

@ -19,8 +19,8 @@
#include <grpc/slice.h>
#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/internal.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/ext/transport/chttp2/transport/ping_abuse_policy.h"
#include "src/core/ext/transport/chttp2/transport/ping_rate_policy.h"
#include "src/core/lib/channel/channel_args.h"

@ -1281,7 +1281,6 @@ src/core/ext/transport/chttp2/transport/decode_huff.cc \
src/core/ext/transport/chttp2/transport/decode_huff.h \
src/core/ext/transport/chttp2/transport/flow_control.cc \
src/core/ext/transport/chttp2/transport/flow_control.h \
src/core/ext/transport/chttp2/transport/frame.h \
src/core/ext/transport/chttp2/transport/frame_data.cc \
src/core/ext/transport/chttp2/transport/frame_data.h \
src/core/ext/transport/chttp2/transport/frame_goaway.cc \
@ -1312,6 +1311,7 @@ src/core/ext/transport/chttp2/transport/http_trace.h \
src/core/ext/transport/chttp2/transport/huffsyms.cc \
src/core/ext/transport/chttp2/transport/huffsyms.h \
src/core/ext/transport/chttp2/transport/internal.h \
src/core/ext/transport/chttp2/transport/legacy_frame.h \
src/core/ext/transport/chttp2/transport/parsing.cc \
src/core/ext/transport/chttp2/transport/ping_abuse_policy.cc \
src/core/ext/transport/chttp2/transport/ping_abuse_policy.h \

@ -1057,7 +1057,6 @@ src/core/ext/transport/chttp2/transport/decode_huff.cc \
src/core/ext/transport/chttp2/transport/decode_huff.h \
src/core/ext/transport/chttp2/transport/flow_control.cc \
src/core/ext/transport/chttp2/transport/flow_control.h \
src/core/ext/transport/chttp2/transport/frame.h \
src/core/ext/transport/chttp2/transport/frame_data.cc \
src/core/ext/transport/chttp2/transport/frame_data.h \
src/core/ext/transport/chttp2/transport/frame_goaway.cc \
@ -1088,6 +1087,7 @@ src/core/ext/transport/chttp2/transport/http_trace.h \
src/core/ext/transport/chttp2/transport/huffsyms.cc \
src/core/ext/transport/chttp2/transport/huffsyms.h \
src/core/ext/transport/chttp2/transport/internal.h \
src/core/ext/transport/chttp2/transport/legacy_frame.h \
src/core/ext/transport/chttp2/transport/parsing.cc \
src/core/ext/transport/chttp2/transport/ping_abuse_policy.cc \
src/core/ext/transport/chttp2/transport/ping_abuse_policy.h \

@ -3707,30 +3707,6 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "frame_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
@ -10087,6 +10063,54 @@
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "test_core_transport_chaotic_good_frame_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "test_core_transport_chttp2_frame_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save