diff options
31 files changed, 1062 insertions, 681 deletions
@@ -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", diff --git a/CMakeLists.txt b/CMakeLists.txt index 3724caf8f5..aa9de4ca98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) @@ -14539,6 +14545,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) + add_executable(http2_stats_test test/core/end2end/cq_verifier.cc test/core/end2end/end2end_test_main.cc @@ -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 \ diff --git a/Package.swift b/Package.swift index 4b7c86cfa7..5962c89119 100644 --- a/Package.swift +++ b/Package.swift @@ -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", diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 81b264232e..71e595e0d9 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -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 @@ -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 \ diff --git a/config.w32 b/config.w32 index d36b3beb2f..27dfbf9ead 100644 --- a/config.w32 +++ b/config.w32 @@ -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 " + diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index 3656484f5b..2d6f37cee5 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -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', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index ae204f1fb0..b077d471cb 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -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', diff --git a/grpc.gemspec b/grpc.gemspec index 8e10a8c324..165f5778df 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -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 ) @@ -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', diff --git a/package.xml b/package.xml index 7059b0c805..3c9247b661 100644 --- a/package.xml +++ b/package.xml @@ -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" /> diff --git a/src/core/BUILD b/src/core/BUILD index 65a64fc5e5..7b233ef207 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -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", ], ) diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index 7c3c7dcd6e..f917662bf8 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -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()); }); } diff --git a/src/core/ext/transport/chttp2/transport/flow_control.cc b/src/core/ext/transport/chttp2/transport/flow_control.cc index 20031119b3..a24828e7a1 100644 --- a/src/core/ext/transport/chttp2/transport/flow_control.cc +++ b/src/core/ext/transport/chttp2/transport/flow_control.cc @@ -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); } diff --git a/src/core/ext/transport/chttp2/transport/flow_control.h b/src/core/ext/transport/chttp2/transport/flow_control.h index f155ad34e6..82dde68754 100644 --- a/src/core/ext/transport/chttp2/transport/flow_control.h +++ b/src/core/ext/transport/chttp2/transport/flow_control.h @@ -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)( diff --git a/src/core/ext/transport/chttp2/transport/frame.h b/src/core/ext/transport/chttp2/transport/frame.h index fd6abe81df..be20ef4a0c 100644 --- a/src/core/ext/transport/chttp2/transport/frame.h +++ b/src/core/ext/transport/chttp2/transport/frame.h @@ -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; diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.cc b/src/core/ext/transport/chttp2/transport/frame_settings.cc index e4bf9d4fdb..c0e24c336e 100644 --- a/src/core/ext/transport/chttp2/transport/frame_settings.cc +++ b/src/core/ext/transport/chttp2/transport/frame_settings.cc @@ -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; } } } diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.h b/src/core/ext/transport/chttp2/transport/frame_settings.h index 24a97c905d..56dc5baae6 100644 --- a/src/core/ext/transport/chttp2/transport/frame_settings.h +++ b/src/core/ext/transport/chttp2/transport/frame_settings.h @@ -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, diff --git a/src/core/ext/transport/chttp2/transport/http2_settings.cc b/src/core/ext/transport/chttp2/transport/http2_settings.cc index d966a7aebe..d9e4341250 100644 --- a/src/core/ext/transport/chttp2/transport/http2_settings.cc +++ b/src/core/ext/transport/chttp2/transport/http2_settings.cc @@ -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 diff --git a/src/core/ext/transport/chttp2/transport/http2_settings.h b/src/core/ext/transport/chttp2/transport/http2_settings.h index 314a5c0414..8578d91340 100644 --- a/src/core/ext/transport/chttp2/transport/http2_settings.h +++ b/src/core/ext/transport/chttp2/transport/http2_settings.h @@ -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 diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h index 7e105e8f69..d41d6356f2 100644 --- a/src/core/ext/transport/chttp2/transport/internal.h +++ b/src/core/ext/transport/chttp2/transport/internal.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 diff --git a/src/core/ext/transport/chttp2/transport/parsing.cc b/src/core/ext/transport/chttp2/transport/parsing.cc index 7ccdffee98..f2f5e7dc53 100644 --- a/src/core/ext/transport/chttp2/transport/parsing.cc +++ b/src/core/ext/transport/chttp2/transport/parsing.cc @@ -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 = diff --git a/src/core/ext/transport/chttp2/transport/writing.cc b/src/core/ext/transport/chttp2/transport/writing.cc index 8ca99c4fdf..b1f9f30d2f 100644 --- a/src/core/ext/transport/chttp2/transport/writing.cc +++ b/src/core/ext/transport/chttp2/transport/writing.cc @@ -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(); diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 021c735053..6bf08a818f 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -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', diff --git a/test/core/transport/chttp2/BUILD b/test/core/transport/chttp2/BUILD index 77ce6b4d10..1c11d2f554 100644 --- a/test/core/transport/chttp2/BUILD +++ b/test/core/transport/chttp2/BUILD @@ -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", + ], +) diff --git a/test/core/transport/chttp2/http2_settings_test.cc b/test/core/transport/chttp2/http2_settings_test.cc new file mode 100644 index 0000000000..776a131be4 --- /dev/null +++ b/test/core/transport/chttp2/http2_settings_test.cc @@ -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(); +} diff --git a/tools/codegen/core/gen_settings_ids.py b/tools/codegen/core/gen_settings_ids.py deleted file mode 100755 index 1ac8740748..0000000000 --- a/tools/codegen/core/gen_settings_ids.py +++ /dev/null @@ -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() diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index 608c24cf63..dd6261d57f 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -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 \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index cbc40815ef..0dda802746 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -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 \ diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index a8edaa5d45..7e6fcc1d51 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -4742,6 +4742,30 @@ "flaky": false, "gtest": true, "language": "c++", + "name": "http2_settings_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": "http2_stats_test", "platforms": [ "linux", |