[chttp2] Rework settings management to be a c++ type (#35449)

Closes #35449

COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/35449 from ctiller:h2-settings f67e542a8b
PiperOrigin-RevId: 597970333
pull/35530/head
Craig Tiller 11 months ago committed by Copybara-Service
parent 650bb21abd
commit 9f6789e2b2
  1. 1
      BUILD
  2. 39
      CMakeLists.txt
  3. 2
      Makefile
  4. 2
      Package.swift
  5. 19
      build_autogenerated.yaml
  6. 1
      config.m4
  7. 1
      config.w32
  8. 2
      gRPC-C++.podspec
  9. 3
      gRPC-Core.podspec
  10. 2
      grpc.gemspec
  11. 2
      grpc.gyp
  12. 2
      package.xml
  13. 6
      src/core/BUILD
  14. 195
      src/core/ext/transport/chttp2/transport/chttp2_transport.cc
  15. 31
      src/core/ext/transport/chttp2/transport/flow_control.cc
  16. 2
      src/core/ext/transport/chttp2/transport/flow_control.h
  17. 2
      src/core/ext/transport/chttp2/transport/frame.h
  18. 112
      src/core/ext/transport/chttp2/transport/frame_settings.cc
  19. 11
      src/core/ext/transport/chttp2/transport/frame_settings.h
  20. 154
      src/core/ext/transport/chttp2/transport/http2_settings.cc
  21. 179
      src/core/ext/transport/chttp2/transport/http2_settings.h
  22. 23
      src/core/ext/transport/chttp2/transport/internal.h
  23. 53
      src/core/ext/transport/chttp2/transport/parsing.cc
  24. 69
      src/core/ext/transport/chttp2/transport/writing.cc
  25. 1
      src/python/grpcio/grpc_core_dependencies.py
  26. 17
      test/core/transport/chttp2/BUILD
  27. 514
      test/core/transport/chttp2/http2_settings_test.cc
  28. 270
      tools/codegen/core/gen_settings_ids.py
  29. 2
      tools/doxygen/Doxyfile.c++.internal
  30. 2
      tools/doxygen/Doxyfile.core.internal
  31. 24
      tools/run_tests/generated/tests.json

@ -4077,6 +4077,7 @@ grpc_cc_library(
"//src/core:error",
"//src/core:error_utils",
"//src/core:experiments",
"//src/core:gpr_manual_constructor",
"//src/core:http2_errors",
"//src/core:http2_settings",
"//src/core:init_internally",

39
CMakeLists.txt generated

@ -1097,6 +1097,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx hpack_parser_test)
add_dependencies(buildtests_cxx hpack_size_test)
add_dependencies(buildtests_cxx http2_client)
add_dependencies(buildtests_cxx http2_settings_test)
add_dependencies(buildtests_cxx http2_stats_test)
add_dependencies(buildtests_cxx http_proxy_mapper_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@ -1846,6 +1847,7 @@ add_library(grpc
src/core/ext/transport/chttp2/transport/chttp2_transport.cc
src/core/ext/transport/chttp2/transport/decode_huff.cc
src/core/ext/transport/chttp2/transport/flow_control.cc
src/core/ext/transport/chttp2/transport/frame.cc
src/core/ext/transport/chttp2/transport/frame_data.cc
src/core/ext/transport/chttp2/transport/frame_goaway.cc
src/core/ext/transport/chttp2/transport/frame_ping.cc
@ -2899,6 +2901,7 @@ add_library(grpc_unsecure
src/core/ext/transport/chttp2/transport/chttp2_transport.cc
src/core/ext/transport/chttp2/transport/decode_huff.cc
src/core/ext/transport/chttp2/transport/flow_control.cc
src/core/ext/transport/chttp2/transport/frame.cc
src/core/ext/transport/chttp2/transport/frame_data.cc
src/core/ext/transport/chttp2/transport/frame_goaway.cc
src/core/ext/transport/chttp2/transport/frame_ping.cc
@ -12175,6 +12178,7 @@ if(gRPC_BUILD_TESTS)
add_executable(flow_control_test
src/core/ext/transport/chttp2/transport/flow_control.cc
src/core/ext/transport/chttp2/transport/frame.cc
src/core/ext/transport/chttp2/transport/http2_settings.cc
src/core/ext/upb-gen/google/protobuf/any.upb_minitable.c
src/core/ext/upb-gen/google/protobuf/descriptor.upb_minitable.c
@ -12199,6 +12203,7 @@ add_executable(flow_control_test
src/core/lib/resource_quota/trace.cc
src/core/lib/slice/percent_encoding.cc
src/core/lib/slice/slice.cc
src/core/lib/slice/slice_buffer.cc
src/core/lib/slice/slice_refcount.cc
src/core/lib/slice/slice_string_helpers.cc
src/core/lib/transport/bdp_estimator.cc
@ -12244,6 +12249,7 @@ target_link_libraries(flow_control_test
absl::hash
absl::type_traits
absl::statusor
absl::span
gpr
)
@ -14536,6 +14542,39 @@ target_link_libraries(http2_client
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(http2_settings_test
test/core/transport/chttp2/http2_settings_test.cc
)
target_compile_features(http2_settings_test PUBLIC cxx_std_14)
target_include_directories(http2_settings_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(http2_settings_test
${_gRPC_ALLTARGETS_LIBRARIES}
gtest
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)

2
Makefile generated

@ -1051,6 +1051,7 @@ LIBGRPC_SRC = \
src/core/ext/transport/chttp2/transport/chttp2_transport.cc \
src/core/ext/transport/chttp2/transport/decode_huff.cc \
src/core/ext/transport/chttp2/transport/flow_control.cc \
src/core/ext/transport/chttp2/transport/frame.cc \
src/core/ext/transport/chttp2/transport/frame_data.cc \
src/core/ext/transport/chttp2/transport/frame_goaway.cc \
src/core/ext/transport/chttp2/transport/frame_ping.cc \
@ -1956,6 +1957,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/ext/transport/chttp2/transport/chttp2_transport.cc \
src/core/ext/transport/chttp2/transport/decode_huff.cc \
src/core/ext/transport/chttp2/transport/flow_control.cc \
src/core/ext/transport/chttp2/transport/frame.cc \
src/core/ext/transport/chttp2/transport/frame_data.cc \
src/core/ext/transport/chttp2/transport/frame_goaway.cc \
src/core/ext/transport/chttp2/transport/frame_ping.cc \

2
Package.swift generated

@ -289,6 +289,8 @@ 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.cc",
"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",

@ -299,6 +299,7 @@ libs:
- 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
@ -1311,6 +1312,7 @@ libs:
- src/core/ext/transport/chttp2/transport/chttp2_transport.cc
- src/core/ext/transport/chttp2/transport/decode_huff.cc
- src/core/ext/transport/chttp2/transport/flow_control.cc
- src/core/ext/transport/chttp2/transport/frame.cc
- src/core/ext/transport/chttp2/transport/frame_data.cc
- src/core/ext/transport/chttp2/transport/frame_goaway.cc
- src/core/ext/transport/chttp2/transport/frame_ping.cc
@ -2239,6 +2241,7 @@ libs:
- 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
@ -2723,6 +2726,7 @@ libs:
- src/core/ext/transport/chttp2/transport/chttp2_transport.cc
- src/core/ext/transport/chttp2/transport/decode_huff.cc
- src/core/ext/transport/chttp2/transport/flow_control.cc
- src/core/ext/transport/chttp2/transport/frame.cc
- src/core/ext/transport/chttp2/transport/frame_data.cc
- src/core/ext/transport/chttp2/transport/frame_goaway.cc
- src/core/ext/transport/chttp2/transport/frame_ping.cc
@ -8978,6 +8982,7 @@ targets:
language: c++
headers:
- src/core/ext/transport/chttp2/transport/flow_control.h
- src/core/ext/transport/chttp2/transport/frame.h
- src/core/ext/transport/chttp2/transport/http2_settings.h
- src/core/ext/upb-gen/google/protobuf/any.upb.h
- src/core/ext/upb-gen/google/protobuf/any.upb_minitable.h
@ -9024,6 +9029,7 @@ targets:
- src/core/lib/resource_quota/trace.h
- src/core/lib/slice/percent_encoding.h
- src/core/lib/slice/slice.h
- src/core/lib/slice/slice_buffer.h
- src/core/lib/slice/slice_internal.h
- src/core/lib/slice/slice_refcount.h
- src/core/lib/slice/slice_string_helpers.h
@ -9052,6 +9058,7 @@ targets:
- third_party/upb/upb/wire/types.h
src:
- src/core/ext/transport/chttp2/transport/flow_control.cc
- src/core/ext/transport/chttp2/transport/frame.cc
- src/core/ext/transport/chttp2/transport/http2_settings.cc
- src/core/ext/upb-gen/google/protobuf/any.upb_minitable.c
- src/core/ext/upb-gen/google/protobuf/descriptor.upb_minitable.c
@ -9076,6 +9083,7 @@ targets:
- src/core/lib/resource_quota/trace.cc
- src/core/lib/slice/percent_encoding.cc
- src/core/lib/slice/slice.cc
- src/core/lib/slice/slice_buffer.cc
- src/core/lib/slice/slice_refcount.cc
- src/core/lib/slice/slice_string_helpers.cc
- src/core/lib/transport/bdp_estimator.cc
@ -9099,6 +9107,7 @@ targets:
- absl/hash:hash
- absl/meta:type_traits
- absl/status:statusor
- absl/types:span
- gpr
uses_polling: false
- name: for_each_test
@ -10375,6 +10384,16 @@ targets:
deps:
- grpc++_test_config
- grpc++_test_util
- name: http2_settings_test
gtest: true
build: test
language: c++
headers: []
src:
- test/core/transport/chttp2/http2_settings_test.cc
deps:
- gtest
- grpc_test_util
- name: http2_stats_test
gtest: true
build: test

1
config.m4 generated

@ -133,6 +133,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/ext/transport/chttp2/transport/chttp2_transport.cc \
src/core/ext/transport/chttp2/transport/decode_huff.cc \
src/core/ext/transport/chttp2/transport/flow_control.cc \
src/core/ext/transport/chttp2/transport/frame.cc \
src/core/ext/transport/chttp2/transport/frame_data.cc \
src/core/ext/transport/chttp2/transport/frame_goaway.cc \
src/core/ext/transport/chttp2/transport/frame_ping.cc \

1
config.w32 generated

@ -98,6 +98,7 @@ if (PHP_GRPC != "no") {
"src\\core\\ext\\transport\\chttp2\\transport\\chttp2_transport.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\decode_huff.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\flow_control.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\frame.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\frame_data.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\frame_goaway.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\frame_ping.cc " +

2
gRPC-C++.podspec generated

@ -368,6 +368,7 @@ 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',
@ -1611,6 +1612,7 @@ 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',

3
gRPC-Core.podspec generated

@ -392,6 +392,8 @@ 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.cc',
'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',
@ -2382,6 +2384,7 @@ 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',

2
grpc.gemspec generated

@ -295,6 +295,8 @@ 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.cc )
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 )

2
grpc.gyp generated

@ -364,6 +364,7 @@
'src/core/ext/transport/chttp2/transport/chttp2_transport.cc',
'src/core/ext/transport/chttp2/transport/decode_huff.cc',
'src/core/ext/transport/chttp2/transport/flow_control.cc',
'src/core/ext/transport/chttp2/transport/frame.cc',
'src/core/ext/transport/chttp2/transport/frame_data.cc',
'src/core/ext/transport/chttp2/transport/frame_goaway.cc',
'src/core/ext/transport/chttp2/transport/frame_ping.cc',
@ -1209,6 +1210,7 @@
'src/core/ext/transport/chttp2/transport/chttp2_transport.cc',
'src/core/ext/transport/chttp2/transport/decode_huff.cc',
'src/core/ext/transport/chttp2/transport/flow_control.cc',
'src/core/ext/transport/chttp2/transport/frame.cc',
'src/core/ext/transport/chttp2/transport/frame_data.cc',
'src/core/ext/transport/chttp2/transport/frame_goaway.cc',
'src/core/ext/transport/chttp2/transport/frame_ping.cc',

2
package.xml generated

@ -277,6 +277,8 @@
<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.cc" 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" />

@ -6137,9 +6137,15 @@ grpc_cc_library(
hdrs = [
"ext/transport/chttp2/transport/http2_settings.h",
],
external_deps = [
"absl/functional:function_ref",
"absl/strings",
"absl/types:optional",
],
deps = [
"http2_errors",
"useful",
"//:chttp2_frame",
"//:gpr_platform",
],
)

@ -163,10 +163,6 @@ static void read_action_locked(grpc_core::RefCountedPtr<grpc_chttp2_transport>,
static void continue_read_action_locked(
grpc_core::RefCountedPtr<grpc_chttp2_transport> t);
// Set a transport level setting, and push it to our peer
static void queue_setting_update(grpc_chttp2_transport* t,
grpc_chttp2_setting_id id, uint32_t value);
static void close_from_api(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
grpc_error_handle error, bool tarpit);
@ -550,93 +546,54 @@ static void read_channel_args(grpc_chttp2_transport* t,
t->max_header_list_size_soft_limit = soft_limit;
}
static const struct {
absl::string_view channel_arg_name;
grpc_chttp2_setting_id setting_id;
int default_value;
int min;
int max;
bool availability[2] /* server, client */;
} settings_map[] = {{GRPC_ARG_MAX_CONCURRENT_STREAMS,
GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
-1,
0,
INT32_MAX,
{true, false}},
{GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER,
GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE,
-1,
0,
INT32_MAX,
{true, true}},
{GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE,
GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
-1,
0,
INT32_MAX,
{true, true}},
{GRPC_ARG_HTTP2_MAX_FRAME_SIZE,
GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE,
-1,
16384,
16777215,
{true, true}},
{GRPC_ARG_HTTP2_ENABLE_TRUE_BINARY,
GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA,
1,
0,
1,
{true, true}},
{GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES,
GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE,
-1,
5,
INT32_MAX,
{true, true}}};
for (size_t i = 0; i < GPR_ARRAY_SIZE(settings_map); i++) {
const auto& setting = settings_map[i];
if (setting.availability[is_client]) {
const int value = channel_args.GetInt(setting.channel_arg_name)
.value_or(setting.default_value);
if (value >= 0) {
const int clamped_value =
grpc_core::Clamp(value, setting.min, setting.max);
queue_setting_update(t, setting.setting_id, clamped_value);
if (setting.setting_id == GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {
t->max_concurrent_streams_policy.SetTarget(clamped_value);
}
} else if (setting.setting_id ==
GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE) {
// Set value to 1.25 * soft limit if this is larger than
// `DEFAULT_MAX_HEADER_LIST_SIZE` and
// `GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE` is not set.
const int soft_limit = channel_args.GetInt(GRPC_ARG_MAX_METADATA_SIZE)
.value_or(setting.default_value);
const int value = (soft_limit >= 0 && soft_limit < (INT_MAX / 1.25))
? static_cast<int>(soft_limit * 1.25)
: soft_limit;
if (value > DEFAULT_MAX_HEADER_LIST_SIZE) {
queue_setting_update(
t, setting.setting_id,
grpc_core::Clamp(value, setting.min, setting.max));
}
}
} else if (channel_args.Contains(setting.channel_arg_name)) {
gpr_log(GPR_DEBUG, "%s is not available on %s",
std::string(setting.channel_arg_name).c_str(),
is_client ? "clients" : "servers");
int value;
if (!is_client) {
value = channel_args.GetInt(GRPC_ARG_MAX_CONCURRENT_STREAMS).value_or(-1);
if (value >= 0) {
t->settings.mutable_local().SetMaxConcurrentStreams(value);
t->max_concurrent_streams_policy.SetTarget(value);
}
} else if (channel_args.Contains(GRPC_ARG_MAX_CONCURRENT_STREAMS)) {
gpr_log(GPR_DEBUG, "%s is not available on clients",
GRPC_ARG_MAX_CONCURRENT_STREAMS);
}
value =
channel_args.GetInt(GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER).value_or(-1);
if (value >= 0) {
t->settings.mutable_local().SetHeaderTableSize(value);
}
value = channel_args.GetInt(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE).value_or(-1);
if (value >= 0) {
t->settings.mutable_local().SetMaxHeaderListSize(value);
} else {
// Set value to 1.25 * soft limit if this is larger than
// `DEFAULT_MAX_HEADER_LIST_SIZE` and
// `GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE` is not set.
const int soft_limit =
channel_args.GetInt(GRPC_ARG_MAX_METADATA_SIZE).value_or(-1);
const int value = (soft_limit >= 0 && soft_limit < (INT_MAX / 1.25))
? static_cast<int>(soft_limit * 1.25)
: soft_limit;
if (value > DEFAULT_MAX_HEADER_LIST_SIZE) {
t->settings.mutable_local().SetMaxHeaderListSize(value);
}
}
value = channel_args.GetInt(GRPC_ARG_HTTP2_MAX_FRAME_SIZE).value_or(-1);
if (value >= 0) {
t->settings.mutable_local().SetMaxFrameSize(value);
}
value =
channel_args.GetInt(GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES).value_or(-1);
if (value >= 0) {
t->settings.mutable_local().SetInitialWindowSize(value);
}
value = channel_args.GetInt(GRPC_ARG_HTTP2_ENABLE_TRUE_BINARY).value_or(-1);
if (value >= 0) {
t->settings.mutable_local().SetAllowTrueBinaryMetadata(value != 0);
}
if (t->enable_preferred_rx_crypto_frame_advertisement) {
const grpc_chttp2_setting_parameters* sp =
&grpc_chttp2_settings_parameters
[GRPC_CHTTP2_SETTINGS_GRPC_PREFERRED_RECEIVE_CRYPTO_FRAME_SIZE];
queue_setting_update(
t, GRPC_CHTTP2_SETTINGS_GRPC_PREFERRED_RECEIVE_CRYPTO_FRAME_SIZE,
grpc_core::Clamp(INT_MAX, static_cast<int>(sp->min_value),
static_cast<int>(sp->max_value)));
t->settings.mutable_local().SetPreferredReceiveCryptoMessageSize(INT_MAX);
}
t->ping_on_rst_stream_percent = grpc_core::Clamp(
@ -710,33 +667,22 @@ grpc_chttp2_transport::grpc_chttp2_transport(
grpc_slice_from_copied_string(GRPC_CHTTP2_CLIENT_CONNECT_STRING));
}
grpc_slice_buffer_init(&qbuf);
// copy in initial settings to all setting sets
size_t i;
int j;
for (i = 0; i < GRPC_CHTTP2_NUM_SETTINGS; i++) {
for (j = 0; j < GRPC_NUM_SETTING_SETS; j++) {
settings[j][i] = grpc_chttp2_settings_parameters[i].default_value;
}
}
grpc_chttp2_goaway_parser_init(&goaway_parser);
// configure http2 the way we like it
if (is_client) {
queue_setting_update(this, GRPC_CHTTP2_SETTINGS_ENABLE_PUSH, 0);
queue_setting_update(this, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 0);
settings.mutable_local().SetEnablePush(false);
settings.mutable_local().SetMaxConcurrentStreams(0);
}
queue_setting_update(this, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
DEFAULT_MAX_HEADER_LIST_SIZE);
queue_setting_update(this,
GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA, 1);
settings.mutable_local().SetMaxHeaderListSize(DEFAULT_MAX_HEADER_LIST_SIZE);
settings.mutable_local().SetAllowTrueBinaryMetadata(true);
read_channel_args(this, channel_args, is_client);
// Initially allow *UP TO* MAX_CONCURRENT_STREAMS incoming before we start
// blanket cancelling them.
num_incoming_streams_before_settings_ack =
settings[GRPC_LOCAL_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS];
settings.local().max_concurrent_streams();
grpc_core::ExecCtx exec_ctx;
combiner->Run(
@ -1126,9 +1072,7 @@ static void write_action(grpc_chttp2_transport* t) {
// Choose max_frame_size as the prefered rx crypto frame size indicated by the
// peer.
int max_frame_size =
t->settings
[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_GRPC_PREFERRED_RECEIVE_CRYPTO_FRAME_SIZE];
t->settings.peer().preferred_receive_crypto_message_size();
// Note: max frame size is 0 if the remote peer does not support adjusting the
// sending frame size.
if (max_frame_size == 0) {
@ -1205,23 +1149,6 @@ static void write_action_end_locked(
grpc_chttp2_end_write(t.get(), error);
}
// Dirties an HTTP2 setting to be sent out next time a writing path occurs.
// If the change needs to occur immediately, manually initiate a write.
static void queue_setting_update(grpc_chttp2_transport* t,
grpc_chttp2_setting_id id, uint32_t value) {
const grpc_chttp2_setting_parameters* sp =
&grpc_chttp2_settings_parameters[id];
uint32_t use_value = grpc_core::Clamp(value, sp->min_value, sp->max_value);
if (use_value != value) {
gpr_log(GPR_INFO, "Requested parameter %s clamped from %d to %d", sp->name,
value, use_value);
}
if (use_value != t->settings[GRPC_LOCAL_SETTINGS][id]) {
t->settings[GRPC_LOCAL_SETTINGS][id] = use_value;
t->dirtied_local_settings = true;
}
}
// Cancel out streams that haven't yet started if we have received a GOAWAY
static void cancel_unstarted_streams(grpc_chttp2_transport* t,
grpc_error_handle error, bool tarpit) {
@ -1320,9 +1247,7 @@ static void maybe_start_some_streams(grpc_chttp2_transport* t) {
// start streams where we have free grpc_chttp2_stream ids and free
// * concurrency
while (t->next_stream_id <= MAX_CLIENT_STREAM_ID &&
t->stream_map.size() <
t->settings[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] &&
t->stream_map.size() < t->settings.peer().max_concurrent_streams() &&
grpc_chttp2_list_pop_waiting_for_concurrency(t, &s)) {
// safe since we can't (legally) be parsing this stream yet
GRPC_CHTTP2_IF_TRACING(gpr_log(
@ -2707,21 +2632,19 @@ void grpc_chttp2_act_on_flowctl_action(
GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL, []() {});
WithUrgency(t, action.send_initial_window_update(),
GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS, [t, &action]() {
queue_setting_update(t,
GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE,
action.initial_window_size());
});
WithUrgency(t, action.send_max_frame_size_update(),
GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS, [t, &action]() {
queue_setting_update(t, GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE,
action.max_frame_size());
t->settings.mutable_local().SetInitialWindowSize(
action.initial_window_size());
});
WithUrgency(
t, action.send_max_frame_size_update(),
GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS, [t, &action]() {
t->settings.mutable_local().SetMaxFrameSize(action.max_frame_size());
});
if (t->enable_preferred_rx_crypto_frame_advertisement) {
WithUrgency(
t, action.preferred_rx_crypto_frame_size_update(),
GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS, [t, &action]() {
queue_setting_update(
t, GRPC_CHTTP2_SETTINGS_GRPC_PREFERRED_RECEIVE_CRYPTO_FRAME_SIZE,
t->settings.mutable_local().SetPreferredReceiveCryptoMessageSize(
action.preferred_rx_crypto_frame_size());
});
}

@ -230,18 +230,14 @@ TransportFlowControl::TargetInitialWindowSizeBasedOnMemoryPressureAndBdp()
}
void TransportFlowControl::UpdateSetting(
grpc_chttp2_setting_id id, int64_t* desired_value,
uint32_t new_desired_value, FlowControlAction* action,
absl::string_view name, int64_t* desired_value, uint32_t new_desired_value,
FlowControlAction* action,
FlowControlAction& (FlowControlAction::*set)(FlowControlAction::Urgency,
uint32_t)) {
new_desired_value =
Clamp(new_desired_value, grpc_chttp2_settings_parameters[id].min_value,
grpc_chttp2_settings_parameters[id].max_value);
if (new_desired_value != *desired_value) {
if (grpc_flowctl_trace.enabled()) {
gpr_log(GPR_INFO, "[flowctl] UPDATE SETTING %s from %" PRId64 " to %d",
grpc_chttp2_settings_parameters[id].name, *desired_value,
new_desired_value);
std::string(name).c_str(), *desired_value, new_desired_value);
}
// Reaching zero can only happen for initial window size, and if it occurs
// we really want to wake up writes and ensure all the queued stream
@ -290,13 +286,15 @@ FlowControlAction TransportFlowControl::PeriodicUpdate() {
}
// Though initial window 'could' drop to 0, we keep the floor at
// kMinInitialWindowSize
UpdateSetting(GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE,
&target_initial_window_size_, target, &action,
&FlowControlAction::set_send_initial_window_update);
UpdateSetting(Http2Settings::initial_window_size_name(),
&target_initial_window_size_,
std::min(target, Http2Settings::max_initial_window_size()),
&action, &FlowControlAction::set_send_initial_window_update);
// we target the max of BDP or bandwidth in microseconds.
UpdateSetting(GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE, &target_frame_size_,
target, &action,
&FlowControlAction::set_send_max_frame_size_update);
UpdateSetting(Http2Settings::max_frame_size_name(), &target_frame_size_,
Clamp(target, Http2Settings::min_max_frame_size(),
Http2Settings::max_max_frame_size()),
&action, &FlowControlAction::set_send_max_frame_size_update);
if (IsTcpFrameSizeTuningEnabled()) {
// Advertise PREFERRED_RECEIVE_CRYPTO_FRAME_SIZE to peer. By advertising
@ -306,10 +304,11 @@ FlowControlAction TransportFlowControl::PeriodicUpdate() {
// Clamp(target_frame_size_ * 2, 16384, 0x7fffffff). In the future, this
// maybe updated to a different function of the memory pressure.
UpdateSetting(
GRPC_CHTTP2_SETTINGS_GRPC_PREFERRED_RECEIVE_CRYPTO_FRAME_SIZE,
Http2Settings::preferred_receive_crypto_message_size_name(),
&target_preferred_rx_crypto_frame_size_,
Clamp(static_cast<unsigned int>(target_frame_size_ * 2), 16384u,
0x7ffffffu),
Clamp(static_cast<unsigned int>(target_frame_size_ * 2),
Http2Settings::min_preferred_receive_crypto_message_size(),
Http2Settings::max_preferred_receive_crypto_message_size()),
&action,
&FlowControlAction::set_preferred_rx_crypto_frame_size_update);
}

@ -330,7 +330,7 @@ class TransportFlowControl final {
private:
double TargetInitialWindowSizeBasedOnMemoryPressureAndBdp() const;
static void UpdateSetting(grpc_chttp2_setting_id id, int64_t* desired_value,
static void UpdateSetting(absl::string_view name, int64_t* desired_value,
uint32_t new_desired_value,
FlowControlAction* action,
FlowControlAction& (FlowControlAction::*set)(

@ -102,6 +102,8 @@ struct Http2RstStreamFrame {
// SETTINGS frame
struct Http2SettingsFrame {
struct Setting {
Setting(uint16_t id, uint32_t value) : id(id), value(value) {}
uint16_t id;
uint32_t value;

@ -33,6 +33,7 @@
#include "src/core/ext/transport/chttp2/transport/flow_control.h"
#include "src/core/ext/transport/chttp2/transport/frame_goaway.h"
#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"
@ -55,38 +56,6 @@ static uint8_t* fill_header(uint8_t* out, uint32_t length, uint8_t flags) {
return out;
}
grpc_slice grpc_chttp2_settings_create(uint32_t* old_settings,
const uint32_t* new_settings,
uint32_t force_mask, size_t count) {
size_t i;
uint32_t n = 0;
grpc_slice output;
uint8_t* p;
for (i = 0; i < count; i++) {
n += (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0);
}
output = GRPC_SLICE_MALLOC(9 + 6 * n);
p = fill_header(GRPC_SLICE_START_PTR(output), 6 * n, 0);
for (i = 0; i < count; i++) {
if (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0) {
*p++ = static_cast<uint8_t>(grpc_setting_id_to_wire_id[i] >> 8);
*p++ = static_cast<uint8_t>(grpc_setting_id_to_wire_id[i]);
*p++ = static_cast<uint8_t>(new_settings[i] >> 24);
*p++ = static_cast<uint8_t>(new_settings[i] >> 16);
*p++ = static_cast<uint8_t>(new_settings[i] >> 8);
*p++ = static_cast<uint8_t>(new_settings[i]);
old_settings[i] = new_settings[i];
}
}
GPR_ASSERT(p == GRPC_SLICE_END_PTR(output));
return output;
}
grpc_slice grpc_chttp2_settings_ack_create(void) {
grpc_slice output = GRPC_SLICE_MALLOC(9);
fill_header(GRPC_SLICE_START_PTR(output), 0, GRPC_CHTTP2_FLAG_ACK);
@ -95,10 +64,9 @@ grpc_slice grpc_chttp2_settings_ack_create(void) {
grpc_error_handle grpc_chttp2_settings_parser_begin_frame(
grpc_chttp2_settings_parser* parser, uint32_t length, uint8_t flags,
uint32_t* settings) {
parser->target_settings = settings;
memcpy(parser->incoming_settings, settings,
GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
grpc_core::Http2Settings& settings) {
parser->target_settings = &settings;
parser->incoming_settings.Init(settings);
parser->is_ack = 0;
parser->state = GRPC_CHTTP2_SPS_ID0;
if (flags == GRPC_CHTTP2_FLAG_ACK) {
@ -125,7 +93,6 @@ grpc_error_handle grpc_chttp2_settings_parser_parse(void* p,
static_cast<grpc_chttp2_settings_parser*>(p);
const uint8_t* cur = GRPC_SLICE_START_PTR(slice);
const uint8_t* end = GRPC_SLICE_END_PTR(slice);
grpc_chttp2_setting_id id;
if (parser->is_ack) {
return absl::OkStatus();
@ -137,8 +104,7 @@ grpc_error_handle grpc_chttp2_settings_parser_parse(void* p,
if (cur == end) {
parser->state = GRPC_CHTTP2_SPS_ID0;
if (is_last) {
memcpy(parser->target_settings, parser->incoming_settings,
GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
*parser->target_settings = *parser->incoming_settings;
t->num_pending_induced_frames++;
grpc_slice_buffer_add(&t->qbuf, grpc_chttp2_settings_ack_create());
grpc_chttp2_initiate_write(t,
@ -187,7 +153,7 @@ grpc_error_handle grpc_chttp2_settings_parser_parse(void* p,
parser->value |= (static_cast<uint32_t>(*cur)) << 8;
cur++;
ABSL_FALLTHROUGH_INTENDED;
case GRPC_CHTTP2_SPS_VAL3:
case GRPC_CHTTP2_SPS_VAL3: {
if (cur == end) {
parser->state = GRPC_CHTTP2_SPS_VAL3;
return absl::OkStatus();
@ -197,47 +163,35 @@ grpc_error_handle grpc_chttp2_settings_parser_parse(void* p,
parser->value |= *cur;
cur++;
if (grpc_wire_id_to_setting_id(parser->id, &id)) {
const grpc_chttp2_setting_parameters* sp =
&grpc_chttp2_settings_parameters[id];
if (parser->value < sp->min_value || parser->value > sp->max_value) {
switch (sp->invalid_value_behavior) {
case GRPC_CHTTP2_CLAMP_INVALID_VALUE:
parser->value = grpc_core::Clamp(parser->value, sp->min_value,
sp->max_value);
break;
case GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE:
grpc_chttp2_goaway_append(
t->last_new_stream_id, sp->error_value,
grpc_slice_from_static_string("HTTP2 settings error"),
&t->qbuf);
return GRPC_ERROR_CREATE(absl::StrFormat(
"invalid value %u passed for %s", parser->value, sp->name));
}
}
if (id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE &&
parser->incoming_settings[id] != parser->value) {
t->initial_window_update += static_cast<int64_t>(parser->value) -
parser->incoming_settings[id];
if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
GRPC_TRACE_FLAG_ENABLED(grpc_flowctl_trace)) {
gpr_log(GPR_INFO, "%p[%s] adding %d for initial_window change", t,
t->is_client ? "cli" : "svr",
static_cast<int>(t->initial_window_update));
}
}
parser->incoming_settings[id] = parser->value;
if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
gpr_log(GPR_INFO, "CHTTP2:%s:%s: got setting %s = %d",
t->is_client ? "CLI" : "SVR",
std::string(t->peer_string.as_string_view()).c_str(),
sp->name, parser->value);
if (parser->id == grpc_core::Http2Settings::kInitialWindowSizeWireId) {
t->initial_window_update +=
static_cast<int64_t>(parser->value) -
parser->incoming_settings->initial_window_size();
if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
GRPC_TRACE_FLAG_ENABLED(grpc_flowctl_trace)) {
gpr_log(GPR_INFO, "%p[%s] adding %d for initial_window change", t,
t->is_client ? "cli" : "svr",
static_cast<int>(t->initial_window_update));
}
} else if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
gpr_log(GPR_DEBUG, "CHTTP2: Ignoring unknown setting %d (value %d)",
parser->id, parser->value);
}
break;
auto error =
parser->incoming_settings->Apply(parser->id, parser->value);
if (error != GRPC_HTTP2_NO_ERROR) {
grpc_chttp2_goaway_append(
t->last_new_stream_id, error,
grpc_slice_from_static_string("HTTP2 settings error"), &t->qbuf);
return GRPC_ERROR_CREATE(absl::StrFormat(
"invalid value %u passed for %s", parser->value,
grpc_core::Http2Settings::WireIdToName(parser->id).c_str()));
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
gpr_log(GPR_INFO, "CHTTP2:%s:%s: got setting %s = %d",
t->is_client ? "CLI" : "SVR",
std::string(t->peer_string.as_string_view()).c_str(),
grpc_core::Http2Settings::WireIdToName(parser->id).c_str(),
parser->value);
}
} break;
}
}
}

@ -28,6 +28,7 @@
#include "src/core/ext/transport/chttp2/transport/http2_settings.h"
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
#include "src/core/lib/gprpp/manual_constructor.h"
#include "src/core/lib/iomgr/error.h"
typedef enum {
@ -41,22 +42,18 @@ typedef enum {
struct grpc_chttp2_settings_parser {
grpc_chttp2_settings_parse_state state;
uint32_t* target_settings;
grpc_core::Http2Settings* target_settings;
grpc_core::ManualConstructor<grpc_core::Http2Settings> incoming_settings;
uint8_t is_ack;
uint16_t id;
uint32_t value;
uint32_t incoming_settings[GRPC_CHTTP2_NUM_SETTINGS];
};
// Create a settings frame by diffing old & new, and updating old to be new
grpc_slice grpc_chttp2_settings_create(uint32_t* old_settings,
const uint32_t* new_settings,
uint32_t force_mask, size_t count);
// Create an ack settings frame
grpc_slice grpc_chttp2_settings_ack_create(void);
grpc_error_handle grpc_chttp2_settings_parser_begin_frame(
grpc_chttp2_settings_parser* parser, uint32_t length, uint8_t flags,
uint32_t* settings);
grpc_core::Http2Settings& settings);
grpc_error_handle grpc_chttp2_settings_parser_parse(void* parser,
grpc_chttp2_transport* t,
grpc_chttp2_stream* s,

@ -22,43 +22,133 @@
#include "src/core/ext/transport/chttp2/transport/http2_settings.h"
#include "absl/strings/str_cat.h"
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/transport/http2_errors.h"
const uint16_t grpc_setting_id_to_wire_id[] = {1, 2, 3, 4, 5, 6, 65027, 65028};
namespace grpc_core {
void Http2Settings::Diff(
bool is_first_send, const Http2Settings& old,
absl::FunctionRef<void(uint16_t key, uint32_t value)> cb) const {
if (header_table_size_ != old.header_table_size_) {
cb(kHeaderTableSizeWireId, header_table_size_);
}
if (enable_push_ != old.enable_push_) {
cb(kEnablePushWireId, enable_push_);
}
if (max_concurrent_streams_ != old.max_concurrent_streams_) {
cb(kMaxConcurrentStreamsWireId, max_concurrent_streams_);
}
if (is_first_send || initial_window_size_ != old.initial_window_size_) {
cb(kInitialWindowSizeWireId, initial_window_size_);
}
if (max_frame_size_ != old.max_frame_size_) {
cb(kMaxFrameSizeWireId, max_frame_size_);
}
if (max_header_list_size_ != old.max_header_list_size_) {
cb(kMaxHeaderListSizeWireId, max_header_list_size_);
}
if (allow_true_binary_metadata_ != old.allow_true_binary_metadata_) {
cb(kGrpcAllowTrueBinaryMetadataWireId, allow_true_binary_metadata_);
}
if (preferred_receive_crypto_message_size_ !=
old.preferred_receive_crypto_message_size_) {
cb(kGrpcPreferredReceiveCryptoFrameSizeWireId,
preferred_receive_crypto_message_size_);
}
}
std::string Http2Settings::WireIdToName(uint16_t wire_id) {
switch (wire_id) {
case kHeaderTableSizeWireId:
return std::string(header_table_size_name());
case kEnablePushWireId:
return std::string(enable_push_name());
case kMaxConcurrentStreamsWireId:
return std::string(max_concurrent_streams_name());
case kInitialWindowSizeWireId:
return std::string(initial_window_size_name());
case kMaxFrameSizeWireId:
return std::string(max_frame_size_name());
case kMaxHeaderListSizeWireId:
return std::string(max_header_list_size_name());
case kGrpcAllowTrueBinaryMetadataWireId:
return std::string(allow_true_binary_metadata_name());
case kGrpcPreferredReceiveCryptoFrameSizeWireId:
return std::string(preferred_receive_crypto_message_size_name());
default:
return absl::StrCat("UNKNOWN (", wire_id, ")");
}
}
grpc_http2_error_code Http2Settings::Apply(uint16_t key, uint32_t value) {
switch (key) {
case kHeaderTableSizeWireId:
header_table_size_ = value;
break;
case kEnablePushWireId:
if (value > 1) return GRPC_HTTP2_PROTOCOL_ERROR;
enable_push_ = value != 0;
break;
case kMaxConcurrentStreamsWireId:
max_concurrent_streams_ = value;
break;
case kInitialWindowSizeWireId:
if (value > max_initial_window_size()) {
return GRPC_HTTP2_FLOW_CONTROL_ERROR;
}
initial_window_size_ = value;
break;
case kMaxFrameSizeWireId:
if (value < min_max_frame_size() || value > max_max_frame_size()) {
return GRPC_HTTP2_PROTOCOL_ERROR;
}
max_frame_size_ = value;
break;
case kMaxHeaderListSizeWireId:
max_header_list_size_ = std::min(value, 16777216u);
break;
case kGrpcAllowTrueBinaryMetadataWireId:
if (value > 1) return GRPC_HTTP2_PROTOCOL_ERROR;
allow_true_binary_metadata_ = value != 0;
break;
case kGrpcPreferredReceiveCryptoFrameSizeWireId:
preferred_receive_crypto_message_size_ =
Clamp(value, min_preferred_receive_crypto_message_size(),
max_preferred_receive_crypto_message_size());
break;
}
return GRPC_HTTP2_NO_ERROR;
}
bool grpc_wire_id_to_setting_id(uint32_t wire_id, grpc_chttp2_setting_id* out) {
uint32_t i = wire_id - 1;
uint32_t x = i % 256;
uint32_t y = i / 256;
uint32_t h = x;
switch (y) {
case 254:
h += 4;
absl::optional<Http2SettingsFrame> Http2SettingsManager::MaybeSendUpdate() {
switch (update_state_) {
case UpdateState::kSending:
return absl::nullopt;
case UpdateState::kIdle:
if (local_ == sent_) return absl::nullopt;
break;
case UpdateState::kFirst:
break;
}
*out = static_cast<grpc_chttp2_setting_id>(h);
return h < GPR_ARRAY_SIZE(grpc_setting_id_to_wire_id) &&
grpc_setting_id_to_wire_id[h] == wire_id;
Http2SettingsFrame frame;
local_.Diff(update_state_ == UpdateState::kFirst, sent_,
[&frame](uint16_t key, uint32_t value) {
frame.settings.emplace_back(key, value);
});
sent_ = local_;
update_state_ = UpdateState::kSending;
return frame;
}
bool Http2SettingsManager::AckLastSend() {
if (update_state_ != UpdateState::kSending) return false;
update_state_ = UpdateState::kIdle;
acked_ = sent_;
return true;
}
const grpc_chttp2_setting_parameters
grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS] = {
{"HEADER_TABLE_SIZE", 4096u, 0u, 4294967295u,
GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
{"ENABLE_PUSH", 1u, 0u, 1u, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE,
GRPC_HTTP2_PROTOCOL_ERROR},
{"MAX_CONCURRENT_STREAMS", 4294967295u, 0u, 4294967295u,
GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
{"INITIAL_WINDOW_SIZE", 65535u, 0u, 2147483647u,
GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE,
GRPC_HTTP2_FLOW_CONTROL_ERROR},
{"MAX_FRAME_SIZE", 16384u, 16384u, 16777215u,
GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
{"MAX_HEADER_LIST_SIZE", 16777216u, 0u, 16777216u,
GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
{"GRPC_ALLOW_TRUE_BINARY_METADATA", 0u, 0u, 1u,
GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
{"GRPC_PREFERRED_RECEIVE_CRYPTO_FRAME_SIZE", 0u, 16384u, 2147483647u,
GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
};
} // namespace grpc_core

@ -14,10 +14,6 @@
// limitations under the License.
//
//
// Automatically generated by tools/codegen/core/gen_settings_ids.py
//
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H
#define GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H
@ -25,38 +21,147 @@
#include <stdint.h>
typedef enum {
GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0, // wire id 1
GRPC_CHTTP2_SETTINGS_ENABLE_PUSH = 1, // wire id 2
GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 2, // wire id 3
GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 3, // wire id 4
GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE = 4, // wire id 5
GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 5, // wire id 6
GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA = 6, // wire id 65027
GRPC_CHTTP2_SETTINGS_GRPC_PREFERRED_RECEIVE_CRYPTO_FRAME_SIZE =
7, // wire id 65028
} grpc_chttp2_setting_id;
#define GRPC_CHTTP2_NUM_SETTINGS 8
extern const uint16_t grpc_setting_id_to_wire_id[];
bool grpc_wire_id_to_setting_id(uint32_t wire_id, grpc_chttp2_setting_id* out);
typedef enum {
GRPC_CHTTP2_CLAMP_INVALID_VALUE,
GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE
} grpc_chttp2_invalid_value_behavior;
typedef struct {
const char* name;
uint32_t default_value;
uint32_t min_value;
uint32_t max_value;
grpc_chttp2_invalid_value_behavior invalid_value_behavior;
uint32_t error_value;
} grpc_chttp2_setting_parameters;
extern const grpc_chttp2_setting_parameters
grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS];
#include <cstdint>
#include "absl/functional/function_ref.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/transport/http2_errors.h"
namespace grpc_core {
class Http2Settings {
public:
enum : uint16_t {
kHeaderTableSizeWireId = 1,
kEnablePushWireId = 2,
kMaxConcurrentStreamsWireId = 3,
kInitialWindowSizeWireId = 4,
kMaxFrameSizeWireId = 5,
kMaxHeaderListSizeWireId = 6,
kGrpcAllowTrueBinaryMetadataWireId = 65027,
kGrpcPreferredReceiveCryptoFrameSizeWireId = 65028,
};
void Diff(bool is_first_send, const Http2Settings& old,
absl::FunctionRef<void(uint16_t key, uint32_t value)> cb) const;
GRPC_MUST_USE_RESULT grpc_http2_error_code Apply(uint16_t key,
uint32_t value);
uint32_t header_table_size() const { return header_table_size_; }
uint32_t max_concurrent_streams() const { return max_concurrent_streams_; }
uint32_t initial_window_size() const { return initial_window_size_; }
uint32_t max_frame_size() const { return max_frame_size_; }
uint32_t max_header_list_size() const { return max_header_list_size_; }
uint32_t preferred_receive_crypto_message_size() const {
return preferred_receive_crypto_message_size_;
}
bool enable_push() const { return enable_push_; }
bool allow_true_binary_metadata() const {
return allow_true_binary_metadata_;
}
void SetHeaderTableSize(uint32_t x) { header_table_size_ = x; }
void SetMaxConcurrentStreams(uint32_t x) { max_concurrent_streams_ = x; }
void SetInitialWindowSize(uint32_t x) {
initial_window_size_ = std::min(x, max_initial_window_size());
}
void SetEnablePush(bool x) { enable_push_ = x; }
void SetMaxHeaderListSize(uint32_t x) {
max_header_list_size_ = std::min(x, 16777216u);
}
void SetAllowTrueBinaryMetadata(bool x) { allow_true_binary_metadata_ = x; }
void SetMaxFrameSize(uint32_t x) {
max_frame_size_ = Clamp(x, min_max_frame_size(), max_max_frame_size());
}
void SetPreferredReceiveCryptoMessageSize(uint32_t x) {
preferred_receive_crypto_message_size_ =
Clamp(x, min_preferred_receive_crypto_message_size(),
max_preferred_receive_crypto_message_size());
}
static absl::string_view header_table_size_name() {
return "HEADER_TABLE_SIZE";
}
static absl::string_view max_concurrent_streams_name() {
return "MAX_CONCURRENT_STREAMS";
}
static absl::string_view initial_window_size_name() {
return "INITIAL_WINDOW_SIZE";
}
static absl::string_view max_frame_size_name() { return "MAX_FRAME_SIZE"; }
static absl::string_view max_header_list_size_name() {
return "MAX_HEADER_LIST_SIZE";
}
static absl::string_view enable_push_name() { return "ENABLE_PUSH"; }
static absl::string_view allow_true_binary_metadata_name() {
return "GRPC_ALLOW_TRUE_BINARY_METADATA";
}
static absl::string_view preferred_receive_crypto_message_size_name() {
return "GRPC_PREFERRED_RECEIVE_MESSAGE_SIZE";
}
static uint32_t max_initial_window_size() { return 2147483647u; }
static uint32_t max_max_frame_size() { return 16777215u; }
static uint32_t min_max_frame_size() { return 16384u; }
static uint32_t min_preferred_receive_crypto_message_size() { return 16384u; }
static uint32_t max_preferred_receive_crypto_message_size() {
return 2147483647u;
}
static std::string WireIdToName(uint16_t wire_id);
bool operator==(const Http2Settings& rhs) const {
return header_table_size_ == rhs.header_table_size_ &&
max_concurrent_streams_ == rhs.max_concurrent_streams_ &&
initial_window_size_ == rhs.initial_window_size_ &&
max_frame_size_ == rhs.max_frame_size_ &&
max_header_list_size_ == rhs.max_header_list_size_ &&
preferred_receive_crypto_message_size_ ==
rhs.preferred_receive_crypto_message_size_ &&
enable_push_ == rhs.enable_push_ &&
allow_true_binary_metadata_ == rhs.allow_true_binary_metadata_;
}
bool operator!=(const Http2Settings& rhs) const { return !operator==(rhs); }
private:
uint32_t header_table_size_ = 4096;
uint32_t max_concurrent_streams_ = 4294967295u;
uint32_t initial_window_size_ = 65535u;
uint32_t max_frame_size_ = 16384u;
uint32_t max_header_list_size_ = 16777216u;
uint32_t preferred_receive_crypto_message_size_ = 0u;
bool enable_push_ = true;
bool allow_true_binary_metadata_ = false;
};
class Http2SettingsManager {
public:
Http2Settings& mutable_local() { return local_; }
const Http2Settings& local() const { return local_; }
const Http2Settings& acked() const { return acked_; }
Http2Settings& mutable_peer() { return peer_; }
const Http2Settings& peer() const { return peer_; }
absl::optional<Http2SettingsFrame> MaybeSendUpdate();
GRPC_MUST_USE_RESULT bool AckLastSend();
private:
enum class UpdateState : uint8_t {
kFirst,
kSending,
kIdle,
};
UpdateState update_state_ = UpdateState::kFirst;
Http2Settings local_;
Http2Settings sent_;
Http2Settings peer_;
Http2Settings acked_;
};
} // namespace grpc_core
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H

@ -203,18 +203,6 @@ struct grpc_chttp2_stream_link {
grpc_chttp2_stream* next;
grpc_chttp2_stream* prev;
};
// We keep several sets of connection wide parameters
typedef enum {
// The settings our peer has asked for (and we have acked)
GRPC_PEER_SETTINGS = 0,
// The settings we'd like to have
GRPC_LOCAL_SETTINGS,
// The settings we've published to our peer
GRPC_SENT_SETTINGS,
// The settings the peer has acked
GRPC_ACKED_SETTINGS,
GRPC_NUM_SETTING_SETS
} grpc_chttp2_setting_set;
typedef enum {
GRPC_CHTTP2_NO_GOAWAY_SEND,
@ -359,12 +347,8 @@ struct grpc_chttp2_transport final
grpc_chttp2_sent_goaway_state sent_goaway_state = GRPC_CHTTP2_NO_GOAWAY_SEND;
/// bitmask of setting indexes to send out
/// Hack: it's common for implementations to assume 65536 bytes initial send
/// window -- this should by rights be 0
uint32_t force_send_settings = 1 << GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
/// settings values
uint32_t settings[GRPC_NUM_SETTING_SETS][GRPC_CHTTP2_NUM_SETTINGS];
grpc_core::Http2SettingsManager settings;
grpc_event_engine::experimental::EventEngine::TaskHandle
settings_ack_watchdog =
@ -538,11 +522,6 @@ struct grpc_chttp2_transport final
/// is this a client?
bool is_client;
/// are the local settings dirty and need to be sent?
bool dirtied_local_settings = true;
/// have local settings been sent?
bool sent_local_settings = false;
/// If start_bdp_ping_locked has been called
bool bdp_ping_started = false;
// True if pings should be acked

@ -357,13 +357,10 @@ absl::variant<size_t, absl::Status> grpc_chttp2_perform_read(
}
goto dts_fh_0; // loop
} else if (t->incoming_frame_size >
t->settings[GRPC_ACKED_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]) {
return GRPC_ERROR_CREATE(
absl::StrFormat("Frame size %d is larger than max frame size %d",
t->incoming_frame_size,
t->settings[GRPC_ACKED_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]));
t->settings.acked().max_frame_size()) {
return GRPC_ERROR_CREATE(absl::StrFormat(
"Frame size %d is larger than max frame size %d",
t->incoming_frame_size, t->settings.acked().max_frame_size()));
}
if (++cur == end) {
return absl::OkStatus();
@ -505,8 +502,7 @@ static grpc_error_handle init_header_skip_frame_parser(
/*metadata_size_soft_limit=*/
t->max_header_list_size_soft_limit,
/*metadata_size_hard_limit=*/
t->settings[GRPC_ACKED_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE],
t->settings.acked().max_header_list_size(),
hpack_boundary_type(t, is_eoh), priority_type,
hpack_parser_log_info(t, HPackParser::LogInfo::kDontKnow));
return absl::OkStatus();
@ -646,10 +642,8 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
"ignoring grpc_chttp2_stream with non-client generated index %d",
t->incoming_stream_id));
return init_header_skip_frame_parser(t, priority_type, is_eoh);
} else if (GPR_UNLIKELY(
t->stream_map.size() + t->extra_streams >=
t->settings[GRPC_ACKED_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS])) {
} else if (GPR_UNLIKELY(t->stream_map.size() + t->extra_streams >=
t->settings.acked().max_concurrent_streams())) {
if (grpc_core::IsRfcMaxConcurrentStreamsEnabled()) {
++t->num_pending_induced_frames;
grpc_slice_buffer_add(
@ -678,8 +672,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
t->max_concurrent_streams_policy.AdvertiseValue() &&
grpc_core::RandomEarlyDetection(
t->max_concurrent_streams_policy.AdvertiseValue(),
t->settings[GRPC_ACKED_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS])
t->settings.acked().max_concurrent_streams())
.Reject(t->stream_map.size(), t->bitgen))) {
// We are under the limit of max concurrent streams for the current
// setting, but are over the next value that will be advertised.
@ -788,15 +781,13 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
return GRPC_ERROR_CREATE(
"Trailing metadata frame received without an end-o-stream");
}
t->hpack_parser.BeginFrame(
incoming_metadata_buffer,
/*metadata_size_soft_limit=*/
t->max_header_list_size_soft_limit,
/*metadata_size_hard_limit=*/
t->settings[GRPC_ACKED_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE],
hpack_boundary_type(t, is_eoh), priority_type,
hpack_parser_log_info(t, frame_type));
t->hpack_parser.BeginFrame(incoming_metadata_buffer,
/*metadata_size_soft_limit=*/
t->max_header_list_size_soft_limit,
/*metadata_size_hard_limit=*/
t->settings.acked().max_header_list_size(),
hpack_boundary_type(t, is_eoh), priority_type,
hpack_parser_log_info(t, frame_type));
return absl::OkStatus();
}
@ -867,21 +858,20 @@ static grpc_error_handle init_settings_frame_parser(grpc_chttp2_transport* t) {
grpc_error_handle err = grpc_chttp2_settings_parser_begin_frame(
&t->simple.settings, t->incoming_frame_size, t->incoming_frame_flags,
t->settings[GRPC_PEER_SETTINGS]);
t->settings.mutable_peer());
if (!err.ok()) {
return err;
}
if (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) {
t->max_concurrent_streams_policy.AckLastSend();
memcpy(t->settings[GRPC_ACKED_SETTINGS], t->settings[GRPC_SENT_SETTINGS],
GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
if (!t->settings.AckLastSend()) {
return GRPC_ERROR_CREATE("Received unexpected settings ack");
}
t->hpack_parser.hpack_table()->SetMaxBytes(
t->settings[GRPC_ACKED_SETTINGS]
[GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]);
t->settings.acked().header_table_size());
grpc_chttp2_act_on_flowctl_action(
t->flow_control.SetAckedInitialWindow(
t->settings[GRPC_ACKED_SETTINGS]
[GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]),
t->settings.acked().initial_window_size()),
t, nullptr);
if (t->settings_ack_watchdog !=
grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid) {
@ -889,7 +879,6 @@ static grpc_error_handle init_settings_frame_parser(grpc_chttp2_transport* t) {
t->settings_ack_watchdog,
grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid));
}
t->sent_local_settings = false;
// This is more streams than can be started in http2, so setting this
// effictively removes the limit for the rest of the connection.
t->num_incoming_streams_before_settings_ack =

@ -218,15 +218,12 @@ static void report_stall(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
":peer_initwin=%d:t_win=%" PRId64 ":s_win=%d:s_delta=%" PRId64 "]",
std::string(t->peer_string.as_string_view()).c_str(), t, s->id, staller,
s->flow_controlled_buffer.length, s->flow_controlled_bytes_flowed,
t->settings[GRPC_ACKED_SETTINGS]
[GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
t->settings.acked().initial_window_size(),
t->flow_control.remote_window(),
static_cast<uint32_t>(std::max(
int64_t{0},
s->flow_control.remote_window_delta() +
static_cast<int64_t>(
t->settings[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]))),
int64_t{0}, s->flow_control.remote_window_delta() +
static_cast<int64_t>(
t->settings.peer().initial_window_size()))),
s->flow_control.remote_window_delta());
}
}
@ -266,21 +263,12 @@ class WriteContext {
}
void FlushSettings() {
const bool dirty =
t_->dirtied_local_settings ||
t_->settings[GRPC_SENT_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] !=
t_->max_concurrent_streams_policy.AdvertiseValue();
if (dirty && !t_->sent_local_settings) {
t_->settings[GRPC_LOCAL_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] =
t_->max_concurrent_streams_policy.AdvertiseValue();
grpc_slice_buffer_add(
t_->outbuf.c_slice_buffer(),
grpc_chttp2_settings_create(t_->settings[GRPC_SENT_SETTINGS],
t_->settings[GRPC_LOCAL_SETTINGS],
t_->force_send_settings,
GRPC_CHTTP2_NUM_SETTINGS));
t_->settings.mutable_local().SetMaxConcurrentStreams(
t_->max_concurrent_streams_policy.AdvertiseValue());
auto update = t_->settings.MaybeSendUpdate();
if (update.has_value()) {
grpc_core::Http2Frame frame(std::move(*update));
Serialize(absl::Span<grpc_core::Http2Frame>(&frame, 1), t_->outbuf);
if (t_->keepalive_timeout != grpc_core::Duration::Infinity()) {
GPR_ASSERT(
t_->settings_ack_watchdog ==
@ -294,9 +282,6 @@ class WriteContext {
grpc_chttp2_settings_timeout(std::move(t));
});
}
t_->force_send_settings = false;
t_->dirtied_local_settings = false;
t_->sent_local_settings = true;
t_->flow_control.FlushedSettings();
t_->max_concurrent_streams_policy.FlushedSettings();
grpc_core::global_stats().IncrementHttp2SettingsWrites();
@ -336,8 +321,7 @@ class WriteContext {
void EnactHpackSettings() {
t_->hpack_compressor.SetMaxTableSize(
t_->settings[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]);
t_->settings.peer().header_table_size());
}
void UpdateStreamsNoLongerStalled() {
@ -410,17 +394,14 @@ class DataSendContext {
return static_cast<uint32_t>(std::max(
int64_t{0},
s_->flow_control.remote_window_delta() +
static_cast<int64_t>(
t_->settings[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE])));
static_cast<int64_t>(t_->settings.peer().initial_window_size())));
}
uint32_t max_outgoing() const {
return grpc_core::Clamp<uint32_t>(
std::min<int64_t>(
{t_->settings[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE],
stream_remote_window(), t_->flow_control.remote_window(),
{t_->settings.peer().max_frame_size(), stream_remote_window(),
t_->flow_control.remote_window(),
grpc_core::IsWriteSizeCapEnabled()
? static_cast<int64_t>(write_context_->target_write_size())
: std::numeric_limits<uint32_t>::max()}),
@ -494,14 +475,10 @@ class StreamWriteContext {
grpc_core::HPackCompressor::EncodeHeaderOptions{
s_->id, // stream_id
false, // is_eof
t_->settings
[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] !=
0, // use_true_binary_metadata
t_->settings
[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], // max_frame_size
&s_->stats.outgoing // stats
t_->settings.peer()
.allow_true_binary_metadata(), // use_true_binary_metadata
t_->settings.peer().max_frame_size(), // max_frame_size
&s_->stats.outgoing // stats
},
*s_->send_initial_metadata, t_->outbuf.c_slice_buffer());
grpc_chttp2_reset_ping_clock(t_);
@ -596,14 +573,8 @@ class StreamWriteContext {
}
t_->hpack_compressor.EncodeHeaders(
grpc_core::HPackCompressor::EncodeHeaderOptions{
s_->id, true,
t_->settings
[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] !=
0,
t_->settings[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE],
&s_->stats.outgoing},
s_->id, true, t_->settings.peer().allow_true_binary_metadata(),
t_->settings.peer().max_frame_size(), &s_->stats.outgoing},
*s_->send_trailing_metadata, t_->outbuf.c_slice_buffer());
}
write_context_->IncTrailingMetadataWrites();

@ -107,6 +107,7 @@ CORE_SOURCE_FILES = [
'src/core/ext/transport/chttp2/transport/chttp2_transport.cc',
'src/core/ext/transport/chttp2/transport/decode_huff.cc',
'src/core/ext/transport/chttp2/transport/flow_control.cc',
'src/core/ext/transport/chttp2/transport/frame.cc',
'src/core/ext/transport/chttp2/transport/frame_data.cc',
'src/core/ext/transport/chttp2/transport/frame_goaway.cc',
'src/core/ext/transport/chttp2/transport/frame_ping.cc',

@ -446,3 +446,20 @@ grpc_cc_test(
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "http2_settings_test",
srcs = [
"http2_settings_test.cc",
],
external_deps = [
"gtest",
],
language = "C++",
deps = [
"//:gpr",
"//:grpc",
"//src/core:http2_settings",
"//test/core/util:grpc_test_util",
],
)

@ -0,0 +1,514 @@
// Copyright 2024 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/http2_settings.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace grpc_core {
TEST(Http2SettingsTest, CanSetAndRetrieveSettings) {
Http2Settings settings;
settings.SetHeaderTableSize(1);
settings.SetEnablePush(true);
settings.SetMaxConcurrentStreams(3);
settings.SetInitialWindowSize(4);
settings.SetMaxFrameSize(50000);
settings.SetMaxHeaderListSize(6);
settings.SetAllowTrueBinaryMetadata(true);
settings.SetPreferredReceiveCryptoMessageSize(77777);
EXPECT_EQ(settings.header_table_size(), 1u);
EXPECT_EQ(settings.enable_push(), true);
EXPECT_EQ(settings.max_concurrent_streams(), 3u);
EXPECT_EQ(settings.initial_window_size(), 4u);
EXPECT_EQ(settings.max_frame_size(), 50000u);
EXPECT_EQ(settings.max_header_list_size(), 6u);
EXPECT_EQ(settings.allow_true_binary_metadata(), true);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 77777u);
settings.SetHeaderTableSize(10);
settings.SetEnablePush(false);
settings.SetMaxConcurrentStreams(30);
settings.SetInitialWindowSize(40);
settings.SetMaxFrameSize(5000000);
settings.SetMaxHeaderListSize(60);
settings.SetAllowTrueBinaryMetadata(false);
settings.SetPreferredReceiveCryptoMessageSize(70000);
EXPECT_EQ(settings.header_table_size(), 10u);
EXPECT_EQ(settings.enable_push(), false);
EXPECT_EQ(settings.max_concurrent_streams(), 30u);
EXPECT_EQ(settings.initial_window_size(), 40u);
EXPECT_EQ(settings.max_frame_size(), 5000000u);
EXPECT_EQ(settings.max_header_list_size(), 60u);
EXPECT_EQ(settings.allow_true_binary_metadata(), false);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 70000u);
}
TEST(Http2SettingsTest, InitialWindowSizeLimits) {
Http2Settings settings;
settings.SetInitialWindowSize(0);
EXPECT_EQ(settings.initial_window_size(), 0u);
settings.SetInitialWindowSize(0x7fffffff);
EXPECT_EQ(settings.initial_window_size(), 0x7fffffffu);
settings.SetInitialWindowSize(0x80000000);
EXPECT_EQ(settings.initial_window_size(), 0x7fffffffu);
settings.SetInitialWindowSize(0xffffffff);
EXPECT_EQ(settings.initial_window_size(), 0x7fffffffu);
}
TEST(Http2SettingsTest, MaxFrameSizeLimits) {
Http2Settings settings;
settings.SetMaxFrameSize(0);
EXPECT_EQ(settings.max_frame_size(), 16384u);
settings.SetMaxFrameSize(16384);
EXPECT_EQ(settings.max_frame_size(), 16384u);
settings.SetMaxFrameSize(16385);
EXPECT_EQ(settings.max_frame_size(), 16385u);
settings.SetMaxFrameSize(16777215);
EXPECT_EQ(settings.max_frame_size(), 16777215u);
settings.SetMaxFrameSize(16777216);
EXPECT_EQ(settings.max_frame_size(), 16777215u);
settings.SetMaxFrameSize(16777217);
EXPECT_EQ(settings.max_frame_size(), 16777215u);
settings.SetMaxFrameSize(0xffffffff);
EXPECT_EQ(settings.max_frame_size(), 16777215u);
}
TEST(Http2SettingsTest, PreferredReceiveCryptoMessageSize) {
Http2Settings settings;
settings.SetPreferredReceiveCryptoMessageSize(0);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 16384u);
settings.SetPreferredReceiveCryptoMessageSize(16384);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 16384u);
settings.SetPreferredReceiveCryptoMessageSize(16385);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 16385u);
settings.SetPreferredReceiveCryptoMessageSize(16777215);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 16777215u);
settings.SetPreferredReceiveCryptoMessageSize(16777216);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 16777216u);
settings.SetPreferredReceiveCryptoMessageSize(16777217);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 16777217u);
settings.SetPreferredReceiveCryptoMessageSize(0x7fffffff);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 0x7fffffffu);
settings.SetPreferredReceiveCryptoMessageSize(0x80000000);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 0x7fffffffu);
settings.SetPreferredReceiveCryptoMessageSize(0xffffffff);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 0x7fffffffu);
}
namespace {
using KeyValue = std::pair<uint16_t, uint32_t>;
using KeyValueVec = std::vector<KeyValue>;
KeyValueVec Diff(const Http2Settings& a, const Http2Settings& b,
bool is_first_send) {
KeyValueVec diffs;
a.Diff(is_first_send, b, [&diffs](uint16_t key, uint32_t value) {
diffs.emplace_back(key, value);
});
return diffs;
}
bool operator==(const KeyValue& a, const Http2SettingsFrame::Setting& b) {
return a.first == b.id && a.second == b.value;
}
} // namespace
TEST(Http2SettingsTest, DiffOnFreshlyInitializedSettings) {
const Http2Settings settings1;
const Http2Settings settings2;
EXPECT_THAT(Diff(settings1, settings2, false), ::testing::IsEmpty());
EXPECT_THAT(Diff(settings1, settings2, true),
::testing::UnorderedElementsAre(KeyValue{4, 65535}));
}
TEST(Http2SettingsTest, DiffOnSettingsWithOneValueSet) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetHeaderTableSize(1);
EXPECT_THAT(Diff(settings1, settings2, false),
::testing::UnorderedElementsAre(KeyValue{1, 1}));
EXPECT_THAT(
Diff(settings1, settings2, true),
::testing::UnorderedElementsAre(KeyValue{1, 1}, KeyValue{4, 65535}));
}
TEST(Http2SettingsTest, DiffOnSettingsWithTwoValuesSet) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetHeaderTableSize(1);
settings1.SetEnablePush(false);
EXPECT_THAT(Diff(settings1, settings2, false),
::testing::UnorderedElementsAre(KeyValue{1, 1}, KeyValue{2, 0}));
EXPECT_THAT(Diff(settings1, settings2, true),
::testing::UnorderedElementsAre(KeyValue{1, 1}, KeyValue{2, 0},
KeyValue{4, 65535}));
}
TEST(Http2SettingsTest, DiffOnSettingsWithThreeValuesSet) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetHeaderTableSize(1);
settings1.SetEnablePush(false);
settings1.SetMaxConcurrentStreams(3);
EXPECT_THAT(Diff(settings1, settings2, false),
::testing::UnorderedElementsAre(KeyValue{1, 1}, KeyValue{2, 0},
KeyValue{3, 3}));
EXPECT_THAT(
Diff(settings1, settings2, true),
::testing::UnorderedElementsAre(KeyValue{1, 1}, KeyValue{2, 0},
KeyValue{3, 3}, KeyValue{4, 65535}));
}
TEST(Http2SettingsTest, DiffOnSettingsWithFourValuesSet) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetHeaderTableSize(1);
settings1.SetEnablePush(false);
settings1.SetMaxConcurrentStreams(3);
settings1.SetInitialWindowSize(4);
EXPECT_THAT(Diff(settings1, settings2, false),
::testing::UnorderedElementsAre(KeyValue{1, 1}, KeyValue{2, 0},
KeyValue{3, 3}, KeyValue{4, 4}));
EXPECT_THAT(Diff(settings1, settings2, true),
::testing::UnorderedElementsAre(KeyValue{1, 1}, KeyValue{2, 0},
KeyValue{3, 3}, KeyValue{4, 4}));
}
TEST(Http2SettingsTest, DiffOnSettingsWithFiveValuesSet) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetHeaderTableSize(1);
settings1.SetEnablePush(false);
settings1.SetMaxConcurrentStreams(3);
settings1.SetInitialWindowSize(4);
settings1.SetMaxFrameSize(50000);
EXPECT_THAT(Diff(settings1, settings2, false),
::testing::UnorderedElementsAre(KeyValue{1, 1}, KeyValue{2, 0},
KeyValue{3, 3}, KeyValue{4, 4},
KeyValue{5, 50000}));
EXPECT_THAT(Diff(settings1, settings2, true),
::testing::UnorderedElementsAre(KeyValue{1, 1}, KeyValue{2, 0},
KeyValue{3, 3}, KeyValue{4, 4},
KeyValue{5, 50000}));
}
TEST(Http2SettingsTest, DiffOnSettingsWithSixValuesSet) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetHeaderTableSize(1);
settings1.SetEnablePush(false);
settings1.SetMaxConcurrentStreams(3);
settings1.SetInitialWindowSize(4);
settings1.SetMaxFrameSize(50000);
settings1.SetMaxHeaderListSize(6);
EXPECT_THAT(Diff(settings1, settings2, false),
::testing::UnorderedElementsAre(
KeyValue{1, 1}, KeyValue{2, 0}, KeyValue{3, 3},
KeyValue{4, 4}, KeyValue{5, 50000}, KeyValue{6, 6}));
EXPECT_THAT(Diff(settings1, settings2, true),
::testing::UnorderedElementsAre(
KeyValue{1, 1}, KeyValue{2, 0}, KeyValue{3, 3},
KeyValue{4, 4}, KeyValue{5, 50000}, KeyValue{6, 6}));
}
TEST(Http2SettingsTest, DiffOnSettingsWithSevenValuesSet) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetHeaderTableSize(1);
settings1.SetEnablePush(false);
settings1.SetMaxConcurrentStreams(3);
settings1.SetInitialWindowSize(4);
settings1.SetMaxFrameSize(50000);
settings1.SetMaxHeaderListSize(6);
settings1.SetAllowTrueBinaryMetadata(true);
EXPECT_THAT(
Diff(settings1, settings2, false),
::testing::UnorderedElementsAre(
KeyValue{1, 1}, KeyValue{2, 0}, KeyValue{3, 3}, KeyValue{4, 4},
KeyValue{5, 50000}, KeyValue{6, 6}, KeyValue{65027, 1}));
EXPECT_THAT(
Diff(settings1, settings2, true),
::testing::UnorderedElementsAre(
KeyValue{1, 1}, KeyValue{2, 0}, KeyValue{3, 3}, KeyValue{4, 4},
KeyValue{5, 50000}, KeyValue{6, 6}, KeyValue{65027, 1}));
}
TEST(Http2SettingsTest, DiffOnSettingsWithEightValuesSet) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetHeaderTableSize(1);
settings1.SetEnablePush(false);
settings1.SetMaxConcurrentStreams(3);
settings1.SetInitialWindowSize(4);
settings1.SetMaxFrameSize(50000);
settings1.SetMaxHeaderListSize(6);
settings1.SetAllowTrueBinaryMetadata(true);
settings1.SetPreferredReceiveCryptoMessageSize(77777);
EXPECT_THAT(Diff(settings1, settings2, false),
::testing::UnorderedElementsAre(
KeyValue{1, 1}, KeyValue{2, 0}, KeyValue{3, 3},
KeyValue{4, 4}, KeyValue{5, 50000}, KeyValue{6, 6},
KeyValue{65027, 1}, KeyValue{65028, 77777}));
EXPECT_THAT(Diff(settings1, settings2, true),
::testing::UnorderedElementsAre(
KeyValue{1, 1}, KeyValue{2, 0}, KeyValue{3, 3},
KeyValue{4, 4}, KeyValue{5, 50000}, KeyValue{6, 6},
KeyValue{65027, 1}, KeyValue{65028, 77777}));
}
TEST(Http2SettingsTest, ChangingHeaderTableSizeChangesEquality) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetHeaderTableSize(1);
EXPECT_NE(settings1, settings2);
settings2.SetHeaderTableSize(1);
EXPECT_EQ(settings1, settings2);
settings2.SetHeaderTableSize(2);
EXPECT_NE(settings1, settings2);
}
TEST(Http2SettingsTest, ChangingEnablePushChangesEquality) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetEnablePush(false);
EXPECT_NE(settings1, settings2);
settings2.SetEnablePush(false);
EXPECT_EQ(settings1, settings2);
settings2.SetEnablePush(true);
EXPECT_NE(settings1, settings2);
}
TEST(Http2SettingsTest, ChangingMaxConcurrentStreamsChangesEquality) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetMaxConcurrentStreams(1);
EXPECT_NE(settings1, settings2);
settings2.SetMaxConcurrentStreams(1);
EXPECT_EQ(settings1, settings2);
settings2.SetMaxConcurrentStreams(2);
EXPECT_NE(settings1, settings2);
}
TEST(Http2SettingsTest, ChangingInitialWindowSizeChangesEquality) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetInitialWindowSize(1);
EXPECT_NE(settings1, settings2);
settings2.SetInitialWindowSize(1);
EXPECT_EQ(settings1, settings2);
settings2.SetInitialWindowSize(2);
EXPECT_NE(settings1, settings2);
}
TEST(Http2SettingsTest, ChangingMaxFrameSizeChangesEquality) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetMaxFrameSize(100000);
EXPECT_NE(settings1, settings2);
settings2.SetMaxFrameSize(100000);
EXPECT_EQ(settings1, settings2);
settings2.SetMaxFrameSize(200000);
EXPECT_NE(settings1, settings2);
}
TEST(Http2SettingsTest, ChangingMaxHeaderListSizeChangesEquality) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetMaxHeaderListSize(1);
EXPECT_NE(settings1, settings2);
settings2.SetMaxHeaderListSize(1);
EXPECT_EQ(settings1, settings2);
settings2.SetMaxHeaderListSize(2);
EXPECT_NE(settings1, settings2);
}
TEST(Http2SettingsTest, ChangingAllowTrueBinaryMetadataChangesEquality) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetAllowTrueBinaryMetadata(true);
EXPECT_NE(settings1, settings2);
settings2.SetAllowTrueBinaryMetadata(true);
EXPECT_EQ(settings1, settings2);
settings2.SetAllowTrueBinaryMetadata(false);
EXPECT_NE(settings1, settings2);
}
TEST(Http2SettingsTest,
ChangingPreferredReceiveCryptoMessageSizeChangesEquality) {
Http2Settings settings1;
Http2Settings settings2;
settings1.SetPreferredReceiveCryptoMessageSize(100000);
EXPECT_NE(settings1, settings2);
settings2.SetPreferredReceiveCryptoMessageSize(100000);
EXPECT_EQ(settings1, settings2);
settings2.SetPreferredReceiveCryptoMessageSize(200000);
EXPECT_NE(settings1, settings2);
}
TEST(Http2SettingsTest, WireIdToNameWorks) {
EXPECT_EQ(Http2Settings::WireIdToName(1), "HEADER_TABLE_SIZE");
EXPECT_EQ(Http2Settings::WireIdToName(2), "ENABLE_PUSH");
EXPECT_EQ(Http2Settings::WireIdToName(3), "MAX_CONCURRENT_STREAMS");
EXPECT_EQ(Http2Settings::WireIdToName(4), "INITIAL_WINDOW_SIZE");
EXPECT_EQ(Http2Settings::WireIdToName(5), "MAX_FRAME_SIZE");
EXPECT_EQ(Http2Settings::WireIdToName(6), "MAX_HEADER_LIST_SIZE");
EXPECT_EQ(Http2Settings::WireIdToName(65027),
"GRPC_ALLOW_TRUE_BINARY_METADATA");
EXPECT_EQ(Http2Settings::WireIdToName(65028),
"GRPC_PREFERRED_RECEIVE_MESSAGE_SIZE");
EXPECT_EQ(Http2Settings::WireIdToName(65029), "UNKNOWN (65029)");
}
TEST(Http2SettingsTest, ApplyHeaderTableSizeWorks) {
Http2Settings settings;
EXPECT_EQ(settings.Apply(1, 1), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.header_table_size(), 1u);
EXPECT_EQ(settings.Apply(1, 0x7fffffff), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.header_table_size(), 0x7fffffffu);
}
TEST(Http2SettingsTest, ApplyEnablePushWorks) {
Http2Settings settings;
EXPECT_EQ(settings.Apply(2, 0), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.enable_push(), false);
EXPECT_EQ(settings.Apply(2, 1), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.enable_push(), true);
EXPECT_EQ(settings.Apply(2, 2), GRPC_HTTP2_PROTOCOL_ERROR);
}
TEST(Http2SettingsTest, ApplyMaxConcurrentStreamsWorks) {
Http2Settings settings;
EXPECT_EQ(settings.Apply(3, 1), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.max_concurrent_streams(), 1u);
EXPECT_EQ(settings.Apply(3, 0x7fffffff), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.max_concurrent_streams(), 0x7fffffffu);
}
TEST(Http2SettingsTest, ApplyInitialWindowSizeWorks) {
Http2Settings settings;
EXPECT_EQ(settings.Apply(4, 1), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.initial_window_size(), 1u);
EXPECT_EQ(settings.Apply(4, 0x7fffffff), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.initial_window_size(), 0x7fffffffu);
}
TEST(Http2SettingsTest, ApplyMaxFrameSizeWorks) {
Http2Settings settings;
EXPECT_EQ(settings.Apply(5, 16384), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.max_frame_size(), 16384u);
EXPECT_EQ(settings.Apply(5, 16777215), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.max_frame_size(), 16777215);
EXPECT_EQ(settings.Apply(5, 16383), GRPC_HTTP2_PROTOCOL_ERROR);
EXPECT_EQ(settings.Apply(5, 16777216), GRPC_HTTP2_PROTOCOL_ERROR);
}
TEST(Http2SettingsTest, ApplyMaxHeaderListSizeWorks) {
Http2Settings settings;
EXPECT_EQ(settings.Apply(6, 1), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.max_header_list_size(), 1u);
EXPECT_EQ(settings.Apply(6, 0x7fffffff), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.max_header_list_size(), 16777216);
}
TEST(Http2SettingsTest, ApplyAllowTrueBinaryMetadataWorks) {
Http2Settings settings;
EXPECT_EQ(settings.Apply(65027, 0), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.allow_true_binary_metadata(), false);
EXPECT_EQ(settings.Apply(65027, 1), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.allow_true_binary_metadata(), true);
EXPECT_EQ(settings.Apply(65027, 2), GRPC_HTTP2_PROTOCOL_ERROR);
}
TEST(Http2SettingsTest, ApplyPreferredReceiveCryptoMessageSizeWorks) {
Http2Settings settings;
EXPECT_EQ(settings.Apply(65028, 1), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 16384u);
EXPECT_EQ(settings.Apply(65028, 0x7fffffff), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 0x7fffffffu);
EXPECT_EQ(settings.Apply(65028, 0x80000000), GRPC_HTTP2_NO_ERROR);
EXPECT_EQ(settings.preferred_receive_crypto_message_size(), 0x7fffffffu);
}
namespace {
MATCHER_P(SettingsFrame, settings, "") {
if (!arg.has_value()) {
*result_listener << "Expected a settings frame, got nothing";
return false;
}
if (arg->ack) {
*result_listener << "Expected a settings frame, got an ack";
return false;
}
if (arg->settings.size() != settings.size()) {
*result_listener << "Expected settings frame with " << settings.size()
<< " settings, got " << arg->settings.size();
return false;
}
for (size_t i = 0; i < settings.size(); i++) {
bool found = false;
for (size_t j = 0; j < arg->settings.size(); j++) {
if (settings[i] == arg->settings[j]) {
found = true;
break;
}
}
if (!found) {
*result_listener << "Expected settings frame with setting "
<< settings[i].first << " = " << settings[i].second
<< ", but it was not found";
return false;
}
}
return true;
}
} // namespace
TEST(Http2SettingsManagerTest, ImmediatelyNeedsToSend) {
Http2SettingsManager settings_manager;
EXPECT_THAT(settings_manager.MaybeSendUpdate(),
SettingsFrame(KeyValueVec{{4, 65535}}));
}
TEST(Http2SettingsManagerTest, SendAckWorks) {
Http2SettingsManager settings_manager;
settings_manager.mutable_local().SetInitialWindowSize(100000);
EXPECT_EQ(settings_manager.acked().initial_window_size(), 65535u);
EXPECT_THAT(settings_manager.MaybeSendUpdate(),
SettingsFrame(KeyValueVec{{4, 100000}}));
EXPECT_TRUE(settings_manager.AckLastSend());
EXPECT_EQ(settings_manager.acked().initial_window_size(), 100000u);
}
TEST(Http2SettingsManagerTest, AckWithoutSendFails) {
Http2SettingsManager settings_manager;
EXPECT_FALSE(settings_manager.AckLastSend());
}
TEST(Http2SettingsManagerTest, AckAfterAckFails) {
Http2SettingsManager settings_manager;
settings_manager.mutable_local().SetInitialWindowSize(100000);
EXPECT_THAT(settings_manager.MaybeSendUpdate(),
SettingsFrame(KeyValueVec{{4, 100000}}));
EXPECT_TRUE(settings_manager.AckLastSend());
EXPECT_FALSE(settings_manager.AckLastSend());
}
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -1,270 +0,0 @@
#!/usr/bin/env python3
# Copyright 2017 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.
from __future__ import print_function
import collections
import sys
import perfection
_MAX_HEADER_LIST_SIZE = 16 * 1024 * 1024
Setting = collections.namedtuple("Setting", "id default min max on_error")
OnError = collections.namedtuple("OnError", "behavior code")
clamp_invalid_value = OnError("CLAMP_INVALID_VALUE", "PROTOCOL_ERROR")
disconnect_on_invalid_value = lambda e: OnError(
"DISCONNECT_ON_INVALID_VALUE", e
)
DecoratedSetting = collections.namedtuple(
"DecoratedSetting", "enum name setting"
)
_SETTINGS = {
"HEADER_TABLE_SIZE": Setting(1, 4096, 0, 0xFFFFFFFF, clamp_invalid_value),
"ENABLE_PUSH": Setting(
2, 1, 0, 1, disconnect_on_invalid_value("PROTOCOL_ERROR")
),
"MAX_CONCURRENT_STREAMS": Setting(
3,
0xFFFFFFFF,
0,
0xFFFFFFFF,
disconnect_on_invalid_value("PROTOCOL_ERROR"),
),
"INITIAL_WINDOW_SIZE": Setting(
4,
65535,
0,
0x7FFFFFFF,
disconnect_on_invalid_value("FLOW_CONTROL_ERROR"),
),
"MAX_FRAME_SIZE": Setting(
5, 16384, 16384, 16777215, disconnect_on_invalid_value("PROTOCOL_ERROR")
),
"MAX_HEADER_LIST_SIZE": Setting(
6, _MAX_HEADER_LIST_SIZE, 0, _MAX_HEADER_LIST_SIZE, clamp_invalid_value
),
"GRPC_ALLOW_TRUE_BINARY_METADATA": Setting(
0xFE03, 0, 0, 1, clamp_invalid_value
),
"GRPC_PREFERRED_RECEIVE_CRYPTO_FRAME_SIZE": Setting(
0xFE04, 0, 16384, 0x7FFFFFFF, clamp_invalid_value
),
}
H = open("src/core/ext/transport/chttp2/transport/http2_settings.h", "w")
C = open("src/core/ext/transport/chttp2/transport/http2_settings.cc", "w")
# utility: print a big comment block into a set of files
def put_banner(files, banner):
for f in files:
print("/*", file=f)
for line in banner:
print(" * %s" % line, file=f)
print(" */", file=f)
print(file=f)
# copy-paste copyright notice from this file
with open(sys.argv[0]) as my_source:
copyright = []
for line in my_source:
if line[0] != "#":
break
for line in my_source:
if line[0] == "#":
copyright.append(line)
break
for line in my_source:
if line[0] != "#":
break
copyright.append(line)
put_banner([H, C], [line[2:].rstrip() for line in copyright])
put_banner(
[H, C],
["Automatically generated by tools/codegen/core/gen_settings_ids.py"],
)
print(
"#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H", file=H
)
print(
"#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H", file=H
)
print(file=H)
print("#include <grpc/support/port_platform.h>", file=H)
print("#include <stdint.h>", file=H)
print(file=H)
print("#include <grpc/support/port_platform.h>", file=C)
print(
'#include "src/core/ext/transport/chttp2/transport/http2_settings.h"',
file=C,
)
print(file=C)
print('#include "src/core/lib/gpr/useful.h"', file=C)
print('#include "src/core/lib/transport/http2_errors.h"', file=C)
print(file=C)
p = perfection.hash_parameters(sorted(x.id for x in list(_SETTINGS.values())))
print(p)
def hash(i):
i += p.offset
x = i % p.t
y = i // p.t
return x + p.r[y]
decorated_settings = [
DecoratedSetting(hash(setting.id), name, setting)
for name, setting in _SETTINGS.items()
]
print("typedef enum {", file=H)
for decorated_setting in sorted(decorated_settings):
print(
" GRPC_CHTTP2_SETTINGS_%s = %d, /* wire id %d */"
% (
decorated_setting.name,
decorated_setting.enum,
decorated_setting.setting.id,
),
file=H,
)
print("} grpc_chttp2_setting_id;", file=H)
print(file=H)
print(
"#define GRPC_CHTTP2_NUM_SETTINGS %d"
% (max(x.enum for x in decorated_settings) + 1),
file=H,
)
print("extern const uint16_t grpc_setting_id_to_wire_id[];", file=H)
print(
"const uint16_t grpc_setting_id_to_wire_id[] = {%s};"
% ",".join("%d" % s for s in p.slots),
file=C,
)
print(file=H)
print(
(
"bool grpc_wire_id_to_setting_id(uint32_t wire_id,"
" grpc_chttp2_setting_id *out);"
),
file=H,
)
cgargs = {
"r": ",".join("%d" % (r if r is not None else 0) for r in p.r),
"t": p.t,
"offset": abs(p.offset),
"offset_sign": "+" if p.offset > 0 else "-",
}
print(
"""
bool grpc_wire_id_to_setting_id(uint32_t wire_id, grpc_chttp2_setting_id *out) {
uint32_t i = wire_id %(offset_sign)s %(offset)d;
uint32_t x = i %% %(t)d;
uint32_t y = i / %(t)d;
uint32_t h = x;
switch (y) {
"""
% cgargs,
file=C,
)
for i, r in enumerate(p.r):
if not r:
continue
if r < 0:
print("case %d: h -= %d; break;" % (i, -r), file=C)
else:
print("case %d: h += %d; break;" % (i, r), file=C)
print(
"""
}
*out = static_cast<grpc_chttp2_setting_id>(h);
return h < GPR_ARRAY_SIZE(grpc_setting_id_to_wire_id) && grpc_setting_id_to_wire_id[h] == wire_id;
}
"""
% cgargs,
file=C,
)
print(
"""
typedef enum {
GRPC_CHTTP2_CLAMP_INVALID_VALUE,
GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE
} grpc_chttp2_invalid_value_behavior;
typedef struct {
const char *name;
uint32_t default_value;
uint32_t min_value;
uint32_t max_value;
grpc_chttp2_invalid_value_behavior invalid_value_behavior;
uint32_t error_value;
} grpc_chttp2_setting_parameters;
extern const grpc_chttp2_setting_parameters grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS];
""",
file=H,
)
print(
(
"const grpc_chttp2_setting_parameters"
" grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS] = {"
),
file=C,
)
i = 0
for decorated_setting in sorted(decorated_settings):
while i < decorated_setting.enum:
print(
(
"{NULL, 0, 0, 0, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE,"
" GRPC_HTTP2_PROTOCOL_ERROR},"
),
file=C,
)
i += 1
print(
'{"%s", %du, %du, %du, GRPC_CHTTP2_%s, GRPC_HTTP2_%s},'
% (
decorated_setting.name,
decorated_setting.setting.default,
decorated_setting.setting.min,
decorated_setting.setting.max,
decorated_setting.setting.on_error.behavior,
decorated_setting.setting.on_error.code,
),
file=C,
)
i += 1
print("};", file=C)
print(file=H)
print(
"#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H */",
file=H,
)
H.close()
C.close()

@ -1293,6 +1293,8 @@ 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.cc \
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 \

@ -1068,6 +1068,8 @@ 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.cc \
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 \

@ -4727,6 +4727,30 @@
],
"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": "http2_settings_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save