From 724441d85bb7302d21709fa39780260cd3204908 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Mon, 3 Apr 2023 22:19:57 -0700 Subject: [PATCH 01/39] [tests] Convert core e2e tests to gtest (#32603) Notes: - `+trace` fixtures haven't run since 2016, so they're disabled for now (https://github.com/grpc/grpc/commit/7ad2d0b4638ab9331c5c1c9a9061ec1263b34900#diff-780fce7267c34170c1d0ea15cc9f65a7f4b79fefe955d185c44e8b3251cf9e38R76) - all current fixtures define `FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER` and hence `authority_not_supported` has not been run in years - deleted - bad_hostname similarly hasn't been triggered in a long while, so deleted - load_reporting_hook has never been enabled, so deleted (https://github.com/grpc/grpc/blame/f23fb4cf3114787806c330985c8fb3213597a09b/test/core/end2end/generate_tests.bzl#L145-L148) - filter_latency & filter_status_code rely on global variables and so don't convert particularly cleanly - and their value seems marginal, so deleted --------- Co-authored-by: ctiller --- .gitattributes | 1 - CMakeLists.txt | 1174 +++++++++-------- build_autogenerated.yaml | 650 ++++----- gRPC-Core.podspec | 153 --- grpc.gyp | 114 -- .../chttp2/transport/chttp2_transport.cc | 2 + .../transport/chttp2/transport/frame_ping.cc | 8 +- .../transport/chttp2/transport/frame_ping.h | 3 - .../chttp2/transport/hpack_parser_table.cc | 1 + .../ext/transport/chttp2/transport/internal.h | 2 + src/core/lib/debug/trace.cc | 23 +- src/core/lib/debug/trace.h | 16 +- src/core/lib/iomgr/timer_generic.cc | 33 +- src/core/lib/slice/slice.h | 2 + src/objective-c/tests/BUILD | 8 +- .../CronetTests/CoreCronetEnd2EndTests.mm | 330 ----- src/objective-c/tests/Podfile | 1 - templates/gRPC-Core.podspec.template | 22 - .../test/core/end2end/end2end_defs.include | 72 - .../core/end2end/end2end_tests.cc.template | 4 - test/core/bad_client/generate_tests.bzl | 1 + test/core/end2end/BUILD | 212 ++- test/core/end2end/README | 7 - test/core/end2end/cq_verifier.cc | 73 +- test/core/end2end/cq_verifier.h | 32 +- test/core/end2end/end2end_test_main.cc | 1070 +++++++++++++++ test/core/end2end/end2end_test_utils.cc | 49 - test/core/end2end/end2end_tests.cc | 1006 ++++---------- test/core/end2end/end2end_tests.h | 642 ++++++++- test/core/end2end/fixtures/h2_census.cc | 87 -- test/core/end2end/fixtures/h2_compress.cc | 96 -- test/core/end2end/fixtures/h2_fakesec.cc | 89 -- test/core/end2end/fixtures/h2_fd.cc | 111 -- test/core/end2end/fixtures/h2_full+pipe.cc | 72 - test/core/end2end/fixtures/h2_full+trace.cc | 95 -- test/core/end2end/fixtures/h2_full.cc | 58 - .../core/end2end/fixtures/h2_full_no_retry.cc | 66 - test/core/end2end/fixtures/h2_http_proxy.cc | 119 -- test/core/end2end/fixtures/h2_insecure.cc | 84 -- .../h2_local_abstract_uds_percent_encoded.cc | 65 - test/core/end2end/fixtures/h2_local_ipv4.cc | 59 - test/core/end2end/fixtures/h2_local_ipv6.cc | 59 - test/core/end2end/fixtures/h2_local_uds.cc | 69 - .../fixtures/h2_local_uds_percent_encoded.cc | 69 - test/core/end2end/fixtures/h2_oauth2_tls12.cc | 58 - test/core/end2end/fixtures/h2_oauth2_tls13.cc | 59 - test/core/end2end/fixtures/h2_proxy.cc | 113 -- .../end2end/fixtures/h2_sockpair+trace.cc | 86 -- test/core/end2end/fixtures/h2_sockpair.cc | 53 - .../end2end/fixtures/h2_sockpair_1byte.cc | 59 - .../fixtures/h2_sockpair_with_minstack.cc | 72 - .../fixtures/h2_ssl_cred_reload_tls12.cc | 68 - .../fixtures/h2_ssl_cred_reload_tls13.cc | 68 - test/core/end2end/fixtures/h2_ssl_proxy.cc | 188 --- test/core/end2end/fixtures/h2_ssl_tls12.cc | 65 - test/core/end2end/fixtures/h2_ssl_tls13.cc | 65 - .../fixtures/h2_tls_certwatch_async_tls1_3.cc | 58 - .../fixtures/h2_tls_certwatch_sync_tls1_2.cc | 58 - test/core/end2end/fixtures/h2_tls_common.h | 4 +- test/core/end2end/fixtures/h2_tls_simple.cc | 58 - .../fixtures/h2_tls_static_async_tls1_3.cc | 58 - test/core/end2end/fixtures/h2_uds.cc | 69 - test/core/end2end/fixtures/h2_uds_abstract.cc | 69 - test/core/end2end/fixtures/inproc.cc | 55 - test/core/end2end/fixtures/inproc_fixture.h | 2 +- test/core/end2end/fixtures/local_util.h | 2 +- test/core/end2end/fixtures/secure_fixture.h | 2 +- test/core/end2end/fixtures/sockpair_fixture.h | 58 +- test/core/end2end/gen_build_yaml.py | 53 - test/core/end2end/generate_tests.bzl | 499 ------- test/core/end2end/h2_ssl_cert_test.cc | 9 +- test/core/end2end/run.sh | 17 - .../end2end/tests/authority_not_supported.cc | 142 -- test/core/end2end/tests/bad_hostname.cc | 124 -- test/core/end2end/tests/bad_ping.cc | 355 ++--- test/core/end2end/tests/binary_metadata.cc | 340 ++--- test/core/end2end/tests/call_creds.cc | 668 ++++------ test/core/end2end/tests/call_host_override.cc | 192 +-- .../core/end2end/tests/cancel_after_accept.cc | 256 +--- .../end2end/tests/cancel_after_client_done.cc | 211 +-- .../core/end2end/tests/cancel_after_invoke.cc | 241 ++-- .../end2end/tests/cancel_after_round_trip.cc | 292 +--- .../end2end/tests/cancel_before_invoke.cc | 195 ++- test/core/end2end/tests/cancel_in_a_vacuum.cc | 54 +- test/core/end2end/tests/cancel_test_helpers.h | 36 +- test/core/end2end/tests/cancel_with_status.cc | 167 +-- test/core/end2end/tests/channelz.cc | 303 ++--- test/core/end2end/tests/client_streaming.cc | 234 +--- test/core/end2end/tests/compressed_payload.cc | 943 +++++-------- test/core/end2end/tests/connectivity.cc | 234 +--- test/core/end2end/tests/default_host.cc | 195 +-- .../core/end2end/tests/disappearing_server.cc | 182 +-- test/core/end2end/tests/empty_batch.cc | 59 +- .../core/end2end/tests/filter_causes_close.cc | 187 +-- test/core/end2end/tests/filter_context.cc | 238 +--- test/core/end2end/tests/filter_init_fails.cc | 592 +++------ test/core/end2end/tests/filter_latency.cc | 289 ---- test/core/end2end/tests/filter_status_code.cc | 353 ----- test/core/end2end/tests/filtered_metadata.cc | 211 +-- .../end2end/tests/graceful_server_shutdown.cc | 173 +-- test/core/end2end/tests/grpc_authz.cc | 875 ++++-------- test/core/end2end/tests/high_initial_seqno.cc | 195 +-- test/core/end2end/tests/hpack_size.cc | 506 +++---- .../end2end/tests/invoke_large_request.cc | 252 +--- test/core/end2end/tests/keepalive_timeout.cc | 356 +---- test/core/end2end/tests/large_metadata.cc | 575 ++------ .../core/end2end/tests/load_reporting_hook.cc | 266 ---- .../end2end/tests/max_concurrent_streams.cc | 911 +++---------- test/core/end2end/tests/max_connection_age.cc | 390 ++---- .../core/end2end/tests/max_connection_idle.cc | 257 +--- test/core/end2end/tests/max_message_length.cc | 942 ++++--------- test/core/end2end/tests/negative_deadline.cc | 119 +- test/core/end2end/tests/no_logging.cc | 293 ++-- test/core/end2end/tests/no_op.cc | 28 +- test/core/end2end/tests/payload.cc | 262 +--- test/core/end2end/tests/ping.cc | 95 +- .../core/end2end/tests/ping_pong_streaming.cc | 250 +--- test/core/end2end/tests/proxy_auth.cc | 191 +-- test/core/end2end/tests/registered_call.cc | 184 +-- test/core/end2end/tests/request_with_flags.cc | 175 ++- .../end2end/tests/request_with_payload.cc | 198 +-- .../end2end/tests/resource_quota_server.cc | 380 ++---- test/core/end2end/tests/retry.cc | 317 ++--- ...retry_cancel_after_first_attempt_starts.cc | 179 +-- .../tests/retry_cancel_during_delay.cc | 282 ++-- ...retry_cancel_with_multiple_send_batches.cc | 239 +--- test/core/end2end/tests/retry_cancellation.cc | 258 +--- test/core/end2end/tests/retry_disabled.cc | 232 +--- .../retry_exceeds_buffer_size_in_delay.cc | 296 +---- ...ry_exceeds_buffer_size_in_initial_batch.cc | 235 +--- ...exceeds_buffer_size_in_subsequent_batch.cc | 253 +--- test/core/end2end/tests/retry_lb_drop.cc | 196 +-- test/core/end2end/tests/retry_lb_fail.cc | 176 +-- .../tests/retry_non_retriable_status.cc | 231 +--- ...s_before_recv_trailing_metadata_started.cc | 245 +--- .../tests/retry_per_attempt_recv_timeout.cc | 351 ++--- ...er_attempt_recv_timeout_on_last_attempt.cc | 238 +--- .../tests/retry_recv_initial_metadata.cc | 247 +--- test/core/end2end/tests/retry_recv_message.cc | 232 +--- .../tests/retry_recv_message_replay.cc | 291 ++-- .../retry_recv_trailing_metadata_error.cc | 276 +--- .../tests/retry_send_initial_metadata_refs.cc | 365 ++--- .../core/end2end/tests/retry_send_op_fails.cc | 311 ++--- .../end2end/tests/retry_send_recv_batch.cc | 219 +-- .../tests/retry_server_pushback_delay.cc | 313 +---- .../tests/retry_server_pushback_disabled.cc | 293 +--- test/core/end2end/tests/retry_streaming.cc | 466 ++----- .../tests/retry_streaming_after_commit.cc | 345 ++--- ...reaming_succeeds_before_replay_finished.cc | 406 ++---- test/core/end2end/tests/retry_throttled.cc | 242 +--- .../end2end/tests/retry_too_many_attempts.cc | 285 +--- .../end2end/tests/retry_transparent_goaway.cc | 288 ++-- ...etry_transparent_max_concurrent_streams.cc | 357 ++--- .../retry_transparent_not_sent_on_wire.cc | 287 ++-- .../tests/retry_unref_before_finish.cc | 211 +-- .../end2end/tests/retry_unref_before_recv.cc | 203 +-- .../end2end/tests/server_finishes_request.cc | 153 +-- test/core/end2end/tests/server_streaming.cc | 242 +--- .../end2end/tests/shutdown_finishes_calls.cc | 152 +-- .../end2end/tests/shutdown_finishes_tags.cc | 57 +- .../end2end/tests/simple_delayed_request.cc | 188 +-- test/core/end2end/tests/simple_metadata.cc | 247 +--- test/core/end2end/tests/simple_request.cc | 247 +--- .../end2end/tests/streaming_error_response.cc | 321 ++--- test/core/end2end/tests/trailing_metadata.cc | 253 +--- test/core/end2end/tests/write_buffering.cc | 258 +--- .../end2end/tests/write_buffering_at_end.cc | 246 +--- test/core/iomgr/ios/CFStreamTests/Podfile | 1 - test/cpp/cocoapods/Podfile | 1 - .../extract_metadata_from_bazel_xml.py | 7 - tools/buildgen/generate_build_additions.sh | 1 - tools/run_tests/generated/tests.json | 396 +++--- 172 files changed, 10111 insertions(+), 26124 deletions(-) delete mode 100644 src/objective-c/tests/CronetTests/CoreCronetEnd2EndTests.mm delete mode 100644 templates/test/core/end2end/end2end_defs.include delete mode 100644 templates/test/core/end2end/end2end_tests.cc.template delete mode 100644 test/core/end2end/README create mode 100644 test/core/end2end/end2end_test_main.cc delete mode 100644 test/core/end2end/end2end_test_utils.cc delete mode 100644 test/core/end2end/fixtures/h2_census.cc delete mode 100644 test/core/end2end/fixtures/h2_compress.cc delete mode 100644 test/core/end2end/fixtures/h2_fakesec.cc delete mode 100644 test/core/end2end/fixtures/h2_fd.cc delete mode 100644 test/core/end2end/fixtures/h2_full+pipe.cc delete mode 100644 test/core/end2end/fixtures/h2_full+trace.cc delete mode 100644 test/core/end2end/fixtures/h2_full.cc delete mode 100644 test/core/end2end/fixtures/h2_full_no_retry.cc delete mode 100644 test/core/end2end/fixtures/h2_http_proxy.cc delete mode 100644 test/core/end2end/fixtures/h2_insecure.cc delete mode 100644 test/core/end2end/fixtures/h2_local_abstract_uds_percent_encoded.cc delete mode 100644 test/core/end2end/fixtures/h2_local_ipv4.cc delete mode 100644 test/core/end2end/fixtures/h2_local_ipv6.cc delete mode 100644 test/core/end2end/fixtures/h2_local_uds.cc delete mode 100644 test/core/end2end/fixtures/h2_local_uds_percent_encoded.cc delete mode 100644 test/core/end2end/fixtures/h2_oauth2_tls12.cc delete mode 100644 test/core/end2end/fixtures/h2_oauth2_tls13.cc delete mode 100644 test/core/end2end/fixtures/h2_proxy.cc delete mode 100644 test/core/end2end/fixtures/h2_sockpair+trace.cc delete mode 100644 test/core/end2end/fixtures/h2_sockpair.cc delete mode 100644 test/core/end2end/fixtures/h2_sockpair_1byte.cc delete mode 100644 test/core/end2end/fixtures/h2_sockpair_with_minstack.cc delete mode 100644 test/core/end2end/fixtures/h2_ssl_cred_reload_tls12.cc delete mode 100644 test/core/end2end/fixtures/h2_ssl_cred_reload_tls13.cc delete mode 100644 test/core/end2end/fixtures/h2_ssl_proxy.cc delete mode 100644 test/core/end2end/fixtures/h2_ssl_tls12.cc delete mode 100644 test/core/end2end/fixtures/h2_ssl_tls13.cc delete mode 100644 test/core/end2end/fixtures/h2_tls_certwatch_async_tls1_3.cc delete mode 100644 test/core/end2end/fixtures/h2_tls_certwatch_sync_tls1_2.cc delete mode 100644 test/core/end2end/fixtures/h2_tls_simple.cc delete mode 100644 test/core/end2end/fixtures/h2_tls_static_async_tls1_3.cc delete mode 100644 test/core/end2end/fixtures/h2_uds.cc delete mode 100644 test/core/end2end/fixtures/h2_uds_abstract.cc delete mode 100644 test/core/end2end/fixtures/inproc.cc delete mode 100755 test/core/end2end/gen_build_yaml.py delete mode 100755 test/core/end2end/generate_tests.bzl delete mode 100755 test/core/end2end/run.sh delete mode 100644 test/core/end2end/tests/authority_not_supported.cc delete mode 100644 test/core/end2end/tests/bad_hostname.cc delete mode 100644 test/core/end2end/tests/filter_latency.cc delete mode 100644 test/core/end2end/tests/filter_status_code.cc delete mode 100644 test/core/end2end/tests/load_reporting_hook.cc diff --git a/.gitattributes b/.gitattributes index 7bb09cfbffe94..d42177674f61e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,7 +19,6 @@ binding.gyp linguist-generated=true src/python/grpcio/grpc_core_dependencies.py linguist-generated=true src/ruby/ext/grpc/rb_grpc_imports.generated.h linguist-generated=true src/ruby/ext/grpc/rb_grpc_imports.generated.c linguist-generated=true -test/core/end2end/end2end_tests.cc linguist-generated=true test/core/security/grpc_tls_credentials_options_comparator_test.cc linguist-generated=true test/core/surface/public_headers_must_be_c89.c linguist-generated=true tools/doxygen/Doxyfile.c++.internal linguist-generated=true diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c7891e28f058..fcbec1106cf93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -780,24 +780,10 @@ protobuf_generate_grpc_cpp_with_import_path_correction( if(gRPC_BUILD_TESTS) add_custom_target(buildtests_c) - add_dependencies(buildtests_c bad_server_response_test) - if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) - add_dependencies(buildtests_c bad_ssl_alpn_test) - endif() - if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) - add_dependencies(buildtests_c bad_ssl_cert_test) - endif() - add_dependencies(buildtests_c connection_refused_test) - if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) - add_dependencies(buildtests_c dualstack_socket_test) - endif() if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_c fd_conservation_posix_test) endif() - add_dependencies(buildtests_c goaway_server_test) - add_dependencies(buildtests_c invalid_call_argument_test) add_dependencies(buildtests_c multiple_server_queues_test) - add_dependencies(buildtests_c no_server_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX OR _gRPC_PLATFORM_WINDOWS) add_dependencies(buildtests_c pollset_windows_starvation_test) endif() @@ -850,6 +836,13 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx aws_request_signer_test) add_dependencies(buildtests_cxx b64_test) add_dependencies(buildtests_cxx backoff_test) + add_dependencies(buildtests_cxx bad_server_response_test) + if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + add_dependencies(buildtests_cxx bad_ssl_alpn_test) + endif() + if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + add_dependencies(buildtests_cxx bad_ssl_cert_test) + endif() add_dependencies(buildtests_cxx bad_streaming_id_bad_client_test) add_dependencies(buildtests_cxx badreq_bad_client_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) @@ -915,11 +908,13 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx compression_test) add_dependencies(buildtests_cxx concurrent_connectivity_test) add_dependencies(buildtests_cxx connection_prefix_bad_client_test) + add_dependencies(buildtests_cxx connection_refused_test) add_dependencies(buildtests_cxx connectivity_state_test) add_dependencies(buildtests_cxx context_allocator_end2end_test) add_dependencies(buildtests_cxx context_list_test) add_dependencies(buildtests_cxx context_test) add_dependencies(buildtests_cxx core_configuration_test) + add_dependencies(buildtests_cxx core_end2end_tests) add_dependencies(buildtests_cxx cpp_impl_of_test) add_dependencies(buildtests_cxx cpu_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) @@ -931,6 +926,9 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx dns_resolver_cooldown_test) add_dependencies(buildtests_cxx dns_resolver_test) add_dependencies(buildtests_cxx dual_ref_counted_test) + if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + add_dependencies(buildtests_cxx dualstack_socket_test) + endif() add_dependencies(buildtests_cxx duplicate_header_bad_client_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx end2end_binder_transport_test) @@ -978,6 +976,7 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx fuzzing_event_engine_test) endif() add_dependencies(buildtests_cxx generic_end2end_test) + add_dependencies(buildtests_cxx goaway_server_test) add_dependencies(buildtests_cxx google_c2p_resolver_test) add_dependencies(buildtests_cxx google_mesh_ca_certificate_provider_factory_test) add_dependencies(buildtests_cxx graceful_shutdown_test) @@ -1033,6 +1032,7 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx interceptor_list_test) add_dependencies(buildtests_cxx interop_client) add_dependencies(buildtests_cxx interop_server) + add_dependencies(buildtests_cxx invalid_call_argument_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX OR _gRPC_PLATFORM_WINDOWS) add_dependencies(buildtests_cxx iocp_test) endif() @@ -1074,6 +1074,7 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx mpscq_test) endif() add_dependencies(buildtests_cxx no_destruct_test) + add_dependencies(buildtests_cxx no_server_test) add_dependencies(buildtests_cxx nonblocking_test) add_dependencies(buildtests_cxx notification_test) add_dependencies(buildtests_cxx num_external_connectivity_watchers_test) @@ -1411,160 +1412,6 @@ if(gRPC_INSTALL) ) endif() -if(gRPC_BUILD_TESTS) - -add_library(end2end_tests - test/core/end2end/cq_verifier.cc - test/core/end2end/data/client_certs.cc - test/core/end2end/data/server1_cert.cc - test/core/end2end/data/server1_key.cc - test/core/end2end/data/test_root_cert.cc - test/core/end2end/end2end_test_utils.cc - test/core/end2end/end2end_tests.cc - test/core/end2end/fixtures/http_proxy_fixture.cc - test/core/end2end/fixtures/local_util.cc - test/core/end2end/fixtures/proxy.cc - test/core/end2end/tests/authority_not_supported.cc - test/core/end2end/tests/bad_hostname.cc - test/core/end2end/tests/bad_ping.cc - test/core/end2end/tests/binary_metadata.cc - test/core/end2end/tests/call_creds.cc - test/core/end2end/tests/call_host_override.cc - test/core/end2end/tests/cancel_after_accept.cc - test/core/end2end/tests/cancel_after_client_done.cc - test/core/end2end/tests/cancel_after_invoke.cc - test/core/end2end/tests/cancel_after_round_trip.cc - test/core/end2end/tests/cancel_before_invoke.cc - test/core/end2end/tests/cancel_in_a_vacuum.cc - test/core/end2end/tests/cancel_with_status.cc - test/core/end2end/tests/channelz.cc - test/core/end2end/tests/client_streaming.cc - test/core/end2end/tests/compressed_payload.cc - test/core/end2end/tests/connectivity.cc - test/core/end2end/tests/default_host.cc - test/core/end2end/tests/disappearing_server.cc - test/core/end2end/tests/empty_batch.cc - test/core/end2end/tests/filter_causes_close.cc - test/core/end2end/tests/filter_context.cc - test/core/end2end/tests/filter_init_fails.cc - test/core/end2end/tests/filter_latency.cc - test/core/end2end/tests/filter_status_code.cc - test/core/end2end/tests/filtered_metadata.cc - test/core/end2end/tests/graceful_server_shutdown.cc - test/core/end2end/tests/grpc_authz.cc - test/core/end2end/tests/high_initial_seqno.cc - test/core/end2end/tests/hpack_size.cc - test/core/end2end/tests/invoke_large_request.cc - test/core/end2end/tests/keepalive_timeout.cc - test/core/end2end/tests/large_metadata.cc - test/core/end2end/tests/max_concurrent_streams.cc - test/core/end2end/tests/max_connection_age.cc - test/core/end2end/tests/max_connection_idle.cc - test/core/end2end/tests/max_message_length.cc - test/core/end2end/tests/negative_deadline.cc - test/core/end2end/tests/no_logging.cc - test/core/end2end/tests/no_op.cc - test/core/end2end/tests/payload.cc - test/core/end2end/tests/ping.cc - test/core/end2end/tests/ping_pong_streaming.cc - test/core/end2end/tests/proxy_auth.cc - test/core/end2end/tests/registered_call.cc - test/core/end2end/tests/request_with_flags.cc - test/core/end2end/tests/request_with_payload.cc - test/core/end2end/tests/resource_quota_server.cc - test/core/end2end/tests/retry.cc - test/core/end2end/tests/retry_cancel_after_first_attempt_starts.cc - test/core/end2end/tests/retry_cancel_during_delay.cc - test/core/end2end/tests/retry_cancel_with_multiple_send_batches.cc - test/core/end2end/tests/retry_cancellation.cc - test/core/end2end/tests/retry_disabled.cc - test/core/end2end/tests/retry_exceeds_buffer_size_in_delay.cc - test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc - test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc - test/core/end2end/tests/retry_lb_drop.cc - test/core/end2end/tests/retry_lb_fail.cc - test/core/end2end/tests/retry_non_retriable_status.cc - test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc - test/core/end2end/tests/retry_per_attempt_recv_timeout.cc - test/core/end2end/tests/retry_per_attempt_recv_timeout_on_last_attempt.cc - test/core/end2end/tests/retry_recv_initial_metadata.cc - test/core/end2end/tests/retry_recv_message.cc - test/core/end2end/tests/retry_recv_message_replay.cc - test/core/end2end/tests/retry_recv_trailing_metadata_error.cc - test/core/end2end/tests/retry_send_initial_metadata_refs.cc - test/core/end2end/tests/retry_send_op_fails.cc - test/core/end2end/tests/retry_send_recv_batch.cc - test/core/end2end/tests/retry_server_pushback_delay.cc - test/core/end2end/tests/retry_server_pushback_disabled.cc - test/core/end2end/tests/retry_streaming.cc - test/core/end2end/tests/retry_streaming_after_commit.cc - test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc - test/core/end2end/tests/retry_throttled.cc - test/core/end2end/tests/retry_too_many_attempts.cc - test/core/end2end/tests/retry_transparent_goaway.cc - test/core/end2end/tests/retry_transparent_max_concurrent_streams.cc - test/core/end2end/tests/retry_transparent_not_sent_on_wire.cc - test/core/end2end/tests/retry_unref_before_finish.cc - test/core/end2end/tests/retry_unref_before_recv.cc - test/core/end2end/tests/server_finishes_request.cc - test/core/end2end/tests/server_streaming.cc - test/core/end2end/tests/shutdown_finishes_calls.cc - test/core/end2end/tests/shutdown_finishes_tags.cc - test/core/end2end/tests/simple_delayed_request.cc - test/core/end2end/tests/simple_metadata.cc - test/core/end2end/tests/simple_request.cc - test/core/end2end/tests/streaming_error_response.cc - test/core/end2end/tests/trailing_metadata.cc - test/core/end2end/tests/write_buffering.cc - test/core/end2end/tests/write_buffering_at_end.cc - test/core/util/test_lb_policies.cc -) - -target_compile_features(end2end_tests PUBLIC cxx_std_14) - -set_target_properties(end2end_tests PROPERTIES - VERSION ${gRPC_CORE_VERSION} - SOVERSION ${gRPC_CORE_SOVERSION} -) - -if(WIN32 AND MSVC) - set_target_properties(end2end_tests PROPERTIES COMPILE_PDB_NAME "end2end_tests" - COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" - ) - if(gRPC_INSTALL) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/end2end_tests.pdb - DESTINATION ${gRPC_INSTALL_LIBDIR} OPTIONAL - ) - endif() -endif() - -target_include_directories(end2end_tests - PUBLIC $ $ - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} - ${_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 -) -target_link_libraries(end2end_tests - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_authorization_provider - grpc_test_util -) - - -endif() add_library(gpr src/core/lib/config/config_vars.cc @@ -4985,42 +4832,11 @@ if(gRPC_INSTALL) endif() -if(gRPC_BUILD_TESTS) - -add_executable(bad_server_response_test - test/core/end2end/bad_server_response_test.cc - test/core/end2end/cq_verifier.cc -) -target_compile_features(bad_server_response_test PUBLIC cxx_std_14) -target_include_directories(bad_server_response_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} -) - -target_link_libraries(bad_server_response_test - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util -) - - -endif() if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) - add_executable(bad_ssl_alpn_test - test/core/bad_ssl/bad_ssl_test.cc - test/core/end2end/cq_verifier.cc + add_executable(fd_conservation_posix_test + test/core/iomgr/fd_conservation_posix_test.cc test/core/util/cmdline.cc test/core/util/fuzzer_util.cc test/core/util/grpc_profiler.cc @@ -5034,8 +4850,8 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) test/core/util/subprocess_windows.cc test/core/util/tracer_util.cc ) - target_compile_features(bad_ssl_alpn_test PUBLIC cxx_std_14) - target_include_directories(bad_ssl_alpn_test + target_compile_features(fd_conservation_posix_test PUBLIC cxx_std_14) + target_include_directories(fd_conservation_posix_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include @@ -5049,7 +4865,7 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) ${_gRPC_ZLIB_INCLUDE_DIR} ) - target_link_libraries(bad_ssl_alpn_test + target_link_libraries(fd_conservation_posix_test ${_gRPC_BASELIB_LIBRARIES} ${_gRPC_ZLIB_LIBRARIES} ${_gRPC_ALLTARGETS_LIBRARIES} @@ -5060,26 +4876,42 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) endif() endif() if(gRPC_BUILD_TESTS) -if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) - add_executable(bad_ssl_cert_test - test/core/bad_ssl/bad_ssl_test.cc - test/core/end2end/cq_verifier.cc - test/core/util/cmdline.cc - test/core/util/fuzzer_util.cc - test/core/util/grpc_profiler.cc - test/core/util/histogram.cc - test/core/util/mock_endpoint.cc - test/core/util/parse_hexstring.cc - test/core/util/passthru_endpoint.cc - test/core/util/resolve_localhost_ip46.cc - test/core/util/slice_splitter.cc - test/core/util/subprocess_posix.cc - test/core/util/subprocess_windows.cc - test/core/util/tracer_util.cc +add_executable(multiple_server_queues_test + test/core/end2end/multiple_server_queues_test.cc +) +target_compile_features(multiple_server_queues_test PUBLIC cxx_std_14) +target_include_directories(multiple_server_queues_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} +) + +target_link_libraries(multiple_server_queues_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + +endif() +if(gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX OR _gRPC_PLATFORM_WINDOWS) + + add_executable(pollset_windows_starvation_test + test/core/iomgr/pollset_windows_starvation_test.cc ) - target_compile_features(bad_ssl_cert_test PUBLIC cxx_std_14) - target_include_directories(bad_ssl_cert_test + target_compile_features(pollset_windows_starvation_test PUBLIC cxx_std_14) + target_include_directories(pollset_windows_starvation_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include @@ -5093,7 +4925,7 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) ${_gRPC_ZLIB_INCLUDE_DIR} ) - target_link_libraries(bad_ssl_cert_test + target_link_libraries(pollset_windows_starvation_test ${_gRPC_BASELIB_LIBRARIES} ${_gRPC_ZLIB_LIBRARIES} ${_gRPC_ALLTARGETS_LIBRARIES} @@ -5105,263 +4937,8 @@ endif() endif() if(gRPC_BUILD_TESTS) -add_executable(connection_refused_test - test/core/end2end/connection_refused_test.cc - test/core/end2end/cq_verifier.cc -) -target_compile_features(connection_refused_test PUBLIC cxx_std_14) -target_include_directories(connection_refused_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} -) - -target_link_libraries(connection_refused_test - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util -) - - -endif() -if(gRPC_BUILD_TESTS) -if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) - - add_executable(dualstack_socket_test - test/core/end2end/cq_verifier.cc - test/core/end2end/dualstack_socket_test.cc - ) - target_compile_features(dualstack_socket_test PUBLIC cxx_std_14) - target_include_directories(dualstack_socket_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} - ) - - target_link_libraries(dualstack_socket_test - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util - ) - - -endif() -endif() -if(gRPC_BUILD_TESTS) -if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) - - add_executable(fd_conservation_posix_test - test/core/iomgr/fd_conservation_posix_test.cc - test/core/util/cmdline.cc - test/core/util/fuzzer_util.cc - test/core/util/grpc_profiler.cc - test/core/util/histogram.cc - test/core/util/mock_endpoint.cc - test/core/util/parse_hexstring.cc - test/core/util/passthru_endpoint.cc - test/core/util/resolve_localhost_ip46.cc - test/core/util/slice_splitter.cc - test/core/util/subprocess_posix.cc - test/core/util/subprocess_windows.cc - test/core/util/tracer_util.cc - ) - target_compile_features(fd_conservation_posix_test PUBLIC cxx_std_14) - target_include_directories(fd_conservation_posix_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} - ) - - target_link_libraries(fd_conservation_posix_test - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util - ) - - -endif() -endif() -if(gRPC_BUILD_TESTS) - -add_executable(goaway_server_test - test/core/end2end/cq_verifier.cc - test/core/end2end/goaway_server_test.cc -) -target_compile_features(goaway_server_test PUBLIC cxx_std_14) -target_include_directories(goaway_server_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} -) - -target_link_libraries(goaway_server_test - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util -) - - -endif() -if(gRPC_BUILD_TESTS) - -add_executable(invalid_call_argument_test - test/core/end2end/cq_verifier.cc - test/core/end2end/invalid_call_argument_test.cc -) -target_compile_features(invalid_call_argument_test PUBLIC cxx_std_14) -target_include_directories(invalid_call_argument_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} -) - -target_link_libraries(invalid_call_argument_test - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util -) - - -endif() -if(gRPC_BUILD_TESTS) - -add_executable(multiple_server_queues_test - test/core/end2end/multiple_server_queues_test.cc -) -target_compile_features(multiple_server_queues_test PUBLIC cxx_std_14) -target_include_directories(multiple_server_queues_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} -) - -target_link_libraries(multiple_server_queues_test - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util -) - - -endif() -if(gRPC_BUILD_TESTS) - -add_executable(no_server_test - test/core/end2end/cq_verifier.cc - test/core/end2end/no_server_test.cc -) -target_compile_features(no_server_test PUBLIC cxx_std_14) -target_include_directories(no_server_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} -) - -target_link_libraries(no_server_test - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util -) - - -endif() -if(gRPC_BUILD_TESTS) -if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX OR _gRPC_PLATFORM_WINDOWS) - - add_executable(pollset_windows_starvation_test - test/core/iomgr/pollset_windows_starvation_test.cc - ) - target_compile_features(pollset_windows_starvation_test PUBLIC cxx_std_14) - target_include_directories(pollset_windows_starvation_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} - ) - - target_link_libraries(pollset_windows_starvation_test - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util - ) - - -endif() -endif() -if(gRPC_BUILD_TESTS) - -add_executable(public_headers_must_be_c89 - test/core/surface/public_headers_must_be_c89.c +add_executable(public_headers_must_be_c89 + test/core/surface/public_headers_must_be_c89.c ) target_compile_features(public_headers_must_be_c89 PUBLIC cxx_std_14) target_include_directories(public_headers_must_be_c89 @@ -6810,15 +6387,157 @@ target_include_directories(backoff_test ${_gRPC_PROTO_GENS_DIR} ) -target_link_libraries(backoff_test - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_PROTOBUF_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util -) +target_link_libraries(backoff_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + +endif() +if(gRPC_BUILD_TESTS) + +add_executable(bad_server_response_test + test/core/end2end/bad_server_response_test.cc + test/core/end2end/cq_verifier.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) +target_compile_features(bad_server_response_test PUBLIC cxx_std_14) +target_include_directories(bad_server_response_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(bad_server_response_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + +endif() +if(gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + + add_executable(bad_ssl_alpn_test + test/core/bad_ssl/bad_ssl_test.cc + test/core/end2end/cq_verifier.cc + test/core/util/cmdline.cc + test/core/util/fuzzer_util.cc + test/core/util/grpc_profiler.cc + test/core/util/histogram.cc + test/core/util/mock_endpoint.cc + test/core/util/parse_hexstring.cc + test/core/util/passthru_endpoint.cc + test/core/util/resolve_localhost_ip46.cc + test/core/util/slice_splitter.cc + test/core/util/subprocess_posix.cc + test/core/util/subprocess_windows.cc + test/core/util/tracer_util.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc + ) + target_compile_features(bad_ssl_alpn_test PUBLIC cxx_std_14) + target_include_directories(bad_ssl_alpn_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(bad_ssl_alpn_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util + ) + + +endif() +endif() +if(gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + + add_executable(bad_ssl_cert_test + test/core/bad_ssl/bad_ssl_test.cc + test/core/end2end/cq_verifier.cc + test/core/util/cmdline.cc + test/core/util/fuzzer_util.cc + test/core/util/grpc_profiler.cc + test/core/util/histogram.cc + test/core/util/mock_endpoint.cc + test/core/util/parse_hexstring.cc + test/core/util/passthru_endpoint.cc + test/core/util/resolve_localhost_ip46.cc + test/core/util/slice_splitter.cc + test/core/util/subprocess_posix.cc + test/core/util/subprocess_windows.cc + test/core/util/tracer_util.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc + ) + target_compile_features(bad_ssl_cert_test PUBLIC cxx_std_14) + target_include_directories(bad_ssl_cert_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(bad_ssl_cert_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util + ) +endif() endif() if(gRPC_BUILD_TESTS) @@ -9276,6 +8995,44 @@ target_link_libraries(connection_prefix_bad_client_test ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(connection_refused_test + test/core/end2end/connection_refused_test.cc + test/core/end2end/cq_verifier.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) +target_compile_features(connection_refused_test PUBLIC cxx_std_14) +target_include_directories(connection_refused_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(connection_refused_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + endif() if(gRPC_BUILD_TESTS) @@ -9399,8 +9156,214 @@ add_executable(context_list_test third_party/googletest/googletest/src/gtest-all.cc third_party/googletest/googlemock/src/gmock-all.cc ) -target_compile_features(context_list_test PUBLIC cxx_std_14) -target_include_directories(context_list_test +target_compile_features(context_list_test PUBLIC cxx_std_14) +target_include_directories(context_list_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(context_list_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + +endif() +if(gRPC_BUILD_TESTS) + +add_executable(context_test + test/core/promise/context_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) +target_compile_features(context_test PUBLIC cxx_std_14) +target_include_directories(context_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(context_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + gpr +) + + +endif() +if(gRPC_BUILD_TESTS) + +add_executable(core_configuration_test + test/core/config/core_configuration_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) +target_compile_features(core_configuration_test PUBLIC cxx_std_14) +target_include_directories(core_configuration_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(core_configuration_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc +) + + +endif() +if(gRPC_BUILD_TESTS) + +add_executable(core_end2end_tests + test/core/end2end/cq_verifier.cc + test/core/end2end/end2end_test_main.cc + test/core/end2end/end2end_tests.cc + test/core/end2end/fixtures/http_proxy_fixture.cc + test/core/end2end/fixtures/local_util.cc + test/core/end2end/fixtures/proxy.cc + test/core/end2end/tests/bad_ping.cc + test/core/end2end/tests/binary_metadata.cc + test/core/end2end/tests/call_creds.cc + test/core/end2end/tests/call_host_override.cc + test/core/end2end/tests/cancel_after_accept.cc + test/core/end2end/tests/cancel_after_client_done.cc + test/core/end2end/tests/cancel_after_invoke.cc + test/core/end2end/tests/cancel_after_round_trip.cc + test/core/end2end/tests/cancel_before_invoke.cc + test/core/end2end/tests/cancel_in_a_vacuum.cc + test/core/end2end/tests/cancel_with_status.cc + test/core/end2end/tests/channelz.cc + test/core/end2end/tests/client_streaming.cc + test/core/end2end/tests/compressed_payload.cc + test/core/end2end/tests/connectivity.cc + test/core/end2end/tests/default_host.cc + test/core/end2end/tests/disappearing_server.cc + test/core/end2end/tests/empty_batch.cc + test/core/end2end/tests/filter_causes_close.cc + test/core/end2end/tests/filter_context.cc + test/core/end2end/tests/filter_init_fails.cc + test/core/end2end/tests/filtered_metadata.cc + test/core/end2end/tests/graceful_server_shutdown.cc + test/core/end2end/tests/grpc_authz.cc + test/core/end2end/tests/high_initial_seqno.cc + test/core/end2end/tests/hpack_size.cc + test/core/end2end/tests/invoke_large_request.cc + test/core/end2end/tests/keepalive_timeout.cc + test/core/end2end/tests/large_metadata.cc + test/core/end2end/tests/max_concurrent_streams.cc + test/core/end2end/tests/max_connection_age.cc + test/core/end2end/tests/max_connection_idle.cc + test/core/end2end/tests/max_message_length.cc + test/core/end2end/tests/negative_deadline.cc + test/core/end2end/tests/no_logging.cc + test/core/end2end/tests/no_op.cc + test/core/end2end/tests/payload.cc + test/core/end2end/tests/ping.cc + test/core/end2end/tests/ping_pong_streaming.cc + test/core/end2end/tests/proxy_auth.cc + test/core/end2end/tests/registered_call.cc + test/core/end2end/tests/request_with_flags.cc + test/core/end2end/tests/request_with_payload.cc + test/core/end2end/tests/resource_quota_server.cc + test/core/end2end/tests/retry.cc + test/core/end2end/tests/retry_cancel_after_first_attempt_starts.cc + test/core/end2end/tests/retry_cancel_during_delay.cc + test/core/end2end/tests/retry_cancel_with_multiple_send_batches.cc + test/core/end2end/tests/retry_cancellation.cc + test/core/end2end/tests/retry_disabled.cc + test/core/end2end/tests/retry_exceeds_buffer_size_in_delay.cc + test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc + test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc + test/core/end2end/tests/retry_lb_drop.cc + test/core/end2end/tests/retry_lb_fail.cc + test/core/end2end/tests/retry_non_retriable_status.cc + test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc + test/core/end2end/tests/retry_per_attempt_recv_timeout.cc + test/core/end2end/tests/retry_per_attempt_recv_timeout_on_last_attempt.cc + test/core/end2end/tests/retry_recv_initial_metadata.cc + test/core/end2end/tests/retry_recv_message.cc + test/core/end2end/tests/retry_recv_message_replay.cc + test/core/end2end/tests/retry_recv_trailing_metadata_error.cc + test/core/end2end/tests/retry_send_initial_metadata_refs.cc + test/core/end2end/tests/retry_send_op_fails.cc + test/core/end2end/tests/retry_send_recv_batch.cc + test/core/end2end/tests/retry_server_pushback_delay.cc + test/core/end2end/tests/retry_server_pushback_disabled.cc + test/core/end2end/tests/retry_streaming.cc + test/core/end2end/tests/retry_streaming_after_commit.cc + test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc + test/core/end2end/tests/retry_throttled.cc + test/core/end2end/tests/retry_too_many_attempts.cc + test/core/end2end/tests/retry_transparent_goaway.cc + test/core/end2end/tests/retry_transparent_max_concurrent_streams.cc + test/core/end2end/tests/retry_transparent_not_sent_on_wire.cc + test/core/end2end/tests/retry_unref_before_finish.cc + test/core/end2end/tests/retry_unref_before_recv.cc + test/core/end2end/tests/server_finishes_request.cc + test/core/end2end/tests/server_streaming.cc + test/core/end2end/tests/shutdown_finishes_calls.cc + test/core/end2end/tests/shutdown_finishes_tags.cc + test/core/end2end/tests/simple_delayed_request.cc + test/core/end2end/tests/simple_metadata.cc + test/core/end2end/tests/simple_request.cc + test/core/end2end/tests/streaming_error_response.cc + test/core/end2end/tests/trailing_metadata.cc + test/core/end2end/tests/write_buffering.cc + test/core/end2end/tests/write_buffering_at_end.cc + test/core/util/test_lb_policies.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) +target_compile_features(core_end2end_tests PUBLIC cxx_std_14) +target_include_directories(core_end2end_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include @@ -9419,89 +9382,17 @@ target_include_directories(context_list_test ${_gRPC_PROTO_GENS_DIR} ) -target_link_libraries(context_list_test +target_link_libraries(core_end2end_tests ${_gRPC_BASELIB_LIBRARIES} ${_gRPC_PROTOBUF_LIBRARIES} ${_gRPC_ZLIB_LIBRARIES} ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_authorization_provider + grpc_unsecure grpc_test_util ) -endif() -if(gRPC_BUILD_TESTS) - -add_executable(context_test - test/core/promise/context_test.cc - third_party/googletest/googletest/src/gtest-all.cc - third_party/googletest/googlemock/src/gmock-all.cc -) -target_compile_features(context_test PUBLIC cxx_std_14) -target_include_directories(context_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(context_test - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_PROTOBUF_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - gpr -) - - -endif() -if(gRPC_BUILD_TESTS) - -add_executable(core_configuration_test - test/core/config/core_configuration_test.cc - third_party/googletest/googletest/src/gtest-all.cc - third_party/googletest/googlemock/src/gmock-all.cc -) -target_compile_features(core_configuration_test PUBLIC cxx_std_14) -target_include_directories(core_configuration_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(core_configuration_test - ${_gRPC_BASELIB_LIBRARIES} - ${_gRPC_PROTOBUF_LIBRARIES} - ${_gRPC_ZLIB_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc -) - - endif() if(gRPC_BUILD_TESTS) @@ -9854,6 +9745,46 @@ target_link_libraries(dual_ref_counted_test ) +endif() +if(gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + + add_executable(dualstack_socket_test + test/core/end2end/cq_verifier.cc + test/core/end2end/dualstack_socket_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc + ) + target_compile_features(dualstack_socket_test PUBLIC cxx_std_14) + target_include_directories(dualstack_socket_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(dualstack_socket_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util + ) + + +endif() endif() if(gRPC_BUILD_TESTS) @@ -11858,6 +11789,44 @@ target_link_libraries(generic_end2end_test ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(goaway_server_test + test/core/end2end/cq_verifier.cc + test/core/end2end/goaway_server_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) +target_compile_features(goaway_server_test PUBLIC cxx_std_14) +target_include_directories(goaway_server_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(goaway_server_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + endif() if(gRPC_BUILD_TESTS) @@ -13064,6 +13033,7 @@ add_executable(h2_ssl_cert_test test/core/end2end/data/server1_cert.cc test/core/end2end/data/server1_key.cc test/core/end2end/data/test_root_cert.cc + test/core/end2end/end2end_tests.cc test/core/end2end/fixtures/local_util.cc test/core/end2end/h2_ssl_cert_test.cc third_party/googletest/googletest/src/gtest-all.cc @@ -14230,6 +14200,44 @@ target_link_libraries(interop_server ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(invalid_call_argument_test + test/core/end2end/cq_verifier.cc + test/core/end2end/invalid_call_argument_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) +target_compile_features(invalid_call_argument_test PUBLIC cxx_std_14) +target_include_directories(invalid_call_argument_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(invalid_call_argument_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + endif() if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX OR _gRPC_PLATFORM_WINDOWS) @@ -15565,6 +15573,44 @@ target_link_libraries(no_destruct_test ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(no_server_test + test/core/end2end/cq_verifier.cc + test/core/end2end/no_server_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) +target_compile_features(no_server_test PUBLIC cxx_std_14) +target_include_directories(no_server_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(no_server_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + endif() if(gRPC_BUILD_TESTS) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 341ddcd2b65d1..a36f98314ff30 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -12,134 +12,6 @@ libs: - third_party/address_sorting/address_sorting_posix.c - third_party/address_sorting/address_sorting_windows.c deps: [] -- name: end2end_tests - build: private - language: c - public_headers: [] - headers: - - test/core/end2end/cq_verifier.h - - test/core/end2end/data/ssl_test_data.h - - test/core/end2end/end2end_tests.h - - test/core/end2end/fixtures/h2_oauth2_common.h - - test/core/end2end/fixtures/h2_ssl_cred_reload_fixture.h - - test/core/end2end/fixtures/h2_ssl_tls_common.h - - test/core/end2end/fixtures/h2_tls_common.h - - test/core/end2end/fixtures/http_proxy_fixture.h - - test/core/end2end/fixtures/inproc_fixture.h - - test/core/end2end/fixtures/local_util.h - - test/core/end2end/fixtures/proxy.h - - test/core/end2end/fixtures/secure_fixture.h - - test/core/end2end/fixtures/sockpair_fixture.h - - test/core/end2end/tests/cancel_test_helpers.h - - test/core/util/test_lb_policies.h - src: - - test/core/end2end/cq_verifier.cc - - test/core/end2end/data/client_certs.cc - - test/core/end2end/data/server1_cert.cc - - test/core/end2end/data/server1_key.cc - - test/core/end2end/data/test_root_cert.cc - - test/core/end2end/end2end_test_utils.cc - - test/core/end2end/end2end_tests.cc - - test/core/end2end/fixtures/http_proxy_fixture.cc - - test/core/end2end/fixtures/local_util.cc - - test/core/end2end/fixtures/proxy.cc - - test/core/end2end/tests/authority_not_supported.cc - - test/core/end2end/tests/bad_hostname.cc - - test/core/end2end/tests/bad_ping.cc - - test/core/end2end/tests/binary_metadata.cc - - test/core/end2end/tests/call_creds.cc - - test/core/end2end/tests/call_host_override.cc - - test/core/end2end/tests/cancel_after_accept.cc - - test/core/end2end/tests/cancel_after_client_done.cc - - test/core/end2end/tests/cancel_after_invoke.cc - - test/core/end2end/tests/cancel_after_round_trip.cc - - test/core/end2end/tests/cancel_before_invoke.cc - - test/core/end2end/tests/cancel_in_a_vacuum.cc - - test/core/end2end/tests/cancel_with_status.cc - - test/core/end2end/tests/channelz.cc - - test/core/end2end/tests/client_streaming.cc - - test/core/end2end/tests/compressed_payload.cc - - test/core/end2end/tests/connectivity.cc - - test/core/end2end/tests/default_host.cc - - test/core/end2end/tests/disappearing_server.cc - - test/core/end2end/tests/empty_batch.cc - - test/core/end2end/tests/filter_causes_close.cc - - test/core/end2end/tests/filter_context.cc - - test/core/end2end/tests/filter_init_fails.cc - - test/core/end2end/tests/filter_latency.cc - - test/core/end2end/tests/filter_status_code.cc - - test/core/end2end/tests/filtered_metadata.cc - - test/core/end2end/tests/graceful_server_shutdown.cc - - test/core/end2end/tests/grpc_authz.cc - - test/core/end2end/tests/high_initial_seqno.cc - - test/core/end2end/tests/hpack_size.cc - - test/core/end2end/tests/invoke_large_request.cc - - test/core/end2end/tests/keepalive_timeout.cc - - test/core/end2end/tests/large_metadata.cc - - test/core/end2end/tests/max_concurrent_streams.cc - - test/core/end2end/tests/max_connection_age.cc - - test/core/end2end/tests/max_connection_idle.cc - - test/core/end2end/tests/max_message_length.cc - - test/core/end2end/tests/negative_deadline.cc - - test/core/end2end/tests/no_logging.cc - - test/core/end2end/tests/no_op.cc - - test/core/end2end/tests/payload.cc - - test/core/end2end/tests/ping.cc - - test/core/end2end/tests/ping_pong_streaming.cc - - test/core/end2end/tests/proxy_auth.cc - - test/core/end2end/tests/registered_call.cc - - test/core/end2end/tests/request_with_flags.cc - - test/core/end2end/tests/request_with_payload.cc - - test/core/end2end/tests/resource_quota_server.cc - - test/core/end2end/tests/retry.cc - - test/core/end2end/tests/retry_cancel_after_first_attempt_starts.cc - - test/core/end2end/tests/retry_cancel_during_delay.cc - - test/core/end2end/tests/retry_cancel_with_multiple_send_batches.cc - - test/core/end2end/tests/retry_cancellation.cc - - test/core/end2end/tests/retry_disabled.cc - - test/core/end2end/tests/retry_exceeds_buffer_size_in_delay.cc - - test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc - - test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc - - test/core/end2end/tests/retry_lb_drop.cc - - test/core/end2end/tests/retry_lb_fail.cc - - test/core/end2end/tests/retry_non_retriable_status.cc - - test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc - - test/core/end2end/tests/retry_per_attempt_recv_timeout.cc - - test/core/end2end/tests/retry_per_attempt_recv_timeout_on_last_attempt.cc - - test/core/end2end/tests/retry_recv_initial_metadata.cc - - test/core/end2end/tests/retry_recv_message.cc - - test/core/end2end/tests/retry_recv_message_replay.cc - - test/core/end2end/tests/retry_recv_trailing_metadata_error.cc - - test/core/end2end/tests/retry_send_initial_metadata_refs.cc - - test/core/end2end/tests/retry_send_op_fails.cc - - test/core/end2end/tests/retry_send_recv_batch.cc - - test/core/end2end/tests/retry_server_pushback_delay.cc - - test/core/end2end/tests/retry_server_pushback_disabled.cc - - test/core/end2end/tests/retry_streaming.cc - - test/core/end2end/tests/retry_streaming_after_commit.cc - - test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc - - test/core/end2end/tests/retry_throttled.cc - - test/core/end2end/tests/retry_too_many_attempts.cc - - test/core/end2end/tests/retry_transparent_goaway.cc - - test/core/end2end/tests/retry_transparent_max_concurrent_streams.cc - - test/core/end2end/tests/retry_transparent_not_sent_on_wire.cc - - test/core/end2end/tests/retry_unref_before_finish.cc - - test/core/end2end/tests/retry_unref_before_recv.cc - - test/core/end2end/tests/server_finishes_request.cc - - test/core/end2end/tests/server_streaming.cc - - test/core/end2end/tests/shutdown_finishes_calls.cc - - test/core/end2end/tests/shutdown_finishes_tags.cc - - test/core/end2end/tests/simple_delayed_request.cc - - test/core/end2end/tests/simple_metadata.cc - - test/core/end2end/tests/simple_request.cc - - test/core/end2end/tests/streaming_error_response.cc - - test/core/end2end/tests/trailing_metadata.cc - - test/core/end2end/tests/write_buffering.cc - - test/core/end2end/tests/write_buffering_at_end.cc - - test/core/util/test_lb_policies.cc - deps: - - grpc_authorization_provider - - grpc_test_util - name: gpr build: all language: c @@ -4185,118 +4057,6 @@ libs: deps: - grpc++ targets: -- name: bad_server_response_test - build: test - language: c - headers: - - test/core/end2end/cq_verifier.h - src: - - test/core/end2end/bad_server_response_test.cc - - test/core/end2end/cq_verifier.cc - deps: - - grpc_test_util -- name: bad_ssl_alpn_test - build: test - language: c - headers: - - test/core/end2end/cq_verifier.h - - test/core/util/cmdline.h - - test/core/util/evaluate_args_test_util.h - - test/core/util/fuzzer_util.h - - test/core/util/grpc_profiler.h - - test/core/util/histogram.h - - test/core/util/mock_authorization_endpoint.h - - test/core/util/mock_endpoint.h - - test/core/util/parse_hexstring.h - - test/core/util/passthru_endpoint.h - - test/core/util/resolve_localhost_ip46.h - - test/core/util/slice_splitter.h - - test/core/util/subprocess.h - - test/core/util/tracer_util.h - src: - - test/core/bad_ssl/bad_ssl_test.cc - - test/core/end2end/cq_verifier.cc - - test/core/util/cmdline.cc - - test/core/util/fuzzer_util.cc - - test/core/util/grpc_profiler.cc - - test/core/util/histogram.cc - - test/core/util/mock_endpoint.cc - - test/core/util/parse_hexstring.cc - - test/core/util/passthru_endpoint.cc - - test/core/util/resolve_localhost_ip46.cc - - test/core/util/slice_splitter.cc - - test/core/util/subprocess_posix.cc - - test/core/util/subprocess_windows.cc - - test/core/util/tracer_util.cc - deps: - - grpc_test_util - platforms: - - linux - - posix - - mac -- name: bad_ssl_cert_test - build: test - language: c - headers: - - test/core/end2end/cq_verifier.h - - test/core/util/cmdline.h - - test/core/util/evaluate_args_test_util.h - - test/core/util/fuzzer_util.h - - test/core/util/grpc_profiler.h - - test/core/util/histogram.h - - test/core/util/mock_authorization_endpoint.h - - test/core/util/mock_endpoint.h - - test/core/util/parse_hexstring.h - - test/core/util/passthru_endpoint.h - - test/core/util/resolve_localhost_ip46.h - - test/core/util/slice_splitter.h - - test/core/util/subprocess.h - - test/core/util/tracer_util.h - src: - - test/core/bad_ssl/bad_ssl_test.cc - - test/core/end2end/cq_verifier.cc - - test/core/util/cmdline.cc - - test/core/util/fuzzer_util.cc - - test/core/util/grpc_profiler.cc - - test/core/util/histogram.cc - - test/core/util/mock_endpoint.cc - - test/core/util/parse_hexstring.cc - - test/core/util/passthru_endpoint.cc - - test/core/util/resolve_localhost_ip46.cc - - test/core/util/slice_splitter.cc - - test/core/util/subprocess_posix.cc - - test/core/util/subprocess_windows.cc - - test/core/util/tracer_util.cc - deps: - - grpc_test_util - platforms: - - linux - - posix - - mac -- name: connection_refused_test - build: test - language: c - headers: - - test/core/end2end/cq_verifier.h - src: - - test/core/end2end/connection_refused_test.cc - - test/core/end2end/cq_verifier.cc - deps: - - grpc_test_util -- name: dualstack_socket_test - build: test - language: c - headers: - - test/core/end2end/cq_verifier.h - src: - - test/core/end2end/cq_verifier.cc - - test/core/end2end/dualstack_socket_test.cc - deps: - - grpc_test_util - platforms: - - linux - - posix - - mac - name: fd_conservation_posix_test build: test language: c @@ -4334,26 +4094,6 @@ targets: - linux - posix - mac -- name: goaway_server_test - build: test - language: c - headers: - - test/core/end2end/cq_verifier.h - src: - - test/core/end2end/cq_verifier.cc - - test/core/end2end/goaway_server_test.cc - deps: - - grpc_test_util -- name: invalid_call_argument_test - build: test - language: c - headers: - - test/core/end2end/cq_verifier.h - src: - - test/core/end2end/cq_verifier.cc - - test/core/end2end/invalid_call_argument_test.cc - deps: - - grpc_test_util - name: multiple_server_queues_test build: test language: c @@ -4362,16 +4102,6 @@ targets: - test/core/end2end/multiple_server_queues_test.cc deps: - grpc_test_util -- name: no_server_test - build: test - language: c - headers: - - test/core/end2end/cq_verifier.h - src: - - test/core/end2end/cq_verifier.cc - - test/core/end2end/no_server_test.cc - deps: - - grpc_test_util - name: pollset_windows_starvation_test build: test language: c @@ -4905,21 +4635,146 @@ targets: deps: - grpc_test_util uses_polling: false -- name: auth_property_iterator_test +- name: auth_property_iterator_test + gtest: true + build: test + language: c++ + headers: [] + src: + - test/cpp/common/auth_property_iterator_test.cc + deps: + - grpc++_test_util + uses_polling: false +- name: authorization_matchers_test + gtest: true + build: test + language: c++ + headers: + - test/core/util/cmdline.h + - test/core/util/evaluate_args_test_util.h + - test/core/util/fuzzer_util.h + - test/core/util/grpc_profiler.h + - test/core/util/histogram.h + - test/core/util/mock_authorization_endpoint.h + - test/core/util/mock_endpoint.h + - test/core/util/parse_hexstring.h + - test/core/util/passthru_endpoint.h + - test/core/util/resolve_localhost_ip46.h + - test/core/util/slice_splitter.h + - test/core/util/subprocess.h + - test/core/util/tracer_util.h + src: + - test/core/security/authorization_matchers_test.cc + - test/core/util/cmdline.cc + - test/core/util/fuzzer_util.cc + - test/core/util/grpc_profiler.cc + - test/core/util/histogram.cc + - test/core/util/mock_endpoint.cc + - test/core/util/parse_hexstring.cc + - test/core/util/passthru_endpoint.cc + - test/core/util/resolve_localhost_ip46.cc + - test/core/util/slice_splitter.cc + - test/core/util/subprocess_posix.cc + - test/core/util/subprocess_windows.cc + - test/core/util/tracer_util.cc + deps: + - grpc_test_util +- name: authorization_policy_provider_test + gtest: true + build: test + language: c++ + headers: [] + src: + - src/cpp/server/authorization_policy_provider.cc + - test/cpp/server/authorization_policy_provider_test.cc + deps: + - grpc++ + - grpc_authorization_provider + - grpc_test_util +- name: avl_test + gtest: true + build: test + language: c++ + headers: + - src/core/lib/avl/avl.h + - src/core/lib/gpr/useful.h + src: + - test/core/avl/avl_test.cc + deps: + - absl/strings:strings + - absl/types:variant + uses_polling: false +- name: aws_request_signer_test + gtest: true + build: test + language: c++ + headers: + - test/core/util/cmdline.h + - test/core/util/evaluate_args_test_util.h + - test/core/util/fuzzer_util.h + - test/core/util/grpc_profiler.h + - test/core/util/histogram.h + - test/core/util/mock_authorization_endpoint.h + - test/core/util/mock_endpoint.h + - test/core/util/parse_hexstring.h + - test/core/util/passthru_endpoint.h + - test/core/util/resolve_localhost_ip46.h + - test/core/util/slice_splitter.h + - test/core/util/subprocess.h + - test/core/util/tracer_util.h + src: + - test/core/security/aws_request_signer_test.cc + - test/core/util/cmdline.cc + - test/core/util/fuzzer_util.cc + - test/core/util/grpc_profiler.cc + - test/core/util/histogram.cc + - test/core/util/mock_endpoint.cc + - test/core/util/parse_hexstring.cc + - test/core/util/passthru_endpoint.cc + - test/core/util/resolve_localhost_ip46.cc + - test/core/util/slice_splitter.cc + - test/core/util/subprocess_posix.cc + - test/core/util/subprocess_windows.cc + - test/core/util/tracer_util.cc + deps: + - grpc_test_util +- name: b64_test + gtest: true + build: test + language: c++ + headers: [] + src: + - test/core/slice/b64_test.cc + deps: + - grpc_test_util + uses_polling: false +- name: backoff_test + gtest: true + build: test + language: c++ + headers: [] + src: + - test/core/backoff/backoff_test.cc + deps: + - grpc_test_util + uses_polling: false +- name: bad_server_response_test gtest: true build: test language: c++ - headers: [] + headers: + - test/core/end2end/cq_verifier.h src: - - test/cpp/common/auth_property_iterator_test.cc + - test/core/end2end/bad_server_response_test.cc + - test/core/end2end/cq_verifier.cc deps: - - grpc++_test_util - uses_polling: false -- name: authorization_matchers_test + - grpc_test_util +- name: bad_ssl_alpn_test gtest: true build: test language: c++ headers: + - test/core/end2end/cq_verifier.h - test/core/util/cmdline.h - test/core/util/evaluate_args_test_util.h - test/core/util/fuzzer_util.h @@ -4934,7 +4789,8 @@ targets: - test/core/util/subprocess.h - test/core/util/tracer_util.h src: - - test/core/security/authorization_matchers_test.cc + - test/core/bad_ssl/bad_ssl_test.cc + - test/core/end2end/cq_verifier.cc - test/core/util/cmdline.cc - test/core/util/fuzzer_util.cc - test/core/util/grpc_profiler.cc @@ -4949,36 +4805,16 @@ targets: - test/core/util/tracer_util.cc deps: - grpc_test_util -- name: authorization_policy_provider_test - gtest: true - build: test - language: c++ - headers: [] - src: - - src/cpp/server/authorization_policy_provider.cc - - test/cpp/server/authorization_policy_provider_test.cc - deps: - - grpc++ - - grpc_authorization_provider - - grpc_test_util -- name: avl_test - gtest: true - build: test - language: c++ - headers: - - src/core/lib/avl/avl.h - - src/core/lib/gpr/useful.h - src: - - test/core/avl/avl_test.cc - deps: - - absl/strings:strings - - absl/types:variant - uses_polling: false -- name: aws_request_signer_test + platforms: + - linux + - posix + - mac +- name: bad_ssl_cert_test gtest: true build: test language: c++ headers: + - test/core/end2end/cq_verifier.h - test/core/util/cmdline.h - test/core/util/evaluate_args_test_util.h - test/core/util/fuzzer_util.h @@ -4993,7 +4829,8 @@ targets: - test/core/util/subprocess.h - test/core/util/tracer_util.h src: - - test/core/security/aws_request_signer_test.cc + - test/core/bad_ssl/bad_ssl_test.cc + - test/core/end2end/cq_verifier.cc - test/core/util/cmdline.cc - test/core/util/fuzzer_util.cc - test/core/util/grpc_profiler.cc @@ -5008,26 +4845,10 @@ targets: - test/core/util/tracer_util.cc deps: - grpc_test_util -- name: b64_test - gtest: true - build: test - language: c++ - headers: [] - src: - - test/core/slice/b64_test.cc - deps: - - grpc_test_util - uses_polling: false -- name: backoff_test - gtest: true - build: test - language: c++ - headers: [] - src: - - test/core/backoff/backoff_test.cc - deps: - - grpc_test_util - uses_polling: false + platforms: + - linux + - posix + - mac - name: bad_streaming_id_bad_client_test gtest: true build: test @@ -6225,6 +6046,17 @@ targets: - test/core/end2end/cq_verifier.cc deps: - grpc_test_util +- name: connection_refused_test + gtest: true + build: test + language: c++ + headers: + - test/core/end2end/cq_verifier.h + src: + - test/core/end2end/connection_refused_test.cc + - test/core/end2end/cq_verifier.cc + deps: + - grpc_test_util - name: connectivity_state_test gtest: true build: test @@ -6330,6 +6162,126 @@ targets: deps: - grpc uses_polling: false +- name: core_end2end_tests + gtest: true + build: test + language: c++ + headers: + - test/core/end2end/cq_verifier.h + - test/core/end2end/end2end_tests.h + - test/core/end2end/fixtures/h2_oauth2_common.h + - test/core/end2end/fixtures/h2_ssl_cred_reload_fixture.h + - test/core/end2end/fixtures/h2_ssl_tls_common.h + - test/core/end2end/fixtures/h2_tls_common.h + - test/core/end2end/fixtures/http_proxy_fixture.h + - test/core/end2end/fixtures/inproc_fixture.h + - test/core/end2end/fixtures/local_util.h + - test/core/end2end/fixtures/proxy.h + - test/core/end2end/fixtures/secure_fixture.h + - test/core/end2end/fixtures/sockpair_fixture.h + - test/core/end2end/tests/cancel_test_helpers.h + - test/core/util/test_lb_policies.h + src: + - test/core/end2end/cq_verifier.cc + - test/core/end2end/end2end_test_main.cc + - test/core/end2end/end2end_tests.cc + - test/core/end2end/fixtures/http_proxy_fixture.cc + - test/core/end2end/fixtures/local_util.cc + - test/core/end2end/fixtures/proxy.cc + - test/core/end2end/tests/bad_ping.cc + - test/core/end2end/tests/binary_metadata.cc + - test/core/end2end/tests/call_creds.cc + - test/core/end2end/tests/call_host_override.cc + - test/core/end2end/tests/cancel_after_accept.cc + - test/core/end2end/tests/cancel_after_client_done.cc + - test/core/end2end/tests/cancel_after_invoke.cc + - test/core/end2end/tests/cancel_after_round_trip.cc + - test/core/end2end/tests/cancel_before_invoke.cc + - test/core/end2end/tests/cancel_in_a_vacuum.cc + - test/core/end2end/tests/cancel_with_status.cc + - test/core/end2end/tests/channelz.cc + - test/core/end2end/tests/client_streaming.cc + - test/core/end2end/tests/compressed_payload.cc + - test/core/end2end/tests/connectivity.cc + - test/core/end2end/tests/default_host.cc + - test/core/end2end/tests/disappearing_server.cc + - test/core/end2end/tests/empty_batch.cc + - test/core/end2end/tests/filter_causes_close.cc + - test/core/end2end/tests/filter_context.cc + - test/core/end2end/tests/filter_init_fails.cc + - test/core/end2end/tests/filtered_metadata.cc + - test/core/end2end/tests/graceful_server_shutdown.cc + - test/core/end2end/tests/grpc_authz.cc + - test/core/end2end/tests/high_initial_seqno.cc + - test/core/end2end/tests/hpack_size.cc + - test/core/end2end/tests/invoke_large_request.cc + - test/core/end2end/tests/keepalive_timeout.cc + - test/core/end2end/tests/large_metadata.cc + - test/core/end2end/tests/max_concurrent_streams.cc + - test/core/end2end/tests/max_connection_age.cc + - test/core/end2end/tests/max_connection_idle.cc + - test/core/end2end/tests/max_message_length.cc + - test/core/end2end/tests/negative_deadline.cc + - test/core/end2end/tests/no_logging.cc + - test/core/end2end/tests/no_op.cc + - test/core/end2end/tests/payload.cc + - test/core/end2end/tests/ping.cc + - test/core/end2end/tests/ping_pong_streaming.cc + - test/core/end2end/tests/proxy_auth.cc + - test/core/end2end/tests/registered_call.cc + - test/core/end2end/tests/request_with_flags.cc + - test/core/end2end/tests/request_with_payload.cc + - test/core/end2end/tests/resource_quota_server.cc + - test/core/end2end/tests/retry.cc + - test/core/end2end/tests/retry_cancel_after_first_attempt_starts.cc + - test/core/end2end/tests/retry_cancel_during_delay.cc + - test/core/end2end/tests/retry_cancel_with_multiple_send_batches.cc + - test/core/end2end/tests/retry_cancellation.cc + - test/core/end2end/tests/retry_disabled.cc + - test/core/end2end/tests/retry_exceeds_buffer_size_in_delay.cc + - test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc + - test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc + - test/core/end2end/tests/retry_lb_drop.cc + - test/core/end2end/tests/retry_lb_fail.cc + - test/core/end2end/tests/retry_non_retriable_status.cc + - test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc + - test/core/end2end/tests/retry_per_attempt_recv_timeout.cc + - test/core/end2end/tests/retry_per_attempt_recv_timeout_on_last_attempt.cc + - test/core/end2end/tests/retry_recv_initial_metadata.cc + - test/core/end2end/tests/retry_recv_message.cc + - test/core/end2end/tests/retry_recv_message_replay.cc + - test/core/end2end/tests/retry_recv_trailing_metadata_error.cc + - test/core/end2end/tests/retry_send_initial_metadata_refs.cc + - test/core/end2end/tests/retry_send_op_fails.cc + - test/core/end2end/tests/retry_send_recv_batch.cc + - test/core/end2end/tests/retry_server_pushback_delay.cc + - test/core/end2end/tests/retry_server_pushback_disabled.cc + - test/core/end2end/tests/retry_streaming.cc + - test/core/end2end/tests/retry_streaming_after_commit.cc + - test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc + - test/core/end2end/tests/retry_throttled.cc + - test/core/end2end/tests/retry_too_many_attempts.cc + - test/core/end2end/tests/retry_transparent_goaway.cc + - test/core/end2end/tests/retry_transparent_max_concurrent_streams.cc + - test/core/end2end/tests/retry_transparent_not_sent_on_wire.cc + - test/core/end2end/tests/retry_unref_before_finish.cc + - test/core/end2end/tests/retry_unref_before_recv.cc + - test/core/end2end/tests/server_finishes_request.cc + - test/core/end2end/tests/server_streaming.cc + - test/core/end2end/tests/shutdown_finishes_calls.cc + - test/core/end2end/tests/shutdown_finishes_tags.cc + - test/core/end2end/tests/simple_delayed_request.cc + - test/core/end2end/tests/simple_metadata.cc + - test/core/end2end/tests/simple_request.cc + - test/core/end2end/tests/streaming_error_response.cc + - test/core/end2end/tests/trailing_metadata.cc + - test/core/end2end/tests/write_buffering.cc + - test/core/end2end/tests/write_buffering_at_end.cc + - test/core/util/test_lb_policies.cc + deps: + - grpc_authorization_provider + - grpc_unsecure + - grpc_test_util - name: cpp_impl_of_test gtest: true build: test @@ -6425,6 +6377,21 @@ targets: - test/core/gprpp/dual_ref_counted_test.cc deps: - grpc_test_util +- name: dualstack_socket_test + gtest: true + build: test + language: c++ + headers: + - test/core/end2end/cq_verifier.h + src: + - test/core/end2end/cq_verifier.cc + - test/core/end2end/dualstack_socket_test.cc + deps: + - grpc_test_util + platforms: + - linux + - posix + - mac - name: duplicate_header_bad_client_test gtest: true build: test @@ -7968,6 +7935,17 @@ targets: - test/cpp/end2end/generic_end2end_test.cc deps: - grpc++_test_util +- name: goaway_server_test + gtest: true + build: test + language: c++ + headers: + - test/core/end2end/cq_verifier.h + src: + - test/core/end2end/cq_verifier.cc + - test/core/end2end/goaway_server_test.cc + deps: + - grpc_test_util - name: google_c2p_resolver_test gtest: true build: test @@ -8505,12 +8483,14 @@ targets: - test/core/end2end/fixtures/local_util.h - test/core/end2end/fixtures/secure_fixture.h - test/core/end2end/fixtures/sockpair_fixture.h + - test/core/end2end/tests/cancel_test_helpers.h src: - test/core/end2end/cq_verifier.cc - test/core/end2end/data/client_certs.cc - test/core/end2end/data/server1_cert.cc - test/core/end2end/data/server1_key.cc - test/core/end2end/data/test_root_cert.cc + - test/core/end2end/end2end_tests.cc - test/core/end2end/fixtures/local_util.cc - test/core/end2end/h2_ssl_cert_test.cc deps: @@ -9043,6 +9023,17 @@ targets: deps: - grpc++_test_config - grpc++_test_util +- name: invalid_call_argument_test + gtest: true + build: test + language: c++ + headers: + - test/core/end2end/cq_verifier.h + src: + - test/core/end2end/cq_verifier.cc + - test/core/end2end/invalid_call_argument_test.cc + deps: + - grpc_test_util - name: iocp_test gtest: true build: test @@ -9676,6 +9667,17 @@ targets: - test/core/gprpp/no_destruct_test.cc deps: [] uses_polling: false +- name: no_server_test + gtest: true + build: test + language: c++ + headers: + - test/core/end2end/cq_verifier.h + src: + - test/core/end2end/cq_verifier.cc + - test/core/end2end/no_server_test.cc + deps: + - grpc_test_util - name: nonblocking_test gtest: true build: test diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index df22e350a6edc..c071ff7c43bac 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -2799,159 +2799,6 @@ Pod::Spec.new do |s| 'third_party/objective_c/Cronet/bidirectional_stream_c.h' end - s.subspec 'Tests' do |ss| - ss.header_mappings_dir = '.' - - ss.dependency "#{s.name}/Interface", version - ss.dependency "#{s.name}/Implementation", version - ss.dependency 'abseil/debugging/failure_signal_handler', abseil_version - ss.dependency 'abseil/debugging/stacktrace', abseil_version - ss.dependency 'abseil/debugging/symbolize', abseil_version - - ss.source_files = 'src/core/lib/security/authorization/grpc_authorization_policy_provider.cc', - 'src/core/lib/security/authorization/grpc_authorization_policy_provider.h', - 'src/core/lib/security/authorization/rbac_translator.cc', - 'src/core/lib/security/authorization/rbac_translator.h', - 'test/core/end2end/cq_verifier.cc', - 'test/core/end2end/cq_verifier.h', - 'test/core/end2end/data/client_certs.cc', - 'test/core/end2end/data/server1_cert.cc', - 'test/core/end2end/data/server1_key.cc', - 'test/core/end2end/data/ssl_test_data.h', - 'test/core/end2end/data/test_root_cert.cc', - 'test/core/end2end/end2end_test_utils.cc', - 'test/core/end2end/end2end_tests.cc', - 'test/core/end2end/end2end_tests.h', - 'test/core/end2end/fixtures/h2_oauth2_common.h', - 'test/core/end2end/fixtures/h2_ssl_cred_reload_fixture.h', - 'test/core/end2end/fixtures/h2_ssl_tls_common.h', - 'test/core/end2end/fixtures/h2_tls_common.h', - 'test/core/end2end/fixtures/http_proxy_fixture.cc', - 'test/core/end2end/fixtures/http_proxy_fixture.h', - 'test/core/end2end/fixtures/inproc_fixture.h', - 'test/core/end2end/fixtures/local_util.cc', - 'test/core/end2end/fixtures/local_util.h', - 'test/core/end2end/fixtures/proxy.cc', - 'test/core/end2end/fixtures/proxy.h', - 'test/core/end2end/fixtures/secure_fixture.h', - 'test/core/end2end/fixtures/sockpair_fixture.h', - 'test/core/end2end/tests/authority_not_supported.cc', - 'test/core/end2end/tests/bad_hostname.cc', - 'test/core/end2end/tests/bad_ping.cc', - 'test/core/end2end/tests/binary_metadata.cc', - 'test/core/end2end/tests/call_creds.cc', - 'test/core/end2end/tests/call_host_override.cc', - 'test/core/end2end/tests/cancel_after_accept.cc', - 'test/core/end2end/tests/cancel_after_client_done.cc', - 'test/core/end2end/tests/cancel_after_invoke.cc', - 'test/core/end2end/tests/cancel_after_round_trip.cc', - 'test/core/end2end/tests/cancel_before_invoke.cc', - 'test/core/end2end/tests/cancel_in_a_vacuum.cc', - 'test/core/end2end/tests/cancel_test_helpers.h', - 'test/core/end2end/tests/cancel_with_status.cc', - 'test/core/end2end/tests/channelz.cc', - 'test/core/end2end/tests/client_streaming.cc', - 'test/core/end2end/tests/compressed_payload.cc', - 'test/core/end2end/tests/connectivity.cc', - 'test/core/end2end/tests/default_host.cc', - 'test/core/end2end/tests/disappearing_server.cc', - 'test/core/end2end/tests/empty_batch.cc', - 'test/core/end2end/tests/filter_causes_close.cc', - 'test/core/end2end/tests/filter_context.cc', - 'test/core/end2end/tests/filter_init_fails.cc', - 'test/core/end2end/tests/filter_latency.cc', - 'test/core/end2end/tests/filter_status_code.cc', - 'test/core/end2end/tests/filtered_metadata.cc', - 'test/core/end2end/tests/graceful_server_shutdown.cc', - 'test/core/end2end/tests/grpc_authz.cc', - 'test/core/end2end/tests/high_initial_seqno.cc', - 'test/core/end2end/tests/hpack_size.cc', - 'test/core/end2end/tests/invoke_large_request.cc', - 'test/core/end2end/tests/keepalive_timeout.cc', - 'test/core/end2end/tests/large_metadata.cc', - 'test/core/end2end/tests/max_concurrent_streams.cc', - 'test/core/end2end/tests/max_connection_age.cc', - 'test/core/end2end/tests/max_connection_idle.cc', - 'test/core/end2end/tests/max_message_length.cc', - 'test/core/end2end/tests/negative_deadline.cc', - 'test/core/end2end/tests/no_logging.cc', - 'test/core/end2end/tests/no_op.cc', - 'test/core/end2end/tests/payload.cc', - 'test/core/end2end/tests/ping.cc', - 'test/core/end2end/tests/ping_pong_streaming.cc', - 'test/core/end2end/tests/proxy_auth.cc', - 'test/core/end2end/tests/registered_call.cc', - 'test/core/end2end/tests/request_with_flags.cc', - 'test/core/end2end/tests/request_with_payload.cc', - 'test/core/end2end/tests/resource_quota_server.cc', - 'test/core/end2end/tests/retry.cc', - 'test/core/end2end/tests/retry_cancel_after_first_attempt_starts.cc', - 'test/core/end2end/tests/retry_cancel_during_delay.cc', - 'test/core/end2end/tests/retry_cancel_with_multiple_send_batches.cc', - 'test/core/end2end/tests/retry_cancellation.cc', - 'test/core/end2end/tests/retry_disabled.cc', - 'test/core/end2end/tests/retry_exceeds_buffer_size_in_delay.cc', - 'test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc', - 'test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc', - 'test/core/end2end/tests/retry_lb_drop.cc', - 'test/core/end2end/tests/retry_lb_fail.cc', - 'test/core/end2end/tests/retry_non_retriable_status.cc', - 'test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc', - 'test/core/end2end/tests/retry_per_attempt_recv_timeout.cc', - 'test/core/end2end/tests/retry_per_attempt_recv_timeout_on_last_attempt.cc', - 'test/core/end2end/tests/retry_recv_initial_metadata.cc', - 'test/core/end2end/tests/retry_recv_message.cc', - 'test/core/end2end/tests/retry_recv_message_replay.cc', - 'test/core/end2end/tests/retry_recv_trailing_metadata_error.cc', - 'test/core/end2end/tests/retry_send_initial_metadata_refs.cc', - 'test/core/end2end/tests/retry_send_op_fails.cc', - 'test/core/end2end/tests/retry_send_recv_batch.cc', - 'test/core/end2end/tests/retry_server_pushback_delay.cc', - 'test/core/end2end/tests/retry_server_pushback_disabled.cc', - 'test/core/end2end/tests/retry_streaming.cc', - 'test/core/end2end/tests/retry_streaming_after_commit.cc', - 'test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc', - 'test/core/end2end/tests/retry_throttled.cc', - 'test/core/end2end/tests/retry_too_many_attempts.cc', - 'test/core/end2end/tests/retry_transparent_goaway.cc', - 'test/core/end2end/tests/retry_transparent_max_concurrent_streams.cc', - 'test/core/end2end/tests/retry_transparent_not_sent_on_wire.cc', - 'test/core/end2end/tests/retry_unref_before_finish.cc', - 'test/core/end2end/tests/retry_unref_before_recv.cc', - 'test/core/end2end/tests/server_finishes_request.cc', - 'test/core/end2end/tests/server_streaming.cc', - 'test/core/end2end/tests/shutdown_finishes_calls.cc', - 'test/core/end2end/tests/shutdown_finishes_tags.cc', - 'test/core/end2end/tests/simple_delayed_request.cc', - 'test/core/end2end/tests/simple_metadata.cc', - 'test/core/end2end/tests/simple_request.cc', - 'test/core/end2end/tests/streaming_error_response.cc', - 'test/core/end2end/tests/trailing_metadata.cc', - 'test/core/end2end/tests/write_buffering.cc', - 'test/core/end2end/tests/write_buffering_at_end.cc', - 'test/core/event_engine/test_init.cc', - 'test/core/event_engine/test_init.h', - 'test/core/util/build.cc', - 'test/core/util/build.h', - 'test/core/util/port.cc', - 'test/core/util/port.h', - 'test/core/util/port_isolated_runtime_environment.cc', - 'test/core/util/port_server_client.cc', - 'test/core/util/port_server_client.h', - 'test/core/util/reconnect_server.cc', - 'test/core/util/reconnect_server.h', - 'test/core/util/stack_tracer.cc', - 'test/core/util/stack_tracer.h', - 'test/core/util/test_config.cc', - 'test/core/util/test_config.h', - 'test/core/util/test_lb_policies.cc', - 'test/core/util/test_lb_policies.h', - 'test/core/util/test_tcp_server.cc', - 'test/core/util/test_tcp_server.h', - 'test/core/util/tls_utils.cc', - 'test/core/util/tls_utils.h' - end - # patch include of openssl to openssl_grpc s.prepare_command = <<-END_OF_COMMAND set -e diff --git a/grpc.gyp b/grpc.gyp index e6410454c7bfa..d912b9ffd24d4 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -173,120 +173,6 @@ 'third_party/address_sorting/address_sorting_windows.c', ], }, - { - 'target_name': 'end2end_tests', - 'type': 'static_library', - 'dependencies': [ - 'grpc_authorization_provider', - 'grpc_test_util', - ], - 'sources': [ - 'test/core/end2end/cq_verifier.cc', - 'test/core/end2end/data/client_certs.cc', - 'test/core/end2end/data/server1_cert.cc', - 'test/core/end2end/data/server1_key.cc', - 'test/core/end2end/data/test_root_cert.cc', - 'test/core/end2end/end2end_test_utils.cc', - 'test/core/end2end/end2end_tests.cc', - 'test/core/end2end/fixtures/http_proxy_fixture.cc', - 'test/core/end2end/fixtures/local_util.cc', - 'test/core/end2end/fixtures/proxy.cc', - 'test/core/end2end/tests/authority_not_supported.cc', - 'test/core/end2end/tests/bad_hostname.cc', - 'test/core/end2end/tests/bad_ping.cc', - 'test/core/end2end/tests/binary_metadata.cc', - 'test/core/end2end/tests/call_creds.cc', - 'test/core/end2end/tests/call_host_override.cc', - 'test/core/end2end/tests/cancel_after_accept.cc', - 'test/core/end2end/tests/cancel_after_client_done.cc', - 'test/core/end2end/tests/cancel_after_invoke.cc', - 'test/core/end2end/tests/cancel_after_round_trip.cc', - 'test/core/end2end/tests/cancel_before_invoke.cc', - 'test/core/end2end/tests/cancel_in_a_vacuum.cc', - 'test/core/end2end/tests/cancel_with_status.cc', - 'test/core/end2end/tests/channelz.cc', - 'test/core/end2end/tests/client_streaming.cc', - 'test/core/end2end/tests/compressed_payload.cc', - 'test/core/end2end/tests/connectivity.cc', - 'test/core/end2end/tests/default_host.cc', - 'test/core/end2end/tests/disappearing_server.cc', - 'test/core/end2end/tests/empty_batch.cc', - 'test/core/end2end/tests/filter_causes_close.cc', - 'test/core/end2end/tests/filter_context.cc', - 'test/core/end2end/tests/filter_init_fails.cc', - 'test/core/end2end/tests/filter_latency.cc', - 'test/core/end2end/tests/filter_status_code.cc', - 'test/core/end2end/tests/filtered_metadata.cc', - 'test/core/end2end/tests/graceful_server_shutdown.cc', - 'test/core/end2end/tests/grpc_authz.cc', - 'test/core/end2end/tests/high_initial_seqno.cc', - 'test/core/end2end/tests/hpack_size.cc', - 'test/core/end2end/tests/invoke_large_request.cc', - 'test/core/end2end/tests/keepalive_timeout.cc', - 'test/core/end2end/tests/large_metadata.cc', - 'test/core/end2end/tests/max_concurrent_streams.cc', - 'test/core/end2end/tests/max_connection_age.cc', - 'test/core/end2end/tests/max_connection_idle.cc', - 'test/core/end2end/tests/max_message_length.cc', - 'test/core/end2end/tests/negative_deadline.cc', - 'test/core/end2end/tests/no_logging.cc', - 'test/core/end2end/tests/no_op.cc', - 'test/core/end2end/tests/payload.cc', - 'test/core/end2end/tests/ping.cc', - 'test/core/end2end/tests/ping_pong_streaming.cc', - 'test/core/end2end/tests/proxy_auth.cc', - 'test/core/end2end/tests/registered_call.cc', - 'test/core/end2end/tests/request_with_flags.cc', - 'test/core/end2end/tests/request_with_payload.cc', - 'test/core/end2end/tests/resource_quota_server.cc', - 'test/core/end2end/tests/retry.cc', - 'test/core/end2end/tests/retry_cancel_after_first_attempt_starts.cc', - 'test/core/end2end/tests/retry_cancel_during_delay.cc', - 'test/core/end2end/tests/retry_cancel_with_multiple_send_batches.cc', - 'test/core/end2end/tests/retry_cancellation.cc', - 'test/core/end2end/tests/retry_disabled.cc', - 'test/core/end2end/tests/retry_exceeds_buffer_size_in_delay.cc', - 'test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc', - 'test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc', - 'test/core/end2end/tests/retry_lb_drop.cc', - 'test/core/end2end/tests/retry_lb_fail.cc', - 'test/core/end2end/tests/retry_non_retriable_status.cc', - 'test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc', - 'test/core/end2end/tests/retry_per_attempt_recv_timeout.cc', - 'test/core/end2end/tests/retry_per_attempt_recv_timeout_on_last_attempt.cc', - 'test/core/end2end/tests/retry_recv_initial_metadata.cc', - 'test/core/end2end/tests/retry_recv_message.cc', - 'test/core/end2end/tests/retry_recv_message_replay.cc', - 'test/core/end2end/tests/retry_recv_trailing_metadata_error.cc', - 'test/core/end2end/tests/retry_send_initial_metadata_refs.cc', - 'test/core/end2end/tests/retry_send_op_fails.cc', - 'test/core/end2end/tests/retry_send_recv_batch.cc', - 'test/core/end2end/tests/retry_server_pushback_delay.cc', - 'test/core/end2end/tests/retry_server_pushback_disabled.cc', - 'test/core/end2end/tests/retry_streaming.cc', - 'test/core/end2end/tests/retry_streaming_after_commit.cc', - 'test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc', - 'test/core/end2end/tests/retry_throttled.cc', - 'test/core/end2end/tests/retry_too_many_attempts.cc', - 'test/core/end2end/tests/retry_transparent_goaway.cc', - 'test/core/end2end/tests/retry_transparent_max_concurrent_streams.cc', - 'test/core/end2end/tests/retry_transparent_not_sent_on_wire.cc', - 'test/core/end2end/tests/retry_unref_before_finish.cc', - 'test/core/end2end/tests/retry_unref_before_recv.cc', - 'test/core/end2end/tests/server_finishes_request.cc', - 'test/core/end2end/tests/server_streaming.cc', - 'test/core/end2end/tests/shutdown_finishes_calls.cc', - 'test/core/end2end/tests/shutdown_finishes_tags.cc', - 'test/core/end2end/tests/simple_delayed_request.cc', - 'test/core/end2end/tests/simple_metadata.cc', - 'test/core/end2end/tests/simple_request.cc', - 'test/core/end2end/tests/streaming_error_response.cc', - 'test/core/end2end/tests/trailing_metadata.cc', - 'test/core/end2end/tests/write_buffering.cc', - 'test/core/end2end/tests/write_buffering_at_end.cc', - 'test/core/util/test_lb_policies.cc', - ], - }, { 'target_name': 'gpr', 'type': 'static_library', diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index ab0fcbba904c3..27e3342ecb26f 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -369,6 +369,8 @@ static void read_channel_args(grpc_chttp2_transport* t, .GetObjectRef()); } + t->ack_pings = channel_args.GetBool("grpc.http2.ack_pings").value_or(true); + const int soft_limit = channel_args.GetInt(GRPC_ARG_MAX_METADATA_SIZE).value_or(-1); if (soft_limit < 0) { diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.cc b/src/core/ext/transport/chttp2/transport/frame_ping.cc index 897a8585bc5c7..f9f111a827e41 100644 --- a/src/core/ext/transport/chttp2/transport/frame_ping.cc +++ b/src/core/ext/transport/chttp2/transport/frame_ping.cc @@ -35,8 +35,6 @@ #include "src/core/ext/transport/chttp2/transport/stream_map.h" #include "src/core/lib/gprpp/time.h" -static bool g_disable_ping_ack = false; - grpc_slice grpc_chttp2_ping_create(uint8_t ack, uint64_t opaque_8bytes) { grpc_slice slice = GRPC_SLICE_MALLOC(9 + 8); uint8_t* p = GRPC_SLICE_START_PTR(slice); @@ -116,7 +114,7 @@ grpc_error_handle grpc_chttp2_ping_parser_parse(void* parser, t->ping_recv_state.last_ping_recv_time = now; } - if (!g_disable_ping_ack) { + if (t->ack_pings) { if (t->ping_ack_count == t->ping_ack_capacity) { t->ping_ack_capacity = std::max(t->ping_ack_capacity * 3 / 2, size_t{3}); @@ -132,7 +130,3 @@ grpc_error_handle grpc_chttp2_ping_parser_parse(void* parser, return absl::OkStatus(); } - -void grpc_set_disable_ping_ack(bool disable_ping_ack) { - g_disable_ping_ack = disable_ping_ack; -} diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.h b/src/core/ext/transport/chttp2/transport/frame_ping.h index b49e0eadada4d..6da1a3ad97070 100644 --- a/src/core/ext/transport/chttp2/transport/frame_ping.h +++ b/src/core/ext/transport/chttp2/transport/frame_ping.h @@ -43,7 +43,4 @@ grpc_error_handle grpc_chttp2_ping_parser_parse(void* parser, const grpc_slice& slice, int is_last); -// Test-only function for disabling ping ack -void grpc_set_disable_ping_ack(bool disable_ping_ack); - #endif // GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc b/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc index 1e582e28bba2c..33f04a7487d65 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc +++ b/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc @@ -69,6 +69,7 @@ auto HPackTable::MementoRingBuffer::Lookup(uint32_t index) const void HPackTable::MementoRingBuffer::Rebuild(uint32_t max_entries) { if (max_entries == max_entries_) return; + max_entries_ = max_entries; std::vector entries; entries.reserve(num_entries_); for (size_t i = 0; i < num_entries_; i++) { diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h index c25a520a5c182..921296198d0e8 100644 --- a/src/core/ext/transport/chttp2/transport/internal.h +++ b/src/core/ext/transport/chttp2/transport/internal.h @@ -436,6 +436,8 @@ struct grpc_chttp2_transport /// If start_bdp_ping_locked has been called bool bdp_ping_started = false; + // True if pings should be acked + bool ack_pings = true; // next bdp ping timer handle absl::optional next_bdp_ping_timer_handle; diff --git a/src/core/lib/debug/trace.cc b/src/core/lib/debug/trace.cc index 71d427dac6e96..add527637bb8a 100644 --- a/src/core/lib/debug/trace.cc +++ b/src/core/lib/debug/trace.cc @@ -24,6 +24,7 @@ #include #include +#include #include "absl/strings/string_view.h" @@ -77,12 +78,17 @@ void TraceFlagList::Add(TraceFlag* flag) { void TraceFlagList::LogAllTracers() { gpr_log(GPR_DEBUG, "available tracers:"); - TraceFlag* t; - for (t = root_tracer_; t != nullptr; t = t->next_tracer_) { + for (TraceFlag* t = root_tracer_; t != nullptr; t = t->next_tracer_) { gpr_log(GPR_DEBUG, "\t%s", t->name_); } } +void TraceFlagList::SaveTo(std::map& values) { + for (TraceFlag* t = root_tracer_; t != nullptr; t = t->next_tracer_) { + values[t->name_] = t->enabled(); + } +} + // Flags register themselves on the list during construction TraceFlag::TraceFlag(bool default_enabled, const char* name) : name_(name) { static_assert(std::is_trivially_destructible::value, @@ -91,6 +97,14 @@ TraceFlag::TraceFlag(bool default_enabled, const char* name) : name_(name) { TraceFlagList::Add(this); } +SavedTraceFlags::SavedTraceFlags() { TraceFlagList::SaveTo(values_); } + +void SavedTraceFlags::Restore() { + for (const auto& flag : values_) { + TraceFlagList::Set(flag.first.c_str(), flag.second); + } +} + } // namespace grpc_core static void add(const char* beg, const char* end, char*** ss, size_t* ns) { @@ -138,11 +152,6 @@ static void parse(const char* s) { gpr_free(strings); } -void grpc_tracer_init(const char* env_var_name) { - (void)env_var_name; // suppress unused variable error - grpc_tracer_init(); -} - void grpc_tracer_init() { parse(std::string(grpc_core::ConfigVars::Get().Trace()).c_str()); } diff --git a/src/core/lib/debug/trace.h b/src/core/lib/debug/trace.h index 9e7071075f974..992cc92fc5700 100644 --- a/src/core/lib/debug/trace.h +++ b/src/core/lib/debug/trace.h @@ -22,10 +22,8 @@ #include #include - -// TODO(veblush): Remove this deprecated function once codes depending on this -// function are updated in the internal repo. -void grpc_tracer_init(const char* env_var_name); +#include +#include void grpc_tracer_init(); void grpc_tracer_shutdown(void); @@ -37,6 +35,7 @@ class TraceFlagList { public: static bool Set(const char* name, bool enabled); static void Add(TraceFlag* flag); + static void SaveTo(std::map& values); private: static void LogAllTracers(); @@ -101,6 +100,15 @@ class DebugOnlyTraceFlag { }; #endif +class SavedTraceFlags { + public: + SavedTraceFlags(); + void Restore(); + + private: + std::map values_; +}; + } // namespace grpc_core #endif // GRPC_SRC_CORE_LIB_DEBUG_TRACE_H diff --git a/src/core/lib/iomgr/timer_generic.cc b/src/core/lib/iomgr/timer_generic.cc index 3282fe17d08a0..bed5ccccfe864 100644 --- a/src/core/lib/iomgr/timer_generic.cc +++ b/src/core/lib/iomgr/timer_generic.cc @@ -338,7 +338,7 @@ static void timer_init(grpc_timer* timer, grpc_core::Timestamp deadline, #endif if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_trace)) { - gpr_log(GPR_INFO, "TIMER %p: SET %" PRId64 " now %" PRId64 " call %p[%p]", + gpr_log(GPR_DEBUG, "TIMER %p: SET %" PRId64 " now %" PRId64 " call %p[%p]", timer, deadline.milliseconds_after_process_epoch(), grpc_core::Timestamp::Now().milliseconds_after_process_epoch(), closure, closure->cb); @@ -374,7 +374,7 @@ static void timer_init(grpc_timer* timer, grpc_core::Timestamp deadline, list_join(&shard->list, timer); } if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_trace)) { - gpr_log(GPR_INFO, + gpr_log(GPR_DEBUG, " .. add to shard %d with queue_deadline_cap=%" PRId64 " => is_first_timer=%s", static_cast(shard - g_shards), @@ -397,7 +397,7 @@ static void timer_init(grpc_timer* timer, grpc_core::Timestamp deadline, if (is_first_timer) { gpr_mu_lock(&g_shared_mutables.mu); if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_trace)) { - gpr_log(GPR_INFO, " .. old shard min_deadline=%" PRId64, + gpr_log(GPR_DEBUG, " .. old shard min_deadline=%" PRId64, shard->min_deadline.milliseconds_after_process_epoch()); } if (deadline < shard->min_deadline) { @@ -439,7 +439,7 @@ static void timer_cancel(grpc_timer* timer) { timer_shard* shard = &g_shards[grpc_core::HashPointer(timer, g_num_shards)]; gpr_mu_lock(&shard->mu); if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_trace)) { - gpr_log(GPR_INFO, "TIMER %p: CANCEL pending=%s", timer, + gpr_log(GPR_DEBUG, "TIMER %p: CANCEL pending=%s", timer, timer->pending ? "true" : "false"); } @@ -480,7 +480,7 @@ static bool refill_heap(timer_shard* shard, grpc_core::Timestamp now) { grpc_core::Duration::FromSecondsAsDouble(deadline_delta); if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) { - gpr_log(GPR_INFO, " .. shard[%d]->queue_deadline_cap --> %" PRId64, + gpr_log(GPR_DEBUG, " .. shard[%d]->queue_deadline_cap --> %" PRId64, static_cast(shard - g_shards), shard->queue_deadline_cap.milliseconds_after_process_epoch()); } @@ -492,7 +492,7 @@ static bool refill_heap(timer_shard* shard, grpc_core::Timestamp now) { if (timer_deadline < shard->queue_deadline_cap) { if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) { - gpr_log(GPR_INFO, " .. add timer with deadline %" PRId64 " to heap", + gpr_log(GPR_DEBUG, " .. add timer with deadline %" PRId64 " to heap", timer_deadline.milliseconds_after_process_epoch()); } list_remove(timer); @@ -509,7 +509,7 @@ static grpc_timer* pop_one(timer_shard* shard, grpc_core::Timestamp now) { grpc_timer* timer; for (;;) { if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) { - gpr_log(GPR_INFO, " .. shard[%d]: heap_empty=%s", + gpr_log(GPR_DEBUG, " .. shard[%d]: heap_empty=%s", static_cast(shard - g_shards), grpc_timer_heap_is_empty(&shard->heap) ? "true" : "false"); } @@ -522,14 +522,14 @@ static grpc_timer* pop_one(timer_shard* shard, grpc_core::Timestamp now) { grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch( timer->deadline); if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) { - gpr_log(GPR_INFO, + gpr_log(GPR_DEBUG, " .. check top timer deadline=%" PRId64 " now=%" PRId64, timer_deadline.milliseconds_after_process_epoch(), now.milliseconds_after_process_epoch()); } if (timer_deadline > now) return nullptr; if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_trace)) { - gpr_log(GPR_INFO, "TIMER %p: FIRE %" PRId64 "ms late", timer, + gpr_log(GPR_DEBUG, "TIMER %p: FIRE %" PRId64 "ms late", timer, (now - timer_deadline).millis()); } timer->pending = false; @@ -553,7 +553,7 @@ static size_t pop_timers(timer_shard* shard, grpc_core::Timestamp now, *new_min_deadline = compute_min_deadline(shard); gpr_mu_unlock(&shard->mu); if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) { - gpr_log(GPR_INFO, " .. shard[%d] popped %" PRIdPTR, + gpr_log(GPR_DEBUG, " .. shard[%d] popped %" PRIdPTR, static_cast(shard - g_shards), n); } return n; @@ -593,7 +593,7 @@ static grpc_timer_check_result run_some_expired_timers( if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) { gpr_log( - GPR_INFO, " .. shard[%d]->min_deadline = %" PRId64, + GPR_DEBUG, " .. shard[%d]->min_deadline = %" PRId64, static_cast(g_shard_queue[0] - g_shards), g_shard_queue[0]->min_deadline.milliseconds_after_process_epoch()); } @@ -612,7 +612,7 @@ static grpc_timer_check_result run_some_expired_timers( if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) { gpr_log( - GPR_INFO, + GPR_DEBUG, " .. result --> %d" ", shard[%d]->min_deadline %" PRId64 " --> %" PRId64 ", now=%" PRId64, @@ -671,7 +671,7 @@ static grpc_timer_check_result timer_check(grpc_core::Timestamp* next) { *next = std::min(*next, min_timer); } if (GRPC_TRACE_FLAG_ENABLED(grpc_timer_check_trace)) { - gpr_log(GPR_INFO, "TIMER CHECK SKIP: now=%" PRId64 " min_timer=%" PRId64, + gpr_log(GPR_DEBUG, "TIMER CHECK SKIP: now=%" PRId64 " min_timer=%" PRId64, now.milliseconds_after_process_epoch(), min_timer.milliseconds_after_process_epoch()); } @@ -693,7 +693,7 @@ static grpc_timer_check_result timer_check(grpc_core::Timestamp* next) { } #if GPR_ARCH_64 gpr_log( - GPR_INFO, + GPR_DEBUG, "TIMER CHECK BEGIN: now=%" PRId64 " next=%s tls_min=%" PRId64 " glob_min=%" PRId64, now.milliseconds_after_process_epoch(), next_str.c_str(), @@ -702,7 +702,8 @@ static grpc_timer_check_result timer_check(grpc_core::Timestamp* next) { gpr_atm_no_barrier_load((gpr_atm*)(&g_shared_mutables.min_timer))) .milliseconds_after_process_epoch()); #else - gpr_log(GPR_INFO, "TIMER CHECK BEGIN: now=%" PRId64 " next=%s min=%" PRId64, + gpr_log(GPR_DEBUG, + "TIMER CHECK BEGIN: now=%" PRId64 " next=%s min=%" PRId64, now.milliseconds_after_process_epoch(), next_str.c_str(), min_timer.milliseconds_after_process_epoch()); #endif @@ -718,7 +719,7 @@ static grpc_timer_check_result timer_check(grpc_core::Timestamp* next) { } else { next_str = absl::StrCat(next->milliseconds_after_process_epoch()); } - gpr_log(GPR_INFO, "TIMER CHECK END: r=%d; next=%s", r, next_str.c_str()); + gpr_log(GPR_DEBUG, "TIMER CHECK END: r=%d; next=%s", r, next_str.c_str()); } return r; } diff --git a/src/core/lib/slice/slice.h b/src/core/lib/slice/slice.h index 2640ee806443c..ecbdae683f749 100644 --- a/src/core/lib/slice/slice.h +++ b/src/core/lib/slice/slice.h @@ -298,6 +298,8 @@ class GPR_MSVC_EMPTY_BASE_CLASS_WORKAROUND MutableSlice // Array access uint8_t& operator[](size_t i) { return mutable_data()[i]; } + + using slice_detail::BaseSlice::c_slice_ptr; }; class GPR_MSVC_EMPTY_BASE_CLASS_WORKAROUND Slice diff --git a/src/objective-c/tests/BUILD b/src/objective-c/tests/BUILD index fd4b9c8fedeed..efb3ba8d223a0 100644 --- a/src/objective-c/tests/BUILD +++ b/src/objective-c/tests/BUILD @@ -22,7 +22,6 @@ load( "local_objc_grpc_library", "proto_library_objc_wrapper", ) -load("//test/core/end2end:generate_tests.bzl", "grpc_end2end_tests") load("@build_bazel_rules_apple//apple:resources.bzl", "apple_resource_bundle") load("@build_bazel_rules_apple//apple:macos.bzl", "macos_unit_test") load("@build_bazel_rules_apple//apple:tvos.bzl", "tvos_application", "tvos_unit_test") @@ -33,8 +32,6 @@ package(default_visibility = ["//visibility:public"]) exports_files(["LICENSE"]) -grpc_end2end_tests() - proto_library_objc_wrapper( name = "messages_proto", srcs = ["RemoteTestClient/messages.proto"], @@ -188,7 +185,8 @@ grpc_objc_testing_library( deps = [ "InteropTests-lib", "//src/objective-c:grpc_objc_client_core_cronet_testing", - "//test/core/end2end:end2end_tests", + "//test/core/end2end:cq_verifier", + "//test/core/end2end:ssl_test_data", "//third_party/objective_c/Cronet:cronet_c_for_grpc", ], ) @@ -206,7 +204,7 @@ grpc_objc_testing_library( "//:grpc++_cronet_credentials", "//src/objective-c:grpc_objc_client_core_cronet_testing", "//src/proto/grpc/testing:echo_proto", - "//test/core/end2end:end2end_tests", + "//test/core/end2end:ssl_test_data", ], ) diff --git a/src/objective-c/tests/CronetTests/CoreCronetEnd2EndTests.mm b/src/objective-c/tests/CronetTests/CoreCronetEnd2EndTests.mm deleted file mode 100644 index 02ea853b2bd6f..0000000000000 --- a/src/objective-c/tests/CronetTests/CoreCronetEnd2EndTests.mm +++ /dev/null @@ -1,330 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* - * This test file is derived from fixture h2_ssl.c in core end2end test - * (test/core/end2end/fixture/h2_ssl.c). The structure of the fixture is - * preserved as much as possible - * - * This fixture creates a server full stack using chttp2 and a client - * full stack using Cronet. End-to-end tests are run against this - * configuration - * - */ - -#import -#include "test/core/end2end/end2end_tests.h" - -#include -#include - -#include -#include -#include "src/core/lib/gprpp/crash.h" - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/config/config_vars.h" -#include "src/core/lib/gpr/string.h" -#include "src/core/lib/gpr/tmpfile.h" -#include "src/core/lib/gprpp/host_port.h" -#include "src/core/lib/security/credentials/credentials.h" - -#include "test/core/end2end/data/ssl_test_data.h" -#include "test/core/end2end/fixtures/secure_fixture.h" -#include "test/core/util/port.h" -#include "test/core/util/test_config.h" - -#import -#include - -#import "../ConfigureCronet.h" - -static void process_auth_failure(void *state, grpc_auth_context *ctx, const grpc_metadata *md, - size_t md_count, grpc_process_auth_metadata_done_cb cb, - void *user_data) { - GPR_ASSERT(state == nullptr); - cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_UNAUTHENTICATED, nullptr); -} - -class CronetFixture final : public SecureFixture { - private: - grpc_channel_credentials *MakeClientCreds(const grpc_core::ChannelArgs &args) override { - grpc_core::Crash("unreachable"); - } - grpc_server_credentials *MakeServerCreds(const grpc_core::ChannelArgs &args) override { - grpc_ssl_pem_key_cert_pair pem_cert_key_pair = {test_server1_key, test_server1_cert}; - grpc_server_credentials *ssl_creds = - grpc_ssl_server_credentials_create(nullptr, &pem_cert_key_pair, 1, 0, nullptr); - if (args.Contains(FAIL_AUTH_CHECK_SERVER_ARG_NAME)) { - grpc_auth_metadata_processor processor = {process_auth_failure, nullptr, nullptr}; - grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor); - } - return ssl_creds; - } - grpc_channel *MakeClient(const grpc_core::ChannelArgs &args) override { - stream_engine *cronetEngine = [Cronet getGlobalEngine]; - return grpc_cronet_secure_channel_create(cronetEngine, localaddr().c_str(), args.ToC().get(), - nullptr); - } -}; - -/* All test configurations */ - -static CoreTestConfiguration configs[] = { - {"chttp2/simple_ssl_fullstack", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS, nullptr, - [](const grpc_core::ChannelArgs & /*client_args*/, - const grpc_core::ChannelArgs & /*server_args*/) { - return std::make_unique(); - }}, -}; - -static char *roots_filename; - -@interface CoreCronetEnd2EndTests : XCTestCase - -@end - -@implementation CoreCronetEnd2EndTests - -// The setUp() function is run before the test cases run and only run once -+ (void)setUp { - [super setUp]; - - FILE *roots_file; - size_t roots_size = strlen(test_root_cert); - - char *argv[] = {(char *)"CoreCronetEnd2EndTests"}; - int argc = 1; - grpc_test_init(&argc, argv); - grpc_end2end_tests_pre_init(); - - /* Set the SSL roots env var. */ - roots_file = gpr_tmpfile("chttp2_simple_ssl_fullstack_test", &roots_filename); - GPR_ASSERT(roots_filename != nullptr); - GPR_ASSERT(roots_file != nullptr); - GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size); - fclose(roots_file); - grpc_core::ConfigVars::Overrides overrides; - overrides.default_ssl_roots_file_path = roots_filename; - grpc_core::ConfigVars::SetOverrides(overrides); - - grpc_init(); - - configureCronet(/*enable_netlog=*/false); -} - -// The tearDown() function is run after all test cases finish running -+ (void)tearDown { - grpc_shutdown(); - - /* Cleanup. */ - remove(roots_filename); - gpr_free(roots_filename); - - [super tearDown]; -} - -- (void)testIndividualCase:(char *)test_case { - char *argv[] = {(char *)"h2_ssl", test_case}; - for (int i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(2, argv, configs[i]); - } -} - -// TODO(mxyan): Use NSStringFromSelector(_cmd) to acquire test name from the -// test case method name, so that bodies of test cases can stay identical -- (void)testAuthorityNotSupported { - [self testIndividualCase:(char *)"authority_not_supported"]; -} - -- (void)testBadHostname { - [self testIndividualCase:(char *)"bad_hostname"]; -} - -- (void)testBinaryMetadata { - [self testIndividualCase:(char *)"binary_metadata"]; -} - -- (void)testCallCreds { - // NOT SUPPORTED - // [self testIndividualCase:(char *)"call_creds"]; -} - -- (void)testCancelAfterAccept { - [self testIndividualCase:(char *)"cancel_after_accept"]; -} - -- (void)testCancelAfterClientDone { - [self testIndividualCase:(char *)"cancel_after_client_done"]; -} - -- (void)testCancelAfterInvoke { - [self testIndividualCase:(char *)"cancel_after_invoke"]; -} - -- (void)testCancelAfterRoundTrip { - [self testIndividualCase:(char *)"cancel_after_round_trip"]; -} - -- (void)testCancelBeforeInvoke { - [self testIndividualCase:(char *)"cancel_before_invoke"]; -} - -- (void)testCancelInAVacuum { - [self testIndividualCase:(char *)"cancel_in_a_vacuum"]; -} - -- (void)testCancelWithStatus { - [self testIndividualCase:(char *)"cancel_with_status"]; -} - -- (void)testCompressedPayload { - [self testIndividualCase:(char *)"compressed_payload"]; -} - -- (void)testConnectivity { - // NOT SUPPORTED - // [self testIndividualCase:(char *)"connectivity"]; -} - -- (void)testDefaultHost { - [self testIndividualCase:(char *)"default_host"]; -} - -- (void)testDisappearingServer { - [self testIndividualCase:(char *)"disappearing_server"]; -} - -- (void)testEmptyBatch { - [self testIndividualCase:(char *)"empty_batch"]; -} - -- (void)testFilterCausesClose { - // NOT SUPPORTED - // [self testIndividualCase:(char *)"filter_causes_close"]; -} - -- (void)testGracefulServerShutdown { - [self testIndividualCase:(char *)"graceful_server_shutdown"]; -} - -- (void)testHighInitialSeqno { - [self testIndividualCase:(char *)"high_initial_seqno"]; -} - -- (void)testHpackSize { - // NOT SUPPORTED - // [self testIndividualCase:(char *)"hpack_size"]; -} - -- (void)testIdempotentRequest { - // NOT SUPPORTED - // [self testIndividualCase:(char *)"idempotent_request"]; -} - -- (void)testInvokeLargeRequest { - // NOT SUPPORTED (frame size) - // [self testIndividualCase:(char *)"invoke_large_request"]; -} - -- (void)testLargeMetadata { - // NOT SUPPORTED - // [self testIndividualCase:(char *)"large_metadata"]; -} - -- (void)testMaxConcurrentStreams { - [self testIndividualCase:(char *)"max_concurrent_streams"]; -} - -- (void)testMaxMessageLength { - // NOT SUPPORTED (close_error) - // [self testIndividualCase:(char *)"max_message_length"]; -} - -- (void)testNegativeDeadline { - [self testIndividualCase:(char *)"negative_deadline"]; -} - -- (void)testNoOp { - [self testIndividualCase:(char *)"no_op"]; -} - -- (void)testPayload { - [self testIndividualCase:(char *)"payload"]; -} - -- (void)testPing { - // NOT SUPPORTED - // [self testIndividualCase:(char *)"ping"]; -} - -- (void)testPingPongStreaming { - [self testIndividualCase:(char *)"ping_pong_streaming"]; -} - -- (void)testRegisteredCall { - [self testIndividualCase:(char *)"registered_call"]; -} - -- (void)testRequestWithFlags { - // NOT SUPPORTED - // [self testIndividualCase:(char *)"request_with_flags"]; -} - -- (void)testRequestWithPayload { - [self testIndividualCase:(char *)"request_with_payload"]; -} - -- (void)testServerFinishesRequest { - [self testIndividualCase:(char *)"server_finishes_request"]; -} - -- (void)testServerStreaming { - [self testIndividualCase:(char *)"server_streaming"]; -} - -- (void)testShutdownFinishesCalls { - [self testIndividualCase:(char *)"shutdown_finishes_calls"]; -} - -- (void)testShutdownFinishesTags { - [self testIndividualCase:(char *)"shutdown_finishes_tags"]; -} - -- (void)testSimpleDelayedRequest { - [self testIndividualCase:(char *)"simple_delayed_request"]; -} - -- (void)testSimpleMetadata { - [self testIndividualCase:(char *)"simple_metadata"]; -} - -- (void)testSimpleRequest { - [self testIndividualCase:(char *)"simple_request"]; -} - -- (void)testStreamingErrorResponse { - [self testIndividualCase:(char *)"streaming_error_response"]; -} - -- (void)testTrailingMetadata { - [self testIndividualCase:(char *)"trailing_metadata"]; -} - -@end diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile index 0604af70228a2..f3a8608e0a24c 100644 --- a/src/objective-c/tests/Podfile +++ b/src/objective-c/tests/Podfile @@ -47,7 +47,6 @@ target 'CronetTests' do pod 'gRPC/GRPCCoreCronet', :path => GRPC_LOCAL_SRC pod 'CronetFramework', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c" - pod 'gRPC-Core/Tests', :path => GRPC_LOCAL_SRC, :inhibit_warnings => true end target 'PerfTests' do diff --git a/templates/gRPC-Core.podspec.template b/templates/gRPC-Core.podspec.template index f9c099bb740bc..6eb7d4431fecc 100644 --- a/templates/gRPC-Core.podspec.template +++ b/templates/gRPC-Core.podspec.template @@ -76,7 +76,6 @@ set(address_sorting_unwanted_files)) | set(list_lib_files("re2", ("headers", ))))) grpc_abseil_specs = list_abseil_specs("grpc") - grpc_tests_abseil_specs = list(sorted(set(list_abseil_specs("end2end_tests")) - set(grpc_abseil_specs))) # TODO(jtattermusch): build.yaml is now generated from bazel build # which doesn't have an explicit "grpc_cronet" target. Until it exists @@ -95,15 +94,6 @@ grpc_cronet_files = list(sorted(grpc_cronet_extra_impl_files)) grpc_cronet_public_headers = list(sorted(grpc_cronet_extra_public_headers)) - - grpc_test_util_files = list(sorted( - set(list_lib_files("end2end_tests", ("src", "headers"))) - - set(grpc_private_files) - - set(address_sorting_unwanted_files) - - set([ - # Subprocess is not supported in tvOS and not needed by our tests. - "test/core/util/subprocess_posix.cc", - ]))) %> Pod::Spec.new do |s| s.name = 'gRPC-Core' @@ -228,18 +218,6 @@ ss.source_files = ${ruby_multiline_list(grpc_cronet_files, 22)} end - s.subspec 'Tests' do |ss| - ss.header_mappings_dir = '.' - - ss.dependency "#{s.name}/Interface", version - ss.dependency "#{s.name}/Implementation", version - % for abseil_spec in grpc_tests_abseil_specs: - ss.dependency '${abseil_spec}', abseil_version - % endfor - - ss.source_files = ${ruby_multiline_list(grpc_test_util_files, 22)} - end - # patch include of openssl to openssl_grpc s.prepare_command = <<-END_OF_COMMAND set -e diff --git a/templates/test/core/end2end/end2end_defs.include b/templates/test/core/end2end/end2end_defs.include deleted file mode 100644 index 81ea76c41a02a..0000000000000 --- a/templates/test/core/end2end/end2end_defs.include +++ /dev/null @@ -1,72 +0,0 @@ -<%def name="end2end_selector(tests)"> -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -<% tests = sorted(tests) %>\ -// This file is auto-generated - -#include "test/core/end2end/end2end_tests.h" - -#include -#include - -#include "absl/strings/str_format.h" - -#include - -#include "src/core/lib/gprpp/crash.h" - -static bool g_pre_init_called = false; - -% for test in tests: -extern void ${test}(const CoreTestConfiguration& config); -extern void ${test}_pre_init(void); -% endfor - -void grpc_end2end_tests_pre_init(void) { - GPR_ASSERT(!g_pre_init_called); - g_pre_init_called = true; -% for test in tests: - ${test}_pre_init(); -% endfor -} - -// NOLINTNEXTLINE(readability-function-size) -void grpc_end2end_tests(int argc, char **argv, - const CoreTestConfiguration& config) { - int i; - - GPR_ASSERT(g_pre_init_called); - - if (argc <= 1) { -% for test in tests: - ${test}(config); -% endfor - return; - } - - for (i = 1; i < argc; i++) { -% for test in tests: - if (0 == strcmp("${test}", argv[i])) { - ${test}(config); - continue; - } -% endfor - grpc_core::Crash(absl::StrFormat( "not a test: '%s'", argv[i])); - } -} diff --git a/templates/test/core/end2end/end2end_tests.cc.template b/templates/test/core/end2end/end2end_tests.cc.template deleted file mode 100644 index e6a49f2795a01..0000000000000 --- a/templates/test/core/end2end/end2end_tests.cc.template +++ /dev/null @@ -1,4 +0,0 @@ -%YAML 1.2 ---- | - <%namespace file="end2end_defs.include" import="*"/>\ - ${end2end_selector(core_end2end_tests.keys())} diff --git a/test/core/bad_client/generate_tests.bzl b/test/core/bad_client/generate_tests.bzl index 7ace2600810ea..b6acd9e5f51f0 100755 --- a/test/core/bad_client/generate_tests.bzl +++ b/test/core/bad_client/generate_tests.bzl @@ -43,6 +43,7 @@ def grpc_bad_client_tests(): srcs = ["bad_client.cc"], hdrs = ["bad_client.h"], language = "C++", + testonly = 1, deps = [ "//test/core/util:grpc_test_util", "//:grpc", diff --git a/test/core/end2end/BUILD b/test/core/end2end/BUILD index 3438295a4dfb8..e96aaceca353f 100644 --- a/test/core/end2end/BUILD +++ b/test/core/end2end/BUILD @@ -13,7 +13,6 @@ # limitations under the License. load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_package") -load(":generate_tests.bzl", "grpc_end2end_tests") licenses(["notice"]) @@ -21,15 +20,18 @@ grpc_package(name = "test/core/end2end") grpc_cc_library( name = "cq_verifier", + testonly = 1, srcs = ["cq_verifier.cc"], hdrs = ["cq_verifier.h"], external_deps = [ + "absl/functional:any_invocable", "absl/strings", "absl/strings:str_format", "absl/types:variant", + "gtest", ], language = "C++", - visibility = ["//test:__subpackages__"], + visibility = ["//:__subpackages__"], deps = [ "//:debug_location", "//:gpr", @@ -50,7 +52,7 @@ grpc_cc_library( ], hdrs = ["data/ssl_test_data.h"], language = "C++", - visibility = ["//test:__subpackages__"], + visibility = ["//:__subpackages__"], ) grpc_cc_library( @@ -99,11 +101,44 @@ grpc_cc_library( ], ) +grpc_cc_library( + name = "end2end_test_lib", + testonly = 1, + srcs = [ + "end2end_tests.cc", + ], + hdrs = [ + "end2end_tests.h", + ], + external_deps = [ + "absl/memory", + "absl/random", + "absl/strings", + "absl/types:optional", + "absl/types:variant", + "gtest", + ], + deps = [ + "cq_verifier", + "//:config", + "//:debug_location", + "//:gpr", + "//:grpc", + "//:grpc_public_hdrs", + "//src/core:bitset", + "//src/core:channel_args", + "//src/core:no_destruct", + "//src/core:slice", + "//src/core:time", + "//test/core/util:grpc_test_util", + ], +) + grpc_cc_library( name = "fixture_support", + testonly = 1, srcs = ["fixtures/local_util.cc"], hdrs = [ - "end2end_tests.h", "fixtures/h2_oauth2_common.h", "fixtures/h2_ssl_cred_reload_fixture.h", "fixtures/h2_ssl_tls_common.h", @@ -112,14 +147,17 @@ grpc_cc_library( "fixtures/local_util.h", "fixtures/secure_fixture.h", "fixtures/sockpair_fixture.h", + "tests/cancel_test_helpers.h", ], external_deps = [ "absl/status", "absl/status:statusor", "absl/strings", + "gtest", ], language = "C++", deps = [ + "end2end_test_lib", "//:config", "//:exec_ctx", "//:gpr", @@ -141,6 +179,162 @@ grpc_cc_library( ], ) +grpc_cc_test( + name = "core_end2end_tests", + timeout = "long", + srcs = [ + "end2end_test_main.cc", + "tests/bad_ping.cc", + "tests/binary_metadata.cc", + "tests/call_creds.cc", + "tests/call_host_override.cc", + "tests/cancel_after_accept.cc", + "tests/cancel_after_client_done.cc", + "tests/cancel_after_invoke.cc", + "tests/cancel_after_round_trip.cc", + "tests/cancel_before_invoke.cc", + "tests/cancel_in_a_vacuum.cc", + "tests/cancel_with_status.cc", + "tests/channelz.cc", + "tests/client_streaming.cc", + "tests/compressed_payload.cc", + "tests/connectivity.cc", + "tests/default_host.cc", + "tests/disappearing_server.cc", + "tests/empty_batch.cc", + "tests/filter_causes_close.cc", + "tests/filter_context.cc", + "tests/filter_init_fails.cc", + "tests/filtered_metadata.cc", + "tests/graceful_server_shutdown.cc", + "tests/grpc_authz.cc", + "tests/high_initial_seqno.cc", + "tests/hpack_size.cc", + "tests/invoke_large_request.cc", + "tests/keepalive_timeout.cc", + "tests/large_metadata.cc", + "tests/max_concurrent_streams.cc", + "tests/max_connection_age.cc", + "tests/max_connection_idle.cc", + "tests/max_message_length.cc", + "tests/negative_deadline.cc", + "tests/no_logging.cc", + "tests/no_op.cc", + "tests/payload.cc", + "tests/ping.cc", + "tests/ping_pong_streaming.cc", + "tests/proxy_auth.cc", + "tests/registered_call.cc", + "tests/request_with_flags.cc", + "tests/request_with_payload.cc", + "tests/resource_quota_server.cc", + "tests/retry.cc", + "tests/retry_cancel_after_first_attempt_starts.cc", + "tests/retry_cancel_during_delay.cc", + "tests/retry_cancel_with_multiple_send_batches.cc", + "tests/retry_cancellation.cc", + "tests/retry_disabled.cc", + "tests/retry_exceeds_buffer_size_in_delay.cc", + "tests/retry_exceeds_buffer_size_in_initial_batch.cc", + "tests/retry_exceeds_buffer_size_in_subsequent_batch.cc", + "tests/retry_lb_drop.cc", + "tests/retry_lb_fail.cc", + "tests/retry_non_retriable_status.cc", + "tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc", + "tests/retry_per_attempt_recv_timeout.cc", + "tests/retry_per_attempt_recv_timeout_on_last_attempt.cc", + "tests/retry_recv_initial_metadata.cc", + "tests/retry_recv_message.cc", + "tests/retry_recv_message_replay.cc", + "tests/retry_recv_trailing_metadata_error.cc", + "tests/retry_send_initial_metadata_refs.cc", + "tests/retry_send_op_fails.cc", + "tests/retry_send_recv_batch.cc", + "tests/retry_server_pushback_delay.cc", + "tests/retry_server_pushback_disabled.cc", + "tests/retry_streaming.cc", + "tests/retry_streaming_after_commit.cc", + "tests/retry_streaming_succeeds_before_replay_finished.cc", + "tests/retry_throttled.cc", + "tests/retry_too_many_attempts.cc", + "tests/retry_transparent_goaway.cc", + "tests/retry_transparent_max_concurrent_streams.cc", + "tests/retry_transparent_not_sent_on_wire.cc", + "tests/retry_unref_before_finish.cc", + "tests/retry_unref_before_recv.cc", + "tests/server_finishes_request.cc", + "tests/server_streaming.cc", + "tests/shutdown_finishes_calls.cc", + "tests/shutdown_finishes_tags.cc", + "tests/simple_delayed_request.cc", + "tests/simple_metadata.cc", + "tests/simple_request.cc", + "tests/streaming_error_response.cc", + "tests/trailing_metadata.cc", + "tests/write_buffering.cc", + "tests/write_buffering_at_end.cc", + ], + data = [ + "//src/core/tsi/test_creds:ca.pem", + "//src/core/tsi/test_creds:server1.key", + "//src/core/tsi/test_creds:server1.pem", + ], + external_deps = [ + "absl/status", + "absl/status:statusor", + "absl/strings", + "absl/strings:str_format", + "absl/types:optional", + "gtest", + ], + shard_count = 50, + tags = ["core_end2end_test"], + deps = [ + "cq_verifier", + "end2end_test_lib", + "fixture_support", + "http_proxy", + "proxy", + "//:channel_stack_builder", + "//:config", + "//:config_vars", + "//:debug_location", + "//:exec_ctx", + "//:gpr", + "//:grpc_authorization_provider", + "//:grpc_public_hdrs", + "//:grpc_security_base", + "//:grpc_trace", + "//:grpc_unsecure", + "//:orphanable", + "//:promise", + "//:ref_counted_ptr", + "//:stats", + "//src/core:arena_promise", + "//src/core:bitset", + "//src/core:channel_args", + "//src/core:channel_fwd", + "//src/core:channel_init", + "//src/core:channel_stack_type", + "//src/core:closure", + "//src/core:error", + "//src/core:grpc_authorization_base", + "//src/core:grpc_fake_credentials", + "//src/core:iomgr_port", + "//src/core:json", + "//src/core:lb_policy", + "//src/core:lb_policy_factory", + "//src/core:no_destruct", + "//src/core:notification", + "//src/core:slice", + "//src/core:stats_data", + "//src/core:status_helper", + "//src/core:time", + "//test/core/util:grpc_test_util", + "//test/core/util:test_lb_policies", + ], +) + grpc_cc_test( name = "bad_server_response_test", srcs = ["bad_server_response_test.cc"], @@ -267,8 +461,6 @@ grpc_cc_test( ], ) -grpc_end2end_tests() - grpc_cc_test( name = "h2_ssl_cert_test", srcs = [ @@ -276,21 +468,29 @@ grpc_cc_test( "h2_ssl_cert_test.cc", ], external_deps = [ + "absl/memory", + "absl/strings", "absl/types:optional", + "absl/types:variant", "gtest", "libcrypto", ], language = "C++", deps = [ "cq_verifier", + "end2end_test_lib", "fixture_support", "ssl_test_data", "//:config_vars", + "//:debug_location", "//:gpr", "//:grpc", "//:grpc_public_hdrs", "//:grpc_security_base", + "//src/core:bitset", "//src/core:channel_args", + "//src/core:slice", + "//src/core:time", "//test/core/util:grpc_test_util", ], ) diff --git a/test/core/end2end/README b/test/core/end2end/README deleted file mode 100644 index 51cc144039d60..0000000000000 --- a/test/core/end2end/README +++ /dev/null @@ -1,7 +0,0 @@ -Each fixture (under fixtures/) is paired with each test (under tests/) and -forms a complete end-to-end test. - -To add a new test or fixture: -- add the code to the relevant directory -- update generate_tests.bzl to reflect the change -- regenerate projects diff --git a/test/core/end2end/cq_verifier.cc b/test/core/end2end/cq_verifier.cc index 0be24f53d57f5..67e71626a043e 100644 --- a/test/core/end2end/cq_verifier.cc +++ b/test/core/end2end/cq_verifier.cc @@ -33,6 +33,7 @@ #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" +#include "gtest/gtest.h" #include #include @@ -196,7 +197,9 @@ std::string TagStr(void* tag) { namespace grpc_core { -CqVerifier::CqVerifier(grpc_completion_queue* cq) : cq_(cq) {} +CqVerifier::CqVerifier(grpc_completion_queue* cq, + absl::AnyInvocable fail) + : cq_(cq), fail_(std::move(fail)) {} CqVerifier::~CqVerifier() { Verify(); } @@ -209,30 +212,66 @@ std::string CqVerifier::Expectation::ToString() const { return absl::StrCat("success=", success ? "true" : "false"); }, [](Maybe) { return std::string("maybe"); }, - [](AnyStatus) { return std::string("any success value"); })); + [](AnyStatus) { return std::string("any success value"); }, + [](PerformAction) { return std::string("perform some action"); }, + [](MaybePerformAction) { + return std::string("maybe perform action"); + })); } -std::string CqVerifier::ToString() const { +std::vector CqVerifier::ToStrings() const { std::vector expectations; expectations.reserve(expectations_.size()); for (const auto& e : expectations_) { expectations.push_back(e.ToString()); } - return absl::StrJoin(expectations, "\n"); + return expectations; +} + +std::string CqVerifier::ToString() const { + return absl::StrJoin(ToStrings(), "\n"); } void CqVerifier::FailNoEventReceived(const SourceLocation& location) const { - Crash(absl::StrFormat("[%s:%d] no event received, but expected:%s", - location.file(), location.line(), ToString().c_str())); + fail_(Failure{location, "No event received", ToStrings()}); } void CqVerifier::FailUnexpectedEvent(grpc_event* ev, const SourceLocation& location) const { - gpr_log(GPR_ERROR, "[%s:%d] cq returned unexpected event: %s", - location.file(), location.line(), grpc_event_string(ev).c_str()); - Crash(absl::StrFormat("expected tags:\n%s", ToString().c_str())); + fail_(Failure{location, + absl::StrCat("Unexpected event: ", grpc_event_string(ev)), + ToStrings()}); +} + +void CqVerifier::FailUsingGprCrash(const Failure& failure) { + Crash(absl::StrCat("[", failure.location.file(), ":", failure.location.line(), + "] ", failure.message, "\nexpected:\n", + absl::StrJoin(failure.expected, "\n"))); } +void CqVerifier::FailUsingGtestFail(const Failure& failure) { + std::string message = absl::StrCat(" ", failure.message); + if (!failure.expected.empty()) { + absl::StrAppend(&message, "\n expected:\n"); + for (const auto& line : failure.expected) { + absl::StrAppend(&message, " ", line, "\n"); + } + } else { + absl::StrAppend(&message, "\n expected nothing"); + } + ADD_FAILURE_AT(failure.location.file(), failure.location.line()) << message; +} + +namespace { +bool IsMaybe(const CqVerifier::ExpectedResult& r) { + return Match( + r, [](bool) { return false; }, [](CqVerifier::Maybe) { return true; }, + [](CqVerifier::AnyStatus) { return false; }, + [](const CqVerifier::PerformAction&) { return false; }, + [](const CqVerifier::MaybePerformAction&) { return true; }); +} +} // namespace + void CqVerifier::Verify(Duration timeout, SourceLocation location) { const gpr_timespec deadline = grpc_timeout_milliseconds_to_deadline(timeout.millis()); @@ -254,6 +293,14 @@ void CqVerifier::Verify(Duration timeout, SourceLocation location) { [ev](AnyStatus a) { if (a.result != nullptr) *a.result = ev.success; return true; + }, + [ev](const PerformAction& action) { + action.action(ev.success); + return true; + }, + [ev](const MaybePerformAction& action) { + action.action(ev.success); + return true; }); if (!expected) { FailUnexpectedEvent(&ev, location); @@ -267,16 +314,14 @@ void CqVerifier::Verify(Duration timeout, SourceLocation location) { } expectations_.erase( std::remove_if(expectations_.begin(), expectations_.end(), - [](const Expectation& e) { - return absl::holds_alternative(e.result); - }), + [](const Expectation& e) { return IsMaybe(e.result); }), expectations_.end()); if (!expectations_.empty()) FailNoEventReceived(location); } bool CqVerifier::AllMaybes() const { for (const auto& e : expectations_) { - if (!absl::holds_alternative(e.result)) return false; + if (!IsMaybe(e.result)) return false; } return true; } @@ -293,7 +338,7 @@ void CqVerifier::VerifyEmpty(Duration timeout, SourceLocation location) { void CqVerifier::Expect(void* tag, ExpectedResult result, SourceLocation location) { - expectations_.push_back(Expectation{location, tag, result}); + expectations_.push_back(Expectation{location, tag, std::move(result)}); } } // namespace grpc_core diff --git a/test/core/end2end/cq_verifier.h b/test/core/end2end/cq_verifier.h index d92e6de1b2e66..85f32351e8f91 100644 --- a/test/core/end2end/cq_verifier.h +++ b/test/core/end2end/cq_verifier.h @@ -21,9 +21,11 @@ #include +#include #include #include +#include "absl/functional/any_invocable.h" #include "absl/types/variant.h" #include @@ -48,10 +50,34 @@ class CqVerifier { struct AnyStatus { bool* result = nullptr; }; + // PerformAction - expect the tag, and run a function based on the result + struct PerformAction { + std::function action; + }; + // MaybePerformAction - run a function if a tag is seen + struct MaybePerformAction { + std::function action; + }; + + using ExpectedResult = + absl::variant; + + struct Failure { + SourceLocation location; + std::string message; + std::vector expected; + }; - using ExpectedResult = absl::variant; + static void FailUsingGprCrash(const Failure& failure); + static void FailUsingGtestFail(const Failure& failure); - explicit CqVerifier(grpc_completion_queue* cq); + // Allow customizing the failure handler + // For legacy tests we should use FailUsingGprCrash (the default) + // For gtest based tests we should start migrating to FailUsingGtestFail which + // will produce nicer failure messages. + explicit CqVerifier( + grpc_completion_queue* cq, + absl::AnyInvocable fail = FailUsingGprCrash); ~CqVerifier(); CqVerifier(const CqVerifier&) = delete; @@ -74,6 +100,7 @@ class CqVerifier { SourceLocation location = SourceLocation()); std::string ToString() const; + std::vector ToStrings() const; static void* tag(intptr_t t) { return reinterpret_cast(t); } @@ -93,6 +120,7 @@ class CqVerifier { grpc_completion_queue* const cq_; std::vector expectations_; + mutable absl::AnyInvocable fail_; }; } // namespace grpc_core diff --git a/test/core/end2end/end2end_test_main.cc b/test/core/end2end/end2end_test_main.cc new file mode 100644 index 0000000000000..ddab93c07c95b --- /dev/null +++ b/test/core/end2end/end2end_test_main.cc @@ -0,0 +1,1070 @@ +// Copyright 2022 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/status/status.h" +#include "absl/strings/str_format.h" +#include "absl/types/optional.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/config/config_vars.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gprpp/host_port.h" +#include "src/core/lib/gprpp/no_destruct.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/load_file.h" +#include "src/core/lib/iomgr/port.h" +#include "src/core/lib/security/credentials/fake/fake_credentials.h" +#include "test/core/end2end/end2end_tests.h" +#include "test/core/end2end/fixtures/h2_oauth2_common.h" +#include "test/core/end2end/fixtures/h2_ssl_cred_reload_fixture.h" +#include "test/core/end2end/fixtures/h2_ssl_tls_common.h" +#include "test/core/end2end/fixtures/h2_tls_common.h" +#include "test/core/end2end/fixtures/http_proxy_fixture.h" +#include "test/core/end2end/fixtures/inproc_fixture.h" +#include "test/core/end2end/fixtures/local_util.h" +#include "test/core/end2end/fixtures/proxy.h" +#include "test/core/end2end/fixtures/secure_fixture.h" +#include "test/core/end2end/fixtures/sockpair_fixture.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +// IWYU pragma: no_include + +#ifdef GRPC_POSIX_SOCKET +#include + +#include "src/core/lib/iomgr/socket_utils_posix.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" +#endif + +#ifdef GRPC_POSIX_WAKEUP_FD +#include "src/core/lib/iomgr/wakeup_fd_posix.h" +#endif + +#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem" +#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem" +#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key" + +namespace grpc_core { + +namespace { + +std::atomic unique{0}; + +void ProcessAuthFailure(void* state, grpc_auth_context* /*ctx*/, + const grpc_metadata* /*md*/, size_t /*md_count*/, + grpc_process_auth_metadata_done_cb cb, + void* user_data) { + GPR_ASSERT(state == nullptr); + cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_UNAUTHENTICATED, nullptr); +} + +void AddFailAuthCheckIfNeeded(const ChannelArgs& args, + grpc_server_credentials* creds) { + if (args.Contains(FAIL_AUTH_CHECK_SERVER_ARG_NAME)) { + grpc_auth_metadata_processor processor = {ProcessAuthFailure, nullptr, + nullptr}; + grpc_server_credentials_set_auth_metadata_processor(creds, processor); + } +} + +} // namespace + +class CensusFixture : public CoreTestFixture { + private: + grpc_server* MakeServer(const ChannelArgs& args) override { + grpc_server_credentials* server_creds = + grpc_insecure_server_credentials_create(); + auto* server = grpc_server_create( + args.Set(GRPC_ARG_ENABLE_CENSUS, true).ToC().get(), nullptr); + grpc_server_register_completion_queue(server, cq(), nullptr); + GPR_ASSERT( + grpc_server_add_http2_port(server, localaddr_.c_str(), server_creds)); + grpc_server_credentials_release(server_creds); + grpc_server_start(server); + return server; + } + grpc_channel* MakeClient(const ChannelArgs& args) override { + auto* creds = grpc_insecure_credentials_create(); + auto* client = + grpc_channel_create(localaddr_.c_str(), creds, + args.Set(GRPC_ARG_ENABLE_CENSUS, true).ToC().get()); + grpc_channel_credentials_release(creds); + return client; + } + const std::string localaddr_ = + JoinHostPort("localhost", grpc_pick_unused_port_or_die()); +}; + +class CompressionFixture : public CoreTestFixture { + private: + grpc_server* MakeServer(const ChannelArgs& args) override { + auto* server = grpc_server_create( + args.SetIfUnset(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, + GRPC_COMPRESS_GZIP) + .ToC() + .get(), + nullptr); + grpc_server_register_completion_queue(server, cq(), nullptr); + grpc_server_credentials* server_creds = + grpc_insecure_server_credentials_create(); + GPR_ASSERT( + grpc_server_add_http2_port(server, localaddr_.c_str(), server_creds)); + grpc_server_credentials_release(server_creds); + grpc_server_start(server); + return server; + } + grpc_channel* MakeClient(const ChannelArgs& args) override { + grpc_channel_credentials* creds = grpc_insecure_credentials_create(); + auto* client = grpc_channel_create( + localaddr_.c_str(), creds, + args.SetIfUnset(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, + GRPC_COMPRESS_GZIP) + .ToC() + .get()); + grpc_channel_credentials_release(creds); + return client; + } + + std::string localaddr_ = + JoinHostPort("localhost", grpc_pick_unused_port_or_die()); +}; + +class FakesecFixture : public SecureFixture { + private: + grpc_channel_credentials* MakeClientCreds(const ChannelArgs&) override { + return grpc_fake_transport_security_credentials_create(); + } + grpc_server_credentials* MakeServerCreds(const ChannelArgs& args) override { + grpc_server_credentials* fake_ts_creds = + grpc_fake_transport_security_server_credentials_create(); + AddFailAuthCheckIfNeeded(args, fake_ts_creds); + return fake_ts_creds; + } +}; + +class InsecureCredsFixture : public InsecureFixture { + private: + grpc_server_credentials* MakeServerCreds(const ChannelArgs& args) override { + auto* creds = grpc_insecure_server_credentials_create(); + AddFailAuthCheckIfNeeded(args, creds); + return creds; + } +}; + +class SockpairWithMinstackFixture : public SockpairFixture { + public: + using SockpairFixture::SockpairFixture; + + private: + ChannelArgs MutateClientArgs(ChannelArgs args) override { + return args.Set(GRPC_ARG_MINIMAL_STACK, true); + } + ChannelArgs MutateServerArgs(ChannelArgs args) override { + return args.Set(GRPC_ARG_MINIMAL_STACK, true); + } +}; + +class Sockpair1Byte : public SockpairFixture { + public: + Sockpair1Byte() + : SockpairFixture(ChannelArgs() + .Set(GRPC_ARG_TCP_READ_CHUNK_SIZE, 1) + .Set(GRPC_ARG_TCP_MIN_READ_CHUNK_SIZE, 1) + .Set(GRPC_ARG_TCP_MAX_READ_CHUNK_SIZE, 1)) { + g_fixture_slowdown_factor = 2; + } + ~Sockpair1Byte() override { g_fixture_slowdown_factor = 1; } + + private: + ChannelArgs MutateClientArgs(ChannelArgs args) override { + return args.Set(GRPC_ARG_MINIMAL_STACK, true); + } + ChannelArgs MutateServerArgs(ChannelArgs args) override { + return args.Set(GRPC_ARG_MINIMAL_STACK, true); + } +}; + +#ifdef GRPC_POSIX_SOCKET + +class FdFixture : public CoreTestFixture { + public: + FdFixture() { create_sockets(fd_pair_); } + + private: + grpc_server* MakeServer(const ChannelArgs& args) override { + ExecCtx exec_ctx; + auto* server = grpc_server_create(args.ToC().get(), nullptr); + grpc_server_register_completion_queue(server, cq(), nullptr); + grpc_server_start(server); + grpc_server_credentials* creds = grpc_insecure_server_credentials_create(); + grpc_server_add_channel_from_fd(server, fd_pair_[1], creds); + grpc_server_credentials_release(creds); + return server; + } + grpc_channel* MakeClient(const ChannelArgs& args) override { + ExecCtx exec_ctx; + grpc_channel_credentials* creds = grpc_insecure_credentials_create(); + auto* client = grpc_channel_create_from_fd("fixture_client", fd_pair_[0], + creds, args.ToC().get()); + grpc_channel_credentials_release(creds); + return client; + } + + static void create_sockets(int sv[2]) { + int flags; + grpc_create_socketpair_if_unix(sv); + flags = fcntl(sv[0], F_GETFL, 0); + GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0); + flags = fcntl(sv[1], F_GETFL, 0); + GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0); + GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0]) == + absl::OkStatus()); + GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]) == + absl::OkStatus()); + } + + int fd_pair_[2]; +}; +#endif + +class NoRetryFixture : public InsecureFixture { + private: + ChannelArgs MutateClientArgs(ChannelArgs args) override { + return args.Set(GRPC_ARG_ENABLE_RETRIES, false); + } +}; + +class HttpProxyFilter : public CoreTestFixture { + public: + explicit HttpProxyFilter(const ChannelArgs& client_args) + : proxy_(grpc_end2end_http_proxy_create(client_args.ToC().get())) {} + ~HttpProxyFilter() override { + // Need to shut down the proxy users before closing the proxy (otherwise we + // become stuck). + ShutdownClient(); + ShutdownServer(); + grpc_end2end_http_proxy_destroy(proxy_); + } + + private: + grpc_server* MakeServer(const ChannelArgs& args) override { + auto* server = grpc_server_create(args.ToC().get(), nullptr); + grpc_server_register_completion_queue(server, cq(), nullptr); + grpc_server_credentials* server_creds = + grpc_insecure_server_credentials_create(); + GPR_ASSERT( + grpc_server_add_http2_port(server, server_addr_.c_str(), server_creds)); + grpc_server_credentials_release(server_creds); + grpc_server_start(server); + return server; + } + + grpc_channel* MakeClient(const ChannelArgs& args) override { + // If testing for proxy auth, add credentials to proxy uri + absl::optional proxy_auth_str = + args.GetOwnedString(GRPC_ARG_HTTP_PROXY_AUTH_CREDS); + std::string proxy_uri; + if (!proxy_auth_str.has_value()) { + proxy_uri = absl::StrFormat( + "http://%s", grpc_end2end_http_proxy_get_proxy_name(proxy_)); + } else { + proxy_uri = + absl::StrFormat("http://%s@%s", proxy_auth_str->c_str(), + grpc_end2end_http_proxy_get_proxy_name(proxy_)); + } + grpc_channel_credentials* creds = grpc_insecure_credentials_create(); + auto* client = grpc_channel_create( + server_addr_.c_str(), creds, + args.Set(GRPC_ARG_HTTP_PROXY, proxy_uri).ToC().get()); + grpc_channel_credentials_release(creds); + GPR_ASSERT(client); + return client; + } + + std::string server_addr_ = + JoinHostPort("localhost", grpc_pick_unused_port_or_die()); + grpc_end2end_http_proxy* proxy_; +}; + +class ProxyFixture : public CoreTestFixture { + public: + ProxyFixture(const ChannelArgs& client_args, const ChannelArgs& server_args) + : proxy_(grpc_end2end_proxy_create(&proxy_def_, client_args.ToC().get(), + server_args.ToC().get())) {} + ~ProxyFixture() override { grpc_end2end_proxy_destroy(proxy_); } + + private: + static grpc_server* CreateProxyServer(const char* port, + const grpc_channel_args* server_args) { + grpc_server* s = grpc_server_create(server_args, nullptr); + grpc_server_credentials* server_creds = + grpc_insecure_server_credentials_create(); + GPR_ASSERT(grpc_server_add_http2_port(s, port, server_creds)); + grpc_server_credentials_release(server_creds); + return s; + } + + static grpc_channel* CreateProxyClient(const char* target, + const grpc_channel_args* client_args) { + grpc_channel_credentials* creds = grpc_insecure_credentials_create(); + grpc_channel* channel = grpc_channel_create(target, creds, client_args); + grpc_channel_credentials_release(creds); + return channel; + } + + grpc_server* MakeServer(const ChannelArgs& args) override { + auto* server = grpc_server_create(args.ToC().get(), nullptr); + grpc_server_register_completion_queue(server, cq(), nullptr); + grpc_server_credentials* server_creds = + grpc_insecure_server_credentials_create(); + GPR_ASSERT(grpc_server_add_http2_port( + server, grpc_end2end_proxy_get_server_port(proxy_), server_creds)); + grpc_server_credentials_release(server_creds); + grpc_server_start(server); + return server; + } + + grpc_channel* MakeClient(const ChannelArgs& args) override { + grpc_channel_credentials* creds = grpc_insecure_credentials_create(); + auto* client = grpc_channel_create( + grpc_end2end_proxy_get_client_target(proxy_), creds, args.ToC().get()); + grpc_channel_credentials_release(creds); + GPR_ASSERT(client); + return client; + } + const grpc_end2end_proxy_def proxy_def_ = {CreateProxyServer, + CreateProxyClient}; + grpc_end2end_proxy* proxy_; +}; + +class SslProxyFixture : public CoreTestFixture { + public: + SslProxyFixture(const ChannelArgs& client_args, + const ChannelArgs& server_args) + : proxy_(grpc_end2end_proxy_create(&proxy_def_, client_args.ToC().get(), + server_args.ToC().get())) {} + ~SslProxyFixture() override { grpc_end2end_proxy_destroy(proxy_); } + + private: + static grpc_server* CreateProxyServer(const char* port, + const grpc_channel_args* server_args) { + grpc_server* s = grpc_server_create(server_args, nullptr); + grpc_slice cert_slice, key_slice; + GPR_ASSERT(GRPC_LOG_IF_ERROR( + "load_file", grpc_load_file(SERVER_CERT_PATH, 1, &cert_slice))); + GPR_ASSERT(GRPC_LOG_IF_ERROR( + "load_file", grpc_load_file(SERVER_KEY_PATH, 1, &key_slice))); + const char* server_cert = + reinterpret_cast GRPC_SLICE_START_PTR(cert_slice); + const char* server_key = + reinterpret_cast GRPC_SLICE_START_PTR(key_slice); + grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {server_key, server_cert}; + grpc_server_credentials* ssl_creds = grpc_ssl_server_credentials_create( + nullptr, &pem_key_cert_pair, 1, 0, nullptr); + grpc_slice_unref(cert_slice); + grpc_slice_unref(key_slice); + GPR_ASSERT(grpc_server_add_http2_port(s, port, ssl_creds)); + grpc_server_credentials_release(ssl_creds); + return s; + } + + static grpc_channel* CreateProxyClient(const char* target, + const grpc_channel_args* client_args) { + grpc_channel* channel; + grpc_channel_credentials* ssl_creds = + grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr); + grpc_arg ssl_name_override = { + GRPC_ARG_STRING, + const_cast(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), + {const_cast("foo.test.google.fr")}}; + const grpc_channel_args* new_client_args = + grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1); + channel = grpc_channel_create(target, ssl_creds, new_client_args); + grpc_channel_credentials_release(ssl_creds); + { + ExecCtx exec_ctx; + grpc_channel_args_destroy(new_client_args); + } + return channel; + } + + grpc_server* MakeServer(const ChannelArgs& args) override { + grpc_slice cert_slice, key_slice; + GPR_ASSERT(GRPC_LOG_IF_ERROR( + "load_file", grpc_load_file(SERVER_CERT_PATH, 1, &cert_slice))); + GPR_ASSERT(GRPC_LOG_IF_ERROR( + "load_file", grpc_load_file(SERVER_KEY_PATH, 1, &key_slice))); + const char* server_cert = + reinterpret_cast GRPC_SLICE_START_PTR(cert_slice); + const char* server_key = + reinterpret_cast GRPC_SLICE_START_PTR(key_slice); + grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {server_key, server_cert}; + grpc_server_credentials* ssl_creds = grpc_ssl_server_credentials_create( + nullptr, &pem_key_cert_pair, 1, 0, nullptr); + grpc_slice_unref(cert_slice); + grpc_slice_unref(key_slice); + if (args.Contains(FAIL_AUTH_CHECK_SERVER_ARG_NAME)) { + grpc_auth_metadata_processor processor = {ProcessAuthFailure, nullptr, + nullptr}; + grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor); + } + + auto* server = grpc_server_create(args.ToC().get(), nullptr); + grpc_server_register_completion_queue(server, cq(), nullptr); + GPR_ASSERT(grpc_server_add_http2_port( + server, grpc_end2end_proxy_get_server_port(proxy_), ssl_creds)); + grpc_server_credentials_release(ssl_creds); + grpc_server_start(server); + return server; + } + + grpc_channel* MakeClient(const ChannelArgs& args) override { + grpc_channel_credentials* ssl_creds = + grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr); + auto* client = grpc_channel_create( + grpc_end2end_proxy_get_client_target(proxy_), ssl_creds, + args.Set(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, "foo.test.google.fr") + .ToC() + .get()); + GPR_ASSERT(client != nullptr); + grpc_channel_credentials_release(ssl_creds); + return client; + } + const grpc_end2end_proxy_def proxy_def_ = {CreateProxyServer, + CreateProxyClient}; + grpc_end2end_proxy* proxy_; +}; + +class FixtureWithTracing final : public CoreTestFixture { + public: + explicit FixtureWithTracing(std::unique_ptr fixture) + : fixture_(std::move(fixture)) { + // g_fixture_slowdown_factor = 10; + EXPECT_FALSE(grpc_tracer_set_enabled("doesnt-exist", 0)); + EXPECT_TRUE(grpc_tracer_set_enabled("http", 1)); + EXPECT_TRUE(grpc_tracer_set_enabled("all", 1)); + } + ~FixtureWithTracing() override { + saved_trace_flags_.Restore(); + // g_fixture_slowdown_factor = 1; + } + + grpc_server* MakeServer(const ChannelArgs& args) override { + return fixture_->MakeServer(args); + } + + grpc_channel* MakeClient(const ChannelArgs& args) override { + return fixture_->MakeClient(args); + } + + private: + SavedTraceFlags saved_trace_flags_; + std::unique_ptr fixture_; +}; + +#ifdef GRPC_POSIX_WAKEUP_FD +class InsecureFixtureWithPipeForWakeupFd : public InsecureFixture { + public: + InsecureFixtureWithPipeForWakeupFd() + : old_value_(std::exchange(grpc_allow_specialized_wakeup_fd, 0)) {} + + ~InsecureFixtureWithPipeForWakeupFd() override { + grpc_allow_specialized_wakeup_fd = old_value_; + } + + private: + const int old_value_; +}; +#endif + +std::vector AllConfigs() { + std::vector configs { +#ifdef GRPC_POSIX_SOCKET + CoreTestConfiguration{"Chttp2Fd", FEATURE_MASK_IS_HTTP2, nullptr, + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(); + }}, +#endif + CoreTestConfiguration{ + "Chttp2FakeSecurityFullstack", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS_LEVEL_INSECURE | + FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(); + }}, + CoreTestConfiguration{ + "Chttp2Fullstack", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs& /*client_args*/, + const ChannelArgs& /*server_args*/) { + return std::make_unique(); + }}, + CoreTestConfiguration{ + "Chttp2FullstackCompression", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(); + }}, +#ifdef GPR_LINUX + CoreTestConfiguration{ + "Chttp2FullstackLocalAbstractUdsPercentEncoded", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs& /*client_args*/, + const ChannelArgs& /*server_args*/) { + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + return std::make_unique( + absl::StrFormat( + "unix-abstract:grpc_fullstack_test.%%00.%d.%" PRId64 + ".%" PRId32 ".%d", + getpid(), now.tv_sec, now.tv_nsec, + unique.fetch_add(1, std::memory_order_relaxed)), + UDS); + }}, +#endif + CoreTestConfiguration{"Chttp2FullstackLocalIpv4", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs& /*client_args*/, + const ChannelArgs& /*server_args*/) { + int port = grpc_pick_unused_port_or_die(); + return std::make_unique( + JoinHostPort("127.0.0.1", port), LOCAL_TCP); + }}, + CoreTestConfiguration{"Chttp2FullstackLocalIpv6", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs& /*client_args*/, + const ChannelArgs& /*server_args*/) { + int port = grpc_pick_unused_port_or_die(); + return std::make_unique( + JoinHostPort("[::1]", port), LOCAL_TCP); + }}, +#ifdef GRPC_HAVE_UNIX_SOCKET + CoreTestConfiguration{ + "Chttp2FullstackLocalUdsPercentEncoded", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs& /*client_args*/, + const ChannelArgs& /*server_args*/) { + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + return std::make_unique( + absl::StrFormat( + "unix:/tmp/grpc_fullstack_test.%%25.%d.%" PRId64 + ".%" PRId32 ".%d", + getpid(), now.tv_sec, now.tv_nsec, + unique.fetch_add(1, std::memory_order_relaxed)), + UDS); + }}, + CoreTestConfiguration{ + "Chttp2FullstackLocalUds", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs& /*client_args*/, + const ChannelArgs& /*server_args*/) { + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + return std::make_unique( + absl::StrFormat( + "unix:/tmp/grpc_fullstack_test.%d.%" PRId64 ".%" PRId32 + ".%d", + getpid(), now.tv_sec, now.tv_nsec, + unique.fetch_add(1, std::memory_order_relaxed)), + UDS); + }}, +#endif + CoreTestConfiguration{"Chttp2FullstackNoRetry", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_IS_HTTP2 | + FEATURE_MASK_DOES_NOT_SUPPORT_RETRY, + nullptr, + [](const ChannelArgs& /*client_args*/, + const ChannelArgs& /*server_args*/) { + return std::make_unique(); + }}, + CoreTestConfiguration{ + "Chttp2FullstackWithCensus", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(); + }}, + CoreTestConfiguration{ + "Chttp2FullstackWithProxy", + FEATURE_MASK_SUPPORTS_REQUEST_PROXYING | + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs& client_args, const ChannelArgs& server_args) { + return std::make_unique(client_args, server_args); + }}, + CoreTestConfiguration{ + "Chttp2HttpProxy", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs& client_args, const ChannelArgs&) { + return std::make_unique(client_args); + }}, + CoreTestConfiguration{ + "Chttp2SslProxy", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_SECURE | + FEATURE_MASK_SUPPORTS_REQUEST_PROXYING | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_IS_HTTP2, + "foo.test.google.fr", + [](const ChannelArgs& client_args, const ChannelArgs& server_args) { + return std::make_unique(client_args, + server_args); + }}, + CoreTestConfiguration{ + "Chttp2InsecureCredentials", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS_LEVEL_INSECURE | + FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(); + }, + }, + CoreTestConfiguration{ + "Chttp2SimpleSslWithOauth2FullstackTls12", + FEATURE_MASK_IS_SECURE | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2, + "foo.test.google.fr", + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(grpc_tls_version::TLS1_2); + }}, + CoreTestConfiguration{ + "Chttp2SimpleSslWithOauth2FullstackTls13", + FEATURE_MASK_IS_SECURE | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2, + "foo.test.google.fr", + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(grpc_tls_version::TLS1_3); + }}, + CoreTestConfiguration{ + "Chttp2SimplSslFullstackTls12", + FEATURE_MASK_IS_SECURE | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2, + "foo.test.google.fr", + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(grpc_tls_version::TLS1_2); + }}, + CoreTestConfiguration{ + "Chttp2SimplSslFullstackTls13", + FEATURE_MASK_IS_SECURE | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST | + FEATURE_MASK_IS_HTTP2, + "foo.test.google.fr", + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(grpc_tls_version::TLS1_3); + }}, + CoreTestConfiguration{ + "Chttp2SocketPair", FEATURE_MASK_IS_HTTP2, nullptr, + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(ChannelArgs()); + }}, + CoreTestConfiguration{ + "Chttp2SocketPair1ByteAtATime", + FEATURE_MASK_IS_HTTP2 | FEATURE_MASK_1BYTE_AT_A_TIME, nullptr, + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique( + ChannelArgs() + .Set(GRPC_ARG_TCP_READ_CHUNK_SIZE, 1) + .Set(GRPC_ARG_TCP_MIN_READ_CHUNK_SIZE, 1) + .Set(GRPC_ARG_TCP_MAX_READ_CHUNK_SIZE, 1)); + }}, + CoreTestConfiguration{ + "Chttp2SocketPairMinstack", + FEATURE_MASK_IS_HTTP2 | FEATURE_MASK_IS_MINSTACK, nullptr, + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique( + ChannelArgs()); + }}, + CoreTestConfiguration{ + "Inproc", + FEATURE_MASK_DOES_NOT_SUPPORT_WRITE_BUFFERING, + nullptr, + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(); + }, + }, + CoreTestConfiguration{ + "Chttp2SslCredReloadTls12", + FEATURE_MASK_IS_SECURE | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2, + "foo.test.google.fr", + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(TLS1_2); + }}, + CoreTestConfiguration{ + "Chttp2SslCredReloadTls13", + FEATURE_MASK_IS_SECURE | FEATURE_MASK_IS_HTTP2 | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST, + "foo.test.google.fr", + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique(TLS1_3); + }}, + CoreTestConfiguration{ + // client: certificate watcher provider + async external verifier + // server: certificate watcher provider + async external verifier + // extra: TLS 1.3 + "Chttp2CertWatcherProviderAsyncVerifierTls13", + kH2TLSFeatureMask, + "foo.test.google.fr", + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique( + SecurityPrimitives::TlsVersion::V_13, + SecurityPrimitives::ProviderType::FILE_PROVIDER, + SecurityPrimitives::VerifierType::EXTERNAL_ASYNC_VERIFIER); + }, + }, + CoreTestConfiguration{ + // client: certificate watcher provider + hostname verifier + // server: certificate watcher provider + sync external verifier + // extra: TLS 1.2 + "Chttp2CertWatcherProviderSyncVerifierTls12", + kH2TLSFeatureMask, + "foo.test.google.fr", + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique( + SecurityPrimitives::TlsVersion::V_12, + SecurityPrimitives::ProviderType::FILE_PROVIDER, + SecurityPrimitives::VerifierType::HOSTNAME_VERIFIER); + }, + }, + CoreTestConfiguration{ + // client: static data provider + sync external verifier + // server: static data provider + sync external verifier + // extra: TLS 1.2 + "Chttp2SimpleSslFullstack", + kH2TLSFeatureMask, + "foo.test.google.fr", + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique( + SecurityPrimitives::TlsVersion::V_12, + SecurityPrimitives::ProviderType::STATIC_PROVIDER, + SecurityPrimitives::VerifierType::EXTERNAL_SYNC_VERIFIER); + }, + }, + CoreTestConfiguration{ + // client: static data provider + async external verifier + // server: static data provider + async external verifier + // extra: TLS 1.3 + "Chttp2StaticProviderAsyncVerifierTls13", + kH2TLSFeatureMask, + "foo.test.google.fr", + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique( + SecurityPrimitives::TlsVersion::V_13, + SecurityPrimitives::ProviderType::STATIC_PROVIDER, + SecurityPrimitives::VerifierType::EXTERNAL_ASYNC_VERIFIER); + }, + }, +#ifdef GPR_LINUX + CoreTestConfiguration{ + "Chttp2FullstackUdsAbstractNamespace", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs&, const ChannelArgs&) { + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + return std::make_unique(absl::StrFormat( + "unix-abstract:grpc_fullstack_test.%d.%" PRId64 ".%" PRId32 + ".%d", + getpid(), now.tv_sec, now.tv_nsec, + unique.fetch_add(1, std::memory_order_relaxed))); + }}, +#endif +#ifdef GRPC_HAVE_UNIX_SOCKET + CoreTestConfiguration{ + "Chttp2FullstackUds", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs&, const ChannelArgs&) { + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + return std::make_unique(absl::StrFormat( + "unix:/tmp/grpc_fullstack_test.%d.%" PRId64 ".%" PRId32 ".%d", + getpid(), now.tv_sec, now.tv_nsec, + unique.fetch_add(1, std::memory_order_relaxed))); + }}, +#endif +// TODO(ctiller): these got inadvertently disabled when the project +// switched to Bazel in 2016, and have not been re-enabled since and are now +// quite broken. We should re-enable them however, as they provide defense in +// depth that enabling tracers is safe. When doing so, we'll need to re-enable +// the windows setvbuf statement in main(). +#if 0 + CoreTestConfiguration{ + "Chttp2SocketPairWithTrace", + FEATURE_MASK_IS_HTTP2 | FEATURE_MASK_ENABLES_TRACES, nullptr, + [](const ChannelArgs&, const ChannelArgs&) { + return std::make_unique( + std::make_unique(ChannelArgs())); + }}, + CoreTestConfiguration{"Chttp2FullstackWithTrace", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_IS_HTTP2 | + FEATURE_MASK_ENABLES_TRACES, + nullptr, + [](const ChannelArgs& /*client_args*/, + const ChannelArgs& /*server_args*/) { + return std::make_unique( + std::make_unique()); + }}, +#endif +#ifdef GRPC_POSIX_WAKEUP_FD + CoreTestConfiguration{ + "Chttp2FullstackWithPipeWakeup", + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2, + nullptr, + [](const ChannelArgs& /*client_args*/, + const ChannelArgs& /*server_args*/) { + return std::make_unique(); + }}, +#endif + }; + std::sort(configs.begin(), configs.end(), + [](const CoreTestConfiguration& a, const CoreTestConfiguration& b) { + return strcmp(a.name, b.name) < 0; + }); + return configs; +} + +// A ConfigQuery queries a database a set of test configurations +// that match some criteria. +class ConfigQuery { + public: + ConfigQuery() = default; + ConfigQuery(const ConfigQuery&) = delete; + ConfigQuery& operator=(const ConfigQuery&) = delete; + // Enforce that the returned configurations have the given features. + ConfigQuery& EnforceFeatures(uint32_t features) { + enforce_features_ |= features; + return *this; + } + // Envorce that the returned configurations do not have the given features. + ConfigQuery& ExcludeFeatures(uint32_t features) { + exclude_features_ |= features; + return *this; + } + // Enforce that the returned configurations have the given name (regex). + ConfigQuery& AllowName(const std::string& name) { + allowed_names_.emplace_back( + std::regex(name, std::regex_constants::ECMAScript)); + return *this; + } + // Enforce that the returned configurations do not have the given name + // (regex). + ConfigQuery& ExcludeName(const std::string& name) { + excluded_names_.emplace_back( + std::regex(name, std::regex_constants::ECMAScript)); + return *this; + } + + auto Run() const { + static NoDestruct> kConfigs( + AllConfigs()); + std::vector out; + for (const CoreTestConfiguration& config : *kConfigs) { + if ((config.feature_mask & enforce_features_) == enforce_features_ && + (config.feature_mask & exclude_features_) == 0) { + bool allowed = allowed_names_.empty(); + for (const std::regex& re : allowed_names_) { + if (std::regex_match(config.name, re)) { + allowed = true; + break; + } + } + for (const std::regex& re : excluded_names_) { + if (std::regex_match(config.name, re)) { + allowed = false; + break; + } + } + if (allowed) { + out.push_back(&config); + } + } + } + return ::testing::ValuesIn(out); + } + + private: + uint32_t enforce_features_ = 0; + uint32_t exclude_features_ = 0; + std::vector allowed_names_; + std::vector excluded_names_; +}; + +// Produce a nice name to print next to each test case for gtest. +inline const char* NameFromConfig( + const ::testing::TestParamInfo& config) { + return config.param->name; +} + +INSTANTIATE_TEST_SUITE_P(CoreEnd2endTests, CoreEnd2endTest, ConfigQuery().Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P( + SecureEnd2endTests, SecureEnd2endTest, + ConfigQuery().EnforceFeatures(FEATURE_MASK_IS_SECURE).Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P(CoreLargeSendTests, CoreLargeSendTest, + ConfigQuery() + .ExcludeFeatures(FEATURE_MASK_1BYTE_AT_A_TIME | + FEATURE_MASK_ENABLES_TRACES) + .Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P( + CoreDeadlineTests, CoreDeadlineTest, + ConfigQuery().ExcludeFeatures(FEATURE_MASK_IS_MINSTACK).Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P( + CoreClientChannelTests, CoreClientChannelTest, + ConfigQuery().EnforceFeatures(FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL).Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P( + Http2SingleHopTests, Http2SingleHopTest, + ConfigQuery() + .EnforceFeatures(FEATURE_MASK_IS_HTTP2) + .ExcludeFeatures(FEATURE_MASK_SUPPORTS_REQUEST_PROXYING | + FEATURE_MASK_ENABLES_TRACES) + .Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P( + RetryTests, RetryTest, + ConfigQuery() + .EnforceFeatures(FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL) + .ExcludeFeatures(FEATURE_MASK_DOES_NOT_SUPPORT_RETRY) + .Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P( + WriteBufferingTests, WriteBufferingTest, + ConfigQuery() + .ExcludeFeatures(FEATURE_MASK_DOES_NOT_SUPPORT_WRITE_BUFFERING) + .Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P( + Http2Tests, Http2Test, + ConfigQuery().EnforceFeatures(FEATURE_MASK_IS_HTTP2).Run(), NameFromConfig); + +INSTANTIATE_TEST_SUITE_P( + RetryHttp2Tests, RetryHttp2Test, + ConfigQuery() + .EnforceFeatures(FEATURE_MASK_IS_HTTP2 | + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL) + .ExcludeFeatures(FEATURE_MASK_DOES_NOT_SUPPORT_RETRY | + FEATURE_MASK_SUPPORTS_REQUEST_PROXYING) + .Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P( + ResourceQuotaTests, ResourceQuotaTest, + ConfigQuery() + .ExcludeFeatures(FEATURE_MASK_SUPPORTS_REQUEST_PROXYING | + FEATURE_MASK_1BYTE_AT_A_TIME) + .ExcludeName("Chttp2.*Uds.*") + .ExcludeName("Chttp2HttpProxy") + .Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P( + PerCallCredsTests, PerCallCredsTest, + ConfigQuery() + .EnforceFeatures(FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS) + .Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P( + PerCallCredsOnInsecureTests, PerCallCredsOnInsecureTest, + ConfigQuery() + .EnforceFeatures( + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS_LEVEL_INSECURE) + .Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P( + NoLoggingTests, NoLoggingTest, + ConfigQuery().ExcludeFeatures(FEATURE_MASK_ENABLES_TRACES).Run(), + NameFromConfig); + +INSTANTIATE_TEST_SUITE_P(ProxyAuthTests, ProxyAuthTest, + ConfigQuery().AllowName("Chttp2HttpProxy").Run(), + NameFromConfig); + +} // namespace grpc_core + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); + // TODO(ctiller): make this per fixture? + grpc_core::ConfigVars::Overrides overrides; + overrides.default_ssl_roots_file_path = CA_CERT_PATH; + grpc_core::ConfigVars::SetOverrides(overrides); + return RUN_ALL_TESTS(); +} diff --git a/test/core/end2end/end2end_test_utils.cc b/test/core/end2end/end2end_test_utils.cc deleted file mode 100644 index a3c6b41f238d9..0000000000000 --- a/test/core/end2end/end2end_test_utils.cc +++ /dev/null @@ -1,49 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include -#include - -#include "test/core/end2end/end2end_tests.h" - -const char* get_host_override_string(const char* str, - const CoreTestConfiguration& config) { - if (config.feature_mask & FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER) { - return str; - } else { - return nullptr; - } -} - -const grpc_slice* get_host_override_slice(const char* str, - const CoreTestConfiguration& config) { - const char* r = get_host_override_string(str, config); - if (r != nullptr) { - static grpc_slice ret; - ret = grpc_slice_from_static_string(r); - return &ret; - } - return nullptr; -} - -void validate_host_override_string(const char* pattern, grpc_slice str, - const CoreTestConfiguration& config) { - if (config.feature_mask & FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER) { - GPR_ASSERT(0 == grpc_slice_str_cmp(str, pattern)); - } -} diff --git a/test/core/end2end/end2end_tests.cc b/test/core/end2end/end2end_tests.cc index da3bf9e91bc5c..8c6ba5598f89d 100644 --- a/test/core/end2end/end2end_tests.cc +++ b/test/core/end2end/end2end_tests.cc @@ -17,783 +17,261 @@ // // -// This file is auto-generated - #include "test/core/end2end/end2end_tests.h" -#include -#include +#include + +#include "absl/memory/memory.h" +#include "absl/random/random.h" + +#include +#include +#include -#include "absl/strings/str_format.h" +#include "src/core/lib/compression/message_compress.h" +#include "src/core/lib/config/core_configuration.h" +#include "src/core/lib/gprpp/no_destruct.h" +#include "test/core/end2end/cq_verifier.h" -#include +namespace grpc_core { + +Slice RandomSlice(size_t length) { + size_t i; + static const char chars[] = "abcdefghijklmnopqrstuvwxyz1234567890"; + std::vector output; + output.resize(length); + for (i = 0; i < length; ++i) { + output[i] = chars[rand() % static_cast(sizeof(chars) - 1)]; + } + return Slice::FromCopiedBuffer(output); +} + +Slice RandomBinarySlice(size_t length) { + size_t i; + std::vector output; + output.resize(length); + for (i = 0; i < length; ++i) { + output[i] = rand(); + } + return Slice::FromCopiedBuffer(output); +} -#include "src/core/lib/gprpp/crash.h" +ByteBufferUniquePtr ByteBufferFromSlice(Slice slice) { + return ByteBufferUniquePtr( + grpc_raw_byte_buffer_create(const_cast(&slice.c_slice()), 1), + grpc_byte_buffer_destroy); +} -static bool g_pre_init_called = false; +namespace { +absl::optional FindInMetadataArray(const grpc_metadata_array& md, + absl::string_view key) { + for (size_t i = 0; i < md.count; i++) { + if (key == StringViewFromSlice(md.metadata[i].key)) { + return std::string(StringViewFromSlice(md.metadata[i].value)); + } + } + return absl::nullopt; +} +} // namespace -extern void authority_not_supported(const CoreTestConfiguration& config); -extern void authority_not_supported_pre_init(void); -extern void bad_hostname(const CoreTestConfiguration& config); -extern void bad_hostname_pre_init(void); -extern void bad_ping(const CoreTestConfiguration& config); -extern void bad_ping_pre_init(void); -extern void binary_metadata(const CoreTestConfiguration& config); -extern void binary_metadata_pre_init(void); -extern void call_creds(const CoreTestConfiguration& config); -extern void call_creds_pre_init(void); -extern void call_host_override(const CoreTestConfiguration& config); -extern void call_host_override_pre_init(void); -extern void cancel_after_accept(const CoreTestConfiguration& config); -extern void cancel_after_accept_pre_init(void); -extern void cancel_after_client_done(const CoreTestConfiguration& config); -extern void cancel_after_client_done_pre_init(void); -extern void cancel_after_invoke(const CoreTestConfiguration& config); -extern void cancel_after_invoke_pre_init(void); -extern void cancel_after_round_trip(const CoreTestConfiguration& config); -extern void cancel_after_round_trip_pre_init(void); -extern void cancel_before_invoke(const CoreTestConfiguration& config); -extern void cancel_before_invoke_pre_init(void); -extern void cancel_in_a_vacuum(const CoreTestConfiguration& config); -extern void cancel_in_a_vacuum_pre_init(void); -extern void cancel_with_status(const CoreTestConfiguration& config); -extern void cancel_with_status_pre_init(void); -extern void channelz(const CoreTestConfiguration& config); -extern void channelz_pre_init(void); -extern void client_streaming(const CoreTestConfiguration& config); -extern void client_streaming_pre_init(void); -extern void compressed_payload(const CoreTestConfiguration& config); -extern void compressed_payload_pre_init(void); -extern void connectivity(const CoreTestConfiguration& config); -extern void connectivity_pre_init(void); -extern void default_host(const CoreTestConfiguration& config); -extern void default_host_pre_init(void); -extern void disappearing_server(const CoreTestConfiguration& config); -extern void disappearing_server_pre_init(void); -extern void empty_batch(const CoreTestConfiguration& config); -extern void empty_batch_pre_init(void); -extern void filter_causes_close(const CoreTestConfiguration& config); -extern void filter_causes_close_pre_init(void); -extern void filter_context(const CoreTestConfiguration& config); -extern void filter_context_pre_init(void); -extern void filter_init_fails(const CoreTestConfiguration& config); -extern void filter_init_fails_pre_init(void); -extern void filter_latency(const CoreTestConfiguration& config); -extern void filter_latency_pre_init(void); -extern void filter_status_code(const CoreTestConfiguration& config); -extern void filter_status_code_pre_init(void); -extern void filtered_metadata(const CoreTestConfiguration& config); -extern void filtered_metadata_pre_init(void); -extern void graceful_server_shutdown(const CoreTestConfiguration& config); -extern void graceful_server_shutdown_pre_init(void); -extern void grpc_authz(const CoreTestConfiguration& config); -extern void grpc_authz_pre_init(void); -extern void high_initial_seqno(const CoreTestConfiguration& config); -extern void high_initial_seqno_pre_init(void); -extern void hpack_size(const CoreTestConfiguration& config); -extern void hpack_size_pre_init(void); -extern void invoke_large_request(const CoreTestConfiguration& config); -extern void invoke_large_request_pre_init(void); -extern void keepalive_timeout(const CoreTestConfiguration& config); -extern void keepalive_timeout_pre_init(void); -extern void large_metadata(const CoreTestConfiguration& config); -extern void large_metadata_pre_init(void); -extern void max_concurrent_streams(const CoreTestConfiguration& config); -extern void max_concurrent_streams_pre_init(void); -extern void max_connection_age(const CoreTestConfiguration& config); -extern void max_connection_age_pre_init(void); -extern void max_connection_idle(const CoreTestConfiguration& config); -extern void max_connection_idle_pre_init(void); -extern void max_message_length(const CoreTestConfiguration& config); -extern void max_message_length_pre_init(void); -extern void negative_deadline(const CoreTestConfiguration& config); -extern void negative_deadline_pre_init(void); -extern void no_logging(const CoreTestConfiguration& config); -extern void no_logging_pre_init(void); -extern void no_op(const CoreTestConfiguration& config); -extern void no_op_pre_init(void); -extern void payload(const CoreTestConfiguration& config); -extern void payload_pre_init(void); -extern void ping(const CoreTestConfiguration& config); -extern void ping_pre_init(void); -extern void ping_pong_streaming(const CoreTestConfiguration& config); -extern void ping_pong_streaming_pre_init(void); -extern void proxy_auth(const CoreTestConfiguration& config); -extern void proxy_auth_pre_init(void); -extern void registered_call(const CoreTestConfiguration& config); -extern void registered_call_pre_init(void); -extern void request_with_flags(const CoreTestConfiguration& config); -extern void request_with_flags_pre_init(void); -extern void request_with_payload(const CoreTestConfiguration& config); -extern void request_with_payload_pre_init(void); -extern void resource_quota_server(const CoreTestConfiguration& config); -extern void resource_quota_server_pre_init(void); -extern void retry(const CoreTestConfiguration& config); -extern void retry_pre_init(void); -extern void retry_cancel_after_first_attempt_starts(const CoreTestConfiguration& config); -extern void retry_cancel_after_first_attempt_starts_pre_init(void); -extern void retry_cancel_during_delay(const CoreTestConfiguration& config); -extern void retry_cancel_during_delay_pre_init(void); -extern void retry_cancel_with_multiple_send_batches(const CoreTestConfiguration& config); -extern void retry_cancel_with_multiple_send_batches_pre_init(void); -extern void retry_cancellation(const CoreTestConfiguration& config); -extern void retry_cancellation_pre_init(void); -extern void retry_disabled(const CoreTestConfiguration& config); -extern void retry_disabled_pre_init(void); -extern void retry_exceeds_buffer_size_in_delay(const CoreTestConfiguration& config); -extern void retry_exceeds_buffer_size_in_delay_pre_init(void); -extern void retry_exceeds_buffer_size_in_initial_batch(const CoreTestConfiguration& config); -extern void retry_exceeds_buffer_size_in_initial_batch_pre_init(void); -extern void retry_exceeds_buffer_size_in_subsequent_batch(const CoreTestConfiguration& config); -extern void retry_exceeds_buffer_size_in_subsequent_batch_pre_init(void); -extern void retry_lb_drop(const CoreTestConfiguration& config); -extern void retry_lb_drop_pre_init(void); -extern void retry_lb_fail(const CoreTestConfiguration& config); -extern void retry_lb_fail_pre_init(void); -extern void retry_non_retriable_status(const CoreTestConfiguration& config); -extern void retry_non_retriable_status_pre_init(void); -extern void retry_non_retriable_status_before_recv_trailing_metadata_started(const CoreTestConfiguration& config); -extern void retry_non_retriable_status_before_recv_trailing_metadata_started_pre_init(void); -extern void retry_per_attempt_recv_timeout(const CoreTestConfiguration& config); -extern void retry_per_attempt_recv_timeout_pre_init(void); -extern void retry_per_attempt_recv_timeout_on_last_attempt(const CoreTestConfiguration& config); -extern void retry_per_attempt_recv_timeout_on_last_attempt_pre_init(void); -extern void retry_recv_initial_metadata(const CoreTestConfiguration& config); -extern void retry_recv_initial_metadata_pre_init(void); -extern void retry_recv_message(const CoreTestConfiguration& config); -extern void retry_recv_message_pre_init(void); -extern void retry_recv_message_replay(const CoreTestConfiguration& config); -extern void retry_recv_message_replay_pre_init(void); -extern void retry_recv_trailing_metadata_error(const CoreTestConfiguration& config); -extern void retry_recv_trailing_metadata_error_pre_init(void); -extern void retry_send_initial_metadata_refs(const CoreTestConfiguration& config); -extern void retry_send_initial_metadata_refs_pre_init(void); -extern void retry_send_op_fails(const CoreTestConfiguration& config); -extern void retry_send_op_fails_pre_init(void); -extern void retry_send_recv_batch(const CoreTestConfiguration& config); -extern void retry_send_recv_batch_pre_init(void); -extern void retry_server_pushback_delay(const CoreTestConfiguration& config); -extern void retry_server_pushback_delay_pre_init(void); -extern void retry_server_pushback_disabled(const CoreTestConfiguration& config); -extern void retry_server_pushback_disabled_pre_init(void); -extern void retry_streaming(const CoreTestConfiguration& config); -extern void retry_streaming_pre_init(void); -extern void retry_streaming_after_commit(const CoreTestConfiguration& config); -extern void retry_streaming_after_commit_pre_init(void); -extern void retry_streaming_succeeds_before_replay_finished(const CoreTestConfiguration& config); -extern void retry_streaming_succeeds_before_replay_finished_pre_init(void); -extern void retry_throttled(const CoreTestConfiguration& config); -extern void retry_throttled_pre_init(void); -extern void retry_too_many_attempts(const CoreTestConfiguration& config); -extern void retry_too_many_attempts_pre_init(void); -extern void retry_transparent_goaway(const CoreTestConfiguration& config); -extern void retry_transparent_goaway_pre_init(void); -extern void retry_transparent_max_concurrent_streams(const CoreTestConfiguration& config); -extern void retry_transparent_max_concurrent_streams_pre_init(void); -extern void retry_transparent_not_sent_on_wire(const CoreTestConfiguration& config); -extern void retry_transparent_not_sent_on_wire_pre_init(void); -extern void retry_unref_before_finish(const CoreTestConfiguration& config); -extern void retry_unref_before_finish_pre_init(void); -extern void retry_unref_before_recv(const CoreTestConfiguration& config); -extern void retry_unref_before_recv_pre_init(void); -extern void server_finishes_request(const CoreTestConfiguration& config); -extern void server_finishes_request_pre_init(void); -extern void server_streaming(const CoreTestConfiguration& config); -extern void server_streaming_pre_init(void); -extern void shutdown_finishes_calls(const CoreTestConfiguration& config); -extern void shutdown_finishes_calls_pre_init(void); -extern void shutdown_finishes_tags(const CoreTestConfiguration& config); -extern void shutdown_finishes_tags_pre_init(void); -extern void simple_delayed_request(const CoreTestConfiguration& config); -extern void simple_delayed_request_pre_init(void); -extern void simple_metadata(const CoreTestConfiguration& config); -extern void simple_metadata_pre_init(void); -extern void simple_request(const CoreTestConfiguration& config); -extern void simple_request_pre_init(void); -extern void streaming_error_response(const CoreTestConfiguration& config); -extern void streaming_error_response_pre_init(void); -extern void trailing_metadata(const CoreTestConfiguration& config); -extern void trailing_metadata_pre_init(void); -extern void write_buffering(const CoreTestConfiguration& config); -extern void write_buffering_pre_init(void); -extern void write_buffering_at_end(const CoreTestConfiguration& config); -extern void write_buffering_at_end_pre_init(void); +void CoreEnd2endTest::SetUp() { + CoreConfiguration::Reset(); + initialized_ = false; +} -void grpc_end2end_tests_pre_init(void) { - GPR_ASSERT(!g_pre_init_called); - g_pre_init_called = true; - authority_not_supported_pre_init(); - bad_hostname_pre_init(); - bad_ping_pre_init(); - binary_metadata_pre_init(); - call_creds_pre_init(); - call_host_override_pre_init(); - cancel_after_accept_pre_init(); - cancel_after_client_done_pre_init(); - cancel_after_invoke_pre_init(); - cancel_after_round_trip_pre_init(); - cancel_before_invoke_pre_init(); - cancel_in_a_vacuum_pre_init(); - cancel_with_status_pre_init(); - channelz_pre_init(); - client_streaming_pre_init(); - compressed_payload_pre_init(); - connectivity_pre_init(); - default_host_pre_init(); - disappearing_server_pre_init(); - empty_batch_pre_init(); - filter_causes_close_pre_init(); - filter_context_pre_init(); - filter_init_fails_pre_init(); - filter_latency_pre_init(); - filter_status_code_pre_init(); - filtered_metadata_pre_init(); - graceful_server_shutdown_pre_init(); - grpc_authz_pre_init(); - high_initial_seqno_pre_init(); - hpack_size_pre_init(); - invoke_large_request_pre_init(); - keepalive_timeout_pre_init(); - large_metadata_pre_init(); - max_concurrent_streams_pre_init(); - max_connection_age_pre_init(); - max_connection_idle_pre_init(); - max_message_length_pre_init(); - negative_deadline_pre_init(); - no_logging_pre_init(); - no_op_pre_init(); - payload_pre_init(); - ping_pre_init(); - ping_pong_streaming_pre_init(); - proxy_auth_pre_init(); - registered_call_pre_init(); - request_with_flags_pre_init(); - request_with_payload_pre_init(); - resource_quota_server_pre_init(); - retry_pre_init(); - retry_cancel_after_first_attempt_starts_pre_init(); - retry_cancel_during_delay_pre_init(); - retry_cancel_with_multiple_send_batches_pre_init(); - retry_cancellation_pre_init(); - retry_disabled_pre_init(); - retry_exceeds_buffer_size_in_delay_pre_init(); - retry_exceeds_buffer_size_in_initial_batch_pre_init(); - retry_exceeds_buffer_size_in_subsequent_batch_pre_init(); - retry_lb_drop_pre_init(); - retry_lb_fail_pre_init(); - retry_non_retriable_status_pre_init(); - retry_non_retriable_status_before_recv_trailing_metadata_started_pre_init(); - retry_per_attempt_recv_timeout_pre_init(); - retry_per_attempt_recv_timeout_on_last_attempt_pre_init(); - retry_recv_initial_metadata_pre_init(); - retry_recv_message_pre_init(); - retry_recv_message_replay_pre_init(); - retry_recv_trailing_metadata_error_pre_init(); - retry_send_initial_metadata_refs_pre_init(); - retry_send_op_fails_pre_init(); - retry_send_recv_batch_pre_init(); - retry_server_pushback_delay_pre_init(); - retry_server_pushback_disabled_pre_init(); - retry_streaming_pre_init(); - retry_streaming_after_commit_pre_init(); - retry_streaming_succeeds_before_replay_finished_pre_init(); - retry_throttled_pre_init(); - retry_too_many_attempts_pre_init(); - retry_transparent_goaway_pre_init(); - retry_transparent_max_concurrent_streams_pre_init(); - retry_transparent_not_sent_on_wire_pre_init(); - retry_unref_before_finish_pre_init(); - retry_unref_before_recv_pre_init(); - server_finishes_request_pre_init(); - server_streaming_pre_init(); - shutdown_finishes_calls_pre_init(); - shutdown_finishes_tags_pre_init(); - simple_delayed_request_pre_init(); - simple_metadata_pre_init(); - simple_request_pre_init(); - streaming_error_response_pre_init(); - trailing_metadata_pre_init(); - write_buffering_pre_init(); - write_buffering_at_end_pre_init(); +void CoreEnd2endTest::TearDown() { + const bool do_shutdown = fixture_ != nullptr; + cq_verifier_.reset(); + fixture_.reset(); + if (do_shutdown) grpc_shutdown_blocking(); + initialized_ = false; } -// NOLINTNEXTLINE(readability-function-size) -void grpc_end2end_tests(int argc, char **argv, - const CoreTestConfiguration& config) { - int i; +absl::optional CoreEnd2endTest::IncomingMetadata::Get( + absl::string_view key) const { + return FindInMetadataArray(*metadata_, key); +} - GPR_ASSERT(g_pre_init_called); +grpc_op CoreEnd2endTest::IncomingMetadata::MakeOp() { + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_RECV_INITIAL_METADATA; + op.data.recv_initial_metadata.recv_initial_metadata = metadata_.get(); + return op; +} - if (argc <= 1) { - authority_not_supported(config); - bad_hostname(config); - bad_ping(config); - binary_metadata(config); - call_creds(config); - call_host_override(config); - cancel_after_accept(config); - cancel_after_client_done(config); - cancel_after_invoke(config); - cancel_after_round_trip(config); - cancel_before_invoke(config); - cancel_in_a_vacuum(config); - cancel_with_status(config); - channelz(config); - client_streaming(config); - compressed_payload(config); - connectivity(config); - default_host(config); - disappearing_server(config); - empty_batch(config); - filter_causes_close(config); - filter_context(config); - filter_init_fails(config); - filter_latency(config); - filter_status_code(config); - filtered_metadata(config); - graceful_server_shutdown(config); - grpc_authz(config); - high_initial_seqno(config); - hpack_size(config); - invoke_large_request(config); - keepalive_timeout(config); - large_metadata(config); - max_concurrent_streams(config); - max_connection_age(config); - max_connection_idle(config); - max_message_length(config); - negative_deadline(config); - no_logging(config); - no_op(config); - payload(config); - ping(config); - ping_pong_streaming(config); - proxy_auth(config); - registered_call(config); - request_with_flags(config); - request_with_payload(config); - resource_quota_server(config); - retry(config); - retry_cancel_after_first_attempt_starts(config); - retry_cancel_during_delay(config); - retry_cancel_with_multiple_send_batches(config); - retry_cancellation(config); - retry_disabled(config); - retry_exceeds_buffer_size_in_delay(config); - retry_exceeds_buffer_size_in_initial_batch(config); - retry_exceeds_buffer_size_in_subsequent_batch(config); - retry_lb_drop(config); - retry_lb_fail(config); - retry_non_retriable_status(config); - retry_non_retriable_status_before_recv_trailing_metadata_started(config); - retry_per_attempt_recv_timeout(config); - retry_per_attempt_recv_timeout_on_last_attempt(config); - retry_recv_initial_metadata(config); - retry_recv_message(config); - retry_recv_message_replay(config); - retry_recv_trailing_metadata_error(config); - retry_send_initial_metadata_refs(config); - retry_send_op_fails(config); - retry_send_recv_batch(config); - retry_server_pushback_delay(config); - retry_server_pushback_disabled(config); - retry_streaming(config); - retry_streaming_after_commit(config); - retry_streaming_succeeds_before_replay_finished(config); - retry_throttled(config); - retry_too_many_attempts(config); - retry_transparent_goaway(config); - retry_transparent_max_concurrent_streams(config); - retry_transparent_not_sent_on_wire(config); - retry_unref_before_finish(config); - retry_unref_before_recv(config); - server_finishes_request(config); - server_streaming(config); - shutdown_finishes_calls(config); - shutdown_finishes_tags(config); - simple_delayed_request(config); - simple_metadata(config); - simple_request(config); - streaming_error_response(config); - trailing_metadata(config); - write_buffering(config); - write_buffering_at_end(config); - return; +std::string CoreEnd2endTest::IncomingMessage::payload() const { + Slice out; + if (payload_->data.raw.compression > GRPC_COMPRESS_NONE) { + grpc_slice_buffer decompressed_buffer; + grpc_slice_buffer_init(&decompressed_buffer); + GPR_ASSERT(grpc_msg_decompress(payload_->data.raw.compression, + &payload_->data.raw.slice_buffer, + &decompressed_buffer)); + grpc_byte_buffer* rbb = grpc_raw_byte_buffer_create( + decompressed_buffer.slices, decompressed_buffer.count); + grpc_byte_buffer_reader reader; + GPR_ASSERT(grpc_byte_buffer_reader_init(&reader, rbb)); + out = Slice(grpc_byte_buffer_reader_readall(&reader)); + grpc_byte_buffer_reader_destroy(&reader); + grpc_byte_buffer_destroy(rbb); + grpc_slice_buffer_destroy(&decompressed_buffer); + } else { + grpc_byte_buffer_reader reader; + GPR_ASSERT(grpc_byte_buffer_reader_init(&reader, payload_)); + out = Slice(grpc_byte_buffer_reader_readall(&reader)); + grpc_byte_buffer_reader_destroy(&reader); } + return std::string(out.begin(), out.end()); +} - for (i = 1; i < argc; i++) { - if (0 == strcmp("authority_not_supported", argv[i])) { - authority_not_supported(config); - continue; - } - if (0 == strcmp("bad_hostname", argv[i])) { - bad_hostname(config); - continue; - } - if (0 == strcmp("bad_ping", argv[i])) { - bad_ping(config); - continue; - } - if (0 == strcmp("binary_metadata", argv[i])) { - binary_metadata(config); - continue; - } - if (0 == strcmp("call_creds", argv[i])) { - call_creds(config); - continue; - } - if (0 == strcmp("call_host_override", argv[i])) { - call_host_override(config); - continue; - } - if (0 == strcmp("cancel_after_accept", argv[i])) { - cancel_after_accept(config); - continue; - } - if (0 == strcmp("cancel_after_client_done", argv[i])) { - cancel_after_client_done(config); - continue; - } - if (0 == strcmp("cancel_after_invoke", argv[i])) { - cancel_after_invoke(config); - continue; - } - if (0 == strcmp("cancel_after_round_trip", argv[i])) { - cancel_after_round_trip(config); - continue; - } - if (0 == strcmp("cancel_before_invoke", argv[i])) { - cancel_before_invoke(config); - continue; - } - if (0 == strcmp("cancel_in_a_vacuum", argv[i])) { - cancel_in_a_vacuum(config); - continue; - } - if (0 == strcmp("cancel_with_status", argv[i])) { - cancel_with_status(config); - continue; - } - if (0 == strcmp("channelz", argv[i])) { - channelz(config); - continue; - } - if (0 == strcmp("client_streaming", argv[i])) { - client_streaming(config); - continue; - } - if (0 == strcmp("compressed_payload", argv[i])) { - compressed_payload(config); - continue; - } - if (0 == strcmp("connectivity", argv[i])) { - connectivity(config); - continue; - } - if (0 == strcmp("default_host", argv[i])) { - default_host(config); - continue; - } - if (0 == strcmp("disappearing_server", argv[i])) { - disappearing_server(config); - continue; - } - if (0 == strcmp("empty_batch", argv[i])) { - empty_batch(config); - continue; - } - if (0 == strcmp("filter_causes_close", argv[i])) { - filter_causes_close(config); - continue; - } - if (0 == strcmp("filter_context", argv[i])) { - filter_context(config); - continue; - } - if (0 == strcmp("filter_init_fails", argv[i])) { - filter_init_fails(config); - continue; - } - if (0 == strcmp("filter_latency", argv[i])) { - filter_latency(config); - continue; - } - if (0 == strcmp("filter_status_code", argv[i])) { - filter_status_code(config); - continue; - } - if (0 == strcmp("filtered_metadata", argv[i])) { - filtered_metadata(config); - continue; - } - if (0 == strcmp("graceful_server_shutdown", argv[i])) { - graceful_server_shutdown(config); - continue; - } - if (0 == strcmp("grpc_authz", argv[i])) { - grpc_authz(config); - continue; - } - if (0 == strcmp("high_initial_seqno", argv[i])) { - high_initial_seqno(config); - continue; - } - if (0 == strcmp("hpack_size", argv[i])) { - hpack_size(config); - continue; - } - if (0 == strcmp("invoke_large_request", argv[i])) { - invoke_large_request(config); - continue; - } - if (0 == strcmp("keepalive_timeout", argv[i])) { - keepalive_timeout(config); - continue; - } - if (0 == strcmp("large_metadata", argv[i])) { - large_metadata(config); - continue; - } - if (0 == strcmp("max_concurrent_streams", argv[i])) { - max_concurrent_streams(config); - continue; - } - if (0 == strcmp("max_connection_age", argv[i])) { - max_connection_age(config); - continue; - } - if (0 == strcmp("max_connection_idle", argv[i])) { - max_connection_idle(config); - continue; - } - if (0 == strcmp("max_message_length", argv[i])) { - max_message_length(config); - continue; - } - if (0 == strcmp("negative_deadline", argv[i])) { - negative_deadline(config); - continue; - } - if (0 == strcmp("no_logging", argv[i])) { - no_logging(config); - continue; - } - if (0 == strcmp("no_op", argv[i])) { - no_op(config); - continue; - } - if (0 == strcmp("payload", argv[i])) { - payload(config); - continue; - } - if (0 == strcmp("ping", argv[i])) { - ping(config); - continue; - } - if (0 == strcmp("ping_pong_streaming", argv[i])) { - ping_pong_streaming(config); - continue; - } - if (0 == strcmp("proxy_auth", argv[i])) { - proxy_auth(config); - continue; - } - if (0 == strcmp("registered_call", argv[i])) { - registered_call(config); - continue; - } - if (0 == strcmp("request_with_flags", argv[i])) { - request_with_flags(config); - continue; - } - if (0 == strcmp("request_with_payload", argv[i])) { - request_with_payload(config); - continue; - } - if (0 == strcmp("resource_quota_server", argv[i])) { - resource_quota_server(config); - continue; - } - if (0 == strcmp("retry", argv[i])) { - retry(config); - continue; - } - if (0 == strcmp("retry_cancel_after_first_attempt_starts", argv[i])) { - retry_cancel_after_first_attempt_starts(config); - continue; - } - if (0 == strcmp("retry_cancel_during_delay", argv[i])) { - retry_cancel_during_delay(config); - continue; - } - if (0 == strcmp("retry_cancel_with_multiple_send_batches", argv[i])) { - retry_cancel_with_multiple_send_batches(config); - continue; - } - if (0 == strcmp("retry_cancellation", argv[i])) { - retry_cancellation(config); - continue; - } - if (0 == strcmp("retry_disabled", argv[i])) { - retry_disabled(config); - continue; - } - if (0 == strcmp("retry_exceeds_buffer_size_in_delay", argv[i])) { - retry_exceeds_buffer_size_in_delay(config); - continue; - } - if (0 == strcmp("retry_exceeds_buffer_size_in_initial_batch", argv[i])) { - retry_exceeds_buffer_size_in_initial_batch(config); - continue; - } - if (0 == strcmp("retry_exceeds_buffer_size_in_subsequent_batch", argv[i])) { - retry_exceeds_buffer_size_in_subsequent_batch(config); - continue; - } - if (0 == strcmp("retry_lb_drop", argv[i])) { - retry_lb_drop(config); - continue; - } - if (0 == strcmp("retry_lb_fail", argv[i])) { - retry_lb_fail(config); - continue; - } - if (0 == strcmp("retry_non_retriable_status", argv[i])) { - retry_non_retriable_status(config); - continue; - } - if (0 == strcmp("retry_non_retriable_status_before_recv_trailing_metadata_started", argv[i])) { - retry_non_retriable_status_before_recv_trailing_metadata_started(config); - continue; - } - if (0 == strcmp("retry_per_attempt_recv_timeout", argv[i])) { - retry_per_attempt_recv_timeout(config); - continue; - } - if (0 == strcmp("retry_per_attempt_recv_timeout_on_last_attempt", argv[i])) { - retry_per_attempt_recv_timeout_on_last_attempt(config); - continue; - } - if (0 == strcmp("retry_recv_initial_metadata", argv[i])) { - retry_recv_initial_metadata(config); - continue; - } - if (0 == strcmp("retry_recv_message", argv[i])) { - retry_recv_message(config); - continue; - } - if (0 == strcmp("retry_recv_message_replay", argv[i])) { - retry_recv_message_replay(config); - continue; - } - if (0 == strcmp("retry_recv_trailing_metadata_error", argv[i])) { - retry_recv_trailing_metadata_error(config); - continue; - } - if (0 == strcmp("retry_send_initial_metadata_refs", argv[i])) { - retry_send_initial_metadata_refs(config); - continue; - } - if (0 == strcmp("retry_send_op_fails", argv[i])) { - retry_send_op_fails(config); - continue; - } - if (0 == strcmp("retry_send_recv_batch", argv[i])) { - retry_send_recv_batch(config); - continue; - } - if (0 == strcmp("retry_server_pushback_delay", argv[i])) { - retry_server_pushback_delay(config); - continue; - } - if (0 == strcmp("retry_server_pushback_disabled", argv[i])) { - retry_server_pushback_disabled(config); - continue; - } - if (0 == strcmp("retry_streaming", argv[i])) { - retry_streaming(config); - continue; - } - if (0 == strcmp("retry_streaming_after_commit", argv[i])) { - retry_streaming_after_commit(config); - continue; - } - if (0 == strcmp("retry_streaming_succeeds_before_replay_finished", argv[i])) { - retry_streaming_succeeds_before_replay_finished(config); - continue; - } - if (0 == strcmp("retry_throttled", argv[i])) { - retry_throttled(config); - continue; - } - if (0 == strcmp("retry_too_many_attempts", argv[i])) { - retry_too_many_attempts(config); - continue; - } - if (0 == strcmp("retry_transparent_goaway", argv[i])) { - retry_transparent_goaway(config); - continue; - } - if (0 == strcmp("retry_transparent_max_concurrent_streams", argv[i])) { - retry_transparent_max_concurrent_streams(config); - continue; - } - if (0 == strcmp("retry_transparent_not_sent_on_wire", argv[i])) { - retry_transparent_not_sent_on_wire(config); - continue; - } - if (0 == strcmp("retry_unref_before_finish", argv[i])) { - retry_unref_before_finish(config); - continue; - } - if (0 == strcmp("retry_unref_before_recv", argv[i])) { - retry_unref_before_recv(config); - continue; - } - if (0 == strcmp("server_finishes_request", argv[i])) { - server_finishes_request(config); - continue; - } - if (0 == strcmp("server_streaming", argv[i])) { - server_streaming(config); - continue; - } - if (0 == strcmp("shutdown_finishes_calls", argv[i])) { - shutdown_finishes_calls(config); - continue; - } - if (0 == strcmp("shutdown_finishes_tags", argv[i])) { - shutdown_finishes_tags(config); - continue; - } - if (0 == strcmp("simple_delayed_request", argv[i])) { - simple_delayed_request(config); - continue; - } - if (0 == strcmp("simple_metadata", argv[i])) { - simple_metadata(config); - continue; - } - if (0 == strcmp("simple_request", argv[i])) { - simple_request(config); - continue; - } - if (0 == strcmp("streaming_error_response", argv[i])) { - streaming_error_response(config); - continue; - } - if (0 == strcmp("trailing_metadata", argv[i])) { - trailing_metadata(config); - continue; - } - if (0 == strcmp("write_buffering", argv[i])) { - write_buffering(config); - continue; - } - if (0 == strcmp("write_buffering_at_end", argv[i])) { - write_buffering_at_end(config); - continue; - } - grpc_core::Crash(absl::StrFormat( "not a test: '%s'", argv[i])); +grpc_op CoreEnd2endTest::IncomingMessage::MakeOp() { + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_RECV_MESSAGE; + op.data.recv_message.recv_message = &payload_; + return op; +} + +absl::optional +CoreEnd2endTest::IncomingStatusOnClient::GetTrailingMetadata( + absl::string_view key) const { + return FindInMetadataArray(data_->trailing_metadata, key); +} + +grpc_op CoreEnd2endTest::IncomingStatusOnClient::MakeOp() { + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op.data.recv_status_on_client.trailing_metadata = &data_->trailing_metadata; + op.data.recv_status_on_client.status = &data_->status; + op.data.recv_status_on_client.status_details = + const_cast(&data_->status_details.c_slice()); + op.data.recv_status_on_client.error_string = &data_->error_string; + return op; +} + +grpc_op CoreEnd2endTest::IncomingCloseOnServer::MakeOp() { + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_RECV_CLOSE_ON_SERVER; + op.data.recv_close_on_server.cancelled = &cancelled_; + return op; +} + +CoreEnd2endTest::BatchBuilder& +CoreEnd2endTest::BatchBuilder::SendInitialMetadata( + std::initializer_list> md, + uint32_t flags, absl::optional compression_level) { + auto& v = Make>(); + for (const auto& p : md) { + grpc_metadata m; + m.key = Make(Slice::FromCopiedString(p.first)).c_slice(); + m.value = Make(Slice::FromCopiedString(p.second)).c_slice(); + v.push_back(m); + } + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_SEND_INITIAL_METADATA; + op.flags = flags; + op.data.send_initial_metadata.count = v.size(); + op.data.send_initial_metadata.metadata = v.data(); + if (compression_level.has_value()) { + op.data.send_initial_metadata.maybe_compression_level.is_set = 1; + op.data.send_initial_metadata.maybe_compression_level.level = + compression_level.value(); + } + ops_.push_back(op); + return *this; +} + +CoreEnd2endTest::BatchBuilder& CoreEnd2endTest::BatchBuilder::SendMessage( + Slice payload, uint32_t flags) { + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_SEND_MESSAGE; + op.data.send_message.send_message = + Make(ByteBufferFromSlice(std::move(payload))).get(); + op.flags = flags; + ops_.push_back(op); + return *this; +} + +CoreEnd2endTest::BatchBuilder& +CoreEnd2endTest::BatchBuilder::SendCloseFromClient() { + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + ops_.push_back(op); + return *this; +} + +CoreEnd2endTest::BatchBuilder& +CoreEnd2endTest::BatchBuilder::SendStatusFromServer( + grpc_status_code status, absl::string_view message, + std::initializer_list> md) { + auto& v = Make>(); + for (const auto& p : md) { + grpc_metadata m; + m.key = Make(Slice::FromCopiedString(p.first)).c_slice(); + m.value = Make(Slice::FromCopiedString(p.second)).c_slice(); + v.push_back(m); + } + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_SEND_STATUS_FROM_SERVER; + op.data.send_status_from_server.trailing_metadata_count = v.size(); + op.data.send_status_from_server.trailing_metadata = v.data(); + op.data.send_status_from_server.status = status; + op.data.send_status_from_server.status_details = &Make( + Make(Slice::FromCopiedString(message)).c_slice()); + ops_.push_back(op); + return *this; +} + +CoreEnd2endTest::BatchBuilder::~BatchBuilder() { + grpc_call_error err = grpc_call_start_batch(call_, ops_.data(), ops_.size(), + CqVerifier::tag(tag_), nullptr); + EXPECT_EQ(err, GRPC_CALL_OK) << grpc_call_error_to_string(err); +} + +CoreEnd2endTest::Call CoreEnd2endTest::ClientCallBuilder::Create() { + if (auto* u = absl::get_if(&call_selector_)) { + absl::optional host; + if (u->host.has_value()) host = Slice::FromCopiedString(*u->host); + test_.ForceInitialized(); + return Call(grpc_channel_create_call( + test_.fixture().client(), parent_call_, propagation_mask_, + test_.fixture().cq(), Slice::FromCopiedString(u->method).c_slice(), + host.has_value() ? &host->c_slice() : nullptr, deadline_, nullptr)); + } else { + return Call(grpc_channel_create_registered_call( + test_.fixture().client(), parent_call_, propagation_mask_, + test_.fixture().cq(), absl::get(call_selector_), deadline_, + nullptr)); } } + +CoreEnd2endTest::IncomingCall::IncomingCall(CoreEnd2endTest& test, int tag) + : impl_(std::make_unique()) { + test.ForceInitialized(); + grpc_server_request_call(test.fixture().server(), impl_->call.call_ptr(), + &impl_->call_details, &impl_->request_metadata, + test.fixture().cq(), test.fixture().cq(), + CqVerifier::tag(tag)); +} + +absl::optional CoreEnd2endTest::IncomingCall::GetInitialMetadata( + absl::string_view key) const { + return FindInMetadataArray(impl_->request_metadata, key); +} + +void CoreEnd2endTest::ForceInitialized() { + if (!initialized_) { + initialized_ = true; + fixture().InitServer(ChannelArgs()); + fixture().InitClient(ChannelArgs()); + } +} + +} // namespace grpc_core diff --git a/test/core/end2end/end2end_tests.h b/test/core/end2end/end2end_tests.h index d635fdd4d6ded..9ffdf4680f8cb 100644 --- a/test/core/end2end/end2end_tests.h +++ b/test/core/end2end/end2end_tests.h @@ -19,20 +19,46 @@ #ifndef GRPC_TEST_CORE_END2END_END2END_TESTS_H #define GRPC_TEST_CORE_END2END_END2END_TESTS_H +#include #include +#include #include +#include #include +#include +#include +#include +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "absl/types/variant.h" +#include "gtest/gtest.h" + +#include +#include #include -#include +#include +#include +#include +#include #include +#include #include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/bitset.h" +#include "src/core/lib/gprpp/debug_location.h" +#include "src/core/lib/gprpp/time.h" +#include "src/core/lib/slice/slice.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/call_test_only.h" +#include "src/core/lib/surface/channel.h" +#include "test/core/end2end/cq_verifier.h" #include "test/core/util/test_config.h" // Test feature flags. -#define FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION 1 +#define FEATURE_MASK_DOES_NOT_SUPPORT_RETRY 1 #define FEATURE_MASK_SUPPORTS_HOSTNAME_VERIFICATION 2 // Feature mask supports call credentials with a minimum security level of // GRPC_PRIVACY_AND_INTEGRITY. @@ -42,12 +68,17 @@ #define FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS_LEVEL_INSECURE 8 #define FEATURE_MASK_SUPPORTS_REQUEST_PROXYING 16 #define FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL 32 -#define FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER 64 +#define FEATURE_MASK_IS_HTTP2 64 +#define FEATURE_MASK_ENABLES_TRACES 128 +#define FEATURE_MASK_1BYTE_AT_A_TIME 256 +#define FEATURE_MASK_DOES_NOT_SUPPORT_WRITE_BUFFERING 512 #define FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST 1024 -#define FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES 2048 +#define FEATURE_MASK_IS_MINSTACK 2048 +#define FEATURE_MASK_IS_SECURE 4096 #define FAIL_AUTH_CHECK_SERVER_ARG_NAME "fail_auth_check" +namespace grpc_core { class CoreTestFixture { public: virtual ~CoreTestFixture() { @@ -62,12 +93,12 @@ class CoreTestFixture { grpc_server* server() { return server_; } grpc_channel* client() { return client_; } - void InitServer(const grpc_core::ChannelArgs& args) { + void InitServer(const ChannelArgs& args) { if (server_ != nullptr) ShutdownServer(); server_ = MakeServer(args); GPR_ASSERT(server_ != nullptr); } - void InitClient(const grpc_core::ChannelArgs& args) { + void InitClient(const ChannelArgs& args) { if (client_ != nullptr) ShutdownClient(); client_ = MakeClient(args); GPR_ASSERT(client_ != nullptr); @@ -96,14 +127,14 @@ class CoreTestFixture { client_ = nullptr; } + virtual grpc_server* MakeServer(const ChannelArgs& args) = 0; + virtual grpc_channel* MakeClient(const ChannelArgs& args) = 0; + protected: void SetServer(grpc_server* server); void SetClient(grpc_channel* client); private: - virtual grpc_server* MakeServer(const grpc_core::ChannelArgs& args) = 0; - virtual grpc_channel* MakeClient(const grpc_core::ChannelArgs& args) = 0; - void DrainCq() { grpc_event ev; do { @@ -117,6 +148,12 @@ class CoreTestFixture { grpc_channel* client_ = nullptr; }; +Slice RandomSlice(size_t length); +Slice RandomBinarySlice(size_t length); +using ByteBufferUniquePtr = + std::unique_ptr; +ByteBufferUniquePtr ByteBufferFromSlice(Slice slice); + struct CoreTestConfiguration { // A descriptive name for this test fixture. const char* name; @@ -130,23 +167,584 @@ struct CoreTestConfiguration { const char* overridden_call_host; std::function( - const grpc_core::ChannelArgs& client_args, - const grpc_core::ChannelArgs& server_args)> + const ChannelArgs& client_args, const ChannelArgs& server_args)> create_fixture; }; -void grpc_end2end_tests_pre_init(void); -void grpc_end2end_tests(int argc, char** argv, - const CoreTestConfiguration& config); +// Base class for e2e tests. +// +// Initialization: +// +// At the start of a test, nothing is initialized. CoreConfiguration is reset, +// and there's certainly no server nor client. +// A test can then register whatever builders it wants into the +// CoreConfiguration and have them picked up. If it does not, it will get the +// default CoreConfiguration. +// +// The test may choose to then create a client and server with InitClient() and +// InitServer(). It does not matter which order they are called, nor whether one +// or both are called. It's necessary to call these if the test demands that +// non-empty channel args should be passed to either the client or server. +// +// If a test does not call InitClient() or InitServer(), then upon the first +// call to either NewClientCall() or NewServerCall(), the client and server will +// be instantiated - this saves substantial boilerplate in the most common case +// for our tests. +// +// Notes: +// - older compilers fail matching absl::string_view with some gmock matchers on +// older compilers, and it's tremendously convenient to be able to do so. So +// we use std::string for return types here - performance isn't particularly +// important, so an extra copy is fine. +class CoreEnd2endTest + : public ::testing::TestWithParam { + public: + void SetUp() override; + void TearDown() override; + + class Call; + struct RegisteredCall { + void* p; + }; + + // CallBuilder - results in a call to either grpc_channel_create_call or + // grpc_channel_create_registered_call. + // Affords a fluent interface to specify optional arguments. + class ClientCallBuilder { + public: + ClientCallBuilder(CoreEnd2endTest& test, std::string method) + : test_(test), + call_selector_(UnregisteredCall{std::move(method), absl::nullopt}) {} + ClientCallBuilder(CoreEnd2endTest& test, RegisteredCall registered_call) + : test_(test), call_selector_(registered_call.p) {} + + // Specify the host (otherwise nullptr is passed) + ClientCallBuilder& Host(std::string host) { + absl::get(call_selector_).host = std::move(host); + return *this; + } + // Specify the timeout (otherwise gpr_inf_future is passed) - this time is + // scaled according to the test environment. + ClientCallBuilder& Timeout(Duration timeout) { + if (timeout == Duration::Infinity()) { + deadline_ = gpr_inf_future(GPR_CLOCK_REALTIME); + return *this; + } + deadline_ = grpc_timeout_milliseconds_to_deadline(timeout.millis()); + return *this; + } + // Finally create the call. + Call Create(); + + private: + CoreEnd2endTest& test_; + struct UnregisteredCall { + std::string method; + absl::optional host; + }; + absl::variant call_selector_; + grpc_call* parent_call_ = nullptr; + uint32_t propagation_mask_ = GRPC_PROPAGATE_DEFAULTS; + gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_REALTIME); + }; + + // Receiving container for incoming metadata. + class IncomingMetadata { + public: + IncomingMetadata() = default; + ~IncomingMetadata() { + if (metadata_ != nullptr) grpc_metadata_array_destroy(metadata_.get()); + } + + // Lookup a metadata value by key. + absl::optional Get(absl::string_view key) const; + + // Make a GRPC_RECV_INITIAL_METADATA op - intended for the framework, not + // for tests. + grpc_op MakeOp(); + + private: + std::unique_ptr metadata_ = + std::make_unique( + grpc_metadata_array{0, 0, nullptr}); + }; + + // Receiving container for one incoming message. + class IncomingMessage { + public: + IncomingMessage() = default; + IncomingMessage(const IncomingMessage&) = delete; + IncomingMessage& operator=(const IncomingMessage&) = delete; + ~IncomingMessage() { + if (payload_ != nullptr) grpc_byte_buffer_destroy(payload_); + } + + // Get the payload of the message - concatenated together into a string for + // easy verification. + std::string payload() const; + // Check if the message is the end of the stream. + bool is_end_of_stream() const { return payload_ == nullptr; } + // Get the type of the message. + grpc_byte_buffer_type byte_buffer_type() const { return payload_->type; } + // Get the compression algorithm used for the message. + grpc_compression_algorithm compression() const { + return payload_->data.raw.compression; + } + + // Make a GRPC_OP_RECV_MESSAGE op - intended for the framework, not for + // tests. + grpc_op MakeOp(); + + private: + grpc_byte_buffer* payload_ = nullptr; + }; + + // Receiving container for incoming status on the client from the server. + class IncomingStatusOnClient { + public: + IncomingStatusOnClient() = default; + IncomingStatusOnClient(const IncomingStatusOnClient&) = delete; + IncomingStatusOnClient& operator=(const IncomingStatusOnClient&) = delete; + IncomingStatusOnClient(IncomingStatusOnClient&& other) noexcept = default; + IncomingStatusOnClient& operator=(IncomingStatusOnClient&& other) noexcept = + default; + ~IncomingStatusOnClient() { + if (data_ != nullptr) { + grpc_metadata_array_destroy(&data_->trailing_metadata); + gpr_free(const_cast(data_->error_string)); + } + } + + // Get the status code. + grpc_status_code status() const { return data_->status; } + // Get the status details. + std::string message() const { + return std::string(data_->status_details.as_string_view()); + } + // Get the error string. + std::string error_string() const { + return data_->error_string == nullptr ? "" : data_->error_string; + } + // Get a trailing metadata value by key. + absl::optional GetTrailingMetadata( + absl::string_view key) const; + + // Make a GRPC_OP_RECV_STATUS_ON_CLIENT op - intended for the framework, not + // for tests. + grpc_op MakeOp(); + + private: + struct Data { + grpc_metadata_array trailing_metadata{0, 0, nullptr}; + grpc_status_code status; + Slice status_details; + const char* error_string = nullptr; + }; + std::unique_ptr data_ = std::make_unique(); + }; + + // Receiving container for incoming status on the server from the client. + class IncomingCloseOnServer { + public: + IncomingCloseOnServer() = default; + IncomingCloseOnServer(const IncomingCloseOnServer&) = delete; + IncomingCloseOnServer& operator=(const IncomingCloseOnServer&) = delete; + + // Get the cancellation bit. + bool was_cancelled() const { return cancelled_ != 0; } + + // Make a GRPC_OP_RECV_CLOSE_ON_SERVER op - intended for the framework, not + // for tests. + grpc_op MakeOp(); + + private: + int cancelled_; + }; + + // Build one batch. Returned from NewBatch (use that to instantiate this!) + // Upon destruction of the BatchBuilder, the batch will be executed with any + // added batches. + class BatchBuilder { + public: + BatchBuilder(grpc_call* call, int tag) : call_(call), tag_(tag) {} + ~BatchBuilder(); + + BatchBuilder(const BatchBuilder&) = delete; + BatchBuilder& operator=(const BatchBuilder&) = delete; + BatchBuilder(BatchBuilder&&) noexcept = default; + + // Add a GRPC_OP_SEND_INITIAL_METADATA op. + // Optionally specify flags, compression level. + BatchBuilder& SendInitialMetadata( + std::initializer_list> + md, + uint32_t flags = 0, + absl::optional compression_level = + absl::nullopt); + + // Add a GRPC_OP_SEND_MESSAGE op. + BatchBuilder& SendMessage(Slice payload, uint32_t flags = 0); + BatchBuilder& SendMessage(absl::string_view payload, uint32_t flags = 0) { + return SendMessage(Slice::FromCopiedString(payload), flags); + } + + // Add a GRPC_OP_SEND_CLOSE_FROM_CLIENT op. + BatchBuilder& SendCloseFromClient(); + + // Add a GRPC_OP_SEND_STATUS_FROM_SERVER op. + BatchBuilder& SendStatusFromServer( + grpc_status_code status, absl::string_view message, + std::initializer_list> + md); + + // Add a GRPC_OP_RECV_INITIAL_METADATA op. + BatchBuilder& RecvInitialMetadata(IncomingMetadata& md) { + ops_.emplace_back(md.MakeOp()); + return *this; + } + + // Add a GRPC_OP_RECV_MESSAGE op. + BatchBuilder& RecvMessage(IncomingMessage& msg) { + ops_.emplace_back(msg.MakeOp()); + return *this; + } + + // Add a GRPC_OP_RECV_STATUS_ON_CLIENT op. + BatchBuilder& RecvStatusOnClient(IncomingStatusOnClient& status) { + ops_.emplace_back(status.MakeOp()); + return *this; + } + + // Add a GRPC_OP_RECV_CLOSE_ON_SERVER op. + BatchBuilder& RecvCloseOnServer(IncomingCloseOnServer& close) { + ops_.emplace_back(close.MakeOp()); + return *this; + } + + private: + // We need to track little bits of memory up until the batch is executed. + // One Thing is one such block of memory. + // We specialize it with SpecificThing to track a specific type of memory. + // These get placed on things_ and deleted when the batch is executed. + class Thing { + public: + virtual ~Thing() = default; + }; + template + class SpecificThing final : public Thing { + public: + template + explicit SpecificThing(Args&&... args) + : t_(std::forward(args)...) {} + SpecificThing() = default; + + T& get() { return t_; } + + private: + T t_; + }; + + // Make a thing of type T, and return a reference to it. + template + T& Make(Args&&... args) { + things_.emplace_back(new SpecificThing(std::forward(args)...)); + return static_cast*>(things_.back().get())->get(); + } + + grpc_call* call_; + const int tag_; + std::vector ops_; + std::vector> things_; + }; + + // Wrapper around a grpc_call. + // Instantiated by ClientCallBuilder via NewClientCall for client calls. + // Wrapped by IncomingCall for server calls. + class Call { + public: + explicit Call(grpc_call* call) : call_(call) {} + Call(const Call&) = delete; + Call& operator=(const Call&) = delete; + Call(Call&& other) noexcept : call_(std::exchange(other.call_, nullptr)) {} + ~Call() { + if (call_ != nullptr) grpc_call_unref(call_); + } + // Construct a batch with a tag - upon destruction of the BatchBuilder the + // operation will occur. + BatchBuilder NewBatch(int tag) { return BatchBuilder(call_, tag); } + // Cancel the call + void Cancel() { grpc_call_cancel(call_, nullptr); } + void CancelWithStatus(grpc_status_code status, const char* message) { + grpc_call_cancel_with_status(call_, status, message, nullptr); + } + // Access the peer structure (returns a string that can be matched, etc) - + // or nullopt if grpc_call_get_peer returns nullptr. + absl::optional GetPeer() { + char* peer = grpc_call_get_peer(call_); + if (peer == nullptr) return absl::nullopt; + std::string result(peer); + gpr_free(peer); + return result; + } + + // Set call credentials. + // Takes ownership of creds. + void SetCredentials(grpc_call_credentials* creds) { + EXPECT_EQ(grpc_call_set_credentials(call_, creds), GRPC_CALL_OK); + grpc_call_credentials_release(creds); + } + + // Retrieve the auth context. + std::unique_ptr + GetAuthContext() { + return std::unique_ptr( + grpc_call_auth_context(call_), grpc_auth_context_release); + } + + grpc_call** call_ptr() { return &call_; } + grpc_call* c_call() const { return call_; } + + private: + grpc_call* call_ = nullptr; + }; + + // Wrapper around a server call. + class IncomingCall { + public: + IncomingCall(CoreEnd2endTest& test, int tag); + IncomingCall(const IncomingCall&) = delete; + IncomingCall& operator=(const IncomingCall&) = delete; + IncomingCall(IncomingCall&&) noexcept = default; + + // Construct a batch with a tag - upon destruction of the BatchBuilder the + // operation will occur. Must have received the call first! + BatchBuilder NewBatch(int tag) { return impl_->call.NewBatch(tag); } + void Cancel() { impl_->call.Cancel(); } + + // Return the method being called. + std::string method() const { + return std::string(StringViewFromSlice(impl_->call_details.method)); + } + + // Return the host being called. + std::string host() const { + return std::string(StringViewFromSlice(impl_->call_details.host)); + } + + // Return some initial metadata. + absl::optional GetInitialMetadata(absl::string_view key) const; + + // Return the peer address. + absl::optional GetPeer() { return impl_->call.GetPeer(); } + + // Return the auth context. + std::unique_ptr + GetAuthContext() { + return impl_->call.GetAuthContext(); + } + + // Return the underlying C call object + grpc_call* c_call() { return impl_->call.c_call(); } + + // Return the encodings accepted by the peer. + BitSet GetEncodingsAcceptedByPeer() { + return BitSet::FromInt( + grpc_call_test_only_get_encodings_accepted_by_peer(c_call())); + } + + private: + struct Impl { + Impl() { + grpc_call_details_init(&call_details); + grpc_metadata_array_init(&request_metadata); + } + ~Impl() { + grpc_call_details_destroy(&call_details); + grpc_metadata_array_destroy(&request_metadata); + } + Call call{nullptr}; + grpc_call_details call_details; + grpc_metadata_array request_metadata; + }; + std::unique_ptr impl_; + }; + + // Begin construction of a client call. + ClientCallBuilder NewClientCall(std::string method) { + return ClientCallBuilder(*this, std::move(method)); + } + ClientCallBuilder NewClientCall(RegisteredCall registered_method) { + return ClientCallBuilder(*this, registered_method); + } + // Request a call on the server - notifies `tag` when complete. + IncomingCall RequestCall(int tag) { return IncomingCall(*this, tag); } + + // Pull in CqVerifier types for ergonomics + // TODO(ctiller): evaluate just dropping CqVerifier and folding it in here. + using ExpectedResult = CqVerifier::ExpectedResult; + using Maybe = CqVerifier::Maybe; + using PerformAction = CqVerifier::PerformAction; + using MaybePerformAction = CqVerifier::MaybePerformAction; + using AnyStatus = CqVerifier::AnyStatus; + // Expect a tag with some result. + void Expect(int tag, ExpectedResult result, SourceLocation whence = {}) { + expectations_++; + cq_verifier().Expect(CqVerifier::tag(tag), result, whence); + } + // Step the system until expectations are met or until timeout is reached. + // If there are no expectations logged, then step for 1 second and verify that + // no events occur. + void Step(absl::optional timeout = absl::nullopt, + SourceLocation whence = {}) { + if (expectations_ == 0) { + cq_verifier().VerifyEmpty(timeout.value_or(Duration::Seconds(1)), whence); + return; + } + expectations_ = 0; + cq_verifier().Verify(timeout.value_or(Duration::Seconds(10)), whence); + } + + // Initialize the client. + // If called, then InitServer must be called to create a server (otherwise one + // will be provided). + void InitClient(const ChannelArgs& args) { + initialized_ = true; + fixture().InitClient(args); + } + // Initialize the server. + // If called, then InitClient must be called to create a client (otherwise one + // will be provided). + void InitServer(const ChannelArgs& args) { + initialized_ = true; + fixture().InitServer(args); + } + // Remove the client. + void ShutdownAndDestroyClient() { fixture().ShutdownClient(); } + // Shutdown the server; notify tag on completion. + void ShutdownServerAndNotify(int tag) { + grpc_server_shutdown_and_notify(fixture().server(), fixture().cq(), + CqVerifier::tag(tag)); + } + // Destroy the server. + void DestroyServer() { fixture().DestroyServer(); } + // Shutdown then destroy the server. + void ShutdownAndDestroyServer() { fixture().ShutdownServer(); } + // Cancel any calls on the server. + void CancelAllCallsOnServer() { + grpc_server_cancel_all_calls(fixture().server()); + } + // Ping the server from the client + void PingServerFromClient(int tag) { + grpc_channel_ping(fixture().client(), fixture().cq(), CqVerifier::tag(tag), + nullptr); + } + // Register a call on the client, return its handle. + RegisteredCall RegisterCallOnClient(const char* method, const char* host) { + ForceInitialized(); + return RegisteredCall{ + grpc_channel_register_call(fixture().client(), method, host, nullptr)}; + } + + // Return the current connectivity state of the client. + grpc_connectivity_state CheckConnectivityState(bool try_to_connect) { + return grpc_channel_check_connectivity_state(fixture().client(), + try_to_connect); + } + + // Watch the connectivity state of the client. + void WatchConnectivityState(grpc_connectivity_state last_observed_state, + Duration deadline, int tag) { + grpc_channel_watch_connectivity_state( + fixture().client(), last_observed_state, + grpc_timeout_milliseconds_to_deadline(deadline.millis()), + fixture().cq(), CqVerifier::tag(tag)); + } + + // Return the client channel. + grpc_channel* client() { + ForceInitialized(); + return fixture().client(); + } + + // Return the server channel. + grpc_server* server() { + ForceInitialized(); + return fixture().server(); + } + + // Given a duration, return a timestamp that is that duration in the future - + // with dilation according to test environment (eg sanitizers) + Timestamp TimestampAfterDuration(Duration duration) { + return Timestamp::FromTimespecRoundUp( + grpc_timeout_milliseconds_to_deadline(duration.millis())); + } + + private: + void ForceInitialized(); + + CoreTestFixture& fixture() { + if (fixture_ == nullptr) { + grpc_init(); + fixture_ = GetParam()->create_fixture(ChannelArgs(), ChannelArgs()); + } + return *fixture_; + } + + CqVerifier& cq_verifier() { + if (cq_verifier_ == nullptr) { + cq_verifier_ = absl::make_unique( + fixture().cq(), CqVerifier::FailUsingGtestFail); + } + return *cq_verifier_; + } + + std::unique_ptr fixture_; + std::unique_ptr cq_verifier_; + int expectations_ = 0; + bool initialized_ = false; +}; + +// Define names for additional test suites. +// These make no changes to the actual class, but define new names to register +// tests against. Each new name gets a differing set of configurations in +// end2end_test_main.cc to customize the set of fixtures the tests run against. + +// Test suite for tests that rely on a secure transport +class SecureEnd2endTest : public CoreEnd2endTest {}; +// Test suite for tests that send rather large messages/metadata +class CoreLargeSendTest : public CoreEnd2endTest {}; +// Test suite for tests that need a client channel +class CoreClientChannelTest : public CoreEnd2endTest {}; +// Test suite for tests that require deadline handling +class CoreDeadlineTest : public CoreEnd2endTest {}; +// Test suite for http2 tests that only work over a single hop (unproxyable) +class Http2SingleHopTest : public CoreEnd2endTest {}; +// Test suite for tests that require retry features +class RetryTest : public CoreEnd2endTest {}; +// Test suite for write buffering +class WriteBufferingTest : public CoreEnd2endTest {}; +// Test suite for http2 tests +class Http2Test : public CoreEnd2endTest {}; +// Test suite for http2 tests that require retry features +class RetryHttp2Test : public CoreEnd2endTest {}; +// Test suite for tests that require resource quota +class ResourceQuotaTest : public CoreEnd2endTest {}; +// Test suite for tests that require a transport that supports secure call +// credentials +class PerCallCredsTest : public CoreEnd2endTest {}; +// Test suite for tests that require a transport that supports insecure call +// credentials +class PerCallCredsOnInsecureTest : public CoreEnd2endTest {}; +// Test suite for tests that verify lack of logging in particular situations +class NoLoggingTest : public CoreEnd2endTest {}; +// Test suite for tests that verify proxy authentication +class ProxyAuthTest : public CoreEnd2endTest {}; -const char* get_host_override_string(const char* str, - const CoreTestConfiguration& config); -// Returns a pointer to a statically allocated slice: future invocations -// overwrite past invocations, not threadsafe, etc... -const grpc_slice* get_host_override_slice(const char* str, - const CoreTestConfiguration& config); +} // namespace grpc_core -void validate_host_override_string(const char* pattern, grpc_slice str, - const CoreTestConfiguration& config); +// If this test fixture is being run under minstack, skip the test. +#define SKIP_IF_MINSTACK() \ + if (GetParam()->feature_mask & FEATURE_MASK_IS_MINSTACK) \ + GTEST_SKIP() << "Skipping test for minstack" #endif // GRPC_TEST_CORE_END2END_END2END_TESTS_H diff --git a/test/core/end2end/fixtures/h2_census.cc b/test/core/end2end/fixtures/h2_census.cc deleted file mode 100644 index fc8ec35695f8a..0000000000000 --- a/test/core/end2end/fixtures/h2_census.cc +++ /dev/null @@ -1,87 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include -#include - -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gprpp/host_port.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/util/port.h" -#include "test/core/util/test_config.h" - -class CensusFixture : public CoreTestFixture { - private: - grpc_server* MakeServer(const grpc_core::ChannelArgs& args) override { - grpc_server_credentials* server_creds = - grpc_insecure_server_credentials_create(); - auto* server = grpc_server_create( - args.Set(GRPC_ARG_ENABLE_CENSUS, true).ToC().get(), nullptr); - grpc_server_register_completion_queue(server, cq(), nullptr); - GPR_ASSERT( - grpc_server_add_http2_port(server, localaddr_.c_str(), server_creds)); - grpc_server_credentials_release(server_creds); - grpc_server_start(server); - return server; - } - grpc_channel* MakeClient(const grpc_core::ChannelArgs& args) override { - auto* creds = grpc_insecure_credentials_create(); - auto* client = - grpc_channel_create(localaddr_.c_str(), creds, - args.Set(GRPC_ARG_ENABLE_CENSUS, true).ToC().get()); - grpc_channel_credentials_release(creds); - return client; - } - const std::string localaddr_ = - grpc_core::JoinHostPort("localhost", grpc_pick_unused_port_or_die()); -}; - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack+census", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - nullptr, - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_compress.cc b/test/core/end2end/fixtures/h2_compress.cc deleted file mode 100644 index 8cf05e5831eb3..0000000000000 --- a/test/core/end2end/fixtures/h2_compress.cc +++ /dev/null @@ -1,96 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gprpp/host_port.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/util/port.h" -#include "test/core/util/test_config.h" - -class CompressionFixture : public CoreTestFixture { - private: - grpc_server* MakeServer(const grpc_core::ChannelArgs& args) override { - auto* server = grpc_server_create( - args.SetIfUnset(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, - GRPC_COMPRESS_GZIP) - .ToC() - .get(), - nullptr); - grpc_server_register_completion_queue(server, cq(), nullptr); - grpc_server_credentials* server_creds = - grpc_insecure_server_credentials_create(); - GPR_ASSERT( - grpc_server_add_http2_port(server, localaddr_.c_str(), server_creds)); - grpc_server_credentials_release(server_creds); - grpc_server_start(server); - return server; - } - grpc_channel* MakeClient(const grpc_core::ChannelArgs& args) override { - grpc_channel_credentials* creds = grpc_insecure_credentials_create(); - auto* client = grpc_channel_create( - localaddr_.c_str(), creds, - args.SetIfUnset(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, - GRPC_COMPRESS_GZIP) - .ToC() - .get()); - grpc_channel_credentials_release(creds); - return client; - } - - std::string localaddr_ = - grpc_core::JoinHostPort("localhost", grpc_pick_unused_port_or_die()); -}; - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack_compression", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - nullptr, - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_fakesec.cc b/test/core/end2end/fixtures/h2_fakesec.cc deleted file mode 100644 index 5571fc8748bdd..0000000000000 --- a/test/core/end2end/fixtures/h2_fakesec.cc +++ /dev/null @@ -1,89 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/security/credentials/fake/fake_credentials.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/secure_fixture.h" -#include "test/core/util/test_config.h" - -static void process_auth_failure(void* state, grpc_auth_context* /*ctx*/, - const grpc_metadata* /*md*/, - size_t /*md_count*/, - grpc_process_auth_metadata_done_cb cb, - void* user_data) { - GPR_ASSERT(state == nullptr); - cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_UNAUTHENTICATED, nullptr); -} - -class FakesecFixture : public SecureFixture { - private: - grpc_channel_credentials* MakeClientCreds( - const grpc_core::ChannelArgs&) override { - return grpc_fake_transport_security_credentials_create(); - } - grpc_server_credentials* MakeServerCreds( - const grpc_core::ChannelArgs& args) override { - grpc_server_credentials* fake_ts_creds = - grpc_fake_transport_security_server_credentials_create(); - if (args.Contains(FAIL_AUTH_CHECK_SERVER_ARG_NAME)) { - grpc_auth_metadata_processor processor = {process_auth_failure, nullptr, - nullptr}; - grpc_server_credentials_set_auth_metadata_processor(fake_ts_creds, - processor); - } - return fake_ts_creds; - } -}; - -// All test configurations - -static CoreTestConfiguration configs[] = { - {"chttp2/fake_secure_fullstack", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS_LEVEL_INSECURE, - nullptr, [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(); - }}}; - -int main(int argc, char** argv) { - size_t i; - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_fd.cc b/test/core/end2end/fixtures/h2_fd.cc deleted file mode 100644 index 396d90b1b67a2..0000000000000 --- a/test/core/end2end/fixtures/h2_fd.cc +++ /dev/null @@ -1,111 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include -#include - -#include "absl/status/status.h" - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/port.h" - -// This test won't work except with posix sockets enabled -#ifdef GRPC_POSIX_SOCKET - -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/socket_utils_posix.h" -#include "src/core/lib/iomgr/unix_sockets_posix.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static void create_sockets(int sv[2]) { - int flags; - grpc_create_socketpair_if_unix(sv); - flags = fcntl(sv[0], F_GETFL, 0); - GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0); - flags = fcntl(sv[1], F_GETFL, 0); - GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0); - GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0]) == absl::OkStatus()); - GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]) == absl::OkStatus()); -} - -class FdFixture : public CoreTestFixture { - public: - FdFixture() { create_sockets(fd_pair_); } - - private: - grpc_server* MakeServer(const grpc_core::ChannelArgs& args) override { - grpc_core::ExecCtx exec_ctx; - auto* server = grpc_server_create(args.ToC().get(), nullptr); - grpc_server_register_completion_queue(server, cq(), nullptr); - grpc_server_start(server); - grpc_server_credentials* creds = grpc_insecure_server_credentials_create(); - grpc_server_add_channel_from_fd(server, fd_pair_[1], creds); - grpc_server_credentials_release(creds); - return server; - } - grpc_channel* MakeClient(const grpc_core::ChannelArgs& args) override { - grpc_core::ExecCtx exec_ctx; - grpc_channel_credentials* creds = grpc_insecure_credentials_create(); - auto* client = grpc_channel_create_from_fd("fixture_client", fd_pair_[0], - creds, args.ToC().get()); - grpc_channel_credentials_release(creds); - return client; - } - - int fd_pair_[2]; -}; - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fd", FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, nullptr, - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} - -#else // GRPC_POSIX_SOCKET - -int main(int /* argc */, char** /* argv */) { return 1; } - -#endif // GRPC_POSIX_SOCKET diff --git a/test/core/end2end/fixtures/h2_full+pipe.cc b/test/core/end2end/fixtures/h2_full+pipe.cc deleted file mode 100644 index 4a2e5cecfe918..0000000000000 --- a/test/core/end2end/fixtures/h2_full+pipe.cc +++ /dev/null @@ -1,72 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include -#include - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/port.h" - -// This test requires posix wakeup fds -#ifdef GRPC_POSIX_WAKEUP_FD - -#include - -#include "src/core/lib/iomgr/wakeup_fd_posix.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/secure_fixture.h" -#include "test/core/util/test_config.h" - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - nullptr, - [](const grpc_core::ChannelArgs& /*client_args*/, - const grpc_core::ChannelArgs& /*server_args*/) { - return std::make_unique(); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc_allow_specialized_wakeup_fd = 0; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} - -#else // GRPC_POSIX_WAKEUP_FD - -int main(int argc, char** argv) { return 1; } - -#endif // GRPC_POSIX_WAKEUP_FD diff --git a/test/core/end2end/fixtures/h2_full+trace.cc b/test/core/end2end/fixtures/h2_full+trace.cc deleted file mode 100644 index 8c37b7ae2f3e9..0000000000000 --- a/test/core/end2end/fixtures/h2_full+trace.cc +++ /dev/null @@ -1,95 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include "absl/types/optional.h" - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/port.h" // IWYU pragma: keep - -#ifdef GRPC_POSIX_SOCKET -#include -#endif - -#include -#include -#include - -#include - -#include "src/core/lib/config/config_vars.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/secure_fixture.h" -#include "test/core/util/test_config.h" - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - nullptr, - [](const grpc_core::ChannelArgs& /*client_args*/, - const grpc_core::ChannelArgs& /*server_args*/) { - return std::make_unique(); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - // force tracing on, with a value to force many - // code paths in trace.c to be taken - grpc_core::ConfigVars::Overrides overrides; - overrides.trace = "doesnt-exist,http,all"; - grpc_core::ConfigVars::SetOverrides(overrides); - -#ifdef GRPC_POSIX_SOCKET - g_fixture_slowdown_factor = isatty(STDOUT_FILENO) ? 10 : 1; -#else - g_fixture_slowdown_factor = 10; -#endif - -#ifdef GPR_WINDOWS - // on Windows, writing logs to stderr is very slow - // when stderr is redirected to a disk file. - // The "trace" tests fixtures generates large amount - // of logs, so setting a buffer for stderr prevents certain - // test cases from timing out. - setvbuf(stderr, NULL, _IOLBF, 1024); -#endif - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - GPR_ASSERT(0 == grpc_tracer_set_enabled("also-doesnt-exist", 0)); - GPR_ASSERT(1 == grpc_tracer_set_enabled("http", 1)); - GPR_ASSERT(1 == grpc_tracer_set_enabled("all", 1)); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_full.cc b/test/core/end2end/fixtures/h2_full.cc deleted file mode 100644 index 89753647dbacf..0000000000000 --- a/test/core/end2end/fixtures/h2_full.cc +++ /dev/null @@ -1,58 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/secure_fixture.h" -#include "test/core/util/test_config.h" - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - nullptr, - [](const grpc_core::ChannelArgs& /*client_args*/, - const grpc_core::ChannelArgs& /*server_args*/) { - return std::make_unique(); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_full_no_retry.cc b/test/core/end2end/fixtures/h2_full_no_retry.cc deleted file mode 100644 index e3e361e95c968..0000000000000 --- a/test/core/end2end/fixtures/h2_full_no_retry.cc +++ /dev/null @@ -1,66 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/secure_fixture.h" -#include "test/core/util/test_config.h" - -class NoRetryFixture : public InsecureFixture { - private: - grpc_core::ChannelArgs MutateClientArgs( - grpc_core::ChannelArgs args) override { - return args.Set(GRPC_ARG_ENABLE_RETRIES, false); - } -}; - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - nullptr, - [](const grpc_core::ChannelArgs& /*client_args*/, - const grpc_core::ChannelArgs& /*server_args*/) { - return std::make_unique(); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_http_proxy.cc b/test/core/end2end/fixtures/h2_http_proxy.cc deleted file mode 100644 index ebd01a5b9a89b..0000000000000 --- a/test/core/end2end/fixtures/h2_http_proxy.cc +++ /dev/null @@ -1,119 +0,0 @@ -// -// -// Copyright 2016 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 - -#include -#include -#include -#include - -#include "absl/strings/str_format.h" -#include "absl/types/optional.h" - -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gprpp/host_port.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/http_proxy_fixture.h" -#include "test/core/util/port.h" -#include "test/core/util/test_config.h" - -class HttpProxyFilter : public CoreTestFixture { - public: - explicit HttpProxyFilter(const grpc_core::ChannelArgs& client_args) - : proxy_(grpc_end2end_http_proxy_create(client_args.ToC().get())) {} - ~HttpProxyFilter() override { - // Need to shut down the proxy users before closing the proxy (otherwise we - // become stuck). - ShutdownClient(); - ShutdownServer(); - grpc_end2end_http_proxy_destroy(proxy_); - } - - private: - grpc_server* MakeServer(const grpc_core::ChannelArgs& args) override { - auto* server = grpc_server_create(args.ToC().get(), nullptr); - grpc_server_register_completion_queue(server, cq(), nullptr); - grpc_server_credentials* server_creds = - grpc_insecure_server_credentials_create(); - GPR_ASSERT( - grpc_server_add_http2_port(server, server_addr_.c_str(), server_creds)); - grpc_server_credentials_release(server_creds); - grpc_server_start(server); - return server; - } - - grpc_channel* MakeClient(const grpc_core::ChannelArgs& args) override { - // If testing for proxy auth, add credentials to proxy uri - absl::optional proxy_auth_str = - args.GetOwnedString(GRPC_ARG_HTTP_PROXY_AUTH_CREDS); - std::string proxy_uri; - if (!proxy_auth_str.has_value()) { - proxy_uri = absl::StrFormat( - "http://%s", grpc_end2end_http_proxy_get_proxy_name(proxy_)); - } else { - proxy_uri = - absl::StrFormat("http://%s@%s", proxy_auth_str->c_str(), - grpc_end2end_http_proxy_get_proxy_name(proxy_)); - } - grpc_channel_credentials* creds = grpc_insecure_credentials_create(); - auto* client = grpc_channel_create( - server_addr_.c_str(), creds, - args.Set(GRPC_ARG_HTTP_PROXY, proxy_uri).ToC().get()); - grpc_channel_credentials_release(creds); - GPR_ASSERT(client); - return client; - } - - std::string server_addr_ = - grpc_core::JoinHostPort("localhost", grpc_pick_unused_port_or_die()); - grpc_end2end_http_proxy* proxy_; -}; - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - nullptr, - [](const grpc_core::ChannelArgs& client_args, - const grpc_core::ChannelArgs&) { - return std::make_unique(client_args); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_insecure.cc b/test/core/end2end/fixtures/h2_insecure.cc deleted file mode 100644 index 02506838893d6..0000000000000 --- a/test/core/end2end/fixtures/h2_insecure.cc +++ /dev/null @@ -1,84 +0,0 @@ -// -// -// Copyright 2020 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 - -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/secure_fixture.h" -#include "test/core/util/test_config.h" - -namespace { - -void ProcessAuthFailure(void* state, grpc_auth_context* /*ctx*/, - const grpc_metadata* /*md*/, size_t /*md_count*/, - grpc_process_auth_metadata_done_cb cb, - void* user_data) { - GPR_ASSERT(state == nullptr); - cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_UNAUTHENTICATED, nullptr); -} - -class InsecureCredsFixture : public InsecureFixture { - private: - grpc_server_credentials* MakeServerCreds( - const grpc_core::ChannelArgs& args) override { - auto* creds = grpc_insecure_server_credentials_create(); - if (args.Contains(FAIL_AUTH_CHECK_SERVER_ARG_NAME)) { - grpc_auth_metadata_processor processor = {ProcessAuthFailure, nullptr, - nullptr}; - grpc_server_credentials_set_auth_metadata_processor(creds, processor); - } - return creds; - } -}; - -// All test configurations -CoreTestConfiguration configs[] = { - {"chttp2/insecure_fullstack", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS_LEVEL_INSECURE, - nullptr, - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(); - }}, -}; - -} // namespace - -int main(int argc, char** argv) { - size_t i; - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_local_abstract_uds_percent_encoded.cc b/test/core/end2end/fixtures/h2_local_abstract_uds_percent_encoded.cc deleted file mode 100644 index ee760f68cab91..0000000000000 --- a/test/core/end2end/fixtures/h2_local_abstract_uds_percent_encoded.cc +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2021 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 -#include - -#include -#include -#include -#include - -#include "absl/strings/str_format.h" - -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/local_util.h" -#include "test/core/util/test_config.h" - -static std::atomic unique{0}; - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack_local_abstract_uds_percent_encoded", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS, - nullptr, - [](const grpc_core::ChannelArgs& /*client_args*/, - const grpc_core::ChannelArgs& /*server_args*/) { - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - return std::make_unique( - absl::StrFormat("unix-abstract:grpc_fullstack_test.%%00.%d.%" PRId64 - ".%" PRId32 ".%d", - getpid(), now.tv_sec, now.tv_nsec, - unique.fetch_add(1, std::memory_order_relaxed)), - UDS); - }}}; - -int main(int argc, char** argv) { - size_t i; - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - grpc_shutdown(); - return 0; -} diff --git a/test/core/end2end/fixtures/h2_local_ipv4.cc b/test/core/end2end/fixtures/h2_local_ipv4.cc deleted file mode 100644 index 2dcb1ddec1bb4..0000000000000 --- a/test/core/end2end/fixtures/h2_local_ipv4.cc +++ /dev/null @@ -1,59 +0,0 @@ -// -// -// Copyright 2018 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 - -#include -#include - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gprpp/host_port.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/local_util.h" -#include "test/core/util/port.h" -#include "test/core/util/test_config.h" - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack_local_ipv4", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS, - nullptr, - [](const grpc_core::ChannelArgs& /*client_args*/, - const grpc_core::ChannelArgs& /*server_args*/) { - int port = grpc_pick_unused_port_or_die(); - return std::make_unique( - grpc_core::JoinHostPort("127.0.0.1", port), LOCAL_TCP); - }}}; - -int main(int argc, char** argv) { - size_t i; - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - grpc_shutdown(); - return 0; -} diff --git a/test/core/end2end/fixtures/h2_local_ipv6.cc b/test/core/end2end/fixtures/h2_local_ipv6.cc deleted file mode 100644 index 30012345d1d8a..0000000000000 --- a/test/core/end2end/fixtures/h2_local_ipv6.cc +++ /dev/null @@ -1,59 +0,0 @@ -// -// -// Copyright 2018 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 - -#include -#include - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gprpp/host_port.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/local_util.h" -#include "test/core/util/port.h" -#include "test/core/util/test_config.h" - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack_local_ipv6", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS, - nullptr, - [](const grpc_core::ChannelArgs& /*client_args*/, - const grpc_core::ChannelArgs& /*server_args*/) { - int port = grpc_pick_unused_port_or_die(); - return std::make_unique( - grpc_core::JoinHostPort("[::1]", port), LOCAL_TCP); - }}}; - -int main(int argc, char** argv) { - size_t i; - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - grpc_shutdown(); - return 0; -} diff --git a/test/core/end2end/fixtures/h2_local_uds.cc b/test/core/end2end/fixtures/h2_local_uds.cc deleted file mode 100644 index 0ecc1ac0bbc37..0000000000000 --- a/test/core/end2end/fixtures/h2_local_uds.cc +++ /dev/null @@ -1,69 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include -#include - -#include -#include -#include -#include - -#include "absl/strings/str_format.h" - -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/local_util.h" -#include "test/core/util/test_config.h" - -static std::atomic unique{1}; - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack_local_uds", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS, - nullptr, - [](const grpc_core::ChannelArgs& /*client_args*/, - const grpc_core::ChannelArgs& /*server_args*/) { - gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); - return std::make_unique( - absl::StrFormat("unix:/tmp/grpc_fullstack_test.%d.%" PRId64 - ".%" PRId32 ".%d", - getpid(), now.tv_sec, now.tv_nsec, - unique.fetch_add(1, std::memory_order_relaxed)), - UDS); - }}}; - -int main(int argc, char** argv) { - size_t i; - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - grpc_shutdown(); - return 0; -} diff --git a/test/core/end2end/fixtures/h2_local_uds_percent_encoded.cc b/test/core/end2end/fixtures/h2_local_uds_percent_encoded.cc deleted file mode 100644 index cd44807c745c9..0000000000000 --- a/test/core/end2end/fixtures/h2_local_uds_percent_encoded.cc +++ /dev/null @@ -1,69 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include -#include - -#include -#include -#include -#include - -#include "absl/strings/str_format.h" - -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/local_util.h" -#include "test/core/util/test_config.h" - -static std::atomic unique{1}; - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack_local_uds_percent_encoded", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS, - nullptr, - [](const grpc_core::ChannelArgs& /*client_args*/, - const grpc_core::ChannelArgs& /*server_args*/) { - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - return std::make_unique( - absl::StrFormat("unix:/tmp/grpc_fullstack_test.%%25.%d.%" PRId64 - ".%" PRId32 ".%d", - getpid(), now.tv_sec, now.tv_nsec, - unique.fetch_add(1, std::memory_order_relaxed)), - UDS); - }}}; - -int main(int argc, char** argv) { - size_t i; - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - grpc_shutdown(); - return 0; -} diff --git a/test/core/end2end/fixtures/h2_oauth2_tls12.cc b/test/core/end2end/fixtures/h2_oauth2_tls12.cc deleted file mode 100644 index 0a0c747ff123f..0000000000000 --- a/test/core/end2end/fixtures/h2_oauth2_tls12.cc +++ /dev/null @@ -1,58 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/h2_oauth2_common.h" -#include "test/core/util/test_config.h" - -// All test configurations - -static CoreTestConfiguration configs[] = { - {"chttp2/simple_ssl_with_oauth2_fullstack_tls1_2", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - "foo.test.google.fr", - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(grpc_tls_version::TLS1_2); - }}}; - -int main(int argc, char** argv) { - size_t i; - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_oauth2_tls13.cc b/test/core/end2end/fixtures/h2_oauth2_tls13.cc deleted file mode 100644 index 72559dd82ca61..0000000000000 --- a/test/core/end2end/fixtures/h2_oauth2_tls13.cc +++ /dev/null @@ -1,59 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/h2_oauth2_common.h" -#include "test/core/util/test_config.h" - -// All test configurations - -static CoreTestConfiguration configs[] = { - {"chttp2/simple_ssl_with_oauth2_fullstack_tls1_3", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | - FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST, - "foo.test.google.fr", - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(grpc_tls_version::TLS1_3); - }}}; - -int main(int argc, char** argv) { - size_t i; - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_proxy.cc b/test/core/end2end/fixtures/h2_proxy.cc deleted file mode 100644 index 4dc7864e0ef86..0000000000000 --- a/test/core/end2end/fixtures/h2_proxy.cc +++ /dev/null @@ -1,113 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include - -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/proxy.h" -#include "test/core/util/test_config.h" - -class ProxyFixture : public CoreTestFixture { - public: - ProxyFixture(const grpc_core::ChannelArgs& client_args, - const grpc_core::ChannelArgs& server_args) - : proxy_(grpc_end2end_proxy_create(&proxy_def_, client_args.ToC().get(), - server_args.ToC().get())) {} - ~ProxyFixture() override { grpc_end2end_proxy_destroy(proxy_); } - - private: - static grpc_server* CreateProxyServer(const char* port, - const grpc_channel_args* server_args) { - grpc_server* s = grpc_server_create(server_args, nullptr); - grpc_server_credentials* server_creds = - grpc_insecure_server_credentials_create(); - GPR_ASSERT(grpc_server_add_http2_port(s, port, server_creds)); - grpc_server_credentials_release(server_creds); - return s; - } - - static grpc_channel* CreateProxyClient(const char* target, - const grpc_channel_args* client_args) { - grpc_channel_credentials* creds = grpc_insecure_credentials_create(); - grpc_channel* channel = grpc_channel_create(target, creds, client_args); - grpc_channel_credentials_release(creds); - return channel; - } - - grpc_server* MakeServer(const grpc_core::ChannelArgs& args) override { - auto* server = grpc_server_create(args.ToC().get(), nullptr); - grpc_server_register_completion_queue(server, cq(), nullptr); - grpc_server_credentials* server_creds = - grpc_insecure_server_credentials_create(); - GPR_ASSERT(grpc_server_add_http2_port( - server, grpc_end2end_proxy_get_server_port(proxy_), server_creds)); - grpc_server_credentials_release(server_creds); - grpc_server_start(server); - return server; - } - - grpc_channel* MakeClient(const grpc_core::ChannelArgs& args) override { - grpc_channel_credentials* creds = grpc_insecure_credentials_create(); - auto* client = grpc_channel_create( - grpc_end2end_proxy_get_client_target(proxy_), creds, args.ToC().get()); - grpc_channel_credentials_release(creds); - GPR_ASSERT(client); - return client; - } - const grpc_end2end_proxy_def proxy_def_ = {CreateProxyServer, - CreateProxyClient}; - grpc_end2end_proxy* proxy_; -}; - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack+proxy", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_REQUEST_PROXYING | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - nullptr, - [](const grpc_core::ChannelArgs& client_args, - const grpc_core::ChannelArgs& server_args) { - return std::make_unique(client_args, server_args); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_sockpair+trace.cc b/test/core/end2end/fixtures/h2_sockpair+trace.cc deleted file mode 100644 index 5180b79deafc8..0000000000000 --- a/test/core/end2end/fixtures/h2_sockpair+trace.cc +++ /dev/null @@ -1,86 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include -#include -#include - -#include "absl/types/optional.h" - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/config/config_vars.h" -#include "src/core/lib/iomgr/port.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/sockpair_fixture.h" -#include "test/core/util/test_config.h" - -#ifdef GRPC_POSIX_SOCKET -#include -#endif - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/socketpair", FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, nullptr, - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(grpc_core::ChannelArgs()); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - // force tracing on, with a value to force many - // code paths in trace.cc to be taken - grpc_core::ConfigVars::Overrides overrides; - overrides.trace = "doesnt-exist,http,all"; - grpc_core::ConfigVars::SetOverrides(overrides); - -#ifdef GRPC_POSIX_SOCKET - g_fixture_slowdown_factor = isatty(STDOUT_FILENO) ? 10 : 1; -#else - g_fixture_slowdown_factor = 10; -#endif - -#ifdef GPR_WINDOWS - // on Windows, writing logs to stderr is very slow - // when stderr is redirected to a disk file. - // The "trace" tests fixtures generates large amount - // of logs, so setting a buffer for stderr prevents certain - // test cases from timing out. - setvbuf(stderr, NULL, _IOLBF, 1024); -#endif - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - GPR_ASSERT(0 == grpc_tracer_set_enabled("also-doesnt-exist", 0)); - GPR_ASSERT(1 == grpc_tracer_set_enabled("http", 1)); - GPR_ASSERT(1 == grpc_tracer_set_enabled("all", 1)); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_sockpair.cc b/test/core/end2end/fixtures/h2_sockpair.cc deleted file mode 100644 index f9e8b45537c69..0000000000000 --- a/test/core/end2end/fixtures/h2_sockpair.cc +++ /dev/null @@ -1,53 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/sockpair_fixture.h" -#include "test/core/util/test_config.h" - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/socketpair", FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, nullptr, - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(grpc_core::ChannelArgs()); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_sockpair_1byte.cc b/test/core/end2end/fixtures/h2_sockpair_1byte.cc deleted file mode 100644 index 504460e2fe7d6..0000000000000 --- a/test/core/end2end/fixtures/h2_sockpair_1byte.cc +++ /dev/null @@ -1,59 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/sockpair_fixture.h" -#include "test/core/util/test_config.h" - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/socketpair", FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, nullptr, - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique( - grpc_core::ChannelArgs() - .Set(GRPC_ARG_TCP_READ_CHUNK_SIZE, 1) - .Set(GRPC_ARG_TCP_MIN_READ_CHUNK_SIZE, 1) - .Set(GRPC_ARG_TCP_MAX_READ_CHUNK_SIZE, 1)); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - g_fixture_slowdown_factor = 2; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_sockpair_with_minstack.cc b/test/core/end2end/fixtures/h2_sockpair_with_minstack.cc deleted file mode 100644 index a70d2aff383a6..0000000000000 --- a/test/core/end2end/fixtures/h2_sockpair_with_minstack.cc +++ /dev/null @@ -1,72 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/sockpair_fixture.h" -#include "test/core/util/test_config.h" - -class SockpairWithMinstackFixture : public SockpairFixture { - public: - using SockpairFixture::SockpairFixture; - - private: - grpc_core::ChannelArgs MutateClientArgs( - grpc_core::ChannelArgs args) override { - return args.Set(GRPC_ARG_MINIMAL_STACK, true); - } - grpc_core::ChannelArgs MutateServerArgs( - grpc_core::ChannelArgs args) override { - return args.Set(GRPC_ARG_MINIMAL_STACK, true); - } -}; - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/socketpair+minstack", - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | - FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES, - nullptr, - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique( - grpc_core::ChannelArgs()); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_ssl_cred_reload_tls12.cc b/test/core/end2end/fixtures/h2_ssl_cred_reload_tls12.cc deleted file mode 100644 index 9e9bb87bcdfe4..0000000000000 --- a/test/core/end2end/fixtures/h2_ssl_cred_reload_tls12.cc +++ /dev/null @@ -1,68 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include -#include - -#include "absl/types/optional.h" - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/config/config_vars.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/h2_ssl_cred_reload_fixture.h" -#include "test/core/util/test_config.h" - -// All test configurations - -static CoreTestConfiguration configs[] = { - {"chttp2/simple_ssl_fullstack_tls1_2", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - "foo.test.google.fr", - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(TLS1_2); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_core::ConfigVars::Overrides overrides; - overrides.default_ssl_roots_file_path = SslCredReloadFixture::CaCertPath(); - grpc_core::ConfigVars::SetOverrides(overrides); - - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_ssl_cred_reload_tls13.cc b/test/core/end2end/fixtures/h2_ssl_cred_reload_tls13.cc deleted file mode 100644 index d2ee2ba7f3ddb..0000000000000 --- a/test/core/end2end/fixtures/h2_ssl_cred_reload_tls13.cc +++ /dev/null @@ -1,68 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include -#include - -#include "absl/types/optional.h" - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/config/config_vars.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/h2_ssl_cred_reload_fixture.h" -#include "test/core/util/test_config.h" - -// All test configurations - -static CoreTestConfiguration configs[] = { - {"chttp2/simple_ssl_fullstack_tls1_3", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | - FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST, - "foo.test.google.fr", - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(TLS1_3); - }}}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_core::ConfigVars::Overrides overrides; - overrides.default_ssl_roots_file_path = SslCredReloadFixture::CaCertPath(); - grpc_core::ConfigVars::SetOverrides(overrides); - - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_ssl_proxy.cc b/test/core/end2end/fixtures/h2_ssl_proxy.cc deleted file mode 100644 index b7a3866ef9b57..0000000000000 --- a/test/core/end2end/fixtures/h2_ssl_proxy.cc +++ /dev/null @@ -1,188 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include -#include - -#include "absl/types/optional.h" - -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/config/config_vars.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/load_file.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/proxy.h" -#include "test/core/util/test_config.h" - -#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem" -#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem" -#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key" - -static void process_auth_failure(void* state, grpc_auth_context* /*ctx*/, - const grpc_metadata* /*md*/, - size_t /*md_count*/, - grpc_process_auth_metadata_done_cb cb, - void* user_data) { - GPR_ASSERT(state == nullptr); - cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_UNAUTHENTICATED, nullptr); -} - -class SslProxyFixture : public CoreTestFixture { - public: - SslProxyFixture(const grpc_core::ChannelArgs& client_args, - const grpc_core::ChannelArgs& server_args) - : proxy_(grpc_end2end_proxy_create(&proxy_def_, client_args.ToC().get(), - server_args.ToC().get())) {} - ~SslProxyFixture() override { grpc_end2end_proxy_destroy(proxy_); } - - private: - static grpc_server* CreateProxyServer(const char* port, - const grpc_channel_args* server_args) { - grpc_server* s = grpc_server_create(server_args, nullptr); - grpc_slice cert_slice, key_slice; - GPR_ASSERT(GRPC_LOG_IF_ERROR( - "load_file", grpc_load_file(SERVER_CERT_PATH, 1, &cert_slice))); - GPR_ASSERT(GRPC_LOG_IF_ERROR( - "load_file", grpc_load_file(SERVER_KEY_PATH, 1, &key_slice))); - const char* server_cert = - reinterpret_cast GRPC_SLICE_START_PTR(cert_slice); - const char* server_key = - reinterpret_cast GRPC_SLICE_START_PTR(key_slice); - grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {server_key, server_cert}; - grpc_server_credentials* ssl_creds = grpc_ssl_server_credentials_create( - nullptr, &pem_key_cert_pair, 1, 0, nullptr); - grpc_slice_unref(cert_slice); - grpc_slice_unref(key_slice); - GPR_ASSERT(grpc_server_add_http2_port(s, port, ssl_creds)); - grpc_server_credentials_release(ssl_creds); - return s; - } - - static grpc_channel* CreateProxyClient(const char* target, - const grpc_channel_args* client_args) { - grpc_channel* channel; - grpc_channel_credentials* ssl_creds = - grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr); - grpc_arg ssl_name_override = { - GRPC_ARG_STRING, - const_cast(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), - {const_cast("foo.test.google.fr")}}; - const grpc_channel_args* new_client_args = - grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1); - channel = grpc_channel_create(target, ssl_creds, new_client_args); - grpc_channel_credentials_release(ssl_creds); - { - grpc_core::ExecCtx exec_ctx; - grpc_channel_args_destroy(new_client_args); - } - return channel; - } - - grpc_server* MakeServer(const grpc_core::ChannelArgs& args) override { - grpc_slice cert_slice, key_slice; - GPR_ASSERT(GRPC_LOG_IF_ERROR( - "load_file", grpc_load_file(SERVER_CERT_PATH, 1, &cert_slice))); - GPR_ASSERT(GRPC_LOG_IF_ERROR( - "load_file", grpc_load_file(SERVER_KEY_PATH, 1, &key_slice))); - const char* server_cert = - reinterpret_cast GRPC_SLICE_START_PTR(cert_slice); - const char* server_key = - reinterpret_cast GRPC_SLICE_START_PTR(key_slice); - grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {server_key, server_cert}; - grpc_server_credentials* ssl_creds = grpc_ssl_server_credentials_create( - nullptr, &pem_key_cert_pair, 1, 0, nullptr); - grpc_slice_unref(cert_slice); - grpc_slice_unref(key_slice); - if (args.Contains(FAIL_AUTH_CHECK_SERVER_ARG_NAME)) { - grpc_auth_metadata_processor processor = {process_auth_failure, nullptr, - nullptr}; - grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor); - } - - auto* server = grpc_server_create(args.ToC().get(), nullptr); - grpc_server_register_completion_queue(server, cq(), nullptr); - GPR_ASSERT(grpc_server_add_http2_port( - server, grpc_end2end_proxy_get_server_port(proxy_), ssl_creds)); - grpc_server_credentials_release(ssl_creds); - grpc_server_start(server); - return server; - } - - grpc_channel* MakeClient(const grpc_core::ChannelArgs& args) override { - grpc_channel_credentials* ssl_creds = - grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr); - auto* client = grpc_channel_create( - grpc_end2end_proxy_get_client_target(proxy_), ssl_creds, - args.Set(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, "foo.test.google.fr") - .ToC() - .get()); - GPR_ASSERT(client != nullptr); - grpc_channel_credentials_release(ssl_creds); - return client; - } - const grpc_end2end_proxy_def proxy_def_ = {CreateProxyServer, - CreateProxyClient}; - grpc_end2end_proxy* proxy_; -}; - -// All test configurations - -static CoreTestConfiguration configs[] = { - {"chttp2/simple_ssl_fullstack", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_REQUEST_PROXYING | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - "foo.test.google.fr", - [](const grpc_core::ChannelArgs& client_args, - const grpc_core::ChannelArgs& server_args) { - return std::make_unique(client_args, server_args); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - - grpc_core::ConfigVars::Overrides overrides; - overrides.default_ssl_roots_file_path = CA_CERT_PATH; - grpc_core::ConfigVars::SetOverrides(overrides); - - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_ssl_tls12.cc b/test/core/end2end/fixtures/h2_ssl_tls12.cc deleted file mode 100644 index 252c3145f8559..0000000000000 --- a/test/core/end2end/fixtures/h2_ssl_tls12.cc +++ /dev/null @@ -1,65 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include -#include - -#include "absl/types/optional.h" - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/config/config_vars.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/h2_ssl_tls_common.h" -#include "test/core/util/test_config.h" - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/simple_ssl_fullstack_tls1_2", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - "foo.test.google.fr", - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(grpc_tls_version::TLS1_2); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_core::ConfigVars::Overrides overrides; - overrides.default_ssl_roots_file_path = SslTlsFixture::CaCertPath(); - grpc_core::ConfigVars::SetOverrides(overrides); - - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - return 0; -} diff --git a/test/core/end2end/fixtures/h2_ssl_tls13.cc b/test/core/end2end/fixtures/h2_ssl_tls13.cc deleted file mode 100644 index 73d4793d524ef..0000000000000 --- a/test/core/end2end/fixtures/h2_ssl_tls13.cc +++ /dev/null @@ -1,65 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include -#include - -#include "absl/types/optional.h" - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/config/config_vars.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/h2_ssl_tls_common.h" -#include "test/core/util/test_config.h" - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/simple_ssl_fullstack_tls1_3", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | - FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST, - "foo.test.google.fr", - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(grpc_tls_version::TLS1_3); - }}}; - -int main(int argc, char** argv) { - size_t i; - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_core::ConfigVars::Overrides overrides; - overrides.default_ssl_roots_file_path = SslTlsFixture::CaCertPath(); - grpc_core::ConfigVars::SetOverrides(overrides); - - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - return 0; -} diff --git a/test/core/end2end/fixtures/h2_tls_certwatch_async_tls1_3.cc b/test/core/end2end/fixtures/h2_tls_certwatch_async_tls1_3.cc deleted file mode 100644 index feb6e196d6dc4..0000000000000 --- a/test/core/end2end/fixtures/h2_tls_certwatch_async_tls1_3.cc +++ /dev/null @@ -1,58 +0,0 @@ -// -// -// Copyright 2018 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 -#include -#include - -#include "absl/types/optional.h" - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/config/config_vars.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/h2_tls_common.h" -#include "test/core/util/test_config.h" - -static CoreTestConfiguration config = { - // client: certificate watcher provider + async external verifier - // server: certificate watcher provider + async external verifier - // extra: TLS 1.3 - "chttp2/cert_watcher_provider_async_verifier_tls1_3", - kH2TLSFeatureMask, - "foo.test.google.fr", - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique( - SecurityPrimitives::TlsVersion::V_13, - SecurityPrimitives::ProviderType::FILE_PROVIDER, - SecurityPrimitives::VerifierType::EXTERNAL_ASYNC_VERIFIER); - }, -}; - -int main(int argc, char** argv) { - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_core::ConfigVars::Overrides overrides; - overrides.default_ssl_roots_file_path = CA_CERT_PATH; - grpc_core::ConfigVars::SetOverrides(overrides); - grpc_init(); - grpc_end2end_tests(argc, argv, config); - grpc_shutdown(); - return 0; -} diff --git a/test/core/end2end/fixtures/h2_tls_certwatch_sync_tls1_2.cc b/test/core/end2end/fixtures/h2_tls_certwatch_sync_tls1_2.cc deleted file mode 100644 index e35dc1aa332ca..0000000000000 --- a/test/core/end2end/fixtures/h2_tls_certwatch_sync_tls1_2.cc +++ /dev/null @@ -1,58 +0,0 @@ -// -// -// Copyright 2018 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 -#include -#include - -#include "absl/types/optional.h" - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/config/config_vars.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/h2_tls_common.h" -#include "test/core/util/test_config.h" - -static CoreTestConfiguration config = { - // client: certificate watcher provider + hostname verifier - // server: certificate watcher provider + sync external verifier - // extra: TLS 1.2 - "chttp2/cert_watcher_provider_sync_verifier_tls1_2", - kH2TLSFeatureMask, - "foo.test.google.fr", - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique( - SecurityPrimitives::TlsVersion::V_12, - SecurityPrimitives::ProviderType::FILE_PROVIDER, - SecurityPrimitives::VerifierType::HOSTNAME_VERIFIER); - }, -}; - -int main(int argc, char** argv) { - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_core::ConfigVars::Overrides overrides; - overrides.default_ssl_roots_file_path = CA_CERT_PATH; - grpc_core::ConfigVars::SetOverrides(overrides); - grpc_init(); - grpc_end2end_tests(argc, argv, config); - grpc_shutdown(); - return 0; -} diff --git a/test/core/end2end/fixtures/h2_tls_common.h b/test/core/end2end/fixtures/h2_tls_common.h index f4e382126561f..8854436885f55 100644 --- a/test/core/end2end/fixtures/h2_tls_common.h +++ b/test/core/end2end/fixtures/h2_tls_common.h @@ -229,9 +229,7 @@ class TlsFixture : public SecureFixture { }; static const uint32_t kH2TLSFeatureMask = - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER; + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_IS_HTTP2; #endif // GRPC_TEST_CORE_END2END_FIXTURES_H2_TLS_COMMON_H diff --git a/test/core/end2end/fixtures/h2_tls_simple.cc b/test/core/end2end/fixtures/h2_tls_simple.cc deleted file mode 100644 index 0298fa1004292..0000000000000 --- a/test/core/end2end/fixtures/h2_tls_simple.cc +++ /dev/null @@ -1,58 +0,0 @@ -// -// -// Copyright 2018 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 -#include -#include - -#include "absl/types/optional.h" - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/config/config_vars.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/h2_tls_common.h" -#include "test/core/util/test_config.h" - -static CoreTestConfiguration config = { - // client: static data provider + sync external verifier - // server: static data provider + sync external verifier - // extra: TLS 1.2 - "chttp2/simple_ssl_fullstack", - kH2TLSFeatureMask, - "foo.test.google.fr", - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique( - SecurityPrimitives::TlsVersion::V_12, - SecurityPrimitives::ProviderType::STATIC_PROVIDER, - SecurityPrimitives::VerifierType::EXTERNAL_SYNC_VERIFIER); - }, -}; - -int main(int argc, char** argv) { - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_core::ConfigVars::Overrides overrides; - overrides.default_ssl_roots_file_path = CA_CERT_PATH; - grpc_core::ConfigVars::SetOverrides(overrides); - grpc_init(); - grpc_end2end_tests(argc, argv, config); - grpc_shutdown(); - return 0; -} diff --git a/test/core/end2end/fixtures/h2_tls_static_async_tls1_3.cc b/test/core/end2end/fixtures/h2_tls_static_async_tls1_3.cc deleted file mode 100644 index a3f12e0613073..0000000000000 --- a/test/core/end2end/fixtures/h2_tls_static_async_tls1_3.cc +++ /dev/null @@ -1,58 +0,0 @@ -// -// -// Copyright 2018 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 -#include -#include - -#include "absl/types/optional.h" - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/config/config_vars.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/h2_tls_common.h" -#include "test/core/util/test_config.h" - -static CoreTestConfiguration config = { - // client: static data provider + async external verifier - // server: static data provider + async external verifier - // extra: TLS 1.3 - "chttp2/static_provider_async_verifier_tls1_3", - kH2TLSFeatureMask, - "foo.test.google.fr", - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique( - SecurityPrimitives::TlsVersion::V_13, - SecurityPrimitives::ProviderType::STATIC_PROVIDER, - SecurityPrimitives::VerifierType::EXTERNAL_ASYNC_VERIFIER); - }, -}; - -int main(int argc, char** argv) { - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_core::ConfigVars::Overrides overrides; - overrides.default_ssl_roots_file_path = CA_CERT_PATH; - grpc_core::ConfigVars::SetOverrides(overrides); - grpc_init(); - grpc_end2end_tests(argc, argv, config); - grpc_shutdown(); - return 0; -} diff --git a/test/core/end2end/fixtures/h2_uds.cc b/test/core/end2end/fixtures/h2_uds.cc deleted file mode 100644 index 0c49dd68eef45..0000000000000 --- a/test/core/end2end/fixtures/h2_uds.cc +++ /dev/null @@ -1,69 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include -#include - -#include -#include -#include -#include - -#include "absl/strings/str_format.h" - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/secure_fixture.h" -#include "test/core/util/test_config.h" - -static std::atomic unique{1}; - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack_uds", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - nullptr, - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); - return std::make_unique(absl::StrFormat( - "unix:/tmp/grpc_fullstack_test.%d.%" PRId64 ".%" PRId32 ".%d", - getpid(), now.tv_sec, now.tv_nsec, - unique.fetch_add(1, std::memory_order_relaxed))); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/h2_uds_abstract.cc b/test/core/end2end/fixtures/h2_uds_abstract.cc deleted file mode 100644 index 62d066af114a2..0000000000000 --- a/test/core/end2end/fixtures/h2_uds_abstract.cc +++ /dev/null @@ -1,69 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include -#include - -#include -#include -#include -#include - -#include "absl/strings/str_format.h" - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/secure_fixture.h" -#include "test/core/util/test_config.h" - -static std::atomic unique{1}; - -// All test configurations -static CoreTestConfiguration configs[] = { - {"chttp2/fullstack_uds_abstract_namespace", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - nullptr, - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); - return std::make_unique(absl::StrFormat( - "unix-abstract:grpc_fullstack_test.%d.%" PRId64 ".%" PRId32 ".%d", - getpid(), now.tv_sec, now.tv_nsec, - unique.fetch_add(1, std::memory_order_relaxed))); - }}, -}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/inproc.cc b/test/core/end2end/fixtures/inproc.cc deleted file mode 100644 index e17873d167605..0000000000000 --- a/test/core/end2end/fixtures/inproc.cc +++ /dev/null @@ -1,55 +0,0 @@ -// -// -// 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. -// -// - -#include - -#include -#include - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/fixtures/inproc_fixture.h" -#include "test/core/util/test_config.h" - -// All test configurations -static CoreTestConfiguration configs[] = {{ - "inproc", - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - nullptr, - [](const grpc_core::ChannelArgs&, const grpc_core::ChannelArgs&) { - return std::make_unique(); - }, -}}; - -int main(int argc, char** argv) { - size_t i; - - grpc::testing::TestEnvironment env(&argc, argv); - grpc_end2end_tests_pre_init(); - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - return 0; -} diff --git a/test/core/end2end/fixtures/inproc_fixture.h b/test/core/end2end/fixtures/inproc_fixture.h index 4acdcdd7a7c81..8cd38d4352692 100644 --- a/test/core/end2end/fixtures/inproc_fixture.h +++ b/test/core/end2end/fixtures/inproc_fixture.h @@ -21,7 +21,7 @@ #include "src/core/lib/channel/channel_args.h" #include "test/core/end2end/end2end_tests.h" -class InprocFixture : public CoreTestFixture { +class InprocFixture : public grpc_core::CoreTestFixture { private: grpc_server* MakeServer(const grpc_core::ChannelArgs& args) override { auto* server = grpc_server_create(args.ToC().get(), nullptr); diff --git a/test/core/end2end/fixtures/local_util.h b/test/core/end2end/fixtures/local_util.h index 05a89e51c63b3..3077b50b2a143 100644 --- a/test/core/end2end/fixtures/local_util.h +++ b/test/core/end2end/fixtures/local_util.h @@ -27,7 +27,7 @@ #include "src/core/lib/channel/channel_args.h" #include "test/core/end2end/end2end_tests.h" -class LocalTestFixture final : public CoreTestFixture { +class LocalTestFixture final : public grpc_core::CoreTestFixture { public: LocalTestFixture(std::string localaddr, grpc_local_connect_type type); diff --git a/test/core/end2end/fixtures/secure_fixture.h b/test/core/end2end/fixtures/secure_fixture.h index a64f5634f485d..35745e0fff5a7 100644 --- a/test/core/end2end/fixtures/secure_fixture.h +++ b/test/core/end2end/fixtures/secure_fixture.h @@ -29,7 +29,7 @@ // Base class for a fixture that just needs to select cred types (or mutate // client/server channel args). -class SecureFixture : public CoreTestFixture { +class SecureFixture : public grpc_core::CoreTestFixture { public: explicit SecureFixture(std::string localaddr = grpc_core::JoinHostPort( "localhost", grpc_pick_unused_port_or_die())) diff --git a/test/core/end2end/fixtures/sockpair_fixture.h b/test/core/end2end/fixtures/sockpair_fixture.h index d1618cead0af0..930974eb3f067 100644 --- a/test/core/end2end/fixtures/sockpair_fixture.h +++ b/test/core/end2end/fixtures/sockpair_fixture.h @@ -15,8 +15,11 @@ #ifndef GRPC_TEST_CORE_END2END_FIXTURES_SOCKPAIR_FIXTURE_H #define GRPC_TEST_CORE_END2END_FIXTURES_SOCKPAIR_FIXTURE_H +#include + #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "gtest/gtest.h" #include #include @@ -40,32 +43,44 @@ #include "src/core/lib/transport/transport_fwd.h" #include "test/core/end2end/end2end_tests.h" +namespace grpc_core { + class SockpairFixture : public CoreTestFixture { public: - explicit SockpairFixture(const grpc_core::ChannelArgs& ep_args) + explicit SockpairFixture(const ChannelArgs& ep_args) : ep_(grpc_iomgr_create_endpoint_pair("fixture", ep_args.ToC().get())) {} - private: - virtual grpc_core::ChannelArgs MutateClientArgs(grpc_core::ChannelArgs args) { - return args; - } - virtual grpc_core::ChannelArgs MutateServerArgs(grpc_core::ChannelArgs args) { - return args; + ~SockpairFixture() override { + ExecCtx exec_ctx; + if (ep_.client != nullptr) { + grpc_endpoint_shutdown(ep_.client, absl::InternalError("done")); + grpc_endpoint_destroy(ep_.client); + } + if (ep_.server != nullptr) { + grpc_endpoint_shutdown(ep_.server, absl::InternalError("done")); + grpc_endpoint_destroy(ep_.server); + } } - grpc_server* MakeServer(const grpc_core::ChannelArgs& in_args) override { + + private: + virtual ChannelArgs MutateClientArgs(ChannelArgs args) { return args; } + virtual ChannelArgs MutateServerArgs(ChannelArgs args) { return args; } + grpc_server* MakeServer(const ChannelArgs& in_args) override { auto args = MutateServerArgs(in_args); - grpc_core::ExecCtx exec_ctx; + ExecCtx exec_ctx; grpc_transport* transport; auto* server = grpc_server_create(args.ToC().get(), nullptr); grpc_server_register_completion_queue(server, cq(), nullptr); grpc_server_start(server); - auto server_channel_args = grpc_core::CoreConfiguration::Get() + auto server_channel_args = CoreConfiguration::Get() .channel_args_preconditioning() .PreconditionChannelArgs(args.ToC().get()); - transport = - grpc_create_chttp2_transport(server_channel_args, ep_.server, false); - grpc_endpoint_add_to_pollset(ep_.server, grpc_cq_pollset(cq())); - grpc_core::Server* core_server = grpc_core::Server::FromC(server); + auto* server_endpoint = std::exchange(ep_.server, nullptr); + EXPECT_NE(server_endpoint, nullptr); + transport = grpc_create_chttp2_transport(server_channel_args, + server_endpoint, false); + grpc_endpoint_add_to_pollset(server_endpoint, grpc_cq_pollset(cq())); + Server* core_server = Server::FromC(server); grpc_error_handle error = core_server->SetupTransport( transport, nullptr, core_server->channel_args(), nullptr); if (error.ok()) { @@ -75,9 +90,9 @@ class SockpairFixture : public CoreTestFixture { } return server; } - grpc_channel* MakeClient(const grpc_core::ChannelArgs& in_args) override { - grpc_core::ExecCtx exec_ctx; - auto args = grpc_core::CoreConfiguration::Get() + grpc_channel* MakeClient(const ChannelArgs& in_args) override { + ExecCtx exec_ctx; + auto args = CoreConfiguration::Get() .channel_args_preconditioning() .PreconditionChannelArgs( MutateClientArgs(in_args) @@ -85,9 +100,11 @@ class SockpairFixture : public CoreTestFixture { .ToC() .get()); grpc_transport* transport; - transport = grpc_create_chttp2_transport(args, ep_.client, true); - auto channel = grpc_core::Channel::Create( - "socketpair-target", args, GRPC_CLIENT_DIRECT_CHANNEL, transport); + auto* client_endpoint = std::exchange(ep_.client, nullptr); + EXPECT_NE(client_endpoint, nullptr); + transport = grpc_create_chttp2_transport(args, client_endpoint, true); + auto channel = Channel::Create("socketpair-target", args, + GRPC_CLIENT_DIRECT_CHANNEL, transport); grpc_channel* client; if (channel.ok()) { client = channel->release()->c_ptr(); @@ -104,5 +121,6 @@ class SockpairFixture : public CoreTestFixture { grpc_endpoint_pair ep_; }; +} // namespace grpc_core #endif // GRPC_TEST_CORE_END2END_FIXTURES_SOCKPAIR_FIXTURE_H diff --git a/test/core/end2end/gen_build_yaml.py b/test/core/end2end/gen_build_yaml.py deleted file mode 100755 index 4d004b96b9100..0000000000000 --- a/test/core/end2end/gen_build_yaml.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Generates the list of end2end test cases from generate_tests.bzl""" - -import os -import sys - -import yaml - -_ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../../..')) -os.chdir(_ROOT) - - -def load(*args): - """Replacement of bazel's load() function""" - pass - - -def struct(**kwargs): - return kwargs # all the args as a dict - - -# generate_tests.bzl is now the source of truth for end2end tests. -# The .bzl file is basically a python file and we can "execute" it -# to get access to the variables it defines. -exec( - compile( - open('test/core/end2end/generate_tests.bzl', "rb").read(), - 'test/core/end2end/generate_tests.bzl', 'exec')) - - -def main(): - json = { - # needed by end2end_tests.cc.template - 'core_end2end_tests': - dict((t, END2END_TESTS[t]['secure']) for t in END2END_TESTS.keys()) - } - print(yaml.dump(json)) - - -if __name__ == '__main__': - main() diff --git a/test/core/end2end/generate_tests.bzl b/test/core/end2end/generate_tests.bzl deleted file mode 100755 index 1a5cab9aeb3e4..0000000000000 --- a/test/core/end2end/generate_tests.bzl +++ /dev/null @@ -1,499 +0,0 @@ -#!/usr/bin/env python2.7 -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Generates the appropriate build.json data for all the end2end tests.""" - -load( - "//bazel:grpc_build_system.bzl", - "grpc_cc_binary", - "grpc_cc_library", - "grpc_sh_test", -) -load("flaky.bzl", "FLAKY_TESTS") - -def _fixture_options( - fullstack = True, - includes_proxy = False, - dns_resolver = True, - name_resolution = True, - secure = True, - tracing = False, - _platforms = ["windows", "linux", "mac", "posix"], - is_inproc = False, - is_1byte = False, - is_http2 = True, - is_minstack = False, - supports_proxy_auth = False, - supports_write_buffering = True, - client_channel = True, - supports_msvc = True, - supports_retry = None, - tags = []): - if supports_retry == None: - supports_retry = client_channel - return struct( - fullstack = fullstack, - includes_proxy = includes_proxy, - dns_resolver = dns_resolver, - name_resolution = name_resolution, - secure = secure, - tracing = tracing, - is_inproc = is_inproc, - is_1byte = is_1byte, - is_http2 = is_http2, - is_minstack = is_minstack, - supports_proxy_auth = supports_proxy_auth, - supports_write_buffering = supports_write_buffering, - client_channel = client_channel, - supports_msvc = supports_msvc, - _platforms = _platforms, - supports_retry = supports_retry, - tags = tags, - ) - -# maps fixture name to whether it requires the security library -END2END_FIXTURES = { - "h2_compress": _fixture_options(), - "h2_census": _fixture_options(), - # TODO(juanlishen): This is disabled for now, but should be considered to re-enable once we have - # decided how the load reporting service should be enabled. - #'h2_load_reporting': _fixture_options(), - "h2_fakesec": _fixture_options(), - "h2_fd": _fixture_options( - dns_resolver = False, - fullstack = False, - client_channel = False, - _platforms = ["linux", "mac", "posix"], - tags = ["no_test_ios"], - ), - "h2_full": _fixture_options( - tags = ["event_engine_client_test"], - ), - "h2_full_no_retry": _fixture_options(supports_retry = False), - "h2_full+pipe": _fixture_options(_platforms = ["linux"]), - "h2_full+trace": _fixture_options(tracing = True), - "h2_http_proxy": _fixture_options(supports_proxy_auth = True), - "h2_insecure": _fixture_options(secure = True), - "h2_oauth2_tls12": _fixture_options(), - "h2_oauth2_tls13": _fixture_options(), - "h2_proxy": _fixture_options(includes_proxy = True), - "h2_sockpair_1byte": _fixture_options( - fullstack = False, - dns_resolver = False, - client_channel = False, - is_1byte = True, - ), - "h2_sockpair": _fixture_options( - fullstack = False, - dns_resolver = False, - client_channel = False, - ), - "h2_sockpair_with_minstack": _fixture_options( - fullstack = False, - dns_resolver = False, - client_channel = False, - is_minstack = True, - ), - "h2_sockpair+trace": _fixture_options( - fullstack = False, - dns_resolver = False, - tracing = True, - client_channel = False, - ), - "h2_ssl_tls12": _fixture_options(secure = True), - "h2_ssl_tls13": _fixture_options(secure = True), - "h2_ssl_cred_reload_tls12": _fixture_options(secure = True), - "h2_ssl_cred_reload_tls13": _fixture_options(secure = True), - "h2_tls_simple": _fixture_options(secure = True), - "h2_tls_static_async_tls1_3": _fixture_options(secure = True), - "h2_tls_certwatch_sync_tls1_2": _fixture_options(secure = True), - "h2_tls_certwatch_async_tls1_3": _fixture_options(secure = True), - "h2_local_abstract_uds_percent_encoded": _fixture_options( - secure = True, - dns_resolver = False, - _platforms = ["linux", "posix"], - ), - "h2_local_uds": _fixture_options( - secure = True, - dns_resolver = False, - _platforms = ["linux", "mac", "posix"], - ), - "h2_local_uds_percent_encoded": _fixture_options( - secure = True, - dns_resolver = False, - _platforms = ["linux", "mac", "posix"], - ), - "h2_local_ipv4": _fixture_options( - secure = True, - dns_resolver = False, - _platforms = ["linux", "mac", "posix"], - tags = ["requires-net:ipv4", "requires-net:loopback", "event_engine_client"], - ), - "h2_local_ipv6": _fixture_options( - secure = True, - dns_resolver = False, - _platforms = ["linux", "mac", "posix"], - ), - "h2_ssl_proxy": _fixture_options(includes_proxy = True, secure = True), - "h2_uds": _fixture_options( - dns_resolver = False, - _platforms = ["linux", "mac", "posix"], - ), - "h2_uds_abstract": _fixture_options( - dns_resolver = False, - _platforms = ["linux", "posix"], - ), - "inproc": _fixture_options( - secure = True, - fullstack = False, - dns_resolver = False, - name_resolution = False, - is_inproc = True, - is_http2 = False, - supports_write_buffering = False, - client_channel = False, - ), -} - -def _test_options( - needs_fullstack = False, - needs_dns = False, - needs_names = False, - proxyable = True, - secure = False, - traceable = False, - exclude_inproc = False, - exclude_1byte = False, - exclude_minstack = False, - needs_http2 = False, - needs_proxy_auth = False, - needs_write_buffering = False, - needs_client_channel = False, - needs_retry = False, - short_name = None, - tags = [], - exclude_pollers = []): - return struct( - needs_fullstack = needs_fullstack, - needs_dns = needs_dns, - needs_names = needs_names, - proxyable = proxyable, - secure = secure, - traceable = traceable, - exclude_inproc = exclude_inproc, - exclude_1byte = exclude_1byte, - exclude_minstack = exclude_minstack, - needs_http2 = needs_http2, - needs_proxy_auth = needs_proxy_auth, - needs_write_buffering = needs_write_buffering, - needs_client_channel = needs_client_channel, - needs_retry = needs_retry, - short_name = short_name, - tags = tags, - exclude_pollers = exclude_pollers, - ) - -# maps test names to options -END2END_TESTS = { - "bad_hostname": _test_options(needs_names = True), - "bad_ping": _test_options(needs_fullstack = True, proxyable = False), - "binary_metadata": _test_options(), - "resource_quota_server": _test_options( - proxyable = False, - # TODO(b/151212019): Test case known to be flaky under epoll1. - exclude_pollers = ["epoll1"], - exclude_1byte = True, - ), - "call_creds": _test_options(secure = True), - "call_host_override": _test_options( - needs_fullstack = True, - needs_dns = True, - needs_names = True, - ), - "cancel_after_accept": _test_options(), - "cancel_after_client_done": _test_options(), - "cancel_after_invoke": _test_options(), - "cancel_after_round_trip": _test_options(), - "cancel_before_invoke": _test_options(), - "cancel_in_a_vacuum": _test_options(), - "cancel_with_status": _test_options(), - "client_streaming": _test_options(), - "compressed_payload": _test_options(proxyable = False, exclude_inproc = True, exclude_minstack = True), - "connectivity": _test_options( - needs_fullstack = True, - needs_names = True, - proxyable = False, - ), - "channelz": _test_options(), - "default_host": _test_options( - needs_fullstack = True, - needs_dns = True, - needs_names = True, - ), - "disappearing_server": _test_options(needs_fullstack = True, needs_names = True), - "empty_batch": _test_options(), - "filter_causes_close": _test_options(), - "filter_init_fails": _test_options(), - "filter_context": _test_options(), - "filtered_metadata": _test_options(), - "graceful_server_shutdown": _test_options(exclude_inproc = True), - "grpc_authz": _test_options(secure = True), - "hpack_size": _test_options( - proxyable = False, - traceable = False, - exclude_inproc = True, - ), - "high_initial_seqno": _test_options(), - "invoke_large_request": _test_options(exclude_1byte = True, tags = ["flow_control_test"]), - "keepalive_timeout": _test_options(proxyable = False, needs_http2 = True), - "large_metadata": _test_options(exclude_1byte = True), - "max_concurrent_streams": _test_options( - proxyable = False, - exclude_inproc = True, - exclude_minstack = True, - ), - "max_connection_age": _test_options(exclude_minstack = True, exclude_inproc = True), - "max_connection_idle": _test_options(needs_fullstack = True, proxyable = False), - "max_message_length": _test_options(exclude_minstack = True), - "negative_deadline": _test_options(exclude_minstack = True), - "no_logging": _test_options(traceable = False), - "no_op": _test_options(), - "payload": _test_options(exclude_1byte = True), - # TODO(juanlishen): This is disabled for now because it depends on some generated functions in - # end2end_tests.cc, which are not generated because they would depend on OpenCensus while - # OpenCensus can only be built via Bazel so far. - # 'load_reporting_hook': _test_options(), - "ping_pong_streaming": _test_options(tags = ["flow_control_test"]), - "ping": _test_options(needs_fullstack = True, proxyable = False), - "proxy_auth": _test_options(needs_proxy_auth = True), - "registered_call": _test_options(), - "request_with_flags": _test_options(proxyable = False), - "request_with_payload": _test_options(), - "retry": _test_options(needs_client_channel = True, needs_retry = True), - "retry_cancellation": _test_options(needs_client_channel = True, needs_retry = True), - "retry_cancel_during_delay": _test_options(needs_client_channel = True, needs_retry = True), - "retry_cancel_with_multiple_send_batches": _test_options( - # TODO(jtattermusch): too long bazel test name makes the test flaky on Windows RBE - # See b/151617965 - short_name = "retry_cancel3", - needs_client_channel = True, - needs_retry = True, - ), - "retry_cancel_after_first_attempt_starts": _test_options( - # TODO(jtattermusch): too long bazel test name makes the test flaky on Windows RBE - # See b/151617965 - short_name = "retry_cancel4", - needs_client_channel = True, - needs_retry = True, - ), - "retry_disabled": _test_options(needs_client_channel = True, needs_retry = True), - "retry_exceeds_buffer_size_in_delay": _test_options(needs_client_channel = True, needs_retry = True), - "retry_exceeds_buffer_size_in_initial_batch": _test_options( - needs_client_channel = True, - # TODO(jtattermusch): too long bazel test name makes the test flaky on Windows RBE - # See b/151617965 - short_name = "retry_exceeds_buffer_size_in_init", - needs_retry = True, - ), - "retry_exceeds_buffer_size_in_subsequent_batch": _test_options( - needs_client_channel = True, - # TODO(jtattermusch): too long bazel test name makes the test flaky on Windows RBE - # See b/151617965 - short_name = "retry_exceeds_buffer_size_in_subseq", - needs_retry = True, - ), - "retry_lb_drop": _test_options(needs_client_channel = True, needs_retry = True), - "retry_lb_fail": _test_options(needs_client_channel = True, needs_retry = True), - "retry_non_retriable_status": _test_options(needs_client_channel = True, needs_retry = True), - "retry_non_retriable_status_before_recv_trailing_metadata_started": _test_options( - needs_client_channel = True, - # TODO(jtattermusch): too long bazel test name makes the test flaky on Windows RBE - # See b/151617965 - short_name = "retry_non_retriable_status2", - needs_retry = True, - ), - "retry_per_attempt_recv_timeout": _test_options(needs_client_channel = True, needs_retry = True), - "retry_per_attempt_recv_timeout_on_last_attempt": _test_options( - needs_client_channel = True, - # TODO(jtattermusch): too long bazel test name makes the test flaky on Windows RBE - # See b/151617965 - short_name = "retry_per_attempt_recv_timeout2", - needs_retry = True, - ), - "retry_recv_initial_metadata": _test_options(needs_client_channel = True, needs_retry = True), - "retry_recv_message": _test_options(needs_client_channel = True, needs_retry = True), - "retry_recv_message_replay": _test_options(needs_client_channel = True, needs_retry = True), - "retry_recv_trailing_metadata_error": _test_options(needs_client_channel = True, needs_retry = True), - "retry_send_initial_metadata_refs": _test_options(needs_client_channel = True, needs_retry = True), - "retry_send_op_fails": _test_options(needs_client_channel = True, needs_retry = True), - "retry_send_recv_batch": _test_options(needs_client_channel = True, needs_retry = True), - "retry_server_pushback_delay": _test_options(needs_client_channel = True, needs_retry = True), - "retry_server_pushback_disabled": _test_options(needs_client_channel = True, needs_retry = True), - "retry_streaming": _test_options(needs_client_channel = True, needs_retry = True), - "retry_streaming_after_commit": _test_options(needs_client_channel = True, needs_retry = True), - "retry_streaming_succeeds_before_replay_finished": _test_options( - needs_client_channel = True, - # TODO(jtattermusch): too long bazel test name makes the test flaky on Windows RBE - # See b/151617965 - short_name = "retry_streaming2", - needs_retry = True, - ), - "retry_throttled": _test_options(needs_client_channel = True, needs_retry = True), - "retry_too_many_attempts": _test_options(needs_client_channel = True, needs_retry = True), - "retry_transparent_goaway": _test_options(needs_client_channel = True, needs_retry = True), - "retry_transparent_not_sent_on_wire": _test_options(needs_client_channel = True, needs_retry = True), - "retry_transparent_max_concurrent_streams": _test_options( - needs_client_channel = True, - proxyable = False, - # TODO(jtattermusch): too long bazel test name makes the test flaky on Windows RBE - # See b/151617965 - short_name = "retry_transparent_mcs", - needs_retry = True, - ), - "retry_unref_before_finish": _test_options(needs_client_channel = True, needs_retry = True), - "retry_unref_before_recv": _test_options(needs_client_channel = True, needs_retry = True), - "server_finishes_request": _test_options(), - "server_streaming": _test_options(needs_http2 = True), - "shutdown_finishes_calls": _test_options(), - "shutdown_finishes_tags": _test_options(), - "simple_delayed_request": _test_options(needs_fullstack = True), - "simple_metadata": _test_options(), - "simple_request": _test_options(), - "streaming_error_response": _test_options(), - "trailing_metadata": _test_options(), - "authority_not_supported": _test_options(), - "filter_latency": _test_options(), - "filter_status_code": _test_options(), - "write_buffering": _test_options(needs_write_buffering = True), - "write_buffering_at_end": _test_options(needs_write_buffering = True), -} - -def _compatible(fopt, topt): - if topt.needs_fullstack: - if not fopt.fullstack: - return False - if topt.needs_dns: - if not fopt.dns_resolver: - return False - if topt.needs_names: - if not fopt.name_resolution: - return False - if not topt.proxyable: - if fopt.includes_proxy: - return False - if not topt.traceable: - if fopt.tracing: - return False - if topt.exclude_inproc: - if fopt.is_inproc: - return False - if topt.exclude_1byte: - if fopt.is_1byte: - return False - if topt.exclude_minstack: - if fopt.is_minstack: - return False - if topt.needs_http2: - if not fopt.is_http2: - return False - if topt.needs_proxy_auth: - if not fopt.supports_proxy_auth: - return False - if topt.needs_write_buffering: - if not fopt.supports_write_buffering: - return False - if topt.needs_client_channel: - if not fopt.client_channel: - return False - if topt.needs_retry: - if not fopt.supports_retry: - return False - return True - -def _platform_support_tags(fopt): - result = [] - if not "windows" in fopt._platforms: - result.append("no_windows") - if not "mac" in fopt._platforms: - result.append("no_mac") - if not "linux" in fopt._platforms: - result.append("no_linux") - return result - -# buildifier: disable=unnamed-macro -def grpc_end2end_tests(): - """Instantiates the gRPC end2end tests.""" - grpc_cc_library( - name = "end2end_tests", - srcs = ["end2end_tests.cc", "end2end_test_utils.cc"] + - ["tests/%s.cc" % t for t in sorted(END2END_TESTS.keys())], - hdrs = [ - "tests/cancel_test_helpers.h", - "end2end_tests.h", - ], - language = "C++", - testonly = 1, - deps = [ - ":cq_verifier", - ":ssl_test_data", - ":http_proxy", - ":proxy", - ":fixture_support", - "//test/core/util:test_lb_policies", - "//:grpc_authorization_provider", - "//:grpc_http_filters", - "//src/core:event_log", - ], - visibility = [ - "//src/objective-c/tests:__subpackages__", - ], - ) - for f, fopt in END2END_FIXTURES.items(): - bin_name = "%s_test" % f - grpc_cc_binary( - name = bin_name, - srcs = ["fixtures/%s.cc" % f, "fixtures/h2_tls_common.h"], - language = "C++", - testonly = 1, - data = [ - "//src/core/tsi/test_creds:ca.pem", - "//src/core/tsi/test_creds:server1.key", - "//src/core/tsi/test_creds:server1.pem", - ], - deps = [ - ":end2end_tests", - "//test/core/util:grpc_test_util", - "//:grpc", - "//:gpr", - "//:grpc_http_filters", - ], - tags = _platform_support_tags(fopt) + fopt.tags, - ) - for t, topt in END2END_TESTS.items(): - if not _compatible(fopt, topt): - continue - test_short_name = str(t) if not topt.short_name else topt.short_name - name = "%s_test@%s" % (f, test_short_name) - grpc_sh_test( - name = name, - srcs = ["run.sh"], - data = [":" + bin_name], - args = ["$(location %s)" % bin_name, t], - tags = _platform_support_tags(fopt) + fopt.tags + topt.tags + [ - "no_test_ios", - "core_end2end_test", - ], - flaky = name in FLAKY_TESTS, - exclude_pollers = topt.exclude_pollers, - ) diff --git a/test/core/end2end/h2_ssl_cert_test.cc b/test/core/end2end/h2_ssl_cert_test.cc index de3bcc9cf1d09..0f713c785ca0c 100644 --- a/test/core/end2end/h2_ssl_cert_test.cc +++ b/test/core/end2end/h2_ssl_cert_test.cc @@ -141,8 +141,7 @@ typedef enum { SUCCESS, FAIL } test_result; #define SSL_TEST(request_type, cert_type, result) \ { \ {TEST_NAME(request_type, cert_type, result), \ - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | \ - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | \ + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | \ FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL, \ "foo.test.google.fr", TestFixture::MakeFactory(request_type, cert_type)}, \ result \ @@ -150,7 +149,7 @@ typedef enum { SUCCESS, FAIL } test_result; // All test configurations struct CoreTestConfigWrapper { - CoreTestConfiguration config; + grpc_core::CoreTestConfiguration config; test_result result; }; @@ -194,7 +193,7 @@ static CoreTestConfigWrapper configs[] = { BAD_CERT_PAIR, FAIL), }; -static void simple_request_body(CoreTestFixture* f, +static void simple_request_body(grpc_core::CoreTestFixture* f, test_result expected_result) { grpc_call* c; gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); @@ -239,7 +238,7 @@ class H2SslCertTest : public ::testing::TestWithParam { } void TearDown() override { fixture_.reset(); } - std::unique_ptr fixture_; + std::unique_ptr fixture_; }; TEST_P(H2SslCertTest, SimpleRequestBody) { diff --git a/test/core/end2end/run.sh b/test/core/end2end/run.sh deleted file mode 100755 index eaad9fb41a5d1..0000000000000 --- a/test/core/end2end/run.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -# Arbitrary shell script runner -# 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. -set -ex -"$@" diff --git a/test/core/end2end/tests/authority_not_supported.cc b/test/core/end2end/tests/authority_not_supported.cc deleted file mode 100644 index db34001fb03ad..0000000000000 --- a/test/core/end2end/tests/authority_not_supported.cc +++ /dev/null @@ -1,142 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Request/response with metadata and payload. -static void test_with_authority_header(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_metadata meta_c[2] = {{grpc_slice_from_static_string("key1"), - grpc_slice_from_static_string("val1"), - {{nullptr, nullptr, nullptr, nullptr}}}, - {grpc_slice_from_static_string("key2"), - grpc_slice_from_static_string("val2"), - {{nullptr, nullptr, nullptr, nullptr}}}}; - auto f = begin_test(config, "test_with_authority_header", nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - - grpc_slice host = grpc_slice_from_static_string("foo.test.google.fr"); - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - &host, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 2; - op->data.send_initial_metadata.metadata = meta_c; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_CANCELLED); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - - grpc_call_unref(c); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload_recv); -} - -void authority_not_supported(const CoreTestConfiguration& config) { - if (config.feature_mask & FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER) { - return; - } - test_with_authority_header(config); -} - -void authority_not_supported_pre_init(void) {} diff --git a/test/core/end2end/tests/bad_hostname.cc b/test/core/end2end/tests/bad_hostname.cc deleted file mode 100644 index 77b24be5c571d..0000000000000 --- a/test/core/end2end/tests/bad_hostname.cc +++ /dev/null @@ -1,124 +0,0 @@ -// -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - return f; -} - -static void simple_request_body(CoreTestFixture* f) { - grpc_call* c; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - - grpc_slice host = grpc_slice_from_static_string("slartibartfast.local"); - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - &host, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_INTERNAL); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); -} - -static void test_invoke_simple_request(const CoreTestConfiguration& config) { - auto f = begin_test(config, "test_invoke_simple_request", nullptr, nullptr); - simple_request_body(f.get()); -} - -void bad_hostname(const CoreTestConfiguration& config) { - if (config.feature_mask & FEATURE_MASK_SUPPORTS_HOSTNAME_VERIFICATION) { - test_invoke_simple_request(config); - } -} - -void bad_hostname_pre_init(void) {} diff --git a/test/core/end2end/tests/bad_ping.cc b/test/core/end2end/tests/bad_ping.cc index d7e7f0cebd9e0..19ec4bcb9adf2 100644 --- a/test/core/end2end/tests/bad_ping.cc +++ b/test/core/end2end/tests/bad_ping.cc @@ -16,104 +16,41 @@ // // -#include - -#include -#include +#include "gtest/gtest.h" #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/gprpp/time.h" -#include "src/core/lib/surface/channel.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" #define MAX_PING_STRIKES 2 -// Send more pings than server allows to trigger server's GOAWAY. -static void test_bad_ping(const CoreTestConfiguration& config) { - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - grpc_core::CqVerifier cqv(f->cq()); - auto client_args = grpc_core::ChannelArgs() - .Set(GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA, 0) - .Set(GRPC_ARG_HTTP2_BDP_PROBE, 0); - auto server_args = - grpc_core::ChannelArgs() - .Set(GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS, - grpc_core::Duration::Minutes(5).millis()) - .Set(GRPC_ARG_HTTP2_MAX_PING_STRIKES, MAX_PING_STRIKES) - .Set(GRPC_ARG_HTTP2_BDP_PROBE, 0); - - f->InitClient(client_args); - f->InitServer(server_args); - - grpc_call* c; - grpc_call* s; - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(10); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); +namespace grpc_core { +namespace { +// Send more pings than server allows to trigger server's GOAWAY. +TEST_P(RetryHttp2Test, BadPing) { + InitClient(ChannelArgs() + .Set(GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA, 0) + .Set(GRPC_ARG_HTTP2_BDP_PROBE, 0)); + InitServer(ChannelArgs() + .Set(GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS, + Duration::Minutes(5).millis()) + .Set(GRPC_ARG_HTTP2_MAX_PING_STRIKES, MAX_PING_STRIKES) + .Set(GRPC_ARG_HTTP2_BDP_PROBE, 0)); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(10)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); // Send too many pings to the server to trigger the punishment: // The first ping will let server mark its last_recv time. Afterwards, each // ping will trigger a ping strike, and we need at least MAX_PING_STRIKES @@ -121,214 +58,84 @@ static void test_bad_ping(const CoreTestConfiguration& config) { // needed here. int i; for (i = 1; i <= MAX_PING_STRIKES + 2; i++) { - grpc_channel_ping(f->client(), f->cq(), grpc_core::CqVerifier::tag(200 + i), - nullptr); - cqv.Expect(grpc_core::CqVerifier::tag(200 + i), true); + PingServerFromClient(200 + i); + Expect(200 + i, true); if (i == MAX_PING_STRIKES + 2) { - cqv.Expect(grpc_core::CqVerifier::tag(1), true); + Expect(1, true); } - cqv.Verify(); + Step(); } - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - grpc_server_shutdown_and_notify(f->server(), f->cq(), - grpc_core::CqVerifier::tag(0xdead)); - cqv.Expect(grpc_core::CqVerifier::tag(0xdead), true); - cqv.Verify(); - - grpc_call_unref(s); - + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Step(); + ShutdownServerAndNotify(103); + Expect(103, true); + Step(); // The connection should be closed immediately after the misbehaved pings, // the in-progress RPC should fail. - GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 1); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_unref(c); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNAVAILABLE); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_TRUE(client_close.was_cancelled()); } // Try sending more pings than server allows, but server should be fine because // max_pings_without_data should limit pings sent out on wire. -static void test_pings_without_data(const CoreTestConfiguration& config) { - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - grpc_core::CqVerifier cqv(f->cq()); +TEST_P(RetryHttp2Test, PingsWithoutData) { // Only allow MAX_PING_STRIKES pings without data (DATA/HEADERS/WINDOW_UPDATE) // so that the transport will throttle the excess pings. - auto client_args = - grpc_core::ChannelArgs() - .Set(GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA, MAX_PING_STRIKES) - .Set(GRPC_ARG_HTTP2_BDP_PROBE, 0); - auto server_args = - grpc_core::ChannelArgs() - .Set(GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS, - grpc_core::Duration::Minutes(5).millis()) - .Set(GRPC_ARG_HTTP2_MAX_PING_STRIKES, MAX_PING_STRIKES) - .Set(GRPC_ARG_HTTP2_BDP_PROBE, 0); - - f->InitClient(client_args); - f->InitServer(server_args); - - grpc_call* c; - grpc_call* s; - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(10); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - + InitClient(ChannelArgs() + .Set(GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA, MAX_PING_STRIKES) + .Set(GRPC_ARG_HTTP2_BDP_PROBE, 0)); + InitServer(ChannelArgs() + .Set(GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS, + Duration::Minutes(5).millis()) + .Set(GRPC_ARG_HTTP2_MAX_PING_STRIKES, MAX_PING_STRIKES) + .Set(GRPC_ARG_HTTP2_BDP_PROBE, 0)); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(10)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); // Send too many pings to the server similar to the previous test case. // However, since we set the MAX_PINGS_WITHOUT_DATA at the client side, only // MAX_PING_STRIKES will actually be sent and the rpc will still succeed. int i; for (i = 1; i <= MAX_PING_STRIKES + 2; i++) { - grpc_channel_ping(f->client(), f->cq(), grpc_core::CqVerifier::tag(200 + i), - nullptr); + PingServerFromClient(200 + i); if (i <= MAX_PING_STRIKES) { - cqv.Expect(grpc_core::CqVerifier::tag(200 + i), true); + Expect(200 + i, true); } - cqv.Verify(); + Step(); } - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); // Client call should return. - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - grpc_server_shutdown_and_notify(f->server(), f->cq(), - grpc_core::CqVerifier::tag(0xdead)); - cqv.Expect(grpc_core::CqVerifier::tag(0xdead), true); - + Expect(1, true); + Step(); + ShutdownServerAndNotify(103); + Expect(103, true); // Also expect the previously blocked pings to complete with an error - cqv.Expect(grpc_core::CqVerifier::tag(200 + MAX_PING_STRIKES + 1), false); - cqv.Expect(grpc_core::CqVerifier::tag(200 + MAX_PING_STRIKES + 2), false); - - cqv.Verify(); - - grpc_call_unref(s); - + Expect(200 + MAX_PING_STRIKES + 1, false); + Expect(200 + MAX_PING_STRIKES + 2, false); + Step(); // The rpc should be successful. - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_unref(c); -} - -void bad_ping(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION); - test_bad_ping(config); - test_pings_without_data(config); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(s.method(), "/foo"); } -void bad_ping_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/binary_metadata.cc b/test/core/end2end/tests/binary_metadata.cc index ec21297b65f07..67a7ea697c14d 100644 --- a/test/core/end2end/tests/binary_metadata.cc +++ b/test/core/end2end/tests/binary_metadata.cc @@ -16,272 +16,104 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/channel_args_preconditioning.h" -#include "src/core/lib/config/core_configuration.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" +#include "src/core/lib/slice/slice.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - client_args = - const_cast(grpc_core::CoreConfiguration::Get() - .channel_args_preconditioning() - .PreconditionChannelArgs(client_args) - .ToC() - .release()); - server_args = - const_cast(grpc_core::CoreConfiguration::Get() - .channel_args_preconditioning() - .PreconditionChannelArgs(server_args) - .ToC() - .release()); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - grpc_channel_args_destroy(client_args); - grpc_channel_args_destroy(server_args); - return f; +namespace grpc_core { + +static void BinaryMetadata(CoreEnd2endTest& test, bool server_true_binary, + bool client_true_binary) { + test.InitServer( + ChannelArgs().Set(GRPC_ARG_HTTP2_ENABLE_TRUE_BINARY, server_true_binary)); + test.InitClient( + ChannelArgs().Set(GRPC_ARG_HTTP2_ENABLE_TRUE_BINARY, client_true_binary)); + + auto key1_payload = RandomBinarySlice(32); + auto key2_payload = RandomBinarySlice(18); + auto key3_payload = RandomBinarySlice(17); + auto key4_payload = RandomBinarySlice(68); + auto key5_payload = RandomBinarySlice(33); + auto key6_payload = RandomBinarySlice(2); + auto request_payload = RandomBinarySlice(7); + auto response_payload = RandomBinarySlice(9); + auto status_string = RandomBinarySlice(256); + + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_md; + CoreEnd2endTest::IncomingMessage server_message; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({ + {"key1-bin", key1_payload.as_string_view()}, + {"key2-bin", key2_payload.as_string_view()}, + }) + .SendMessage(request_payload.Ref()) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_md) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102) + .SendInitialMetadata({ + {"key3-bin", key3_payload.as_string_view()}, + {"key4-bin", key4_payload.as_string_view()}, + }) + .RecvMessage(client_message); + test.Expect(102, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(103) + .RecvCloseOnServer(client_close) + .SendMessage(response_payload.Ref()) + .SendStatusFromServer(GRPC_STATUS_OK, status_string.as_string_view(), + { + {"key5-bin", key5_payload.as_string_view()}, + {"key6-bin", key6_payload.as_string_view()}, + }); + test.Expect(103, true); + test.Expect(1, true); + test.Step(); + + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), status_string.as_string_view()); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(client_message.payload(), request_payload); + EXPECT_EQ(server_message.payload(), response_payload); + EXPECT_EQ(s.GetInitialMetadata("key1-bin"), key1_payload.as_string_view()); + EXPECT_EQ(s.GetInitialMetadata("key2-bin"), key2_payload.as_string_view()); + EXPECT_EQ(server_initial_md.Get("key3-bin"), key3_payload.as_string_view()); + EXPECT_EQ(server_initial_md.Get("key4-bin"), key4_payload.as_string_view()); + EXPECT_EQ(server_status.GetTrailingMetadata("key5-bin"), + key5_payload.as_string_view()); + EXPECT_EQ(server_status.GetTrailingMetadata("key6-bin"), + key6_payload.as_string_view()); } -// Request/response with metadata and payload. -static void test_request_response_with_metadata_and_payload( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_slice response_payload_slice = - grpc_slice_from_copied_string("hello you"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_metadata meta_c[2] = { - {grpc_slice_from_static_string("key1-bin"), - grpc_slice_from_static_string( - "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc"), - {{nullptr, nullptr, nullptr, nullptr}}}, - {grpc_slice_from_static_string("key2-bin"), - grpc_slice_from_static_string( - "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d"), - {{nullptr, nullptr, nullptr, nullptr}}}}; - grpc_metadata meta_s[2] = { - {grpc_slice_from_static_string("key3-bin"), - grpc_slice_from_static_string( - "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee"), - {{nullptr, nullptr, nullptr, nullptr}}}, - {grpc_slice_from_static_string("key4-bin"), - grpc_slice_from_static_string( - "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"), - {{nullptr, nullptr, nullptr, nullptr}}}}; - auto f = begin_test(config, "test_request_response_with_metadata_and_payload", - nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 2; - op->data.send_initial_metadata.metadata = meta_c; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 2; - op->data.send_initial_metadata.metadata = meta_s; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_string = grpc_slice_from_static_string( - "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12" - "\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24" - "\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36" - "\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48" - "\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a" - "\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c" - "\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e" - "\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90" - "\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2" - "\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4" - "\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6" - "\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8" - "\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea" - "\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc" - "\xfd\xfe\xff"); - op->data.send_status_from_server.status_details = &status_string; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT( - 0 == - grpc_slice_str_cmp( - details, - "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" - "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20" - "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30" - "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40" - "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50" - "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60" - "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" - "\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80" - "\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90" - "\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0" - "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0" - "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0" - "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0" - "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0" - "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0" - "\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world")); - GPR_ASSERT(byte_buffer_eq_string(response_payload_recv, "hello you")); - GPR_ASSERT(contains_metadata( - &request_metadata_recv, "key1-bin", - "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc")); - GPR_ASSERT(contains_metadata( - &request_metadata_recv, "key2-bin", - "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d")); - GPR_ASSERT(contains_metadata( - &initial_metadata_recv, "key3-bin", - "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee")); - GPR_ASSERT(contains_metadata( - &initial_metadata_recv, "key4-bin", - "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); +TEST_P(CoreEnd2endTest, BinaryMetadataServerTrueBinaryClientHttp2Fallback) { + BinaryMetadata(*this, true, false); +} - grpc_call_unref(c); - grpc_call_unref(s); +TEST_P(CoreEnd2endTest, BinaryMetadataServerHttp2FallbackClientTrueBinary) { + BinaryMetadata(*this, false, true); +} - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); +TEST_P(CoreEnd2endTest, BinaryMetadataServerTrueBinaryClientTrueBinary) { + BinaryMetadata(*this, true, true); } -void binary_metadata(const CoreTestConfiguration& config) { - test_request_response_with_metadata_and_payload(config); +TEST_P(CoreEnd2endTest, BinaryMetadataServerHttp2FallbackClientHttp2Fallback) { + BinaryMetadata(*this, false, false); } -void binary_metadata_pre_init(void) {} +} // namespace grpc_core diff --git a/test/core/end2end/tests/call_creds.cc b/test/core/end2end/tests/call_creds.cc index eb5ab4ba3ccb0..731b18cadb74f 100644 --- a/test/core/end2end/tests/call_creds.cc +++ b/test/core/end2end/tests/call_creds.cc @@ -16,56 +16,34 @@ // // -#include - -#include #include -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" + #include #include -#include -#include #include #include -#include #include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/security/credentials/credentials.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static const char iam_token[] = "token"; -static const char iam_selector[] = "selector"; -static const char overridden_iam_token[] = "overridden_token"; -static const char overridden_iam_selector[] = "overridden_selector"; -static const char fake_md_key[] = "fake_key"; -static const char fake_md_value[] = "fake_value"; -static const char overridden_fake_md_key[] = "overridden_fake_key"; -static const char overridden_fake_md_value[] = "overridden_fake_value"; +namespace grpc_core { +namespace { -typedef enum { NONE, OVERRIDE, DESTROY, FAIL } override_mode; - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - bool use_secure_call_creds, int fail_server_auth_check) { - gpr_log(GPR_INFO, "Running test: %s%s/%s", test_name, - use_secure_call_creds ? "_with_secure_call_creds" - : "_with_insecure_call_creds", - config.name); - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - f->InitClient(grpc_core::ChannelArgs()); - grpc_core::ChannelArgs server_args; - if (fail_server_auth_check) { - server_args = server_args.Set(FAIL_AUTH_CHECK_SERVER_ARG_NAME, true); - } - f->InitServer(server_args); - return f; -} +const char iam_token[] = "token"; +const char iam_selector[] = "selector"; +const char overridden_iam_token[] = "overridden_token"; +const char overridden_iam_selector[] = "overridden_selector"; +const char fake_md_key[] = "fake_key"; +const char fake_md_value[] = "fake_value"; +const char overridden_fake_md_key[] = "overridden_fake_key"; +const char overridden_fake_md_value[] = "overridden_fake_value"; -static void print_auth_context(int is_client, const grpc_auth_context* ctx) { +void PrintAuthContext(bool is_client, const grpc_auth_context* ctx) { const grpc_auth_property* p; grpc_auth_property_iterator it; gpr_log(GPR_INFO, "%s peer:", is_client ? "client" : "server"); @@ -82,412 +60,270 @@ static void print_auth_context(int is_client, const grpc_auth_context* ctx) { } } -static void request_response_with_payload_and_call_creds( - const char* test_name, const CoreTestConfiguration& config, - override_mode mode, bool use_secure_call_creds) { - grpc_call* c = nullptr; - grpc_call* s = nullptr; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_slice response_payload_slice = - grpc_slice_from_copied_string("hello you"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - grpc_call_credentials* creds = nullptr; - grpc_auth_context* server_auth_context = nullptr; - grpc_auth_context* client_auth_context = nullptr; - - auto f = begin_test(config, test_name, use_secure_call_creds, 0); - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); +void TestRequestResponseWithPayloadAndCallCreds(CoreEnd2endTest& test, + bool use_secure_call_creds) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + grpc_call_credentials* creds; if (use_secure_call_creds) { creds = grpc_google_iam_credentials_create(iam_token, iam_selector, nullptr); } else { creds = grpc_md_only_test_credentials_create(fake_md_key, fake_md_value); } - GPR_ASSERT(creds != nullptr); - GPR_ASSERT(grpc_call_set_credentials(c, creds) == GRPC_CALL_OK); - switch (mode) { - case NONE: - break; - case OVERRIDE: - grpc_call_credentials_release(creds); - if (use_secure_call_creds) { - creds = grpc_google_iam_credentials_create( - overridden_iam_token, overridden_iam_selector, nullptr); - } else { - creds = grpc_md_only_test_credentials_create(overridden_fake_md_key, - overridden_fake_md_value); - } - GPR_ASSERT(creds != nullptr); - GPR_ASSERT(grpc_call_set_credentials(c, creds) == GRPC_CALL_OK); - break; - case DESTROY: - GPR_ASSERT(grpc_call_set_credentials(c, nullptr) == GRPC_CALL_OK); - break; - case FAIL: - // Do nothing - break; + EXPECT_NE(creds, nullptr); + c.SetCredentials(creds); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello world") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + PrintAuthContext(false, s.GetAuthContext().get()); + PrintAuthContext(true, c.GetAuthContext().get()); + // Cannot set creds on the server call object. + EXPECT_NE(grpc_call_set_credentials(s.c_call(), nullptr), GRPC_CALL_OK); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).SendInitialMetadata({}).RecvMessage(client_message); + test.Expect(102, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(103) + .RecvCloseOnServer(client_close) + .SendMessage("hello you") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + test.Expect(103, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(client_message.payload(), "hello world"); + EXPECT_EQ(server_message.payload(), "hello you"); + if (use_secure_call_creds) { + EXPECT_EQ(s.GetInitialMetadata(GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY), + iam_token); + EXPECT_EQ(s.GetInitialMetadata(GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY), + iam_selector); + } else { + EXPECT_EQ(s.GetInitialMetadata(fake_md_key), fake_md_value); } - grpc_call_credentials_release(creds); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); +} - if (mode == FAIL) { - // Expect the call to fail since the channel credentials did not satisfy the - // minimum security level requirements. - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - GPR_ASSERT(status == GRPC_STATUS_UNAUTHENTICATED); +void TestRequestResponseWithPayloadAndOverriddenCallCreds( + CoreEnd2endTest& test, bool use_secure_call_creds) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + grpc_call_credentials* creds; + if (use_secure_call_creds) { + creds = + grpc_google_iam_credentials_create(iam_token, iam_selector, nullptr); } else { - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - server_auth_context = grpc_call_auth_context(s); - GPR_ASSERT(server_auth_context != nullptr); - print_auth_context(0, server_auth_context); - grpc_auth_context_release(server_auth_context); - - client_auth_context = grpc_call_auth_context(c); - GPR_ASSERT(client_auth_context != nullptr); - print_auth_context(1, client_auth_context); - grpc_auth_context_release(client_auth_context); - - // Cannot set creds on the server call object. - GPR_ASSERT(grpc_call_set_credentials(s, nullptr) != GRPC_CALL_OK); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world")); - GPR_ASSERT(byte_buffer_eq_string(response_payload_recv, "hello you")); - - switch (mode) { - case NONE: - if (use_secure_call_creds) { - GPR_ASSERT(contains_metadata( - &request_metadata_recv, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, - iam_token)); - GPR_ASSERT(contains_metadata(&request_metadata_recv, - GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, - iam_selector)); - } else { - GPR_ASSERT(contains_metadata(&request_metadata_recv, fake_md_key, - fake_md_value)); - } - break; - case OVERRIDE: - if (use_secure_call_creds) { - GPR_ASSERT(contains_metadata( - &request_metadata_recv, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, - overridden_iam_token)); - GPR_ASSERT(contains_metadata(&request_metadata_recv, - GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, - overridden_iam_selector)); - } else { - GPR_ASSERT(contains_metadata(&request_metadata_recv, - overridden_fake_md_key, - overridden_fake_md_value)); - } - break; - case DESTROY: - GPR_ASSERT(!contains_metadata(&request_metadata_recv, - GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, - iam_token)); - GPR_ASSERT(!contains_metadata(&request_metadata_recv, - GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, - iam_selector)); - GPR_ASSERT(!contains_metadata(&request_metadata_recv, - GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, - overridden_iam_token)); - GPR_ASSERT(!contains_metadata(&request_metadata_recv, - GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, - overridden_iam_selector)); - GPR_ASSERT(!contains_metadata(&request_metadata_recv, fake_md_key, - fake_md_value)); - GPR_ASSERT(!contains_metadata(&request_metadata_recv, - overridden_fake_md_key, - overridden_fake_md_value)); - break; - case FAIL: - GPR_ASSERT(0); - } - grpc_call_unref(s); + creds = grpc_md_only_test_credentials_create(fake_md_key, fake_md_value); + } + EXPECT_NE(creds, nullptr); + c.SetCredentials(creds); + if (use_secure_call_creds) { + creds = grpc_google_iam_credentials_create( + overridden_iam_token, overridden_iam_selector, nullptr); + } else { + creds = grpc_md_only_test_credentials_create(overridden_fake_md_key, + overridden_fake_md_value); + } + c.SetCredentials(creds); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello world") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + PrintAuthContext(false, s.GetAuthContext().get()); + PrintAuthContext(true, c.GetAuthContext().get()); + // Cannot set creds on the server call object. + EXPECT_NE(grpc_call_set_credentials(s.c_call(), nullptr), GRPC_CALL_OK); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).SendInitialMetadata({}).RecvMessage(client_message); + test.Expect(102, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(103) + .RecvCloseOnServer(client_close) + .SendMessage("hello you") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + test.Expect(103, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(client_message.payload(), "hello world"); + EXPECT_EQ(server_message.payload(), "hello you"); + if (use_secure_call_creds) { + EXPECT_EQ(s.GetInitialMetadata(GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY), + overridden_iam_token); + EXPECT_EQ(s.GetInitialMetadata(GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY), + overridden_iam_selector); + } else { + EXPECT_EQ(s.GetInitialMetadata(overridden_fake_md_key), + overridden_fake_md_value); } - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); } -static void test_request_response_with_payload_and_call_creds( - const CoreTestConfiguration& config, bool use_secure_call_creds) { - request_response_with_payload_and_call_creds( - "test_request_response_with_payload_and_call_creds", config, NONE, - use_secure_call_creds); +void TestRequestResponseWithPayloadAndDeletedCallCreds( + CoreEnd2endTest& test, bool use_secure_call_creds) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + grpc_call_credentials* creds; + if (use_secure_call_creds) { + creds = + grpc_google_iam_credentials_create(iam_token, iam_selector, nullptr); + } else { + creds = grpc_md_only_test_credentials_create(fake_md_key, fake_md_value); + } + EXPECT_NE(creds, nullptr); + c.SetCredentials(creds); + c.SetCredentials(nullptr); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello world") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + PrintAuthContext(false, s.GetAuthContext().get()); + PrintAuthContext(true, c.GetAuthContext().get()); + // Cannot set creds on the server call object. + EXPECT_NE(grpc_call_set_credentials(s.c_call(), nullptr), GRPC_CALL_OK); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).SendInitialMetadata({}).RecvMessage(client_message); + test.Expect(102, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(103) + .RecvCloseOnServer(client_close) + .SendMessage("hello you") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + test.Expect(103, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(client_message.payload(), "hello world"); + EXPECT_EQ(server_message.payload(), "hello you"); + EXPECT_EQ(s.GetInitialMetadata(GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY), + absl::nullopt); + EXPECT_EQ(s.GetInitialMetadata(GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY), + absl::nullopt); + EXPECT_EQ(s.GetInitialMetadata(fake_md_key), absl::nullopt); } -static void test_request_response_with_payload_and_overridden_call_creds( - const CoreTestConfiguration& config, bool use_secure_call_creds) { - request_response_with_payload_and_call_creds( - "test_request_response_with_payload_and_overridden_call_creds", config, - OVERRIDE, use_secure_call_creds); +TEST_P(PerCallCredsOnInsecureTest, RequestWithServerRejectingClientCreds) { + InitClient(ChannelArgs()); + InitServer(ChannelArgs().Set(FAIL_AUTH_CHECK_SERVER_ARG_NAME, true)); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(10)).Create(); + auto* creds = + grpc_md_only_test_credentials_create(fake_md_key, fake_md_value); + EXPECT_NE(creds, nullptr); + c.SetCredentials(creds); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello world") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNAUTHENTICATED); } -static void test_request_response_with_payload_and_deleted_call_creds( - const CoreTestConfiguration& config, bool use_secure_call_creds) { - request_response_with_payload_and_call_creds( - "test_request_response_with_payload_and_deleted_call_creds", config, - DESTROY, use_secure_call_creds); +TEST_P(PerCallCredsTest, RequestResponseWithPayloadAndCallCreds) { + TestRequestResponseWithPayloadAndCallCreds(*this, true); } -static void test_request_response_with_payload_fail_to_send_call_creds( - const CoreTestConfiguration& config, bool use_secure_call_creds) { - request_response_with_payload_and_call_creds( - "test_request_response_with_payload_fail_to_send_call_creds", config, - FAIL, use_secure_call_creds); +TEST_P(PerCallCredsTest, RequestResponseWithPayloadAndOverriddenCallCreds) { + TestRequestResponseWithPayloadAndOverriddenCallCreds(*this, true); } -static void test_request_with_server_rejecting_client_creds( - const CoreTestConfiguration& config) { - grpc_op ops[6]; - grpc_op* op; - grpc_call* c; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_call_credentials* creds; - - auto f = begin_test(config, "test_request_with_server_rejecting_client_creds", - false, 1); - grpc_core::CqVerifier cqv(f->cq()); - - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - creds = grpc_md_only_test_credentials_create(fake_md_key, fake_md_value); - GPR_ASSERT(creds != nullptr); - GPR_ASSERT(grpc_call_set_credentials(c, creds) == GRPC_CALL_OK); - grpc_call_credentials_release(creds); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); +TEST_P(PerCallCredsTest, RequestResponseWithPayloadAndDeletedCallCreds) { + TestRequestResponseWithPayloadAndDeletedCallCreds(*this, true); +} - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(error == GRPC_CALL_OK); +TEST_P(PerCallCredsTest, RequestResponseWithPayloadAndInsecureCallCreds) { + TestRequestResponseWithPayloadAndCallCreds(*this, false); +} - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); +TEST_P(PerCallCredsTest, + RequestResponseWithPayloadAndOverriddenInsecureCallCreds) { + TestRequestResponseWithPayloadAndOverriddenCallCreds(*this, false); +} - GPR_ASSERT(status == GRPC_STATUS_UNAUTHENTICATED); +TEST_P(PerCallCredsTest, + RequestResponseWithPayloadAndDeletedInsecureCallCreds) { + TestRequestResponseWithPayloadAndDeletedCallCreds(*this, false); +} - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); +TEST_P(PerCallCredsOnInsecureTest, + RequestResponseWithPayloadAndInsecureCallCreds) { + TestRequestResponseWithPayloadAndCallCreds(*this, false); +} - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload_recv); - grpc_slice_unref(details); +TEST_P(PerCallCredsOnInsecureTest, + RequestResponseWithPayloadAndOverriddenInsecureCallCreds) { + TestRequestResponseWithPayloadAndOverriddenCallCreds(*this, false); +} - grpc_call_unref(c); +TEST_P(PerCallCredsOnInsecureTest, + RequestResponseWithPayloadAndDeletedInsecureCallCreds) { + TestRequestResponseWithPayloadAndDeletedCallCreds(*this, false); } -void call_creds(const CoreTestConfiguration& config) { - // Test fixtures that support call credentials with a minimum security level - // of GRPC_PRIVACY_AND_INTEGRITY - if (config.feature_mask & FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS) { - test_request_response_with_payload_and_call_creds(config, true); - test_request_response_with_payload_and_overridden_call_creds(config, true); - test_request_response_with_payload_and_deleted_call_creds(config, true); - } - // Test that fixtures that support call credentials with a minimum security - // level of GRPC_SECURITY_NONE cannot send call credentials that require - // higher security level - if (config.feature_mask & - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS_LEVEL_INSECURE) { - test_request_response_with_payload_fail_to_send_call_creds(config, true); - } - // Fixtures that support sending call credentials should be able to send call - // credentials of security level GRPC_SECURITY_NONE. - if (config.feature_mask & FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS || - config.feature_mask & - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS_LEVEL_INSECURE) { - test_request_response_with_payload_and_call_creds(config, false); - test_request_response_with_payload_and_overridden_call_creds(config, false); - test_request_response_with_payload_and_deleted_call_creds(config, false); - test_request_with_server_rejecting_client_creds(config); - } +TEST_P(PerCallCredsOnInsecureTest, FailToSendCallCreds) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + grpc_call_credentials* creds; + creds = grpc_google_iam_credentials_create(iam_token, iam_selector, nullptr); + EXPECT_NE(creds, nullptr); + c.SetCredentials(creds); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello world") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + // Expect the call to fail since the channel credentials did not satisfy the + // minimum security level requirements. + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNAUTHENTICATED); } -void call_creds_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/call_host_override.cc b/test/core/end2end/tests/call_host_override.cc index c947d021d727d..7887c6d5cdcd5 100644 --- a/test/core/end2end/tests/call_host_override.cc +++ b/test/core/end2end/tests/call_host_override.cc @@ -16,162 +16,54 @@ // // -#include - -#include -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient( - grpc_core::ChannelArgs::FromC(client_args) - .Set(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, "foo.test.google.fr:1234")); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - return f; -} - -static void test_invoke_simple_request(const CoreTestConfiguration& config) { - auto f = begin_test(config, "test_invoke_simple_request", nullptr, nullptr); - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call( - f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, f->cq(), - grpc_slice_from_static_string("/foo"), - get_host_override_slice("foo.test.google.fr:1234", config), deadline, - nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(error == GRPC_CALL_OK); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(error == GRPC_CALL_OK); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(error == GRPC_CALL_OK); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - validate_host_override_string("foo.test.google.fr:1234", call_details.host, - config); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); -} -void call_host_override(const CoreTestConfiguration& config) { - test_invoke_simple_request(config); +namespace grpc_core { +namespace { + +TEST_P(CoreClientChannelTest, CallHostOverride) { + InitClient(ChannelArgs().Set(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, + "foo.test.google.fr:1234")); + InitServer(ChannelArgs()); + auto c = NewClientCall("/foo") + .Timeout(Duration::Seconds(5)) + .Host("foo.test.google.fr:1234") + .Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_EQ(s.host(), "foo.test.google.fr:1234"); + EXPECT_FALSE(client_close.was_cancelled()); } -void call_host_override_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/cancel_after_accept.cc b/test/core/end2end/tests/cancel_after_accept.cc index 49fb414d0985c..33d9da9dfbe03 100644 --- a/test/core/end2end/tests/cancel_after_accept.cc +++ b/test/core/end2end/tests/cancel_after_accept.cc @@ -16,216 +16,80 @@ // // -#include - -#include #include -#include -#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/end2end/tests/cancel_test_helpers.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - cancellation_mode mode, bool use_service_config, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "%s", std::string(80, '*').c_str()); - gpr_log(GPR_INFO, "Running test: %s/%s/%s/%s", test_name, config.name, - mode.name, use_service_config ? "service_config" : "client_api"); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { // Cancel after accept, no payload -static void test_cancel_after_accept(const CoreTestConfiguration& config, - cancellation_mode mode, - bool use_service_config) { - grpc_op ops[6]; - grpc_op* op; - grpc_call* c; - grpc_call* s; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_slice response_payload_slice = - grpc_slice_from_copied_string("hello you"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - int was_cancelled = 2; - - grpc_channel_args* args = nullptr; - if (use_service_config) { - grpc_arg arg; - arg.type = GRPC_ARG_STRING; - arg.key = const_cast(GRPC_ARG_SERVICE_CONFIG); - arg.value.string = const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" },\n" - " { \"service\": \"unused\" }\n" - " ],\n" - " \"timeout\": \"5s\"\n" - " } ]\n" - "}"); - args = grpc_channel_args_copy_and_add(args, &arg, 1); - } - - auto f = begin_test(config, "cancel_after_accept", mode, use_service_config, - args, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = use_service_config - ? gpr_inf_future(GPR_CLOCK_MONOTONIC) - : grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(2)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c, nullptr)); - - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == mode.expect_status || status == GRPC_STATUS_INTERNAL); - GPR_ASSERT(was_cancelled == 1); - - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - grpc_slice_unref(details); - - grpc_call_unref(c); - grpc_call_unref(s); +void CancelAfterAccept(CoreEnd2endTest& test, + std::unique_ptr cancellation_mode, + Duration timeout) { + auto c = test.NewClientCall("/service/method").Timeout(timeout).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(1024)) + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message); + auto s = test.RequestCall(2); + test.Expect(2, true); + test.Step(); + CoreEnd2endTest::IncomingMessage client_message; + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(3) + .RecvMessage(client_message) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(1024)) + .RecvCloseOnServer(client_close); + cancellation_mode->Apply(c); + test.Expect(1, true); + test.Expect(3, true); + test.Step(); + EXPECT_THAT(server_status.status(), + ::testing::AnyOf(cancellation_mode->ExpectedStatus(), + GRPC_STATUS_INTERNAL)); + EXPECT_TRUE(client_close.was_cancelled()); +} - if (args != nullptr) { - grpc_core::ExecCtx exec_ctx; - grpc_channel_args_destroy(args); - } +TEST_P(CoreEnd2endTest, CancelAfterAccept) { + CancelAfterAccept(*this, std::make_unique(), + Duration::Seconds(5)); } -void cancel_after_accept(const CoreTestConfiguration& config) { - unsigned i; +TEST_P(CoreDeadlineTest, DeadlineAfterAccept) { + CancelAfterAccept(*this, std::make_unique(), + Duration::Seconds(5)); +} - for (i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); i++) { - if (config.feature_mask & FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES && - cancellation_modes[i].expect_status == GRPC_STATUS_DEADLINE_EXCEEDED) { - continue; - } - test_cancel_after_accept(config, cancellation_modes[i], - false /* use_service_config */); - if (config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL && - cancellation_modes[i].expect_status == GRPC_STATUS_DEADLINE_EXCEEDED) { - test_cancel_after_accept(config, cancellation_modes[i], - true /* use_service_config */); - } - } +TEST_P(CoreClientChannelTest, DeadlineAfterAcceptWithServiceConfig) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" },\n" + " { \"service\": \"unused\" }\n" + " ],\n" + " \"timeout\": \"5s\"\n" + " } ]\n" + "}")); + CancelAfterAccept(*this, std::make_unique(), + Duration::Infinity()); } -void cancel_after_accept_pre_init(void) {} +} // namespace grpc_core diff --git a/test/core/end2end/tests/cancel_after_client_done.cc b/test/core/end2end/tests/cancel_after_client_done.cc index a1c2fb7dfa9f6..244a4e80f958c 100644 --- a/test/core/end2end/tests/cancel_after_client_done.cc +++ b/test/core/end2end/tests/cancel_after_client_done.cc @@ -16,183 +16,62 @@ // // -#include - -#include #include -#include -#include -#include -#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/end2end/tests/cancel_test_helpers.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - cancellation_mode mode, grpc_channel_args* client_args, - grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s/%s", test_name, config.name, - mode.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { // Cancel after accept with a writes closed, no payload -static void test_cancel_after_accept_and_writes_closed( - const CoreTestConfiguration& config, cancellation_mode mode) { - grpc_op ops[6]; - grpc_op* op; - grpc_call* c; - grpc_call* s; - auto f = begin_test(config, "test_cancel_after_accept_and_writes_closed", - mode, nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_slice response_payload_slice = - grpc_slice_from_copied_string("hello you"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(2)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c, nullptr)); - - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == mode.expect_status || status == GRPC_STATUS_INTERNAL); - GPR_ASSERT(was_cancelled == 1); - - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - grpc_slice_unref(details); - - grpc_call_unref(c); - grpc_call_unref(s); +void CancelAfterClientDone( + CoreEnd2endTest& test, + std::unique_ptr cancellation_mode) { + auto c = test.NewClientCall("/service/method") + .Timeout(Duration::Seconds(5)) + .Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(1024)) + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .SendCloseFromClient(); + auto s = test.RequestCall(2); + test.Expect(2, true); + test.Step(); + CoreEnd2endTest::IncomingMessage client_message; + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(3) + .RecvMessage(client_message) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(1024)) + .RecvCloseOnServer(client_close); + cancellation_mode->Apply(c); + test.Expect(1, true); + test.Expect(3, true); + test.Step(); + EXPECT_THAT(server_status.status(), + ::testing::AnyOf(cancellation_mode->ExpectedStatus(), + GRPC_STATUS_INTERNAL)); + EXPECT_TRUE(client_close.was_cancelled()); } -void cancel_after_client_done(const CoreTestConfiguration& config) { - unsigned i; +TEST_P(CoreEnd2endTest, CancelAfterClientDone) { + CancelAfterClientDone(*this, std::make_unique()); +} - for (i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); i++) { - if (config.feature_mask & FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES && - cancellation_modes[i].expect_status == GRPC_STATUS_DEADLINE_EXCEEDED) { - continue; - } - test_cancel_after_accept_and_writes_closed(config, cancellation_modes[i]); - } +TEST_P(CoreDeadlineTest, DeadlineAfterClientDone) { + CancelAfterClientDone(*this, std::make_unique()); } -void cancel_after_client_done_pre_init(void) {} +} // namespace grpc_core diff --git a/test/core/end2end/tests/cancel_after_invoke.cc b/test/core/end2end/tests/cancel_after_invoke.cc index 7def37274485e..d72ff77b1bc43 100644 --- a/test/core/end2end/tests/cancel_after_invoke.cc +++ b/test/core/end2end/tests/cancel_after_invoke.cc @@ -16,143 +16,128 @@ // // -#include -#include -#include - -#include #include -#include -#include -#include -#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/end2end/tests/cancel_test_helpers.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - cancellation_mode mode, size_t test_ops, grpc_channel_args* client_args, - grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s/%s [%" PRIdPTR " ops]", test_name, - config.name, mode.name, test_ops); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; + +namespace grpc_core { + +void CancelAfterInvoke6(CoreEnd2endTest& test, + std::unique_ptr mode) { + auto c = test.NewClientCall("/service/method") + .Timeout(Duration::Seconds(5)) + .Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .RecvInitialMetadata(server_initial_metadata) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(1024)) + .SendCloseFromClient() + .RecvMessage(server_message); + mode->Apply(c); + test.Expect(1, true); + test.Step(); + EXPECT_THAT(server_status.status(), + ::testing::AnyOf(mode->ExpectedStatus(), GRPC_STATUS_INTERNAL)); +} + +void CancelAfterInvoke5(CoreEnd2endTest& test, + std::unique_ptr mode) { + auto c = test.NewClientCall("/service/method") + .Timeout(Duration::Seconds(5)) + .Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .RecvInitialMetadata(server_initial_metadata) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(1024)) + .SendCloseFromClient(); + mode->Apply(c); + test.Expect(1, true); + test.Step(); + EXPECT_THAT(server_status.status(), + ::testing::AnyOf(mode->ExpectedStatus(), GRPC_STATUS_INTERNAL)); +} + +void CancelAfterInvoke4(CoreEnd2endTest& test, + std::unique_ptr mode) { + auto c = test.NewClientCall("/service/method") + .Timeout(Duration::Seconds(5)) + .Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .RecvInitialMetadata(server_initial_metadata) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(1024)); + mode->Apply(c); + test.Expect(1, true); + test.Step(); + EXPECT_THAT(server_status.status(), + ::testing::AnyOf(mode->ExpectedStatus(), GRPC_STATUS_INTERNAL)); +} + +void CancelAfterInvoke3(CoreEnd2endTest& test, + std::unique_ptr mode) { + auto c = test.NewClientCall("/service/method") + .Timeout(Duration::Seconds(5)) + .Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .RecvInitialMetadata(server_initial_metadata) + .SendInitialMetadata({}); + mode->Apply(c); + test.Expect(1, true); + test.Step(); + EXPECT_THAT(server_status.status(), + ::testing::AnyOf(mode->ExpectedStatus(), GRPC_STATUS_INTERNAL)); +} + +TEST_P(CoreEnd2endTest, CancelAfterInvoke6) { + CancelAfterInvoke6(*this, std::make_unique()); +} + +TEST_P(CoreEnd2endTest, CancelAfterInvoke5) { + CancelAfterInvoke5(*this, std::make_unique()); +} + +TEST_P(CoreEnd2endTest, CancelAfterInvoke4) { + CancelAfterInvoke4(*this, std::make_unique()); +} + +TEST_P(CoreEnd2endTest, CancelAfterInvoke3) { + CancelAfterInvoke3(*this, std::make_unique()); +} + +TEST_P(CoreDeadlineTest, DeadlineAfterInvoke6) { + CancelAfterInvoke6(*this, std::make_unique()); +} + +TEST_P(CoreDeadlineTest, DeadlineAfterInvoke5) { + CancelAfterInvoke5(*this, std::make_unique()); } -// Cancel after invoke, no payload -static void test_cancel_after_invoke(const CoreTestConfiguration& config, - cancellation_mode mode, size_t test_ops) { - grpc_op ops[6]; - grpc_op* op; - grpc_call* c; - auto f = begin_test(config, "test_cancel_after_invoke", mode, test_ops, - nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, test_ops, grpc_core::CqVerifier::tag(1), - nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c, nullptr)); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == mode.expect_status || status == GRPC_STATUS_INTERNAL); - - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload_recv); - grpc_slice_unref(details); - - grpc_call_unref(c); +TEST_P(CoreDeadlineTest, DeadlineAfterInvoke4) { + CancelAfterInvoke4(*this, std::make_unique()); } -void cancel_after_invoke(const CoreTestConfiguration& config) { - unsigned i, j; - - for (j = 3; j < 6; j++) { - for (i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); i++) { - if (config.feature_mask & FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES && - cancellation_modes[i].expect_status == - GRPC_STATUS_DEADLINE_EXCEEDED) { - continue; - } - test_cancel_after_invoke(config, cancellation_modes[i], j); - } - } +TEST_P(CoreDeadlineTest, DeadlineAfterInvoke3) { + CancelAfterInvoke3(*this, std::make_unique()); } -void cancel_after_invoke_pre_init(void) {} +} // namespace grpc_core diff --git a/test/core/end2end/tests/cancel_after_round_trip.cc b/test/core/end2end/tests/cancel_after_round_trip.cc index 0bd4e94eb0d1b..a353d2fa47913 100644 --- a/test/core/end2end/tests/cancel_after_round_trip.cc +++ b/test/core/end2end/tests/cancel_after_round_trip.cc @@ -16,247 +16,83 @@ // // -#include - -#include #include -#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/end2end/tests/cancel_test_helpers.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - cancellation_mode mode, bool use_service_config, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s/%s/%s", test_name, config.name, - mode.name, use_service_config ? "service_config" : "client_api"); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; +namespace grpc_core { +namespace { + +void CancelAfterRoundTrip(CoreEnd2endTest& test, + std::unique_ptr mode, + Duration timeout) { + auto c = test.NewClientCall("/service/method").Timeout(timeout).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(100)) + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102) + .RecvMessage(client_message) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(100)); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + CoreEnd2endTest::IncomingMessage server_message_2; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(2).RecvMessage(server_message_2).RecvStatusOnClient(server_status); + mode->Apply(c); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(103).RecvCloseOnServer(client_close).SendMessage(RandomSlice(100)); + test.Expect(2, true); + test.Expect(103, true); + test.Step(); + EXPECT_THAT(server_status.status(), + ::testing::AnyOf(mode->ExpectedStatus(), GRPC_STATUS_INTERNAL)); + EXPECT_TRUE(client_close.was_cancelled()); } -// Cancel after accept, no payload -static void test_cancel_after_round_trip(const CoreTestConfiguration& config, - cancellation_mode mode, - bool use_service_config) { - grpc_op ops[6]; - grpc_op* op; - grpc_call* c; - grpc_call* s; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_slice response_payload_slice = - grpc_slice_from_copied_string("hello you"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload1 = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* response_payload2 = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - int was_cancelled = 2; - - grpc_channel_args* args = nullptr; - if (use_service_config) { - grpc_arg arg; - arg.type = GRPC_ARG_STRING; - arg.key = const_cast(GRPC_ARG_SERVICE_CONFIG); - arg.value.string = const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"timeout\": \"5s\"\n" - " } ]\n" - "}"); - args = grpc_channel_args_copy_and_add(args, &arg, 1); - } - - auto f = begin_test(config, "cancel_after_round_trip", mode, - use_service_config, args, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = use_service_config - ? gpr_inf_future(GPR_CLOCK_MONOTONIC) - : grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload1; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - request_payload_recv = nullptr; - response_payload_recv = nullptr; - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c, nullptr)); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload2; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Verify(); - - GPR_ASSERT(status == mode.expect_status || status == GRPC_STATUS_INTERNAL); - GPR_ASSERT(was_cancelled == 1); - - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload1); - grpc_byte_buffer_destroy(response_payload2); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - grpc_slice_unref(details); - - grpc_call_unref(c); - grpc_call_unref(s); - - if (args != nullptr) { - grpc_core::ExecCtx exec_ctx; - grpc_channel_args_destroy(args); - } +TEST_P(CoreEnd2endTest, CancelAfterRoundTrip) { + CancelAfterRoundTrip(*this, std::make_unique(), + Duration::Seconds(5)); } -void cancel_after_round_trip(const CoreTestConfiguration& config) { - unsigned i; +TEST_P(CoreDeadlineTest, DeadlineAfterRoundTrip) { + CancelAfterRoundTrip(*this, std::make_unique(), + Duration::Seconds(5)); +} - for (i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); i++) { - if (config.feature_mask & FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES && - cancellation_modes[i].expect_status == GRPC_STATUS_DEADLINE_EXCEEDED) { - continue; - } - test_cancel_after_round_trip(config, cancellation_modes[i], - false /* use_service_config */); - if (config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL && - cancellation_modes[i].expect_status == GRPC_STATUS_DEADLINE_EXCEEDED) { - test_cancel_after_round_trip(config, cancellation_modes[i], - true /* use_service_config */); - } - } +TEST_P(CoreClientChannelTest, DeadlineAfterAcceptWithServiceConfig) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"timeout\": \"5s\"\n" + " } ]\n" + "}")); + CancelAfterRoundTrip(*this, std::make_unique(), + Duration::Infinity()); } -void cancel_after_round_trip_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/cancel_before_invoke.cc b/test/core/end2end/tests/cancel_before_invoke.cc index 2ce710f26c7d4..4dd5e0feeebf3 100644 --- a/test/core/end2end/tests/cancel_before_invoke.cc +++ b/test/core/end2end/tests/cancel_before_invoke.cc @@ -16,136 +16,93 @@ // // -#include -#include -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, size_t num_ops, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s [%" PRIdPTR " ops]", test_name, - config.name, num_ops); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; +namespace grpc_core { + +TEST_P(CoreEnd2endTest, CancelBeforeInvoke6) { + auto c = NewClientCall("/service/method").Create(); + c.Cancel(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(1024)) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message); + Expect(1, AnyStatus()); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_CANCELLED); } -// Cancel before invoke -static void test_cancel_before_invoke(const CoreTestConfiguration& config, - size_t test_ops) { - grpc_op ops[6]; - grpc_op* op; - grpc_call* c; - auto f = - begin_test(config, "cancel_before_invoke", test_ops, nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - GPR_ASSERT(GRPC_CALL_OK == grpc_call_cancel(c, nullptr)); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, test_ops, grpc_core::CqVerifier::tag(1), - nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // Filter based stack tracks this as a failed op, promise based stack tracks - // it as a successful one with a failed request. The latter probably makes - // more sense, but since we can't tell from outside which case we have we - // accept either. - cqv.Expect(grpc_core::CqVerifier::tag(1), grpc_core::CqVerifier::AnyStatus()); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_CANCELLED); +TEST_P(CoreEnd2endTest, CancelBeforeInvoke5) { + auto c = NewClientCall("/service/method").Create(); + c.Cancel(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(1024)) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata); + Expect(1, AnyStatus()); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_CANCELLED); +} - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); +TEST_P(CoreEnd2endTest, CancelBeforeInvoke4) { + auto c = NewClientCall("/service/method").Create(); + c.Cancel(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(1024)) + .SendCloseFromClient(); + Expect(1, AnyStatus()); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_CANCELLED); +} - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload_recv); - grpc_slice_unref(details); +TEST_P(CoreEnd2endTest, CancelBeforeInvoke3) { + auto c = NewClientCall("/service/method").Create(); + c.Cancel(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .SendInitialMetadata({}) + .SendMessage(RandomSlice(1024)); + Expect(1, AnyStatus()); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_CANCELLED); +} - grpc_call_unref(c); +TEST_P(CoreEnd2endTest, CancelBeforeInvoke2) { + auto c = NewClientCall("/service/method").Create(); + c.Cancel(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1).RecvStatusOnClient(server_status).SendInitialMetadata({}); + Expect(1, AnyStatus()); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_CANCELLED); } -void cancel_before_invoke(const CoreTestConfiguration& config) { - size_t i; - for (i = 1; i <= 6; i++) { - test_cancel_before_invoke(config, i); - } +TEST_P(CoreEnd2endTest, CancelBeforeInvoke1) { + auto c = NewClientCall("/service/method").Create(); + c.Cancel(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1).RecvStatusOnClient(server_status); + Expect(1, AnyStatus()); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_CANCELLED); } -void cancel_before_invoke_pre_init(void) {} +} // namespace grpc_core diff --git a/test/core/end2end/tests/cancel_in_a_vacuum.cc b/test/core/end2end/tests/cancel_in_a_vacuum.cc index 04ed84a320b4c..135475068e795 100644 --- a/test/core/end2end/tests/cancel_in_a_vacuum.cc +++ b/test/core/end2end/tests/cancel_in_a_vacuum.cc @@ -16,58 +16,18 @@ // // -#include -#include +#include "gtest/gtest.h" -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/end2end/tests/cancel_test_helpers.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - cancellation_mode mode, grpc_channel_args* client_args, - grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s/%s", test_name, config.name, - mode.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} -// Cancel and do nothing -static void test_cancel_in_a_vacuum(const CoreTestConfiguration& config, - cancellation_mode mode) { - grpc_call* c; - auto f = - begin_test(config, "test_cancel_in_a_vacuum", mode, nullptr, nullptr); +namespace grpc_core { - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c, nullptr)); - - grpc_call_unref(c); +TEST_P(CoreEnd2endTest, CancelInAVacuum) { + NewClientCall("/service/method").Create().Cancel(); } -void cancel_in_a_vacuum(const CoreTestConfiguration& config) { - unsigned i; - - for (i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); i++) { - test_cancel_in_a_vacuum(config, cancellation_modes[i]); - } +TEST_P(CoreDeadlineTest, DeadlineInAVacuum) { + NewClientCall("/service/method").Create(); } -void cancel_in_a_vacuum_pre_init(void) {} +} // namespace grpc_core diff --git a/test/core/end2end/tests/cancel_test_helpers.h b/test/core/end2end/tests/cancel_test_helpers.h index 82324a26d14d9..0411fb1905fa3 100644 --- a/test/core/end2end/tests/cancel_test_helpers.h +++ b/test/core/end2end/tests/cancel_test_helpers.h @@ -19,25 +19,31 @@ #ifndef GRPC_TEST_CORE_END2END_TESTS_CANCEL_TEST_HELPERS_H #define GRPC_TEST_CORE_END2END_TESTS_CANCEL_TEST_HELPERS_H -#include #include -typedef struct { - const char* name; - grpc_call_error (*initiate_cancel)(grpc_call* call, void* reserved); - grpc_status_code expect_status; - const char* expect_details; -} cancellation_mode; +#include "test/core/end2end/end2end_tests.h" -static grpc_call_error wait_for_deadline(grpc_call* /*call*/, void* reserved) { - (void)reserved; - return GRPC_CALL_OK; -} +namespace grpc_core { +class CancellationMode { + public: + virtual void Apply(CoreEnd2endTest::Call& call) = 0; + virtual grpc_status_code ExpectedStatus() = 0; + virtual ~CancellationMode() = default; +}; + +class CancelCancellationMode : public CancellationMode { + public: + void Apply(CoreEnd2endTest::Call& call) override { call.Cancel(); } + grpc_status_code ExpectedStatus() override { return GRPC_STATUS_CANCELLED; } +}; -static const cancellation_mode cancellation_modes[] = { - {"cancel", grpc_call_cancel, GRPC_STATUS_CANCELLED, "CANCELLED"}, - {"deadline", wait_for_deadline, GRPC_STATUS_DEADLINE_EXCEEDED, - "Deadline Exceeded"}, +class DeadlineCancellationMode : public CancellationMode { + public: + void Apply(CoreEnd2endTest::Call&) override {} + grpc_status_code ExpectedStatus() override { + return GRPC_STATUS_DEADLINE_EXCEEDED; + } }; +} // namespace grpc_core #endif // GRPC_TEST_CORE_END2END_TESTS_CANCEL_TEST_HELPERS_H diff --git a/test/core/end2end/tests/cancel_with_status.cc b/test/core/end2end/tests/cancel_with_status.cc index 7ea7d156bc23a..fa97a1bb20ac3 100644 --- a/test/core/end2end/tests/cancel_with_status.cc +++ b/test/core/end2end/tests/cancel_with_status.cc @@ -16,122 +16,89 @@ // // -#include -#include -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include #include #include -#include #include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, size_t num_ops, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s [%" PRIdPTR " ops]", test_name, - config.name, num_ops); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static void simple_request_body(const CoreTestConfiguration& /*config*/, - CoreTestFixture* f, size_t num_ops) { - grpc_call* c; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - - gpr_log(GPR_DEBUG, "test with %" PRIuPTR " ops", num_ops); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - GPR_ASSERT(num_ops <= (size_t)(op - ops)); - error = grpc_call_start_batch(c, ops, num_ops, grpc_core::CqVerifier::tag(1), - nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); +namespace grpc_core { +namespace { +TEST_P(CoreEnd2endTest, CancelWithStatus1) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1).RecvStatusOnClient(server_status); char* dynamic_string = gpr_strdup("xyz"); - grpc_call_cancel_with_status(c, GRPC_STATUS_UNIMPLEMENTED, dynamic_string, - nullptr); + c.CancelWithStatus(GRPC_STATUS_UNIMPLEMENTED, dynamic_string); // The API of \a description allows for it to be a dynamic/non-const // string, test this guarantee. gpr_free(dynamic_string); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); +} - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - - grpc_call_unref(c); +TEST_P(CoreEnd2endTest, CancelWithStatus2) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .RecvInitialMetadata(server_initial_metadata); + char* dynamic_string = gpr_strdup("xyz"); + c.CancelWithStatus(GRPC_STATUS_UNIMPLEMENTED, dynamic_string); + // The API of \a description allows for it to be a dynamic/non-const + // string, test this guarantee. + gpr_free(dynamic_string); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); } -static void test_invoke_simple_request(const CoreTestConfiguration& config, - size_t num_ops) { - auto f = begin_test(config, "test_invoke_simple_request", num_ops, nullptr, - nullptr); - simple_request_body(config, f.get(), num_ops); +TEST_P(CoreEnd2endTest, CancelWithStatus3) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .RecvInitialMetadata(server_initial_metadata) + .SendInitialMetadata({}); + char* dynamic_string = gpr_strdup("xyz"); + c.CancelWithStatus(GRPC_STATUS_UNIMPLEMENTED, dynamic_string); + // The API of \a description allows for it to be a dynamic/non-const + // string, test this guarantee. + gpr_free(dynamic_string); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); } -void cancel_with_status(const CoreTestConfiguration& config) { - size_t i; - for (i = 1; i <= 4; i++) { - test_invoke_simple_request(config, i); - } +TEST_P(CoreEnd2endTest, CancelWithStatus4) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .RecvInitialMetadata(server_initial_metadata) + .SendInitialMetadata({}) + .SendCloseFromClient(); + char* dynamic_string = gpr_strdup("xyz"); + c.CancelWithStatus(GRPC_STATUS_UNIMPLEMENTED, dynamic_string); + // The API of \a description allows for it to be a dynamic/non-const + // string, test this guarantee. + gpr_free(dynamic_string); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); } -void cancel_with_status_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/channelz.cc b/test/core/end2end/tests/channelz.cc index 8227832dc27b2..d5e7f6d293865 100644 --- a/test/core/end2end/tests/channelz.cc +++ b/test/core/end2end/tests/channelz.cc @@ -18,252 +18,145 @@ #include "src/core/lib/channel/channelz.h" -#include - -#include -#include #include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/surface/channel.h" #include "src/core/lib/surface/server.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; +using testing::HasSubstr; +using testing::Not; + +namespace grpc_core { +namespace { + +void RunOneRequest(CoreEnd2endTest& test, bool request_is_success) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer( + request_is_success ? GRPC_STATUS_OK : GRPC_STATUS_UNIMPLEMENTED, + "xyz", {}) + .RecvCloseOnServer(client_close); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); } -static void run_one_request(const CoreTestConfiguration& /*config*/, - CoreTestFixture* f, bool request_is_success) { - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->data.recv_status_on_client.error_string = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); +TEST_P(CoreEnd2endTest, Channelz) { + auto args = ChannelArgs() + .Set(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE, 0) + .Set(GRPC_ARG_ENABLE_CHANNELZ, true); + InitServer(args); + InitClient(args); - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); + channelz::ChannelNode* channelz_channel = + grpc_channel_get_channelz_node(client()); + ASSERT_NE(channelz_channel, nullptr); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = - request_is_success ? GRPC_STATUS_OK : GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -static void test_channelz(const CoreTestConfiguration& config) { - grpc_arg arg[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE), - 0), - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ENABLE_CHANNELZ), true)}; - grpc_channel_args args = {GPR_ARRAY_SIZE(arg), arg}; - - auto f = begin_test(config, "test_channelz", &args, &args); - grpc_core::channelz::ChannelNode* channelz_channel = - grpc_channel_get_channelz_node(f->client()); - GPR_ASSERT(channelz_channel != nullptr); - - grpc_core::channelz::ServerNode* channelz_server = - grpc_core::Server::FromC(f->server())->channelz_node(); - GPR_ASSERT(channelz_server != nullptr); + channelz::ServerNode* channelz_server = + Server::FromC(server())->channelz_node(); + ASSERT_NE(channelz_server, nullptr); std::string json = channelz_channel->RenderJsonString(); // nothing is present yet - GPR_ASSERT(json.find("\"callsStarted\"") == json.npos); - GPR_ASSERT(json.find("\"callsFailed\"") == json.npos); - GPR_ASSERT(json.find("\"callsSucceeded\"") == json.npos); + EXPECT_THAT(json, Not(HasSubstr("\"callsStarted\""))); + EXPECT_THAT(json, Not(HasSubstr("\"callsFailed\""))); + EXPECT_THAT(json, Not(HasSubstr("\"callsSucceeded\""))); // one successful request - run_one_request(config, f.get(), true); + RunOneRequest(*this, true); json = channelz_channel->RenderJsonString(); - GPR_ASSERT(json.find("\"callsStarted\":\"1\"") != json.npos); - GPR_ASSERT(json.find("\"callsSucceeded\":\"1\"") != json.npos); + EXPECT_THAT(json, HasSubstr("\"callsStarted\":\"1\"")); + EXPECT_THAT(json, HasSubstr("\"callsSucceeded\":\"1\"")); // one failed request - run_one_request(config, f.get(), false); + RunOneRequest(*this, false); json = channelz_channel->RenderJsonString(); - GPR_ASSERT(json.find("\"callsStarted\":\"2\"") != json.npos); - GPR_ASSERT(json.find("\"callsFailed\":\"1\"") != json.npos); - GPR_ASSERT(json.find("\"callsSucceeded\":\"1\"") != json.npos); + EXPECT_THAT(json, HasSubstr("\"callsStarted\":\"2\"")); + EXPECT_THAT(json, HasSubstr("\"callsFailed\":\"1\"")); + EXPECT_THAT(json, HasSubstr("\"callsSucceeded\":\"1\"")); // channel tracing is not enabled, so these should not be preset. - GPR_ASSERT(json.find("\"trace\"") == json.npos); - GPR_ASSERT(json.find("\"description\":\"Channel created\"") == json.npos); - GPR_ASSERT(json.find("\"severity\":\"CT_INFO\"") == json.npos); + EXPECT_THAT(json, Not(HasSubstr("\"trace\""))); + EXPECT_THAT(json, Not(HasSubstr("\"description\":\"Channel created\""))); + EXPECT_THAT(json, Not(HasSubstr("\"severity\":\"CT_INFO\""))); json = channelz_server->RenderJsonString(); - GPR_ASSERT(json.find("\"callsStarted\":\"2\"") != json.npos); - GPR_ASSERT(json.find("\"callsFailed\":\"1\"") != json.npos); - GPR_ASSERT(json.find("\"callsSucceeded\":\"1\"") != json.npos); + EXPECT_THAT(json, HasSubstr("\"callsStarted\":\"2\"")); + EXPECT_THAT(json, HasSubstr("\"callsFailed\":\"1\"")); + EXPECT_THAT(json, HasSubstr("\"callsSucceeded\":\"1\"")); // channel tracing is not enabled, so these should not be preset. - GPR_ASSERT(json.find("\"trace\"") == json.npos); - GPR_ASSERT(json.find("\"description\":\"Channel created\"") == json.npos); - GPR_ASSERT(json.find("\"severity\":\"CT_INFO\"") == json.npos); + EXPECT_THAT(json, Not(HasSubstr("\"trace\""))); + EXPECT_THAT(json, Not(HasSubstr("\"description\":\"Channel created\""))); + EXPECT_THAT(json, Not(HasSubstr("\"severity\":\"CT_INFO\""))); json = channelz_server->RenderServerSockets(0, 100); - GPR_ASSERT(json.find("\"end\":true") != json.npos); + EXPECT_THAT(json, HasSubstr("\"end\":true")); } -static void test_channelz_with_channel_trace( - const CoreTestConfiguration& config) { - grpc_arg arg[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE), - 1024 * 1024), - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ENABLE_CHANNELZ), true)}; - grpc_channel_args args = {GPR_ARRAY_SIZE(arg), arg}; +TEST_P(CoreEnd2endTest, ChannelzWithChannelTrace) { + auto args = + ChannelArgs() + .Set(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE, 1024 * 1024) + .Set(GRPC_ARG_ENABLE_CHANNELZ, true); + InitServer(args); + InitClient(args); - auto f = begin_test(config, "test_channelz_with_channel_trace", &args, &args); - grpc_core::channelz::ChannelNode* channelz_channel = - grpc_channel_get_channelz_node(f->client()); - GPR_ASSERT(channelz_channel != nullptr); + channelz::ChannelNode* channelz_channel = + grpc_channel_get_channelz_node(client()); + ASSERT_NE(channelz_channel, nullptr); - grpc_core::channelz::ServerNode* channelz_server = - grpc_core::Server::FromC(f->server())->channelz_node(); - GPR_ASSERT(channelz_server != nullptr); + channelz::ServerNode* channelz_server = + Server::FromC(server())->channelz_node(); + ASSERT_NE(channelz_server, nullptr); - run_one_request(config, f.get(), true); + RunOneRequest(*this, true); std::string json = channelz_channel->RenderJsonString(); - GPR_ASSERT(json.find("\"trace\"") != json.npos); - GPR_ASSERT(json.find("\"description\":\"Channel created\"") != json.npos); - GPR_ASSERT(json.find("\"severity\":\"CT_INFO\"") != json.npos); + EXPECT_THAT(json, HasSubstr("\"trace\"")); + EXPECT_THAT(json, HasSubstr("\"description\":\"Channel created\"")); + EXPECT_THAT(json, HasSubstr("\"severity\":\"CT_INFO\"")); json = channelz_server->RenderJsonString(); - GPR_ASSERT(json.find("\"trace\"") != json.npos); - GPR_ASSERT(json.find("\"description\":\"Server created\"") != json.npos); - GPR_ASSERT(json.find("\"severity\":\"CT_INFO\"") != json.npos); -} - -static void test_channelz_disabled(const CoreTestConfiguration& config) { - grpc_arg arg[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE), - 0), - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ENABLE_CHANNELZ), false)}; - grpc_channel_args args = {GPR_ARRAY_SIZE(arg), arg}; - - auto f = begin_test(config, "test_channelz_disabled", &args, &args); - grpc_core::channelz::ChannelNode* channelz_channel = - grpc_channel_get_channelz_node(f->client()); - GPR_ASSERT(channelz_channel == nullptr); - // one successful request - run_one_request(config, f.get(), true); - GPR_ASSERT(channelz_channel == nullptr); + EXPECT_THAT(json, HasSubstr("\"trace\"")); + EXPECT_THAT(json, HasSubstr("\"description\":\"Server created\"")); + EXPECT_THAT(json, HasSubstr("\"severity\":\"CT_INFO\"")); } -void channelz(const CoreTestConfiguration& config) { - test_channelz(config); - test_channelz_with_channel_trace(config); - test_channelz_disabled(config); +TEST_P(CoreEnd2endTest, ChannelzDisabled) { + auto args = ChannelArgs() + .Set(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE, 0) + .Set(GRPC_ARG_ENABLE_CHANNELZ, false); + InitServer(args); + InitClient(args); + channelz::ChannelNode* channelz_channel = + grpc_channel_get_channelz_node(client()); + EXPECT_EQ(channelz_channel, nullptr); + RunOneRequest(*this, true); } -void channelz_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/client_streaming.cc b/test/core/end2end/tests/client_streaming.cc index 705a494cd446d..e2220d54eb175 100644 --- a/test/core/end2end/tests/client_streaming.cc +++ b/test/core/end2end/tests/client_streaming.cc @@ -16,214 +16,70 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Client streaming test where the client sends a bunch of messages and the // server reads them. After reading some messages, the server sends the status. // Client writes fail after that due to the end of stream and the client // subsequently requests and receives the status. -static void test_client_streaming(const CoreTestConfiguration& config, - int messages) { - auto f = begin_test(config, "test_client_streaming", nullptr, nullptr); - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* request_payload = nullptr; - int i; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(100)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(100), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(101), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - +void ClientStreaming(CoreEnd2endTest& test, int messages) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1).SendInitialMetadata({}).RecvInitialMetadata( + server_initial_metadata); + auto s = test.RequestCall(100); + test.Expect(100, true); + test.Step(); + s.NewBatch(101).SendInitialMetadata({}); + test.Expect(101, true); + test.Expect(1, true); + test.Step(); // Client writes bunch of messages and server reads them - for (i = 0; i < messages; i++) { - request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - grpc_byte_buffer_destroy(request_payload); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Verify(); - GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world")); - grpc_byte_buffer_destroy(request_payload_recv); + for (int i = 0; i < messages; i++) { + c.NewBatch(2).SendMessage("hello world"); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).RecvMessage(client_message); + test.Expect(2, true); + test.Expect(102, true); + test.Step(); + EXPECT_EQ(client_message.payload(), "hello world"); } // Server sends status denoting end of stream - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(104), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(104), true); - cqv.Verify(); + s.NewBatch(103).SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}); + test.Expect(103, true); + test.Step(); // Do an empty verify to make sure that the client receives the status - cqv.VerifyEmpty(); + test.Step(); // Client tries sending another message which should fail - request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - grpc_byte_buffer_destroy(request_payload); - cqv.Expect(grpc_core::CqVerifier::tag(103), false); - cqv.Verify(); + c.NewBatch(3).SendMessage("hello world"); + test.Expect(3, false); + test.Step(); // Client sends close and requests status - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Verify(); - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - - grpc_slice_unref(request_payload_slice); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_slice_unref(details); + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(4).SendCloseFromClient().RecvStatusOnClient(server_status); + test.Expect(4, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); } -void client_streaming(const CoreTestConfiguration& config) { - for (int i = 0; i < 10; i++) { - test_client_streaming(config, i); - } -} +TEST_P(CoreEnd2endTest, ClientStreaming0) { ClientStreaming(*this, 0); } +TEST_P(CoreEnd2endTest, ClientStreaming1) { ClientStreaming(*this, 1); } +TEST_P(CoreEnd2endTest, ClientStreaming3) { ClientStreaming(*this, 3); } +TEST_P(CoreEnd2endTest, ClientStreaming10) { ClientStreaming(*this, 10); } +TEST_P(CoreEnd2endTest, ClientStreaming30) { ClientStreaming(*this, 30); } -void client_streaming_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/compressed_payload.cc b/test/core/end2end/tests/compressed_payload.cc index 7a72fe0a34969..1d9a94c80e5cc 100644 --- a/test/core/end2end/tests/compressed_payload.cc +++ b/test/core/end2end/tests/compressed_payload.cc @@ -17,627 +17,414 @@ // #include -#include -#include #include -#include #include +#include -#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "gtest/gtest.h" -#include #include #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" #include "src/core/lib/gprpp/bitset.h" -#include "src/core/lib/surface/call.h" -#include "src/core/lib/surface/call_test_only.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - const grpc_channel_args* client_args, const grpc_channel_args* server_args, - bool decompress_in_core) { - gpr_log(GPR_INFO, "Running test: %s%s/%s", test_name, - decompress_in_core ? "" : "_with_decompression_disabled", - config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} -static void request_for_disabled_algorithm( - const CoreTestConfiguration& config, const char* test_name, - uint32_t send_flags_bitmask, - grpc_compression_algorithm algorithm_to_disable, - grpc_compression_algorithm requested_client_compression_algorithm, - grpc_status_code expected_error, grpc_metadata* client_metadata, - bool decompress_in_core) { - grpc_call* c; - grpc_call* s; - grpc_slice request_payload_slice; - grpc_byte_buffer* request_payload; - grpc_core::ChannelArgs client_args; - grpc_core::ChannelArgs server_args; - - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char str[1024]; - - memset(str, 'x', 1023); - str[1023] = '\0'; - request_payload_slice = grpc_slice_from_copied_string(str); - request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - - client_args = - grpc_core::ChannelArgs().Set(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, - requested_client_compression_algorithm); - server_args = - grpc_core::ChannelArgs() - .Set(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, GRPC_COMPRESS_NONE) - .Set(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET, - grpc_core::BitSet() - .SetAll(true) - .Set(algorithm_to_disable, false) - .ToInt()); - if (!decompress_in_core) { - client_args = - client_args.Set(GRPC_ARG_ENABLE_PER_MESSAGE_DECOMPRESSION, false); - server_args = - server_args.Set(GRPC_ARG_ENABLE_PER_MESSAGE_DECOMPRESSION, false); +namespace grpc_core { +namespace { + +class TestConfigurator { + public: + explicit TestConfigurator(CoreEnd2endTest& test) : test_(test) {} + + TestConfigurator& DisableAlgorithmAtServer( + grpc_compression_algorithm algorithm) { + server_args_ = + server_args_.Set(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET, + BitSet() + .SetAll(true) + .Set(algorithm, false) + .ToInt()); + return *this; } - auto f = begin_test(config, test_name, client_args.ToC().get(), - server_args.ToC().get(), decompress_in_core); - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - if (client_metadata != nullptr) { - op->data.send_initial_metadata.count = 1; - op->data.send_initial_metadata.metadata = client_metadata; - } else { - op->data.send_initial_metadata.count = 0; + TestConfigurator& ClientDefaultAlgorithm( + grpc_compression_algorithm algorithm) { + client_args_ = + client_args_.Set(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, algorithm); + return *this; + } + + TestConfigurator& ServerDefaultAlgorithm( + grpc_compression_algorithm algorithm) { + server_args_ = + server_args_.Set(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, algorithm); + return *this; } - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = send_flags_bitmask; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), false); - - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Verify(); - - // call was cancelled (closed) ... - GPR_ASSERT(was_cancelled != 0); - // with a certain error - GPR_ASSERT(status == expected_error); - - const char* algo_name = nullptr; - GPR_ASSERT(grpc_compression_algorithm_name(algorithm_to_disable, &algo_name)); - std::string expected_details = - absl::StrFormat("Compression algorithm '%s' is disabled.", algo_name); - // and we expect a specific reason for it - GPR_ASSERT(0 == grpc_slice_str_cmp(details, expected_details.c_str())); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_slice_unref(request_payload_slice); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request_payload_recv); -} -static void request_with_payload_template_inner( - const CoreTestConfiguration& config, const char* test_name, - uint32_t client_send_flags_bitmask, - grpc_compression_algorithm default_client_channel_compression_algorithm, - grpc_compression_algorithm default_server_channel_compression_algorithm, - grpc_compression_algorithm expected_algorithm_from_client, - grpc_compression_algorithm expected_algorithm_from_server, - grpc_metadata* client_init_metadata, bool set_server_level, - grpc_compression_level server_compression_level, - bool send_message_before_initial_metadata, bool decompress_in_core) { - grpc_call* c; - grpc_call* s; - grpc_slice request_payload_slice; - grpc_byte_buffer* request_payload = nullptr; - grpc_core::ChannelArgs client_args; - grpc_core::ChannelArgs server_args; - - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload; - grpc_byte_buffer* response_payload_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char request_str[1024]; - char response_str[1024]; - - memset(request_str, 'x', 1023); - request_str[1023] = '\0'; - - memset(response_str, 'y', 1023); - response_str[1023] = '\0'; - - request_payload_slice = grpc_slice_from_copied_string(request_str); - grpc_slice response_payload_slice = - grpc_slice_from_copied_string(response_str); - - client_args = grpc_core::ChannelArgs().Set( - GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, - default_client_channel_compression_algorithm); - server_args = grpc_core::ChannelArgs().Set( - GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, - default_server_channel_compression_algorithm); - if (!decompress_in_core) { - client_args = - client_args.Set(GRPC_ARG_ENABLE_PER_MESSAGE_DECOMPRESSION, false); - server_args = - server_args.Set(GRPC_ARG_ENABLE_PER_MESSAGE_DECOMPRESSION, false); + TestConfigurator& DecompressInApp() { + client_args_ = + client_args_.Set(GRPC_ARG_ENABLE_PER_MESSAGE_DECOMPRESSION, false); + server_args_ = + server_args_.Set(GRPC_ARG_ENABLE_PER_MESSAGE_DECOMPRESSION, false); + return *this; } - auto f = begin_test(config, test_name, client_args.ToC().get(), - server_args.ToC().get(), decompress_in_core); - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - if (send_message_before_initial_metadata) { - request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = client_send_flags_bitmask; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); + + TestConfigurator& ExpectedAlgorithmFromClient( + grpc_compression_algorithm algorithm) { + expected_algorithm_from_client_ = algorithm; + return *this; } - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - if (client_init_metadata != nullptr) { - op->data.send_initial_metadata.count = 1; - op->data.send_initial_metadata.metadata = client_init_metadata; - } else { - op->data.send_initial_metadata.count = 0; + + TestConfigurator& ExpectedAlgorithmFromServer( + grpc_compression_algorithm algorithm) { + expected_algorithm_from_server_ = algorithm; + return *this; } - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(100)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(100), true); - cqv.Verify(); - - GPR_ASSERT(grpc_core::BitCount( - grpc_call_test_only_get_encodings_accepted_by_peer(s)) == - GRPC_COMPRESS_ALGORITHMS_COUNT); - GPR_ASSERT( - grpc_core::GetBit(grpc_call_test_only_get_encodings_accepted_by_peer(s), - GRPC_COMPRESS_NONE) != 0); - GPR_ASSERT( - grpc_core::GetBit(grpc_call_test_only_get_encodings_accepted_by_peer(s), - GRPC_COMPRESS_DEFLATE) != 0); - GPR_ASSERT( - grpc_core::GetBit(grpc_call_test_only_get_encodings_accepted_by_peer(s), - GRPC_COMPRESS_GZIP) != 0); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - if (set_server_level) { - op->data.send_initial_metadata.maybe_compression_level.is_set = true; - op->data.send_initial_metadata.maybe_compression_level.level = - server_compression_level; + + void DisabledAlgorithmTest() { + Init(); + auto c = test_.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + auto s = test_.RequestCall(101); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage(std::string(1024, 'x')) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + test_.Expect(101, true); + test_.Expect(1, true); + test_.Step(); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).SendInitialMetadata({}).RecvMessage(client_message); + CoreEnd2endTest::IncomingCloseOnServer client_close; + test_.Expect(102, false); + s.NewBatch(103).RecvCloseOnServer(client_close); + test_.Expect(103, true); + test_.Step(); + // call was cancelled (closed) ... + EXPECT_NE(client_close.was_cancelled(), 0); + // with a certain error + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + // and we expect a specific reason for it + EXPECT_EQ(server_status.message(), + "Compression algorithm 'gzip' is disabled."); + EXPECT_EQ(s.method(), "/foo"); } - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(101), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - for (int i = 0; i < 2; i++) { - response_payload = grpc_raw_byte_buffer_create(&response_payload_slice, 1); - - if (i > 0 || !send_message_before_initial_metadata) { - request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = client_send_flags_bitmask; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); + + void RequestWithPayload( + uint32_t client_send_flags_bitmask, + std::initializer_list> + client_init_metadata) { + Init(); + auto c = test_.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata(client_init_metadata) + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test_.RequestCall(100); + test_.Expect(100, true); + test_.Step(); + EXPECT_TRUE(s.GetEncodingsAcceptedByPeer().all()); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(101).SendInitialMetadata({}).RecvCloseOnServer(client_close); + for (int i = 0; i < 2; i++) { + c.NewBatch(2).SendMessage(std::string(1024, 'x'), + client_send_flags_bitmask); + test_.Expect(2, true); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).RecvMessage(client_message); + test_.Expect(102, true); + test_.Step(); + EXPECT_EQ(client_message.byte_buffer_type(), GRPC_BB_RAW); + EXPECT_EQ(client_message.payload(), std::string(1024, 'x')); + EXPECT_EQ(client_message.compression(), expected_algorithm_from_client_); + s.NewBatch(103).SendMessage(std::string(1024, 'y')); + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(3).RecvMessage(server_message); + test_.Expect(103, true); + test_.Expect(3, true); + test_.Step(); + EXPECT_EQ(server_message.byte_buffer_type(), GRPC_BB_RAW); + EXPECT_EQ(server_message.payload(), std::string(1024, 'y')); + EXPECT_EQ(server_message.compression(), expected_algorithm_from_server_); } + c.NewBatch(4).SendCloseFromClient(); + s.NewBatch(104).SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + test_.Expect(1, true); + test_.Expect(4, true); + test_.Expect(101, true); + test_.Expect(104, true); + test_.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + } - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - GPR_ASSERT(request_payload_recv->type == GRPC_BB_RAW); - GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, request_str)); - GPR_ASSERT(request_payload_recv->data.raw.compression == - (decompress_in_core ? GRPC_COMPRESS_NONE - : expected_algorithm_from_client)); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Verify(); - - GPR_ASSERT(response_payload_recv->type == GRPC_BB_RAW); - GPR_ASSERT(byte_buffer_eq_string(response_payload_recv, response_str)); - if (server_compression_level > GRPC_COMPRESS_LEVEL_NONE) { - const grpc_compression_algorithm algo_for_server_level = - grpc_call_compression_for_level(s, server_compression_level); - GPR_ASSERT( - response_payload_recv->data.raw.compression == - (decompress_in_core ? GRPC_COMPRESS_NONE : algo_for_server_level)); - } else { - GPR_ASSERT(response_payload_recv->data.raw.compression == - (decompress_in_core ? GRPC_COMPRESS_NONE - : expected_algorithm_from_server)); + void RequestWithSendMessageBeforeInitialMetadata() { + Init(); + auto c = test_.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + c.NewBatch(2).SendMessage(std::string(1024, 'x')); + test_.Expect(2, true); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test_.RequestCall(100); + test_.Expect(100, true); + test_.Step(); + EXPECT_TRUE(s.GetEncodingsAcceptedByPeer().all()); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(101).SendInitialMetadata({}).RecvCloseOnServer(client_close); + for (int i = 0; i < 2; i++) { + if (i > 0) { + c.NewBatch(2).SendMessage(std::string(1024, 'x')); + test_.Expect(2, true); + } + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).RecvMessage(client_message); + test_.Expect(102, true); + test_.Step(); + EXPECT_EQ(client_message.byte_buffer_type(), GRPC_BB_RAW); + EXPECT_EQ(client_message.payload(), std::string(1024, 'x')); + EXPECT_EQ(client_message.compression(), expected_algorithm_from_client_); + s.NewBatch(103).SendMessage(std::string(1024, 'y')); + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(3).RecvMessage(server_message); + test_.Expect(103, true); + test_.Expect(3, true); + test_.Step(); + EXPECT_EQ(server_message.byte_buffer_type(), GRPC_BB_RAW); + EXPECT_EQ(server_message.payload(), std::string(1024, 'y')); + EXPECT_EQ(server_message.compression(), expected_algorithm_from_server_); } + c.NewBatch(4).SendCloseFromClient(); + s.NewBatch(104).SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + test_.Expect(1, true); + test_.Expect(4, true); + test_.Expect(101, true); + test_.Expect(104, true); + test_.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + } - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); + void RequestWithServerLevel(grpc_compression_level server_compression_level) { + Init(); + auto c = test_.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test_.RequestCall(100); + test_.Expect(100, true); + test_.Step(); + EXPECT_TRUE(s.GetEncodingsAcceptedByPeer().all()); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(101) + .SendInitialMetadata({}, 0, server_compression_level) + .RecvCloseOnServer(client_close); + for (int i = 0; i < 2; i++) { + c.NewBatch(2).SendMessage(std::string(1024, 'x')); + test_.Expect(2, true); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).RecvMessage(client_message); + test_.Expect(102, true); + test_.Step(); + EXPECT_EQ(client_message.byte_buffer_type(), GRPC_BB_RAW); + EXPECT_EQ(client_message.payload(), std::string(1024, 'x')); + EXPECT_EQ(client_message.compression(), expected_algorithm_from_client_); + s.NewBatch(103).SendMessage(std::string(1024, 'y')); + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(3).RecvMessage(server_message); + test_.Expect(103, true); + test_.Expect(3, true); + test_.Step(); + EXPECT_EQ(server_message.byte_buffer_type(), GRPC_BB_RAW); + EXPECT_EQ(server_message.payload(), std::string(1024, 'y')); + EXPECT_EQ(server_message.compression(), expected_algorithm_from_server_); + } + c.NewBatch(4).SendCloseFromClient(); + s.NewBatch(104).SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + test_.Expect(1, true); + test_.Expect(4, true); + test_.Expect(101, true); + test_.Expect(104, true); + test_.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); } - grpc_slice_unref(request_payload_slice); - grpc_slice_unref(response_payload_slice); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(4), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(104), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Expect(grpc_core::CqVerifier::tag(4), true); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Expect(grpc_core::CqVerifier::tag(104), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); + + private: + void Init() { + test_.InitClient(client_args_); + test_.InitServer(server_args_); + } + + CoreEnd2endTest& test_; + ChannelArgs client_args_ = ChannelArgs().Set( + GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, GRPC_COMPRESS_NONE); + ChannelArgs server_args_ = ChannelArgs().Set( + GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, GRPC_COMPRESS_NONE); + grpc_compression_algorithm expected_algorithm_from_client_ = + GRPC_COMPRESS_NONE; + grpc_compression_algorithm expected_algorithm_from_server_ = + GRPC_COMPRESS_NONE; +}; + +TEST_P(Http2SingleHopTest, DisabledAlgorithmDecompressInCore) { + TestConfigurator(*this) + .ClientDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .DisableAlgorithmAtServer(GRPC_COMPRESS_GZIP) + .DisabledAlgorithmTest(); +} + +TEST_P(Http2SingleHopTest, DisabledAlgorithmDecompressInApp) { + TestConfigurator(*this) + .ClientDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .DisableAlgorithmAtServer(GRPC_COMPRESS_GZIP) + .DecompressInApp() + .DisabledAlgorithmTest(); +} + +TEST_P(Http2SingleHopTest, + RequestWithExceptionallyUncompressedPayloadDecompressInCore) { + TestConfigurator(*this) + .ClientDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .ServerDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .RequestWithPayload(GRPC_WRITE_NO_COMPRESS, {}); +} + +TEST_P(Http2SingleHopTest, + RequestWithExceptionallyUncompressedPayloadDecompressInApp) { + TestConfigurator(*this) + .ClientDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .ServerDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .DecompressInApp() + .ExpectedAlgorithmFromServer(GRPC_COMPRESS_GZIP) + .RequestWithPayload(GRPC_WRITE_NO_COMPRESS, {}); +} + +TEST_P(Http2SingleHopTest, RequestWithUncompressedPayloadDecompressInCore) { + TestConfigurator(*this).RequestWithPayload(0, {}); +} + +TEST_P(Http2SingleHopTest, RequestWithUncompressedPayloadDecompressInApp) { + TestConfigurator(*this).DecompressInApp().RequestWithPayload(0, {}); +} + +TEST_P(Http2SingleHopTest, RequestWithCompressedPayloadDecompressInCore) { + TestConfigurator(*this) + .ClientDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .ServerDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .RequestWithPayload(0, {}); +} + +TEST_P(Http2SingleHopTest, RequestWithCompressedPayloadDecompressInApp) { + TestConfigurator(*this) + .ClientDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .ServerDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .DecompressInApp() + .ExpectedAlgorithmFromClient(GRPC_COMPRESS_GZIP) + .ExpectedAlgorithmFromServer(GRPC_COMPRESS_GZIP) + .RequestWithPayload(0, {}); +} + +TEST_P(Http2SingleHopTest, + RequestWithSendMessageBeforeInitialMetadataDecompressInCore) { + TestConfigurator(*this) + .ClientDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .ServerDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .RequestWithSendMessageBeforeInitialMetadata(); } -static void request_with_payload_template( - const CoreTestConfiguration& config, const char* test_name, - uint32_t client_send_flags_bitmask, - grpc_compression_algorithm default_client_channel_compression_algorithm, - grpc_compression_algorithm default_server_channel_compression_algorithm, - grpc_compression_algorithm expected_algorithm_from_client, - grpc_compression_algorithm expected_algorithm_from_server, - grpc_metadata* client_init_metadata, bool set_server_level, - grpc_compression_level server_compression_level, - bool send_message_before_initial_metadata) { - request_with_payload_template_inner( - config, test_name, client_send_flags_bitmask, - default_client_channel_compression_algorithm, - default_server_channel_compression_algorithm, - expected_algorithm_from_client, expected_algorithm_from_server, - client_init_metadata, set_server_level, server_compression_level, - send_message_before_initial_metadata, false); - request_with_payload_template_inner( - config, test_name, client_send_flags_bitmask, - default_client_channel_compression_algorithm, - default_server_channel_compression_algorithm, - expected_algorithm_from_client, expected_algorithm_from_server, - client_init_metadata, set_server_level, server_compression_level, - send_message_before_initial_metadata, true); +TEST_P(Http2SingleHopTest, + RequestWithSendMessageBeforeInitialMetadataDecompressInApp) { + TestConfigurator(*this) + .ClientDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .ServerDefaultAlgorithm(GRPC_COMPRESS_GZIP) + .DecompressInApp() + .ExpectedAlgorithmFromClient(GRPC_COMPRESS_GZIP) + .ExpectedAlgorithmFromServer(GRPC_COMPRESS_GZIP) + .RequestWithSendMessageBeforeInitialMetadata(); } -static void test_invoke_request_with_exceptionally_uncompressed_payload( - const CoreTestConfiguration& config) { - request_with_payload_template( - config, "test_invoke_request_with_exceptionally_uncompressed_payload", - GRPC_WRITE_NO_COMPRESS, GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP, - GRPC_COMPRESS_NONE, GRPC_COMPRESS_GZIP, nullptr, false, - /* ignored */ GRPC_COMPRESS_LEVEL_NONE, false); +TEST_P(Http2SingleHopTest, RequestWithServerLevelDecompressInCore) { + TestConfigurator(*this).RequestWithServerLevel(GRPC_COMPRESS_LEVEL_HIGH); } -static void test_invoke_request_with_uncompressed_payload( - const CoreTestConfiguration& config) { - request_with_payload_template( - config, "test_invoke_request_with_uncompressed_payload", 0, - GRPC_COMPRESS_NONE, GRPC_COMPRESS_NONE, GRPC_COMPRESS_NONE, - GRPC_COMPRESS_NONE, nullptr, false, - /* ignored */ GRPC_COMPRESS_LEVEL_NONE, false); +TEST_P(Http2SingleHopTest, RequestWithServerLevelDecompressInApp) { + TestConfigurator(*this) + .DecompressInApp() + .ExpectedAlgorithmFromServer(GRPC_COMPRESS_DEFLATE) + .RequestWithServerLevel(GRPC_COMPRESS_LEVEL_HIGH); } -static void test_invoke_request_with_compressed_payload( - const CoreTestConfiguration& config) { - request_with_payload_template( - config, "test_invoke_request_with_compressed_payload", 0, - GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP, - GRPC_COMPRESS_GZIP, nullptr, false, - /* ignored */ GRPC_COMPRESS_LEVEL_NONE, false); +TEST_P(Http2SingleHopTest, + RequestWithCompressedPayloadMetadataOverrideNoneToGzipDecompressInCore) { + TestConfigurator(*this).RequestWithPayload( + 0, {{"grpc-internal-encoding-request", "gzip"}}); } -static void test_invoke_request_with_send_message_before_initial_metadata( - const CoreTestConfiguration& config) { - request_with_payload_template( - config, "test_invoke_request_with_compressed_payload", 0, - GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP, - GRPC_COMPRESS_GZIP, nullptr, false, - /* ignored */ GRPC_COMPRESS_LEVEL_NONE, true); +TEST_P(Http2SingleHopTest, + RequestWithCompressedPayloadMetadataOverrideNoneToGzipDecompressInApp) { + TestConfigurator(*this) + .DecompressInApp() + .ExpectedAlgorithmFromClient(GRPC_COMPRESS_GZIP) + .RequestWithPayload(0, {{"grpc-internal-encoding-request", "gzip"}}); } -static void test_invoke_request_with_server_level( - const CoreTestConfiguration& config) { - request_with_payload_template( - config, "test_invoke_request_with_server_level", 0, GRPC_COMPRESS_NONE, - GRPC_COMPRESS_NONE, GRPC_COMPRESS_NONE, GRPC_COMPRESS_NONE /* ignored */, - nullptr, true, GRPC_COMPRESS_LEVEL_HIGH, false); +TEST_P( + Http2SingleHopTest, + RequestWithCompressedPayloadMetadataOverrideDeflateToGzipDecompressInCore) { + TestConfigurator(*this) + .ClientDefaultAlgorithm(GRPC_COMPRESS_DEFLATE) + .RequestWithPayload(0, {{"grpc-internal-encoding-request", "gzip"}}); } -static void test_invoke_request_with_compressed_payload_md_override( - const CoreTestConfiguration& config) { - grpc_metadata gzip_compression_override; - grpc_metadata identity_compression_override; - - gzip_compression_override.key = - grpc_slice_from_static_string("grpc-internal-encoding-request"); - gzip_compression_override.value = grpc_slice_from_static_string("gzip"); - memset(&gzip_compression_override.internal_data, 0, - sizeof(gzip_compression_override.internal_data)); - - identity_compression_override.key = - grpc_slice_from_static_string("grpc-internal-encoding-request"); - identity_compression_override.value = - grpc_slice_from_static_string("identity"); - memset(&identity_compression_override.internal_data, 0, - sizeof(identity_compression_override.internal_data)); - - // Channel default NONE (aka IDENTITY), call override to GZIP - request_with_payload_template( - config, "test_invoke_request_with_compressed_payload_md_override_1", 0, - GRPC_COMPRESS_NONE, GRPC_COMPRESS_NONE, GRPC_COMPRESS_GZIP, - GRPC_COMPRESS_NONE, &gzip_compression_override, false, - /*ignored*/ GRPC_COMPRESS_LEVEL_NONE, false); - - // Channel default DEFLATE, call override to GZIP - request_with_payload_template( - config, "test_invoke_request_with_compressed_payload_md_override_2", 0, - GRPC_COMPRESS_DEFLATE, GRPC_COMPRESS_NONE, GRPC_COMPRESS_GZIP, - GRPC_COMPRESS_NONE, &gzip_compression_override, false, - /*ignored*/ GRPC_COMPRESS_LEVEL_NONE, false); - - // Channel default DEFLATE, call override to NONE (aka IDENTITY) - request_with_payload_template( - config, "test_invoke_request_with_compressed_payload_md_override_3", 0, - GRPC_COMPRESS_DEFLATE, GRPC_COMPRESS_NONE, GRPC_COMPRESS_NONE, - GRPC_COMPRESS_NONE, &identity_compression_override, false, - /*ignored*/ GRPC_COMPRESS_LEVEL_NONE, false); +TEST_P( + Http2SingleHopTest, + RequestWithCompressedPayloadMetadataOverrideDeflateToGzipDecompressInApp) { + TestConfigurator(*this) + .ClientDefaultAlgorithm(GRPC_COMPRESS_DEFLATE) + .DecompressInApp() + .ExpectedAlgorithmFromClient(GRPC_COMPRESS_GZIP) + .RequestWithPayload(0, {{"grpc-internal-encoding-request", "gzip"}}); } -static void test_invoke_request_with_disabled_algorithm( - const CoreTestConfiguration& config) { - request_for_disabled_algorithm(config, - "test_invoke_request_with_disabled_algorithm", - 0, GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP, - GRPC_STATUS_UNIMPLEMENTED, nullptr, false); - request_for_disabled_algorithm(config, - "test_invoke_request_with_disabled_algorithm", - 0, GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP, - GRPC_STATUS_UNIMPLEMENTED, nullptr, true); +TEST_P( + Http2SingleHopTest, + RequestWithCompressedPayloadMetadataOverrideDeflateToIdentityDecompressInCore) { + TestConfigurator(*this) + .ClientDefaultAlgorithm(GRPC_COMPRESS_DEFLATE) + .RequestWithPayload(0, {{"grpc-internal-encoding-request", "identity"}}); } -void compressed_payload(const CoreTestConfiguration& config) { - test_invoke_request_with_exceptionally_uncompressed_payload(config); - test_invoke_request_with_uncompressed_payload(config); - test_invoke_request_with_compressed_payload(config); - test_invoke_request_with_send_message_before_initial_metadata(config); - test_invoke_request_with_server_level(config); - test_invoke_request_with_compressed_payload_md_override(config); - test_invoke_request_with_disabled_algorithm(config); +TEST_P( + Http2SingleHopTest, + RequestWithCompressedPayloadMetadataOverrideDeflateToIdentityDecompressInApp) { + TestConfigurator(*this) + .ClientDefaultAlgorithm(GRPC_COMPRESS_DEFLATE) + .DecompressInApp() + .RequestWithPayload(0, {{"grpc-internal-encoding-request", "identity"}}); } -void compressed_payload_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/connectivity.cc b/test/core/end2end/tests/connectivity.cc index 02c89220de234..31f3842fd81ab 100644 --- a/test/core/end2end/tests/connectivity.cc +++ b/test/core/end2end/tests/connectivity.cc @@ -16,213 +16,73 @@ // // -#include -#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/gprpp/time.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -typedef struct { - gpr_event started; - grpc_channel* channel; - grpc_completion_queue* cq; -} child_events; - -struct CallbackContext { - grpc_completion_queue_functor functor; - gpr_event finished; - explicit CallbackContext(void (*cb)(grpc_completion_queue_functor* functor, - int success)) { - functor.functor_run = cb; - functor.inlineable = false; - gpr_event_init(&finished); - } -}; - -static void child_thread(void* arg) { - child_events* ce = static_cast(arg); - grpc_event ev; - gpr_event_set(&ce->started, reinterpret_cast(1)); - gpr_log(GPR_DEBUG, "verifying"); - ev = grpc_completion_queue_next(ce->cq, gpr_inf_future(GPR_CLOCK_MONOTONIC), - nullptr); - GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); - GPR_ASSERT(ev.tag == grpc_core::CqVerifier::tag(1)); - GPR_ASSERT(ev.success == 0); -} - -static void test_connectivity(const CoreTestConfiguration& config) { - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - grpc_connectivity_state state; - grpc_core::CqVerifier cqv(f->cq()); - child_events ce; - - auto client_args = grpc_core::ChannelArgs() - .Set(GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS, 1000) - .Set(GRPC_ARG_MAX_RECONNECT_BACKOFF_MS, 1000) - .Set(GRPC_ARG_MIN_RECONNECT_BACKOFF_MS, 5000); - - f->InitClient(client_args); - - ce.channel = f->client(); - ce.cq = f->cq(); - gpr_event_init(&ce.started); - grpc_core::Thread thd("grpc_connectivity", child_thread, &ce); - thd.Start(); - - gpr_event_wait(&ce.started, gpr_inf_future(GPR_CLOCK_MONOTONIC)); +namespace grpc_core { +namespace { +TEST_P(RetryHttp2Test, ConnectivityWatch) { + InitClient(ChannelArgs() + .Set(GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS, 1000) + .Set(GRPC_ARG_MAX_RECONNECT_BACKOFF_MS, 1000) + .Set(GRPC_ARG_MIN_RECONNECT_BACKOFF_MS, 5000)); // channels should start life in IDLE, and stay there - GPR_ASSERT(grpc_channel_check_connectivity_state(f->client(), 0) == - GRPC_CHANNEL_IDLE); - gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(100)); - GPR_ASSERT(grpc_channel_check_connectivity_state(f->client(), 0) == - GRPC_CHANNEL_IDLE); - + EXPECT_EQ(CheckConnectivityState(false), GRPC_CHANNEL_IDLE); + Step(Duration::Milliseconds(100)); + EXPECT_EQ(CheckConnectivityState(false), GRPC_CHANNEL_IDLE); // start watching for a change - gpr_log(GPR_DEBUG, "watching"); - grpc_channel_watch_connectivity_state(f->client(), GRPC_CHANNEL_IDLE, - gpr_now(GPR_CLOCK_MONOTONIC), f->cq(), - grpc_core::CqVerifier::tag(1)); - - // eventually the child thread completion should trigger - thd.Join(); - + WatchConnectivityState(GRPC_CHANNEL_IDLE, Duration::Milliseconds(500), 1); + Expect(1, false); + Step(Duration::Minutes(1)); // check that we're still in idle, and start connecting - GPR_ASSERT(grpc_channel_check_connectivity_state(f->client(), 1) == - GRPC_CHANNEL_IDLE); + EXPECT_EQ(CheckConnectivityState(true), GRPC_CHANNEL_IDLE); // start watching for a change - grpc_channel_watch_connectivity_state(f->client(), GRPC_CHANNEL_IDLE, - grpc_timeout_seconds_to_deadline(10), - f->cq(), grpc_core::CqVerifier::tag(2)); - + WatchConnectivityState(GRPC_CHANNEL_IDLE, Duration::Seconds(10), 2); // and now the watch should trigger - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - state = grpc_channel_check_connectivity_state(f->client(), 0); - GPR_ASSERT(state == GRPC_CHANNEL_TRANSIENT_FAILURE || - state == GRPC_CHANNEL_CONNECTING); - + Expect(2, true); + Step(); + grpc_connectivity_state state = CheckConnectivityState(false); + EXPECT_THAT(state, ::testing::AnyOf(GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_CHANNEL_CONNECTING)); // quickly followed by a transition to TRANSIENT_FAILURE - grpc_channel_watch_connectivity_state(f->client(), GRPC_CHANNEL_CONNECTING, - grpc_timeout_seconds_to_deadline(10), - f->cq(), grpc_core::CqVerifier::tag(3)); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Verify(); - state = grpc_channel_check_connectivity_state(f->client(), 0); - GPR_ASSERT(state == GRPC_CHANNEL_TRANSIENT_FAILURE || - state == GRPC_CHANNEL_CONNECTING); - - gpr_log(GPR_DEBUG, "*** STARTING SERVER ***"); - + WatchConnectivityState(GRPC_CHANNEL_CONNECTING, Duration::Seconds(10), 3); + Expect(3, true); + Step(); + state = CheckConnectivityState(false); + EXPECT_THAT(state, ::testing::AnyOf(GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_CHANNEL_CONNECTING)); // now let's bring up a server to connect to - f->InitServer(grpc_core::ChannelArgs()); - - gpr_log(GPR_DEBUG, "*** STARTED SERVER ***"); - + InitServer(ChannelArgs()); // we'll go through some set of transitions (some might be missed), until // READY is reached while (state != GRPC_CHANNEL_READY) { - grpc_channel_watch_connectivity_state( - f->client(), state, grpc_timeout_seconds_to_deadline(10), f->cq(), - grpc_core::CqVerifier::tag(4)); - cqv.Expect(grpc_core::CqVerifier::tag(4), true); - cqv.Verify(grpc_core::Duration::Seconds(20)); - state = grpc_channel_check_connectivity_state(f->client(), 0); - GPR_ASSERT(state == GRPC_CHANNEL_READY || - state == GRPC_CHANNEL_CONNECTING || - state == GRPC_CHANNEL_TRANSIENT_FAILURE); + WatchConnectivityState(state, Duration::Seconds(10), 4); + Expect(4, true); + Step(Duration::Seconds(20)); + state = CheckConnectivityState(false); + EXPECT_THAT(state, + ::testing::AnyOf(GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_CHANNEL_CONNECTING, GRPC_CHANNEL_READY)); } - // bring down the server again // we should go immediately to TRANSIENT_FAILURE - gpr_log(GPR_DEBUG, "*** SHUTTING DOWN SERVER ***"); - - grpc_channel_watch_connectivity_state(f->client(), GRPC_CHANNEL_READY, - grpc_timeout_seconds_to_deadline(10), - f->cq(), grpc_core::CqVerifier::tag(5)); - - grpc_server_shutdown_and_notify(f->server(), f->cq(), - grpc_core::CqVerifier::tag(0xdead)); - - cqv.Expect(grpc_core::CqVerifier::tag(5), true); - cqv.Expect(grpc_core::CqVerifier::tag(0xdead), true); - cqv.Verify(); - state = grpc_channel_check_connectivity_state(f->client(), 0); - GPR_ASSERT(state == GRPC_CHANNEL_TRANSIENT_FAILURE || - state == GRPC_CHANNEL_CONNECTING || state == GRPC_CHANNEL_IDLE); -} - -static void cb_watch_connectivity(grpc_completion_queue_functor* functor, - int success) { - CallbackContext* cb_ctx = reinterpret_cast(functor); - - gpr_log(GPR_DEBUG, "cb_watch_connectivity called, verifying"); - - // callback must not have errors - GPR_ASSERT(success != 0); - - gpr_event_set(&cb_ctx->finished, reinterpret_cast(1)); -} - -static void cb_shutdown(grpc_completion_queue_functor* functor, - int /*success*/) { - CallbackContext* cb_ctx = reinterpret_cast(functor); - - gpr_log(GPR_DEBUG, "cb_shutdown called, nothing to do"); - gpr_event_set(&cb_ctx->finished, reinterpret_cast(1)); -} - -static void test_watch_connectivity_cq_callback( - const CoreTestConfiguration& config) { - CallbackContext cb_ctx(cb_watch_connectivity); - CallbackContext cb_shutdown_ctx(cb_shutdown); - grpc_completion_queue* cq; - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - - f->InitClient(grpc_core::ChannelArgs()); - - // start connecting - grpc_channel_check_connectivity_state(f->client(), 1); - - // create the cq callback - cq = grpc_completion_queue_create_for_callback(&cb_shutdown_ctx.functor, - nullptr); - - // start watching for any change, cb is immediately called - // and no dead lock should be raised - grpc_channel_watch_connectivity_state(f->client(), GRPC_CHANNEL_IDLE, - grpc_timeout_seconds_to_deadline(3), cq, - &cb_ctx.functor); - - // we just check that the callback was executed once notifying a connection - // transition - GPR_ASSERT(gpr_event_wait(&cb_ctx.finished, - gpr_inf_future(GPR_CLOCK_MONOTONIC)) != nullptr); - - // shutdown, since shutdown cb might be executed in a background thread - // we actively wait till is executed. - grpc_completion_queue_shutdown(cq); - gpr_event_wait(&cb_shutdown_ctx.finished, - gpr_inf_future(GPR_CLOCK_MONOTONIC)); - grpc_completion_queue_destroy(cq); -} - -void connectivity(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION); - test_connectivity(config); - test_watch_connectivity_cq_callback(config); + WatchConnectivityState(GRPC_CHANNEL_READY, Duration::Seconds(10), 5); + ShutdownServerAndNotify(1000); + Expect(5, true); + Expect(1000, true); + Step(); + state = CheckConnectivityState(false); + EXPECT_THAT(state, + ::testing::AnyOf(GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_CHANNEL_CONNECTING, GRPC_CHANNEL_IDLE)); } -void connectivity_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/default_host.cc b/test/core/end2end/tests/default_host.cc index 28f3c779cd203..bd29654b3d6df 100644 --- a/test/core/end2end/tests/default_host.cc +++ b/test/core/end2end/tests/default_host.cc @@ -16,164 +16,55 @@ // // -#include +#include "absl/types/optional.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include #include -#include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - return f; -} - -static void test_invoke_simple_request(const CoreTestConfiguration& config) { - auto f = begin_test(config, "test_invoke_simple_request", nullptr, nullptr); - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(error == GRPC_CALL_OK); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(error == GRPC_CALL_OK); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(error == GRPC_CALL_OK); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - - if (config.overridden_call_host != nullptr) { - validate_host_override_string(config.overridden_call_host, - call_details.host, config); +using testing::AnyOf; +using testing::StartsWith; + +namespace grpc_core { +namespace { + +TEST_P(CoreClientChannelTest, DefaultHost) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingStatusOnClient server_status; + IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + if (GetParam()->overridden_call_host != nullptr) { + EXPECT_EQ(GetParam()->overridden_call_host, s.host()); } else { - GPR_ASSERT(grpc_slice_buf_start_eq(call_details.host, "localhost", 9) || - grpc_slice_buf_start_eq(call_details.host, "127.0.0.1", 9)); + EXPECT_THAT(s.host(), AnyOf(StartsWith("localhost"), + StartsWith("127.0.0.1"), StartsWith("[::1]"))); } - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void default_host(const CoreTestConfiguration& config) { - test_invoke_simple_request(config); + EXPECT_FALSE(client_close.was_cancelled()); } -void default_host_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/disappearing_server.cc b/test/core/end2end/tests/disappearing_server.cc index f8783c234268a..36847afcffb9d 100644 --- a/test/core/end2end/tests/disappearing_server.cc +++ b/test/core/end2end/tests/disappearing_server.cc @@ -16,117 +16,41 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static void do_request_and_shutdown_server( - const CoreTestConfiguration& /*config*/, CoreTestFixture* f, - grpc_core::CqVerifier& cqv) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - // should be able to shut down the server early - // - and still complete the request - grpc_server_shutdown_and_notify(f->server(), f->cq(), - grpc_core::CqVerifier::tag(1000)); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Expect(grpc_core::CqVerifier::tag(1000), true); - cqv.Verify(); +#ifndef GPR_WINDOWS // b/148110727 for more details +namespace grpc_core { + +static void OneRequestAndShutdownServer(CoreEnd2endTest& test) { + auto c = test.NewClientCall("/service/method") + .Timeout(Duration::Seconds(5)) + .Create(); + CoreEnd2endTest::IncomingMetadata server_initial_md; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_md) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + test.ShutdownServerAndNotify(1000); + CoreEnd2endTest::IncomingCloseOnServer client_closed; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_closed); + test.Expect(102, true); + test.Expect(1, true); + test.Expect(1000, true); + test.Step(); // Please refer https://github.com/grpc/grpc/issues/21221 for additional // details. // TODO(yashykt@) - The following line should be removeable after C-Core @@ -134,47 +58,19 @@ static void do_request_and_shutdown_server( // test remains flaky even after this, an alternative fix would be to send a // request when the server is in the shut down state. // - cqv.VerifyEmpty(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); + test.Step(); - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_closed.was_cancelled()); } -static void disappearing_server_test(const CoreTestConfiguration& config) { - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - grpc_core::CqVerifier cqv(f->cq()); - - gpr_log(GPR_INFO, "Running test: %s/%s", "disappearing_server_test", - config.name); - - f->InitClient(grpc_core::ChannelArgs()); - f->InitServer(grpc_core::ChannelArgs()); - - do_request_and_shutdown_server(config, f.get(), cqv); - - // now destroy and recreate the server - f->InitServer(grpc_core::ChannelArgs()); - - do_request_and_shutdown_server(config, f.get(), cqv); +TEST_P(CoreClientChannelTest, DisappearingServer) { + OneRequestAndShutdownServer(*this); + InitServer(ChannelArgs()); + OneRequestAndShutdownServer(*this); } -void disappearing_server(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION); -#ifndef GPR_WINDOWS // b/148110727 for more details - disappearing_server_test(config); +} // namespace grpc_core #endif // GPR_WINDOWS -} - -void disappearing_server_pre_init(void) {} diff --git a/test/core/end2end/tests/empty_batch.cc b/test/core/end2end/tests/empty_batch.cc index de459b70392d0..046d6d44b7118 100644 --- a/test/core/end2end/tests/empty_batch.cc +++ b/test/core/end2end/tests/empty_batch.cc @@ -16,60 +16,17 @@ // // -#include -#include +#include "gtest/gtest.h" -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static void empty_batch_body(const CoreTestConfiguration& /*config*/, - CoreTestFixture* f) { - grpc_call* c; - grpc_core::CqVerifier cqv(f->cq()); - grpc_call_error error; - grpc_op* op = nullptr; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - error = - grpc_call_start_batch(c, op, 0, grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - grpc_call_unref(c); -} - -static void test_invoke_empty_body(const CoreTestConfiguration& config) { - auto f = begin_test(config, "test_invoke_empty_body", nullptr, nullptr); - empty_batch_body(config, f.get()); -} +namespace grpc_core { -void empty_batch(const CoreTestConfiguration& config) { - test_invoke_empty_body(config); +TEST_P(CoreEnd2endTest, EmptyBatch) { + auto c = NewClientCall("/service/method").Create(); + c.NewBatch(1); + Expect(1, true); + Step(); } -void empty_batch_pre_init(void) {} +} // namespace grpc_core diff --git a/test/core/end2end/tests/filter_causes_close.cc b/test/core/end2end/tests/filter_causes_close.cc index 0ad69dc374150..4583512d83cbd 100644 --- a/test/core/end2end/tests/filter_causes_close.cc +++ b/test/core/end2end/tests/filter_causes_close.cc @@ -17,137 +17,27 @@ // #include -#include - -#include -#include #include "absl/status/status.h" +#include "gtest/gtest.h" -#include -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/config/core_configuration.h" #include "src/core/lib/gprpp/debug_location.h" #include "src/core/lib/gprpp/status_helper.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/surface/channel_stack_type.h" #include "src/core/lib/transport/transport.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} -// Simple request via a server filter that always closes the stream. -static void test_request(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - auto f = begin_test(config, "filter_causes_close", nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_PERMISSION_DENIED); - GPR_ASSERT(0 == - grpc_slice_str_cmp(details, "Failure that's not preventable.")); - - f->ShutdownServer(); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request_payload_recv); -} +namespace grpc_core { +namespace { //****************************************************************************** // Test filter - always closes incoming requests @@ -161,19 +51,19 @@ typedef struct { uint8_t unused; } channel_data; -static void recv_im_ready(void* arg, grpc_error_handle error) { +void recv_im_ready(void* arg, grpc_error_handle error) { grpc_call_element* elem = static_cast(arg); call_data* calld = static_cast(elem->call_data); - grpc_core::Closure::Run( + Closure::Run( DEBUG_LOCATION, calld->recv_im_ready, grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING( "Failure that's not preventable.", &error, 1), - grpc_core::StatusIntProperty::kRpcStatus, + StatusIntProperty::kRpcStatus, GRPC_STATUS_PERMISSION_DENIED)); } -static void start_transport_stream_op_batch( - grpc_call_element* elem, grpc_transport_stream_op_batch* op) { +void start_transport_stream_op_batch(grpc_call_element* elem, + grpc_transport_stream_op_batch* op) { call_data* calld = static_cast(elem->call_data); if (op->recv_initial_metadata) { calld->recv_im_ready = @@ -184,23 +74,23 @@ static void start_transport_stream_op_batch( grpc_call_next_op(elem, op); } -static grpc_error_handle init_call_elem( - grpc_call_element* /*elem*/, const grpc_call_element_args* /*args*/) { +grpc_error_handle init_call_elem(grpc_call_element* /*elem*/, + const grpc_call_element_args* /*args*/) { return absl::OkStatus(); } -static void destroy_call_elem(grpc_call_element* /*elem*/, - const grpc_call_final_info* /*final_info*/, - grpc_closure* /*ignored*/) {} +void destroy_call_elem(grpc_call_element* /*elem*/, + const grpc_call_final_info* /*final_info*/, + grpc_closure* /*ignored*/) {} -static grpc_error_handle init_channel_elem( - grpc_channel_element* /*elem*/, grpc_channel_element_args* /*args*/) { +grpc_error_handle init_channel_elem(grpc_channel_element* /*elem*/, + grpc_channel_element_args* /*args*/) { return absl::OkStatus(); } -static void destroy_channel_elem(grpc_channel_element* /*elem*/) {} +void destroy_channel_elem(grpc_channel_element* /*elem*/) {} -static const grpc_channel_filter test_filter = { +const grpc_channel_filter test_filter = { start_transport_stream_op_batch, nullptr, grpc_channel_next_op, @@ -215,22 +105,29 @@ static const grpc_channel_filter test_filter = { grpc_channel_next_get_info, "filter_causes_close"}; -//****************************************************************************** -// Registration -// - -void filter_causes_close(const CoreTestConfiguration& config) { - grpc_core::CoreConfiguration::RunWithSpecialConfiguration( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::BuildCoreConfiguration(builder); - builder->channel_init()->RegisterStage( - GRPC_SERVER_CHANNEL, 0, - [](grpc_core::ChannelStackBuilder* builder) { - builder->PrependFilter(&test_filter); - return true; - }); - }, - [config] { test_request(config); }); +TEST_P(CoreEnd2endTest, FilterCausesClose) { + CoreConfiguration::RegisterBuilder([](CoreConfiguration::Builder* builder) { + builder->channel_init()->RegisterStage( + GRPC_SERVER_CHANNEL, 0, [](ChannelStackBuilder* builder) { + builder->PrependFilter(&test_filter); + return true; + }); + }); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + Expect(1, true); + Step(); + + EXPECT_EQ(server_status.status(), GRPC_STATUS_PERMISSION_DENIED); + EXPECT_EQ(server_status.message(), "Failure that's not preventable."); } -void filter_causes_close_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/filter_context.cc b/test/core/end2end/tests/filter_context.cc index d59bec91a97c0..83c17ef991e7d 100644 --- a/test/core/end2end/tests/filter_context.cc +++ b/test/core/end2end/tests/filter_context.cc @@ -17,169 +17,31 @@ // #include -#include #include -#include #include -#include #include #include "absl/status/status.h" +#include "gtest/gtest.h" -#include -#include -#include -#include #include #include -#include -#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/channel/context.h" #include "src/core/lib/config/core_configuration.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/surface/channel_stack_type.h" #include "src/core/lib/transport/transport.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -enum { TIMEOUT = 200000 }; - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Simple request to test that filters see a consistent view of the -// call context. -static void test_request(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - auto f = begin_test(config, "filter_context", nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_string = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_string; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(s); - grpc_call_unref(c); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request_payload_recv); -} +namespace grpc_core { +namespace { //****************************************************************************** // Test context filter @@ -189,16 +51,16 @@ struct call_data { grpc_call_context_element* context; }; -static grpc_error_handle init_call_elem(grpc_call_element* elem, - const grpc_call_element_args* args) { +grpc_error_handle init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { call_data* calld = static_cast(elem->call_data); calld->context = args->context; gpr_log(GPR_INFO, "init_call_elem(): context=%p", args->context); return absl::OkStatus(); } -static void start_transport_stream_op_batch( - grpc_call_element* elem, grpc_transport_stream_op_batch* batch) { +void start_transport_stream_op_batch(grpc_call_element* elem, + grpc_transport_stream_op_batch* batch) { call_data* calld = static_cast(elem->call_data); // If batch payload context is not null (which will happen in some // cancellation cases), make sure we get the same context here that we @@ -211,18 +73,18 @@ static void start_transport_stream_op_batch( grpc_call_next_op(elem, batch); } -static void destroy_call_elem(grpc_call_element* /*elem*/, - const grpc_call_final_info* /*final_info*/, - grpc_closure* /*ignored*/) {} +void destroy_call_elem(grpc_call_element* /*elem*/, + const grpc_call_final_info* /*final_info*/, + grpc_closure* /*ignored*/) {} -static grpc_error_handle init_channel_elem( - grpc_channel_element* /*elem*/, grpc_channel_element_args* /*args*/) { +grpc_error_handle init_channel_elem(grpc_channel_element* /*elem*/, + grpc_channel_element_args* /*args*/) { return absl::OkStatus(); } -static void destroy_channel_elem(grpc_channel_element* /*elem*/) {} +void destroy_channel_elem(grpc_channel_element* /*elem*/) {} -static const grpc_channel_filter test_filter = { +const grpc_channel_filter test_filter = { start_transport_stream_op_batch, nullptr, grpc_channel_next_op, @@ -237,31 +99,49 @@ static const grpc_channel_filter test_filter = { grpc_channel_next_get_info, "filter_context"}; -//****************************************************************************** -// Registration -// - -void filter_context(const CoreTestConfiguration& config) { - grpc_core::CoreConfiguration::RunWithSpecialConfiguration( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::BuildCoreConfiguration(builder); - for (auto type : {GRPC_CLIENT_CHANNEL, GRPC_CLIENT_SUBCHANNEL, - GRPC_CLIENT_DIRECT_CHANNEL, GRPC_SERVER_CHANNEL}) { - builder->channel_init()->RegisterStage( - type, INT_MAX, [](grpc_core::ChannelStackBuilder* builder) { - // Want to add the filter as close to the end as possible, to - // make sure that all of the filters work well together. - // However, we can't add it at the very end, because the - // connected channel filter must be the last one. So we add it - // right before the last one. - auto it = builder->mutable_stack()->end(); - --it; - builder->mutable_stack()->insert(it, &test_filter); - return true; - }); - } - }, - [config] { test_request(config); }); +// Simple request to test that filters see a consistent view of the +// call context. +TEST_P(CoreEnd2endTest, FilterContext) { + CoreConfiguration::RegisterBuilder([](CoreConfiguration::Builder* builder) { + for (auto type : {GRPC_CLIENT_CHANNEL, GRPC_CLIENT_SUBCHANNEL, + GRPC_CLIENT_DIRECT_CHANNEL, GRPC_SERVER_CHANNEL}) { + builder->channel_init()->RegisterStage( + type, INT_MAX, [](ChannelStackBuilder* builder) { + // Want to add the filter as close to the end as possible, to + // make sure that all of the filters work well together. + // However, we can't add it at the very end, because the + // connected channel filter must be the last one. So we add it + // right before the last one. + auto it = builder->mutable_stack()->end(); + --it; + builder->mutable_stack()->insert(it, &test_filter); + return true; + }); + } + }); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello world") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); } -void filter_context_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/filter_init_fails.cc b/test/core/end2end/tests/filter_init_fails.cc index 753d7cd729415..44f6b832d6b44 100644 --- a/test/core/end2end/tests/filter_init_fails.cc +++ b/test/core/end2end/tests/filter_init_fails.cc @@ -17,22 +17,17 @@ // #include -#include #include -#include #include #include #include "absl/status/status.h" +#include "absl/types/optional.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" -#include -#include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" @@ -40,371 +35,52 @@ #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/config/core_configuration.h" #include "src/core/lib/gprpp/status_helper.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/promise/arena_promise.h" #include "src/core/lib/promise/promise.h" #include "src/core/lib/surface/channel_stack_type.h" #include "src/core/lib/transport/transport.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -enum { TIMEOUT = 200000 }; +using ::testing::AnyOf; -static bool g_enable_server_channel_filter = false; -static bool g_enable_client_channel_filter = false; -static bool g_enable_client_subchannel_filter = false; -static bool g_channel_filter_init_failure = false; +namespace grpc_core { +namespace { -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Simple request via a SERVER_CHANNEL filter that always fails to -// initialize the call. -static void test_server_channel_filter(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - auto f = begin_test(config, "filter_init_fails", nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - if (g_channel_filter_init_failure) { - // Inproc channel returns invalid_argument and other clients return - // unavailable. - // Windows with sockpair returns unknown. - GPR_ASSERT(status == GRPC_STATUS_UNKNOWN || - status == GRPC_STATUS_UNAVAILABLE || - status == GRPC_STATUS_INVALID_ARGUMENT); - } else { - GPR_ASSERT(status == GRPC_STATUS_PERMISSION_DENIED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "access denied")); - } - - f->ShutdownServer(); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request_payload_recv); -} - -// Simple request via a CLIENT_CHANNEL or CLIENT_DIRECT_CHANNEL filter -// that always fails to initialize the call. -static void test_client_channel_filter(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - auto f = begin_test(config, "filter_init_fails", nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - if (g_channel_filter_init_failure) { - GPR_ASSERT(status == GRPC_STATUS_INVALID_ARGUMENT); - } else { - GPR_ASSERT(status == GRPC_STATUS_PERMISSION_DENIED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "access denied")); - } - - f->ShutdownServer(); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request_payload_recv); -} - -// Simple request via a CLIENT_SUBCHANNEL filter that always fails to -// initialize the call. -static void test_client_subchannel_filter(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - auto f = begin_test(config, "filter_init_fails", nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - if (g_channel_filter_init_failure) { - GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE); - } else { - GPR_ASSERT(status == GRPC_STATUS_PERMISSION_DENIED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "access denied")); - } - - // Reset and create a new call. (The first call uses a different code - // path in client_channel.c than subsequent calls on the same channel, - // and we need to test both.) - grpc_call_unref(c); - status = GRPC_STATUS_OK; - grpc_slice_unref(details); - details = grpc_empty_slice(); - - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - if (g_channel_filter_init_failure) { - GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE); - } else { - GPR_ASSERT(status == GRPC_STATUS_PERMISSION_DENIED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "access denied")); - } - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request_payload_recv); -} - -//****************************************************************************** +// // Test filter - always fails to initialize a call // -static grpc_error_handle init_call_elem( - grpc_call_element* /*elem*/, const grpc_call_element_args* /*args*/) { +grpc_error_handle init_call_elem(grpc_call_element* /*elem*/, + const grpc_call_element_args* /*args*/) { return grpc_error_set_int(GRPC_ERROR_CREATE("access denied"), - grpc_core::StatusIntProperty::kRpcStatus, + StatusIntProperty::kRpcStatus, GRPC_STATUS_PERMISSION_DENIED); } -static void destroy_call_elem(grpc_call_element* /*elem*/, - const grpc_call_final_info* /*final_info*/, - grpc_closure* /*ignored*/) {} +void destroy_call_elem(grpc_call_element* /*elem*/, + const grpc_call_final_info* /*final_info*/, + grpc_closure* /*ignored*/) {} -static grpc_error_handle init_channel_elem( - grpc_channel_element* /*elem*/, grpc_channel_element_args* /*args*/) { - if (g_channel_filter_init_failure) { +grpc_error_handle init_channel_elem(grpc_channel_element* /*elem*/, + grpc_channel_element_args* args) { + if (args->channel_args.GetBool("channel_init_fails").value_or(false)) { return grpc_error_set_int( GRPC_ERROR_CREATE("Test channel filter init error"), - grpc_core::StatusIntProperty::kRpcStatus, GRPC_STATUS_INVALID_ARGUMENT); + StatusIntProperty::kRpcStatus, GRPC_STATUS_INVALID_ARGUMENT); } return absl::OkStatus(); } -static void destroy_channel_elem(grpc_channel_element* /*elem*/) {} +void destroy_channel_elem(grpc_channel_element* /*elem*/) {} -static const grpc_channel_filter test_filter = { +const grpc_channel_filter test_filter = { grpc_call_next_op, - [](grpc_channel_element*, grpc_core::CallArgs, - grpc_core::NextPromiseFactory) - -> grpc_core::ArenaPromise { - return grpc_core::Immediate(grpc_core::ServerMetadataFromStatus( + [](grpc_channel_element*, CallArgs, + NextPromiseFactory) -> ArenaPromise { + return Immediate(ServerMetadataFromStatus( absl::PermissionDeniedError("access denied"))); }, grpc_channel_next_op, @@ -419,63 +95,175 @@ static const grpc_channel_filter test_filter = { grpc_channel_next_get_info, "filter_init_fails"}; -//****************************************************************************** -// Registration -// +void RegisterFilter(grpc_channel_stack_type type) { + CoreConfiguration::RegisterBuilder( + [type](CoreConfiguration::Builder* builder) { + builder->channel_init()->RegisterStage( + type, INT_MAX, [](ChannelStackBuilder* builder) { + // Want to add the filter as close to the end as possible, + // to make sure that all of the filters work well together. + // However, we can't add it at the very end, because either the + // client_channel filter or connected_channel filter must be the + // last one. So we add it right before the last one. + auto it = builder->mutable_stack()->end(); + --it; + builder->mutable_stack()->insert(it, &test_filter); + return true; + }); + }); +} -static void filter_init_fails_internal(const CoreTestConfiguration& config) { - gpr_log(GPR_INFO, "Testing SERVER_CHANNEL filter."); - g_enable_server_channel_filter = true; - test_server_channel_filter(config); - g_enable_server_channel_filter = false; - gpr_log(GPR_INFO, "Testing CLIENT_CHANNEL / CLIENT_DIRECT_CHANNEL filter."); - g_enable_client_channel_filter = true; - test_client_channel_filter(config); - g_enable_client_channel_filter = false; - // If the client handshake completes before the server handshake and the - // client is able to send application data before the server handshake - // completes, then testing the CLIENT_SUBCHANNEL filter will cause the server - // to freeze waiting for the final handshake message from the client. This - // handshake message will never arrive because it would have been sent with - // the first application data message, which failed because of the filter. - if ((config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL) && - !(config.feature_mask & - FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST)) { - gpr_log(GPR_INFO, "Testing CLIENT_SUBCHANNEL filter."); - g_enable_client_subchannel_filter = true; - test_client_subchannel_filter(config); - g_enable_client_subchannel_filter = false; - } +TEST_P(CoreEnd2endTest, DISABLED_ServerFilterChannelInitFails) { + RegisterFilter(GRPC_SERVER_CHANNEL); + InitClient(ChannelArgs()); + InitServer(ChannelArgs().Set("channel_init_fails", true)); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(1, true); + Step(); + // Inproc channel returns invalid_argument and other clients return + // unavailable. + // Windows with sockpair returns unknown. + EXPECT_THAT(server_status.status(), + AnyOf(GRPC_STATUS_UNKNOWN, GRPC_STATUS_UNAVAILABLE, + GRPC_STATUS_INVALID_ARGUMENT)); + ShutdownAndDestroyServer(); +}; + +TEST_P(CoreEnd2endTest, ServerFilterCallInitFails) { + RegisterFilter(GRPC_SERVER_CHANNEL); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_PERMISSION_DENIED); + EXPECT_EQ(server_status.message(), "access denied"); + ShutdownAndDestroyServer(); +}; + +TEST_P(CoreEnd2endTest, DISABLED_ClientFilterChannelInitFails) { + RegisterFilter(GRPC_CLIENT_CHANNEL); + RegisterFilter(GRPC_CLIENT_DIRECT_CHANNEL); + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set("channel_init_fails", true)); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_INVALID_ARGUMENT); +} + +TEST_P(CoreEnd2endTest, ClientFilterCallInitFails) { + RegisterFilter(GRPC_CLIENT_CHANNEL); + RegisterFilter(GRPC_CLIENT_DIRECT_CHANNEL); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_PERMISSION_DENIED); + EXPECT_EQ(server_status.message(), "access denied"); +} + +TEST_P(CoreClientChannelTest, DISABLED_SubchannelFilterChannelInitFails) { + RegisterFilter(GRPC_CLIENT_SUBCHANNEL); + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set("channel_init_fails", true)); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNAVAILABLE); + // Create a new call. (The first call uses a different code path in + // client_channel.c than subsequent calls on the same channel, and we need to + // test both.) + auto c2 = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status2; + CoreEnd2endTest::IncomingMetadata server_initial_metadata2; + CoreEnd2endTest::IncomingMessage server_message2; + c2.NewBatch(2) + .SendInitialMetadata({}) + .SendMessage("hi again") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata2) + .RecvStatusOnClient(server_status2); + Expect(2, true); + Step(); + EXPECT_EQ(server_status2.status(), GRPC_STATUS_UNAVAILABLE); } -void filter_init_fails(const CoreTestConfiguration& config) { - grpc_core::CoreConfiguration::RunWithSpecialConfiguration( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::BuildCoreConfiguration(builder); - auto register_stage = [builder](grpc_channel_stack_type type, - bool* enable) { - builder->channel_init()->RegisterStage( - type, INT_MAX, [enable](grpc_core::ChannelStackBuilder* builder) { - if (!*enable) return true; - // Want to add the filter as close to the end as possible, - // to make sure that all of the filters work well together. - // However, we can't add it at the very end, because either the - // client_channel filter or connected_channel filter must be the - // last one. So we add it right before the last one. - auto it = builder->mutable_stack()->end(); - --it; - builder->mutable_stack()->insert(it, &test_filter); - return true; - }); - }; - register_stage(GRPC_SERVER_CHANNEL, &g_enable_server_channel_filter); - register_stage(GRPC_CLIENT_CHANNEL, &g_enable_client_channel_filter); - register_stage(GRPC_CLIENT_SUBCHANNEL, - &g_enable_client_subchannel_filter); - register_stage(GRPC_CLIENT_DIRECT_CHANNEL, - &g_enable_client_channel_filter); - }, - [&config] { filter_init_fails_internal(config); }); +TEST_P(CoreClientChannelTest, SubchannelFilterCallInitFails) { + RegisterFilter(GRPC_CLIENT_SUBCHANNEL); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_PERMISSION_DENIED); + EXPECT_EQ(server_status.message(), "access denied"); + // Create a new call. (The first call uses a different code path in + // client_channel.c than subsequent calls on the same channel, and we need to + // test both.) + auto c2 = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status2; + CoreEnd2endTest::IncomingMetadata server_initial_metadata2; + CoreEnd2endTest::IncomingMessage server_message2; + c2.NewBatch(2) + .SendInitialMetadata({}) + .SendMessage("hi again") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata2) + .RecvStatusOnClient(server_status2); + Expect(2, true); + Step(); + EXPECT_EQ(server_status2.status(), GRPC_STATUS_PERMISSION_DENIED); + EXPECT_EQ(server_status2.message(), "access denied"); } -void filter_init_fails_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/filter_latency.cc b/test/core/end2end/tests/filter_latency.cc deleted file mode 100644 index b04121dca726f..0000000000000 --- a/test/core/end2end/tests/filter_latency.cc +++ /dev/null @@ -1,289 +0,0 @@ -// -// -// Copyright 2016 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 -#include - -#include -#include -#include -#include - -#include "absl/status/status.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/channel_fwd.h" -#include "src/core/lib/channel/channel_stack.h" -#include "src/core/lib/channel/channel_stack_builder.h" -#include "src/core/lib/config/core_configuration.h" -#include "src/core/lib/iomgr/closure.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/surface/channel_stack_type.h" -#include "test/core/end2end/cq_verifier.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -enum { TIMEOUT = 200000 }; - -static gpr_mu g_mu; -static gpr_timespec g_client_latency; -static gpr_timespec g_server_latency; - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Simple request via a server filter that saves the reported latency value. -static void test_request(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - auto f = begin_test(config, "filter_latency", nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_mu_lock(&g_mu); - g_client_latency = gpr_time_0(GPR_TIMESPAN); - g_server_latency = gpr_time_0(GPR_TIMESPAN); - gpr_mu_unlock(&g_mu); - const gpr_timespec start_time = gpr_now(GPR_CLOCK_REALTIME); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_string = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_string; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(s); - grpc_call_unref(c); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request_payload_recv); - - const gpr_timespec end_time = gpr_now(GPR_CLOCK_REALTIME); - const gpr_timespec max_latency = gpr_time_sub(end_time, start_time); - - // Perform checks after test tear-down - // Guards against the case that there's outstanding channel-related work on a - // call prior to verification - gpr_mu_lock(&g_mu); - GPR_ASSERT(gpr_time_cmp(max_latency, g_client_latency) >= 0); - GPR_ASSERT(gpr_time_cmp(gpr_time_0(GPR_TIMESPAN), g_client_latency) <= 0); - GPR_ASSERT(gpr_time_cmp(max_latency, g_server_latency) >= 0); - GPR_ASSERT(gpr_time_cmp(gpr_time_0(GPR_TIMESPAN), g_server_latency) <= 0); - // Server latency should always be smaller than client latency, however since - // we only calculate latency at destruction time, and that might mean that we - // need to wait for outstanding channel-related work, this isn't verifiable - // right now (the server MAY hold on to the call for longer than the client). - // GPR_ASSERT(gpr_time_cmp(g_server_latency, g_client_latency) < 0); - gpr_mu_unlock(&g_mu); -} - -//****************************************************************************** -// Test latency filter -// - -static grpc_error_handle init_call_elem( - grpc_call_element* /*elem*/, const grpc_call_element_args* /*args*/) { - return absl::OkStatus(); -} - -static void client_destroy_call_elem(grpc_call_element* /*elem*/, - const grpc_call_final_info* final_info, - grpc_closure* /*ignored*/) { - gpr_mu_lock(&g_mu); - g_client_latency = final_info->stats.latency; - gpr_mu_unlock(&g_mu); -} - -static void server_destroy_call_elem(grpc_call_element* /*elem*/, - const grpc_call_final_info* final_info, - grpc_closure* /*ignored*/) { - gpr_mu_lock(&g_mu); - g_server_latency = final_info->stats.latency; - gpr_mu_unlock(&g_mu); -} - -static grpc_error_handle init_channel_elem( - grpc_channel_element* /*elem*/, grpc_channel_element_args* /*args*/) { - return absl::OkStatus(); -} - -static void destroy_channel_elem(grpc_channel_element* /*elem*/) {} - -static const grpc_channel_filter test_client_filter = { - grpc_call_next_op, nullptr, - grpc_channel_next_op, 0, - init_call_elem, grpc_call_stack_ignore_set_pollset_or_pollset_set, - client_destroy_call_elem, 0, - init_channel_elem, grpc_channel_stack_no_post_init, - destroy_channel_elem, grpc_channel_next_get_info, - "client_filter_latency"}; - -static const grpc_channel_filter test_server_filter = { - grpc_call_next_op, nullptr, - grpc_channel_next_op, 0, - init_call_elem, grpc_call_stack_ignore_set_pollset_or_pollset_set, - server_destroy_call_elem, 0, - init_channel_elem, grpc_channel_stack_no_post_init, - destroy_channel_elem, grpc_channel_next_get_info, - "server_filter_latency"}; - -//****************************************************************************** -// Registration -// - -void filter_latency(const CoreTestConfiguration& config) { - grpc_core::CoreConfiguration::RunWithSpecialConfiguration( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::BuildCoreConfiguration(builder); - auto register_stage = [builder](grpc_channel_stack_type type, - const grpc_channel_filter* filter) { - builder->channel_init()->RegisterStage( - type, INT_MAX, [filter](grpc_core::ChannelStackBuilder* builder) { - // Want to add the filter as close to the end as possible, to - // make sure that all of the filters work well together. - // However, we can't add it at the very end, because the - // connected channel filter must be the last one. So we add it - // right before the last one. - auto it = builder->mutable_stack()->end(); - --it; - builder->mutable_stack()->insert(it, filter); - return true; - }); - }; - register_stage(GRPC_CLIENT_CHANNEL, &test_client_filter); - register_stage(GRPC_CLIENT_DIRECT_CHANNEL, &test_client_filter); - register_stage(GRPC_SERVER_CHANNEL, &test_server_filter); - }, - [config] { test_request(config); }); -} - -void filter_latency_pre_init(void) { gpr_mu_init(&g_mu); } diff --git a/test/core/end2end/tests/filter_status_code.cc b/test/core/end2end/tests/filter_status_code.cc deleted file mode 100644 index 89e8f65b7b04b..0000000000000 --- a/test/core/end2end/tests/filter_status_code.cc +++ /dev/null @@ -1,353 +0,0 @@ -// -// -// 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. -// -// - -// This test verifies - -// 1) grpc_call_final_info passed to the filters on destroying a call contains -// the proper status. -// 2) If the response has both an HTTP status code and a gRPC status code, then -// we should prefer the gRPC status code as mentioned in -// https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md -// - -#include -#include - -#include -#include -#include -#include - -#include "absl/status/status.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/channel_fwd.h" -#include "src/core/lib/channel/channel_stack.h" -#include "src/core/lib/channel/channel_stack_builder.h" -#include "src/core/lib/config/core_configuration.h" -#include "src/core/lib/iomgr/closure.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/surface/call.h" -#include "src/core/lib/surface/channel_stack_type.h" -#include "src/core/lib/transport/metadata_batch.h" -#include "src/core/lib/transport/transport.h" -#include "test/core/end2end/cq_verifier.h" -#include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static gpr_mu g_mu; -static grpc_call_stack* g_client_call_stack; -static grpc_call_stack* g_server_call_stack; -static bool g_client_code_recv; -static bool g_server_code_recv; -static gpr_cv g_client_code_cv; -static gpr_cv g_server_code_cv; -static grpc_status_code g_client_status_code; -static grpc_status_code g_server_status_code; - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Simple request via a server filter that saves the reported status code. -static void test_request(const CoreTestConfiguration& config) { - g_client_code_recv = false; - g_server_code_recv = false; - - grpc_call* c; - grpc_call* s; - auto f = begin_test(config, "filter_status_code", nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_mu_lock(&g_mu); - g_client_call_stack = nullptr; - g_server_call_stack = nullptr; - g_client_status_code = GRPC_STATUS_OK; - g_server_status_code = GRPC_STATUS_OK; - gpr_mu_unlock(&g_mu); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - gpr_mu_lock(&g_mu); - g_client_call_stack = grpc_call_get_call_stack(c); - gpr_mu_unlock(&g_mu); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - gpr_mu_lock(&g_mu); - g_server_call_stack = grpc_call_get_call_stack(s); - gpr_mu_unlock(&g_mu); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_string = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_string; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(s); - grpc_call_unref(c); - - // Perform checks after test tear-down - // Guards against the case that there's outstanding channel-related work on a - // call prior to verification - gpr_mu_lock(&g_mu); - if (!g_client_code_recv) { - GPR_ASSERT(gpr_cv_wait(&g_client_code_cv, &g_mu, - grpc_timeout_seconds_to_deadline(3)) == 0); - } - if (!g_server_code_recv) { - GPR_ASSERT(gpr_cv_wait(&g_server_code_cv, &g_mu, - grpc_timeout_seconds_to_deadline(3)) == 0); - } - GPR_ASSERT(g_client_status_code == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(g_server_status_code == GRPC_STATUS_UNIMPLEMENTED); - gpr_mu_unlock(&g_mu); -} - -//****************************************************************************** -// Test status_code filter -// - -typedef struct final_status_data { - grpc_call_stack* call; -} final_status_data; - -static void server_start_transport_stream_op_batch( - grpc_call_element* elem, grpc_transport_stream_op_batch* op) { - auto* data = static_cast(elem->call_data); - gpr_mu_lock(&g_mu); - if (data->call == g_server_call_stack) { - if (op->send_initial_metadata) { - auto* batch = op->payload->send_initial_metadata.send_initial_metadata; - auto* status = batch->get_pointer(grpc_core::HttpStatusMetadata()); - if (status != nullptr) { - // Replace the HTTP status with 404 - *status = 404; - } - } - } - gpr_mu_unlock(&g_mu); - grpc_call_next_op(elem, op); -} - -static grpc_error_handle init_call_elem(grpc_call_element* elem, - const grpc_call_element_args* args) { - final_status_data* data = static_cast(elem->call_data); - data->call = args->call_stack; - return absl::OkStatus(); -} - -static void client_destroy_call_elem(grpc_call_element* elem, - const grpc_call_final_info* final_info, - grpc_closure* /*ignored*/) { - final_status_data* data = static_cast(elem->call_data); - gpr_mu_lock(&g_mu); - // Some fixtures, like proxies, will spawn intermidiate calls - // We only want the results from our explicit calls - if (data->call == g_client_call_stack) { - g_client_status_code = final_info->final_status; - g_client_code_recv = true; - gpr_cv_signal(&g_client_code_cv); - } - gpr_mu_unlock(&g_mu); -} - -static void server_destroy_call_elem(grpc_call_element* elem, - const grpc_call_final_info* final_info, - grpc_closure* /*ignored*/) { - final_status_data* data = static_cast(elem->call_data); - gpr_mu_lock(&g_mu); - // Some fixtures, like proxies, will spawn intermidiate calls - // We only want the results from our explicit calls - if (data->call == g_server_call_stack) { - g_server_status_code = final_info->final_status; - g_server_code_recv = true; - gpr_cv_signal(&g_server_code_cv); - } - gpr_mu_unlock(&g_mu); -} - -static grpc_error_handle init_channel_elem( - grpc_channel_element* /*elem*/, grpc_channel_element_args* /*args*/) { - return absl::OkStatus(); -} - -static void destroy_channel_elem(grpc_channel_element* /*elem*/) {} - -static const grpc_channel_filter test_client_filter = { - grpc_call_next_op, - nullptr, - grpc_channel_next_op, - sizeof(final_status_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - client_destroy_call_elem, - 0, - init_channel_elem, - grpc_channel_stack_no_post_init, - destroy_channel_elem, - grpc_channel_next_get_info, - "client_filter_status_code"}; - -static const grpc_channel_filter test_server_filter = { - server_start_transport_stream_op_batch, - nullptr, - grpc_channel_next_op, - sizeof(final_status_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - server_destroy_call_elem, - 0, - init_channel_elem, - grpc_channel_stack_no_post_init, - destroy_channel_elem, - grpc_channel_next_get_info, - "server_filter_status_code"}; - -//****************************************************************************** -// Registration -// - -void filter_status_code(const CoreTestConfiguration& config) { - grpc_core::CoreConfiguration::RunWithSpecialConfiguration( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::BuildCoreConfiguration(builder); - auto register_stage = [builder](grpc_channel_stack_type type, - const grpc_channel_filter* filter) { - builder->channel_init()->RegisterStage( - type, INT_MAX, [filter](grpc_core::ChannelStackBuilder* builder) { - // Want to add the filter as close to the end as possible, to - // make sure that all of the filters work well together. - // However, we can't add it at the very end, because the - // connected_channel/client_channel filter must be the last one. - // So we add it right before the last one. - auto it = builder->mutable_stack()->end(); - --it; - builder->mutable_stack()->insert(it, filter); - return true; - }); - }; - register_stage(GRPC_CLIENT_CHANNEL, &test_client_filter); - register_stage(GRPC_CLIENT_DIRECT_CHANNEL, &test_client_filter); - register_stage(GRPC_SERVER_CHANNEL, &test_server_filter); - }, - [&config] { test_request(config); }); -} - -void filter_status_code_pre_init(void) { - gpr_mu_init(&g_mu); - gpr_cv_init(&g_client_code_cv); - gpr_cv_init(&g_server_code_cv); -} diff --git a/test/core/end2end/tests/filtered_metadata.cc b/test/core/end2end/tests/filtered_metadata.cc index 025e5aa19539b..4d91bca43c6f8 100644 --- a/test/core/end2end/tests/filtered_metadata.cc +++ b/test/core/end2end/tests/filtered_metadata.cc @@ -16,175 +16,64 @@ // // -#include +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Request/response with metadata which should be filtered -static void test_request_response_with_metadata_to_be_filtered( - const CoreTestConfiguration& config, const char* filtered_md_key, - const char* filter_md_value) { - grpc_call* c; - grpc_call* s; - grpc_metadata meta_c[2] = {{grpc_slice_from_static_string("key1"), - grpc_slice_from_static_string("val1"), - {{nullptr, nullptr, nullptr, nullptr}}}, - {grpc_slice_from_static_string(filtered_md_key), - grpc_slice_from_static_string(filter_md_value), - {{nullptr, nullptr, nullptr, nullptr}}}}; - grpc_metadata meta_s[2] = {{grpc_slice_from_static_string("key2"), - grpc_slice_from_static_string("val2"), - {{nullptr, nullptr, nullptr, nullptr}}}, - {grpc_slice_from_static_string(filtered_md_key), - grpc_slice_from_static_string(filter_md_value), - {{nullptr, nullptr, nullptr, nullptr}}}}; - auto f = - begin_test(config, "test_request_response_with_metadata_to_be_filtered", - nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 2; - op->data.send_initial_metadata.metadata = meta_c; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 2; - op->data.send_initial_metadata.metadata = meta_s; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - GPR_ASSERT(contains_metadata(&request_metadata_recv, "key1", "val1")); - GPR_ASSERT(!contains_metadata(&request_metadata_recv, filtered_md_key, - filter_md_value)); - GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key2", "val2")); - GPR_ASSERT(!contains_metadata(&initial_metadata_recv, filtered_md_key, - filter_md_value)); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); +void TestRequestResponseWithMetadataToBeFiltered( + CoreEnd2endTest& test, absl::string_view filtered_md_key, + absl::string_view filter_md_value) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata( + {{"key1", "val1"}, {filtered_md_key, filter_md_value}}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + + auto s = test.RequestCall(101); + + test.Expect(101, true); + test.Step(); + + s.NewBatch(102).SendInitialMetadata( + {{"key2", "val2"}, {filtered_md_key, filter_md_value}}); + test.Expect(102, true); + test.Step(); + + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(103) + .RecvCloseOnServer(client_close) + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + test.Expect(103, true); + test.Expect(1, true); + test.Step(); + + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(s.GetInitialMetadata("key1"), "val1"); + EXPECT_EQ(s.GetInitialMetadata(filtered_md_key), absl::nullopt); + EXPECT_EQ(server_initial_metadata.Get("key2"), "val2"); + EXPECT_EQ(server_initial_metadata.Get(filtered_md_key), absl::nullopt); } -void filtered_metadata(const CoreTestConfiguration& config) { - test_request_response_with_metadata_to_be_filtered(config, "content-length", - "45"); +TEST_P(CoreEnd2endTest, ContentLengthIsFiltered) { + TestRequestResponseWithMetadataToBeFiltered(*this, "content-length", "45"); } -void filtered_metadata_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/graceful_server_shutdown.cc b/test/core/end2end/tests/graceful_server_shutdown.cc index 494dd3d088879..1f140664db6e7 100644 --- a/test/core/end2end/tests/graceful_server_shutdown.cc +++ b/test/core/end2end/tests/graceful_server_shutdown.cc @@ -16,150 +16,45 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static void test_early_server_shutdown_finishes_inflight_calls( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - auto f = - begin_test(config, "test_early_server_shutdown_finishes_inflight_calls", - nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(10); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); +namespace grpc_core { +namespace { + +TEST_P(Http2Test, GracefulServerShutdown) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(10)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); // shutdown and destroy the server - grpc_server_shutdown_and_notify(f->server(), f->cq(), - grpc_core::CqVerifier::tag(0xdead)); - cqv.VerifyEmpty(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(0xdead), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - grpc_call_unref(s); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); -} - -void graceful_server_shutdown(const CoreTestConfiguration& config) { - test_early_server_shutdown_finishes_inflight_calls(config); + ShutdownServerAndNotify(200); + Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(200, true); + Expect(1, true); + Step(); + + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); } -void graceful_server_shutdown_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/grpc_authz.cc b/test/core/end2end/tests/grpc_authz.cc index 667990b4a14ee..26d5da6cecdce 100644 --- a/test/core/end2end/tests/grpc_authz.cc +++ b/test/core/end2end/tests/grpc_authz.cc @@ -12,502 +12,266 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include - -#include -#include #include -#include #include "absl/status/status.h" #include "absl/strings/string_view.h" +#include "gtest/gtest.h" #include #include -#include -#include #include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/notification.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/security/authorization/authorization_policy_provider.h" #include "src/core/lib/security/authorization/grpc_authorization_policy_provider.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" #include "test/core/util/tls_utils.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "%s", std::string(80, '*').c_str()); - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; +namespace grpc_core { +namespace { + +void TestAllowAuthorizedRequest(CoreEnd2endTest& test) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}) + .RecvCloseOnServer(client_close); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); } -static void test_allow_authorized_request(CoreTestFixture* f) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - const char* error_string = nullptr; - grpc_call_error error; - grpc_slice details = grpc_empty_slice(); - int was_cancelled = 2; - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->data.recv_status_on_client.error_string = &error_string; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - GPR_ASSERT(GRPC_STATUS_OK == status); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - - grpc_slice_unref(details); - gpr_free(const_cast(error_string)); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); +void TestDenyUnauthorizedRequest(CoreEnd2endTest& test) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_PERMISSION_DENIED); + EXPECT_EQ(server_status.message(), "Unauthorized RPC request rejected."); } -static void test_deny_unauthorized_request(CoreTestFixture* f) { - grpc_call* c; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_status_code status; - const char* error_string = nullptr; - grpc_call_error error; - grpc_slice details = grpc_empty_slice(); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->data.recv_status_on_client.error_string = &error_string; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(GRPC_STATUS_PERMISSION_DENIED == status); - GPR_ASSERT(0 == - grpc_slice_str_cmp(details, "Unauthorized RPC request rejected.")); - - grpc_slice_unref(details); - gpr_free(const_cast(error_string)); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - - grpc_call_unref(c); +void InitWithPolicy(CoreEnd2endTest& test, + grpc_authorization_policy_provider* provider) { + test.InitServer(ChannelArgs().Set( + GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER, + ChannelArgs::Pointer(provider, + grpc_authorization_policy_provider_arg_vtable()))); + test.InitClient(ChannelArgs()); } -static void test_static_init_allow_authorized_request( - const CoreTestConfiguration& config) { - const char* authz_policy = - "{" - " \"name\": \"authz\"," - " \"allow_rules\": [" - " {" - " \"name\": \"allow_foo\"," - " \"request\": {" - " \"paths\": [" - " \"*/foo\"" - " ]" - " }" - " }" - " ]" - "}"; +void InitWithStaticData(CoreEnd2endTest& test, const char* authz_policy) { grpc_status_code code = GRPC_STATUS_OK; const char* error_details; grpc_authorization_policy_provider* provider = grpc_authorization_policy_provider_static_data_create(authz_policy, &code, &error_details); - GPR_ASSERT(GRPC_STATUS_OK == code); - grpc_arg args[] = { - grpc_channel_arg_pointer_create( - const_cast(GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER), provider, - grpc_authorization_policy_provider_arg_vtable()), - }; - grpc_channel_args server_args = {GPR_ARRAY_SIZE(args), args}; - - auto f = begin_test(config, "test_static_init_allow_authorized_request", - nullptr, &server_args); - grpc_authorization_policy_provider_release(provider); - test_allow_authorized_request(f.get()); + EXPECT_EQ(code, GRPC_STATUS_OK); + InitWithPolicy(test, provider); } -static void test_static_init_deny_unauthorized_request( - const CoreTestConfiguration& config) { - const char* authz_policy = - "{" - " \"name\": \"authz\"," - " \"allow_rules\": [" - " {" - " \"name\": \"allow_bar\"," - " \"request\": {" - " \"paths\": [" - " \"*/bar\"" - " ]" - " }" - " }" - " ]," - " \"deny_rules\": [" - " {" - " \"name\": \"deny_foo\"," - " \"request\": {" - " \"paths\": [" - " \"*/foo\"" - " ]" - " }" - " }" - " ]" - "}"; - grpc_status_code code = GRPC_STATUS_OK; - const char* error_details; - grpc_authorization_policy_provider* provider = - grpc_authorization_policy_provider_static_data_create(authz_policy, &code, - &error_details); - GPR_ASSERT(GRPC_STATUS_OK == code); - grpc_arg args[] = { - grpc_channel_arg_pointer_create( - const_cast(GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER), provider, - grpc_authorization_policy_provider_arg_vtable()), - }; - grpc_channel_args server_args = {GPR_ARRAY_SIZE(args), args}; - - auto f = begin_test(config, "test_static_init_deny_unauthorized_request", - nullptr, &server_args); - grpc_authorization_policy_provider_release(provider); - test_deny_unauthorized_request(f.get()); +class InitWithTempFile { + public: + InitWithTempFile(CoreEnd2endTest& test, const char* authz_policy) + : tmp_file_(authz_policy) { + grpc_status_code code = GRPC_STATUS_OK; + const char* error_details; + provider_ = grpc_authorization_policy_provider_file_watcher_create( + tmp_file_.name().c_str(), /*refresh_interval_sec=*/1, &code, + &error_details); + GPR_ASSERT(GRPC_STATUS_OK == code); + InitWithPolicy(test, provider_); + } + + InitWithTempFile(const InitWithTempFile&) = delete; + InitWithTempFile& operator=(const InitWithTempFile&) = delete; + + FileWatcherAuthorizationPolicyProvider* provider() { + return dynamic_cast(provider_); + } + + testing::TmpFile& file() { return tmp_file_; } + + private: + testing::TmpFile tmp_file_; + grpc_authorization_policy_provider* provider_; +}; + +TEST_P(SecureEnd2endTest, StaticInitAllowAuthorizedRequest) { + InitWithStaticData(*this, + "{" + " \"name\": \"authz\"," + " \"allow_rules\": [" + " {" + " \"name\": \"allow_foo\"," + " \"request\": {" + " \"paths\": [" + " \"*/foo\"" + " ]" + " }" + " }" + " ]" + "}"); + TestAllowAuthorizedRequest(*this); } -static void test_static_init_deny_request_no_match_in_policy( - const CoreTestConfiguration& config) { - const char* authz_policy = - "{" - " \"name\": \"authz\"," - " \"allow_rules\": [" - " {" - " \"name\": \"allow_bar\"," - " \"request\": {" - " \"paths\": [" - " \"*/bar\"" - " ]" - " }" - " }" - " ]" - "}"; - grpc_status_code code = GRPC_STATUS_OK; - const char* error_details; - grpc_authorization_policy_provider* provider = - grpc_authorization_policy_provider_static_data_create(authz_policy, &code, - &error_details); - GPR_ASSERT(GRPC_STATUS_OK == code); - grpc_arg args[] = { - grpc_channel_arg_pointer_create( - const_cast(GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER), provider, - grpc_authorization_policy_provider_arg_vtable()), - }; - grpc_channel_args server_args = {GPR_ARRAY_SIZE(args), args}; - - auto f = - begin_test(config, "test_static_init_deny_request_no_match_in_policy", - nullptr, &server_args); - grpc_authorization_policy_provider_release(provider); - test_deny_unauthorized_request(f.get()); +TEST_P(SecureEnd2endTest, StaticInitDenyUnauthorizedRequest) { + InitWithStaticData(*this, + "{" + " \"name\": \"authz\"," + " \"allow_rules\": [" + " {" + " \"name\": \"allow_bar\"," + " \"request\": {" + " \"paths\": [" + " \"*/bar\"" + " ]" + " }" + " }" + " ]," + " \"deny_rules\": [" + " {" + " \"name\": \"deny_foo\"," + " \"request\": {" + " \"paths\": [" + " \"*/foo\"" + " ]" + " }" + " }" + " ]" + "}"); + TestDenyUnauthorizedRequest(*this); } -static void test_file_watcher_init_allow_authorized_request( - const CoreTestConfiguration& config) { - const char* authz_policy = - "{" - " \"name\": \"authz\"," - " \"allow_rules\": [" - " {" - " \"name\": \"allow_foo\"," - " \"request\": {" - " \"paths\": [" - " \"*/foo\"" - " ]" - " }" - " }" - " ]" - "}"; - grpc_core::testing::TmpFile tmp_policy(authz_policy); - grpc_status_code code = GRPC_STATUS_OK; - const char* error_details; - grpc_authorization_policy_provider* provider = - grpc_authorization_policy_provider_file_watcher_create( - tmp_policy.name().c_str(), /*refresh_interval_sec=*/1, &code, - &error_details); - GPR_ASSERT(GRPC_STATUS_OK == code); - grpc_arg args[] = { - grpc_channel_arg_pointer_create( - const_cast(GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER), provider, - grpc_authorization_policy_provider_arg_vtable()), - }; - grpc_channel_args server_args = {GPR_ARRAY_SIZE(args), args}; - - auto f = begin_test(config, "test_file_watcher_init_allow_authorized_request", - nullptr, &server_args); - grpc_authorization_policy_provider_release(provider); - test_allow_authorized_request(f.get()); +TEST_P(SecureEnd2endTest, StaticInitDenyRequestNoMatchInPolicy) { + InitWithStaticData(*this, + "{" + " \"name\": \"authz\"," + " \"allow_rules\": [" + " {" + " \"name\": \"allow_bar\"," + " \"request\": {" + " \"paths\": [" + " \"*/bar\"" + " ]" + " }" + " }" + " ]" + "}"); + TestDenyUnauthorizedRequest(*this); } -static void test_file_watcher_init_deny_unauthorized_request( - const CoreTestConfiguration& config) { - const char* authz_policy = - "{" - " \"name\": \"authz\"," - " \"allow_rules\": [" - " {" - " \"name\": \"allow_bar\"," - " \"request\": {" - " \"paths\": [" - " \"*/bar\"" - " ]" - " }" - " }" - " ]," - " \"deny_rules\": [" - " {" - " \"name\": \"deny_foo\"," - " \"request\": {" - " \"paths\": [" - " \"*/foo\"" - " ]" - " }" - " }" - " ]" - "}"; - grpc_core::testing::TmpFile tmp_policy(authz_policy); - grpc_status_code code = GRPC_STATUS_OK; - const char* error_details; - grpc_authorization_policy_provider* provider = - grpc_authorization_policy_provider_file_watcher_create( - tmp_policy.name().c_str(), /*refresh_interval_sec=*/1, &code, - &error_details); - GPR_ASSERT(GRPC_STATUS_OK == code); - grpc_arg args[] = { - grpc_channel_arg_pointer_create( - const_cast(GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER), provider, - grpc_authorization_policy_provider_arg_vtable()), - }; - grpc_channel_args server_args = {GPR_ARRAY_SIZE(args), args}; - - auto f = - begin_test(config, "test_file_watcher_init_deny_unauthorized_request", - nullptr, &server_args); - grpc_authorization_policy_provider_release(provider); - test_deny_unauthorized_request(f.get()); +TEST_P(SecureEnd2endTest, FileWatcherInitAllowAuthorizedRequest) { + InitWithTempFile tmp_policy(*this, + "{" + " \"name\": \"authz\"," + " \"allow_rules\": [" + " {" + " \"name\": \"allow_foo\"," + " \"request\": {" + " \"paths\": [" + " \"*/foo\"" + " ]" + " }" + " }" + " ]" + "}"); + TestAllowAuthorizedRequest(*this); } -static void test_file_watcher_init_deny_request_no_match_in_policy( - const CoreTestConfiguration& config) { - const char* authz_policy = - "{" - " \"name\": \"authz\"," - " \"allow_rules\": [" - " {" - " \"name\": \"allow_bar\"," - " \"request\": {" - " \"paths\": [" - " \"*/bar\"" - " ]" - " }" - " }" - " ]" - "}"; - grpc_core::testing::TmpFile tmp_policy(authz_policy); - grpc_status_code code = GRPC_STATUS_OK; - const char* error_details; - grpc_authorization_policy_provider* provider = - grpc_authorization_policy_provider_file_watcher_create( - tmp_policy.name().c_str(), /*refresh_interval_sec=*/1, &code, - &error_details); - GPR_ASSERT(GRPC_STATUS_OK == code); - grpc_arg args[] = { - grpc_channel_arg_pointer_create( - const_cast(GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER), provider, - grpc_authorization_policy_provider_arg_vtable()), - }; - grpc_channel_args server_args = {GPR_ARRAY_SIZE(args), args}; - - auto f = begin_test(config, - "test_file_watcher_init_deny_request_no_match_in_policy", - nullptr, &server_args); - grpc_authorization_policy_provider_release(provider); - test_deny_unauthorized_request(f.get()); +TEST_P(SecureEnd2endTest, FileWatcherInitDenyUnauthorizedRequest) { + InitWithTempFile tmp_policy(*this, + "{" + " \"name\": \"authz\"," + " \"allow_rules\": [" + " {" + " \"name\": \"allow_bar\"," + " \"request\": {" + " \"paths\": [" + " \"*/bar\"" + " ]" + " }" + " }" + " ]," + " \"deny_rules\": [" + " {" + " \"name\": \"deny_foo\"," + " \"request\": {" + " \"paths\": [" + " \"*/foo\"" + " ]" + " }" + " }" + " ]" + "}"); + TestDenyUnauthorizedRequest(*this); } -static void test_file_watcher_valid_policy_reload( - const CoreTestConfiguration& config) { - const char* authz_policy = - "{" - " \"name\": \"authz\"," - " \"allow_rules\": [" - " {" - " \"name\": \"allow_foo\"," - " \"request\": {" - " \"paths\": [" - " \"*/foo\"" - " ]" - " }" - " }" - " ]" - "}"; - grpc_core::testing::TmpFile tmp_policy(authz_policy); - grpc_status_code code = GRPC_STATUS_OK; - const char* error_details; - grpc_authorization_policy_provider* provider = - grpc_authorization_policy_provider_file_watcher_create( - tmp_policy.name().c_str(), /*refresh_interval_sec=*/1, &code, - &error_details); - GPR_ASSERT(GRPC_STATUS_OK == code); - grpc_arg args[] = { - grpc_channel_arg_pointer_create( - const_cast(GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER), provider, - grpc_authorization_policy_provider_arg_vtable()), - }; - grpc_channel_args server_args = {GPR_ARRAY_SIZE(args), args}; +TEST_P(SecureEnd2endTest, FileWatcherInitDenyRequestNoMatchInPolicy) { + InitWithTempFile tmp_policy(*this, + "{" + " \"name\": \"authz\"," + " \"allow_rules\": [" + " {" + " \"name\": \"allow_bar\"," + " \"request\": {" + " \"paths\": [" + " \"*/bar\"" + " ]" + " }" + " }" + " ]" + "}"); + TestDenyUnauthorizedRequest(*this); +} - auto f = begin_test(config, "test_file_watcher_valid_policy_reload", nullptr, - &server_args); - grpc_authorization_policy_provider_release(provider); - test_allow_authorized_request(f.get()); - gpr_event on_reload_done; - gpr_event_init(&on_reload_done); - std::function callback = +TEST_P(SecureEnd2endTest, FileWatcherValidPolicyReload) { + InitWithTempFile tmp_policy(*this, + "{" + " \"name\": \"authz\"," + " \"allow_rules\": [" + " {" + " \"name\": \"allow_foo\"," + " \"request\": {" + " \"paths\": [" + " \"*/foo\"" + " ]" + " }" + " }" + " ]" + "}"); + TestAllowAuthorizedRequest(*this); + Notification on_reload_done; + tmp_policy.provider()->SetCallbackForTesting( [&on_reload_done](bool contents_changed, absl::Status status) { if (contents_changed) { - GPR_ASSERT(status.ok()); - gpr_event_set(&on_reload_done, reinterpret_cast(1)); + EXPECT_EQ(status, absl::OkStatus()); + on_reload_done.Notify(); } - }; - dynamic_cast(provider) - ->SetCallbackForTesting(std::move(callback)); + }); // Replace existing policy in file with a different authorization policy. - authz_policy = + tmp_policy.file().RewriteFile( "{" " \"name\": \"authz\"," " \"allow_rules\": [" @@ -530,142 +294,84 @@ static void test_file_watcher_valid_policy_reload( " }" " }" " ]" - "}"; - tmp_policy.RewriteFile(authz_policy); - GPR_ASSERT( - reinterpret_cast(1) == - gpr_event_wait(&on_reload_done, gpr_inf_future(GPR_CLOCK_MONOTONIC))); - test_deny_unauthorized_request(f.get()); - dynamic_cast(provider) - ->SetCallbackForTesting(nullptr); + "}"); + on_reload_done.WaitForNotification(); + TestDenyUnauthorizedRequest(*this); + tmp_policy.provider()->SetCallbackForTesting(nullptr); } -static void test_file_watcher_invalid_policy_skip_reload( - const CoreTestConfiguration& config) { - const char* authz_policy = - "{" - " \"name\": \"authz\"," - " \"allow_rules\": [" - " {" - " \"name\": \"allow_foo\"," - " \"request\": {" - " \"paths\": [" - " \"*/foo\"" - " ]" - " }" - " }" - " ]" - "}"; - grpc_core::testing::TmpFile tmp_policy(authz_policy); - grpc_status_code code = GRPC_STATUS_OK; - const char* error_details; - grpc_authorization_policy_provider* provider = - grpc_authorization_policy_provider_file_watcher_create( - tmp_policy.name().c_str(), /*refresh_interval_sec=*/1, &code, - &error_details); - GPR_ASSERT(GRPC_STATUS_OK == code); - grpc_arg args[] = { - grpc_channel_arg_pointer_create( - const_cast(GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER), provider, - grpc_authorization_policy_provider_arg_vtable()), - }; - grpc_channel_args server_args = {GPR_ARRAY_SIZE(args), args}; - - auto f = begin_test(config, "test_file_watcher_invalid_policy_skip_reload", - nullptr, &server_args); - grpc_authorization_policy_provider_release(provider); - test_allow_authorized_request(f.get()); - gpr_event on_reload_done; - gpr_event_init(&on_reload_done); - std::function callback = +TEST_P(SecureEnd2endTest, FileWatcherInvalidPolicySkipReload) { + InitWithTempFile tmp_policy(*this, + "{" + " \"name\": \"authz\"," + " \"allow_rules\": [" + " {" + " \"name\": \"allow_foo\"," + " \"request\": {" + " \"paths\": [" + " \"*/foo\"" + " ]" + " }" + " }" + " ]" + "}"); + TestAllowAuthorizedRequest(*this); + Notification on_reload_done; + tmp_policy.provider()->SetCallbackForTesting( [&on_reload_done](bool contents_changed, absl::Status status) { if (contents_changed) { - GPR_ASSERT(absl::StatusCode::kInvalidArgument == status.code()); - GPR_ASSERT("\"name\" field is not present." == status.message()); - gpr_event_set(&on_reload_done, reinterpret_cast(1)); + EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(status.message(), "\"name\" field is not present."); + on_reload_done.Notify(); } - }; - dynamic_cast(provider) - ->SetCallbackForTesting(std::move(callback)); + }); // Replace exisiting policy in file with an invalid policy. - authz_policy = "{}"; - tmp_policy.RewriteFile(authz_policy); - GPR_ASSERT( - reinterpret_cast(1) == - gpr_event_wait(&on_reload_done, gpr_inf_future(GPR_CLOCK_MONOTONIC))); - test_allow_authorized_request(f.get()); - dynamic_cast(provider) - ->SetCallbackForTesting(nullptr); + tmp_policy.file().RewriteFile("{}"); + on_reload_done.WaitForNotification(); + TestAllowAuthorizedRequest(*this); + tmp_policy.provider()->SetCallbackForTesting(nullptr); } -static void test_file_watcher_recovers_from_failure( - const CoreTestConfiguration& config) { - const char* authz_policy = - "{" - " \"name\": \"authz\"," - " \"allow_rules\": [" - " {" - " \"name\": \"allow_foo\"," - " \"request\": {" - " \"paths\": [" - " \"*/foo\"" - " ]" - " }" - " }" - " ]" - "}"; - grpc_core::testing::TmpFile tmp_policy(authz_policy); - grpc_status_code code = GRPC_STATUS_OK; - const char* error_details; - grpc_authorization_policy_provider* provider = - grpc_authorization_policy_provider_file_watcher_create( - tmp_policy.name().c_str(), /*refresh_interval_sec=*/1, &code, - &error_details); - GPR_ASSERT(GRPC_STATUS_OK == code); - grpc_arg args[] = { - grpc_channel_arg_pointer_create( - const_cast(GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER), provider, - grpc_authorization_policy_provider_arg_vtable()), - }; - grpc_channel_args server_args = {GPR_ARRAY_SIZE(args), args}; - - auto f = begin_test(config, "test_file_watcher_recovers_from_failure", - nullptr, &server_args); - grpc_authorization_policy_provider_release(provider); - test_allow_authorized_request(f.get()); - gpr_event on_first_reload_done; - gpr_event_init(&on_first_reload_done); - std::function callback1 = +TEST_P(SecureEnd2endTest, FileWatcherRecoversFromFailure) { + InitWithTempFile tmp_policy(*this, + "{" + " \"name\": \"authz\"," + " \"allow_rules\": [" + " {" + " \"name\": \"allow_foo\"," + " \"request\": {" + " \"paths\": [" + " \"*/foo\"" + " ]" + " }" + " }" + " ]" + "}"); + TestAllowAuthorizedRequest(*this); + Notification on_first_reload_done; + tmp_policy.provider()->SetCallbackForTesting( [&on_first_reload_done](bool contents_changed, absl::Status status) { if (contents_changed) { - GPR_ASSERT(absl::StatusCode::kInvalidArgument == status.code()); - GPR_ASSERT("\"name\" field is not present." == status.message()); - gpr_event_set(&on_first_reload_done, reinterpret_cast(1)); + EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(status.message(), "\"name\" field is not present."); + on_first_reload_done.Notify(); } - }; - dynamic_cast(provider) - ->SetCallbackForTesting(std::move(callback1)); + }); // Replace exisiting policy in file with an invalid policy. - authz_policy = "{}"; - tmp_policy.RewriteFile(authz_policy); - GPR_ASSERT(reinterpret_cast(1) == - gpr_event_wait(&on_first_reload_done, - gpr_inf_future(GPR_CLOCK_MONOTONIC))); - test_allow_authorized_request(f.get()); - gpr_event on_second_reload_done; - gpr_event_init(&on_second_reload_done); - std::function callback2 = + tmp_policy.file().RewriteFile("{}"); + on_first_reload_done.WaitForNotification(); + TestAllowAuthorizedRequest(*this); + Notification on_second_reload_done; + tmp_policy.provider()->SetCallbackForTesting( [&on_second_reload_done](bool contents_changed, absl::Status status) { if (contents_changed) { - GPR_ASSERT(status.ok()); - gpr_event_set(&on_second_reload_done, reinterpret_cast(1)); + EXPECT_EQ(status, absl::OkStatus()); + on_second_reload_done.Notify(); } - }; - dynamic_cast(provider) - ->SetCallbackForTesting(std::move(callback2)); + }); // Recover from reload errors, by replacing invalid policy in file with a // valid policy. - authz_policy = + tmp_policy.file().RewriteFile( "{" " \"name\": \"authz\"," " \"allow_rules\": [" @@ -688,26 +394,11 @@ static void test_file_watcher_recovers_from_failure( " }" " }" " ]" - "}"; - tmp_policy.RewriteFile(authz_policy); - GPR_ASSERT(reinterpret_cast(1) == - gpr_event_wait(&on_second_reload_done, - gpr_inf_future(GPR_CLOCK_MONOTONIC))); - test_deny_unauthorized_request(f.get()); - dynamic_cast(provider) - ->SetCallbackForTesting(nullptr); -} - -void grpc_authz(const CoreTestConfiguration& config) { - test_static_init_allow_authorized_request(config); - test_static_init_deny_unauthorized_request(config); - test_static_init_deny_request_no_match_in_policy(config); - test_file_watcher_init_allow_authorized_request(config); - test_file_watcher_init_deny_unauthorized_request(config); - test_file_watcher_init_deny_request_no_match_in_policy(config); - test_file_watcher_valid_policy_reload(config); - test_file_watcher_invalid_policy_skip_reload(config); - test_file_watcher_recovers_from_failure(config); + "}"); + on_second_reload_done.WaitForNotification(); + TestDenyUnauthorizedRequest(*this); + tmp_policy.provider()->SetCallbackForTesting(nullptr); } -void grpc_authz_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/high_initial_seqno.cc b/test/core/end2end/tests/high_initial_seqno.cc index 88eb8db09fe01..f73efb3c8edb2 100644 --- a/test/core/end2end/tests/high_initial_seqno.cc +++ b/test/core/end2end/tests/high_initial_seqno.cc @@ -16,175 +16,60 @@ // // -#include - -#include -#include -#include - -#include "absl/strings/str_cat.h" +#include "gtest/gtest.h" #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static void simple_request_body(const CoreTestConfiguration& /*config*/, - CoreTestFixture* f) { - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); +namespace grpc_core { +namespace { + +void SimpleRequest(CoreEnd2endTest& test) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); // TODO(ctiller): this rate limits the test, and it should be removed when // retry has been implemented; until then cross-thread chatter // may result in some requests needing to be cancelled due to // seqno exhaustion. - cqv.VerifyEmpty(); + test.Step(); } -static void test_invoke_10_simple_requests(const CoreTestConfiguration& config, - int initial_sequence_number) { - int i; - - grpc_arg client_arg; - grpc_channel_args client_args; - - client_arg.type = GRPC_ARG_INTEGER; - client_arg.key = const_cast(GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER); - client_arg.value.integer = initial_sequence_number; - - client_args.num_args = 1; - client_args.args = &client_arg; - - std::string name = absl::StrCat("test_invoke_requests first_seqno=", - initial_sequence_number); - auto f = begin_test(config, name.c_str(), &client_args, nullptr); - for (i = 0; i < 10; i++) { - simple_request_body(config, f.get()); - gpr_log(GPR_INFO, "Running test: Passed simple request %d", i); +void TenRequests(CoreEnd2endTest& test, int initial_sequence_number) { + test.InitServer(ChannelArgs()); + test.InitClient(ChannelArgs().Set(GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER, + initial_sequence_number)); + for (int i = 0; i < 10; i++) { + SimpleRequest(test); } } -void high_initial_seqno(const CoreTestConfiguration& config) { - test_invoke_10_simple_requests(config, 16777213); - if (config.feature_mask & FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION) { - test_invoke_10_simple_requests(config, 2147483645); - } -} +TEST_P(Http2Test, HighInitialSeqno) { TenRequests(*this, 16777213); } +TEST_P(RetryHttp2Test, HighInitialSeqno) { TenRequests(*this, 2147483645); } -void high_initial_seqno_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/hpack_size.cc b/test/core/end2end/tests/hpack_size.cc index 11cb650f29188..b55d993063137 100644 --- a/test/core/end2end/tests/hpack_size.cc +++ b/test/core/end2end/tests/hpack_size.cc @@ -17,337 +17,207 @@ // #include -#include -#include -#include -#include -#include +#include +#include -#include "absl/strings/str_format.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "gtest/gtest.h" #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "src/core/lib/gprpp/time.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/no_destruct.h" #include "test/core/end2end/end2end_tests.h" -const char* hobbits[][2] = { - {"Adaldrida", "Brandybuck"}, {"Adamanta", "Took"}, - {"Adalgrim", "Took"}, {"Adelard", "Took"}, - {"Amaranth", "Brandybuck"}, {"Andwise", "Roper"}, - {"Angelica", "Baggins"}, {"Asphodel", "Burrows"}, - {"Balbo", "Baggins"}, {"Bandobras", "Took"}, - {"Belba", "Bolger"}, {"Bell", "Gamgee"}, - {"Belladonna", "Baggins"}, {"Berylla", "Baggins"}, - {"Bilbo", "Baggins"}, {"Bilbo", "Gardner"}, - {"Bill", "Butcher"}, {"Bingo", "Baggins"}, - {"Bodo", "Proudfoot"}, {"Bowman", "Cotton"}, - {"Bungo", "Baggins"}, {"Camellia", "Sackville"}, - {"Carl", "Cotton"}, {"Celandine", "Brandybuck"}, - {"Chica", "Baggins"}, {"Daddy", "Twofoot"}, - {"Daisy", "Boffin"}, {"Diamond", "Took"}, - {"Dinodas", "Brandybuck"}, {"Doderic", "Brandybuck"}, - {"Dodinas", "Brandybuck"}, {"Donnamira", "Boffin"}, - {"Dora", "Baggins"}, {"Drogo", "Baggins"}, - {"Dudo", "Baggins"}, {"Eglantine", "Took"}, - {"Elanor", "Fairbairn"}, {"Elfstan", "Fairbairn"}, - {"Esmeralda", "Brandybuck"}, {"Estella", "Brandybuck"}, - {"Everard", "Took"}, {"Falco", "Chubb-Baggins"}, - {"Faramir", "Took"}, {"Farmer", "Maggot"}, - {"Fastolph", "Bolger"}, {"Ferdibrand", "Took"}, - {"Ferdinand", "Took"}, {"Ferumbras", "Took"}, - {"Ferumbras", "Took"}, {"Filibert", "Bolger"}, - {"Firiel", "Fairbairn"}, {"Flambard", "Took"}, - {"Folco", "Boffin"}, {"Fortinbras", "Took"}, - {"Fortinbras", "Took"}, {"Fosco", "Baggins"}, - {"Fredegar", "Bolger"}, {"Frodo", "Baggins"}, - {"Frodo", "Gardner"}, {"Gerontius", "Took"}, - {"Gilly", "Baggins"}, {"Goldilocks", "Took"}, - {"Gorbadoc", "Brandybuck"}, {"Gorbulas", "Brandybuck"}, - {"Gorhendad", "Brandybuck"}, {"Gormadoc", "Brandybuck"}, - {"Griffo", "Boffin"}, {"Halfast", "Gamgee"}, - {"Halfred", "Gamgee"}, {"Halfred", "Greenhand"}, - {"Hanna", "Brandybuck"}, {"Hamfast", "Gamgee"}, - {"Hamfast", "Gardner"}, {"Hamson", "Gamgee"}, - {"Harding", "Gardner"}, {"Hilda", "Brandybuck"}, - {"Hildibrand", "Took"}, {"Hildifons", "Took"}, - {"Hildigard", "Took"}, {"Hildigrim", "Took"}, - {"Hob", "Gammidge"}, {"Hob", "Hayward"}, - {"Hobson", "Gamgee"}, {"Holfast", "Gardner"}, - {"Holman", "Cotton"}, {"Holman", "Greenhand"}, - {"Hugo", "Boffin"}, {"Hugo", "Bracegirdle"}, - {"Ilberic", "Brandybuck"}, {"Isembard", "Took"}, - {"Isembold", "Took"}, {"Isengar", "Took"}, - {"Isengrim", "Took"}, {"Isengrim", "Took"}, - {"Isumbras", "Took"}, {"Isumbras", "Took"}, - {"Jolly", "Cotton"}, - // - //{"Lalia", "Took"}, - //{"Largo", "Baggins"}, - //{"Laura", "Baggins"}, - //{"Lily", "Goodbody"}, - //{"Lily", "Cotton"}, - //{"Linda", "Proudfoot"}, - //{"Lobelia", "Sackville-Baggins"}, - //{"Longo", "Baggins"}, - //{"Lotho", "Sackville-Baggins"}, - //{"Madoc", "Brandybuck"}, - //{"Malva", "Brandybuck"}, - //{"Marigold", "Cotton"}, - //{"Marmadas", "Brandybuck"}, - //{"Marmadoc", "Brandybuck"}, - //{"Marroc", "Brandybuck"}, - //{"May", "Gamgee"}, - //{"Melilot", "Brandybuck"}, - //{"Menegilda", "Brandybuck"}, - //{"Mentha", "Brandybuck"}, - //{"Meriadoc", "Brandybuck"}, - //{"Merimac", "Brandybuck"}, - //{"Merimas", "Brandybuck"}, - //{"Merry", "Gardner"}, - //{"Milo", "Burrows"}, - //{"Mimosa", "Baggins"}, - //{"Minto", "Burrows"}, - //{"Mirabella", "Brandybuck"}, - //{"Moro", "Burrows"}, - //{"Mosco", "Burrows"}, - //{"Mungo", "Baggins"}, - //{"Myrtle", "Burrows"}, - //{"Odo", "Proudfoot"}, - //{"Odovacar", "Bolger"}, - //{"Olo", "Proudfoot"}, - //{"Orgulas", "Brandybuck"}, - //{"Otho", "Sackville-Baggins"}, - //{"Paladin", "Took"}, - //{"Pansy", "Bolger"}, - //{"Pearl", "Took"}, - //{"Peony", "Burrows"}, - //{"Peregrin", "Took"}, - //{"Pervinca", "Took"}, - //{"Pimpernel", "Took"}, - //{"Pippin", "Gardner"}, - //{"Polo", "Baggins"}, - //{"Ponto", "Baggins"}, - //{"Porto", "Baggins"}, - //{"Posco", "Baggins"}, - //{"Poppy", "Bolger"}, - //{"Primrose", "Gardner"}, - //{"Primula", "Baggins"}, - //{"Prisca", "Bolger"}, - //{"Reginard", "Took"}, - //{"Robin", "Smallburrow"}, - //{"Robin", "Gardner"}, - //{"Rorimac", "Brandybuck"}, - //{"Rosa", "Took"}, - //{"Rosamunda", "Bolger"}, - //{"Rose", "Gardner"}, - //{"Ruby", "Baggins"}, - //{"Ruby", "Gardner"}, - //{"Rudigar", "Bolger"}, - //{"Rufus", "Burrows"}, - //{"Sadoc", "Brandybuck"}, - //{"Salvia", "Bolger"}, - //{"Samwise", "Gamgee"}, - //{"Sancho", "Proudfoot"}, - //{"Saradas", "Brandybuck"}, - //{"Saradoc", "Brandybuck"}, - //{"Seredic", "Brandybuck"}, - //{"Sigismond", "Took"}, - //{"Smeagol", "Gollum"}, - //{"Tanta", "Baggins"}, - //{"Ted", "Sandyman"}, - //{"Tobold", "Hornblower"}, - //{"Togo", "Goodbody"}, - //{"Tolman", "Cotton"}, - //{"Tolman", "Gardner"}, - //{"Widow", "Rumble"}, - //{"Wilcome", "Cotton"}, - //{"Wilcome", "Cotton"}, - //{"Wilibald", "Bolger"}, - //{"Will", "Whitfoot"}, - //{"Wiseman", "Gamwich"} -}; - -const char* dragons[] = {"Ancalagon", "Glaurung", "Scatha", - "Smaug the Magnificent"}; - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; +namespace grpc_core { +namespace { +const NoDestruct>> + hobbits(std::vector>{ + {"Adaldrida", "Brandybuck"}, {"Adamanta", "Took"}, + {"Adalgrim", "Took"}, {"Adelard", "Took"}, + {"Amaranth", "Brandybuck"}, {"Andwise", "Roper"}, + {"Angelica", "Baggins"}, {"Asphodel", "Burrows"}, + {"Balbo", "Baggins"}, {"Bandobras", "Took"}, + {"Belba", "Bolger"}, {"Bell", "Gamgee"}, + {"Belladonna", "Baggins"}, {"Berylla", "Baggins"}, + {"Bilbo", "Baggins"}, {"Bilbo", "Gardner"}, + {"Bill", "Butcher"}, {"Bingo", "Baggins"}, + {"Bodo", "Proudfoot"}, {"Bowman", "Cotton"}, + {"Bungo", "Baggins"}, {"Camellia", "Sackville"}, + {"Carl", "Cotton"}, {"Celandine", "Brandybuck"}, + {"Chica", "Baggins"}, {"Daddy", "Twofoot"}, + {"Daisy", "Boffin"}, {"Diamond", "Took"}, + {"Dinodas", "Brandybuck"}, {"Doderic", "Brandybuck"}, + {"Dodinas", "Brandybuck"}, {"Donnamira", "Boffin"}, + {"Dora", "Baggins"}, {"Drogo", "Baggins"}, + {"Dudo", "Baggins"}, {"Eglantine", "Took"}, + {"Elanor", "Fairbairn"}, {"Elfstan", "Fairbairn"}, + {"Esmeralda", "Brandybuck"}, {"Estella", "Brandybuck"}, + {"Everard", "Took"}, {"Falco", "Chubb-Baggins"}, + {"Faramir", "Took"}, {"Farmer", "Maggot"}, + {"Fastolph", "Bolger"}, {"Ferdibrand", "Took"}, + {"Ferdinand", "Took"}, {"Ferumbras", "Took"}, + {"Ferumbras", "Took"}, {"Filibert", "Bolger"}, + {"Firiel", "Fairbairn"}, {"Flambard", "Took"}, + {"Folco", "Boffin"}, {"Fortinbras", "Took"}, + {"Fortinbras", "Took"}, {"Fosco", "Baggins"}, + {"Fredegar", "Bolger"}, {"Frodo", "Baggins"}, + {"Frodo", "Gardner"}, {"Gerontius", "Took"}, + {"Gilly", "Baggins"}, {"Goldilocks", "Took"}, + {"Gorbadoc", "Brandybuck"}, {"Gorbulas", "Brandybuck"}, + {"Gorhendad", "Brandybuck"}, {"Gormadoc", "Brandybuck"}, + {"Griffo", "Boffin"}, {"Halfast", "Gamgee"}, + {"Halfred", "Gamgee"}, {"Halfred", "Greenhand"}, + {"Hanna", "Brandybuck"}, {"Hamfast", "Gamgee"}, + {"Hamfast", "Gardner"}, {"Hamson", "Gamgee"}, + {"Harding", "Gardner"}, {"Hilda", "Brandybuck"}, + {"Hildibrand", "Took"}, {"Hildifons", "Took"}, + {"Hildigard", "Took"}, {"Hildigrim", "Took"}, + {"Hob", "Gammidge"}, {"Hob", "Hayward"}, + {"Hobson", "Gamgee"}, {"Holfast", "Gardner"}, + {"Holman", "Cotton"}, {"Holman", "Greenhand"}, + {"Hugo", "Boffin"}, {"Hugo", "Bracegirdle"}, + {"Ilberic", "Brandybuck"}, {"Isembard", "Took"}, + {"Isembold", "Took"}, {"Isengar", "Took"}, + {"Isengrim", "Took"}, {"Isengrim", "Took"}, + {"Isumbras", "Took"}, {"Isumbras", "Took"}, + {"Jolly", "Cotton"}, + }); + +const NoDestruct> dragons( + std::vector{"Ancalagon", "Glaurung", "Scatha", + "Smaug the Magnificent"}); + +void SimpleRequestBody(CoreEnd2endTest& test, size_t index) { + const auto& hobbit = (*hobbits)[index % hobbits->size()]; + const auto& dragon = (*dragons)[index % dragons->size()]; + auto method = + absl::StrCat("/", hobbit.first, ".", hobbit.second, "/", dragon); + auto c = test.NewClientCall(method).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({ + {"hobbit-first-name", (*hobbits)[index % hobbits->size()].first}, + {"hobbit-second-name", (*hobbits)[index % hobbits->size()].second}, + {"dragon", (*dragons)[index % dragons->size()]}, + }) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, dragon, {}) + .RecvCloseOnServer(client_close); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), dragon); + EXPECT_EQ(s.method(), method); + EXPECT_FALSE(client_close.was_cancelled()); } -static void simple_request_body(const CoreTestConfiguration& /*config*/, - CoreTestFixture* f, size_t index) { - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_metadata extra_metadata[3]; - grpc_slice details; - int was_cancelled = 2; - - memset(extra_metadata, 0, sizeof(extra_metadata)); - extra_metadata[0].key = grpc_slice_from_static_string("hobbit-first-name"); - extra_metadata[0].value = grpc_slice_from_static_string( - hobbits[index % GPR_ARRAY_SIZE(hobbits)][0]); - extra_metadata[1].key = grpc_slice_from_static_string("hobbit-second-name"); - extra_metadata[1].value = grpc_slice_from_static_string( - hobbits[index % GPR_ARRAY_SIZE(hobbits)][1]); - extra_metadata[2].key = grpc_slice_from_static_string("dragon"); - extra_metadata[2].value = - grpc_slice_from_static_string(dragons[index % GPR_ARRAY_SIZE(dragons)]); - - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, gpr_inf_future(GPR_CLOCK_MONOTONIC), - nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = GPR_ARRAY_SIZE(extra_metadata); - op->data.send_initial_metadata.metadata = extra_metadata; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(grpc_core::Duration::Seconds(120)); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(grpc_core::Duration::Seconds(120)); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -static void test_size(const CoreTestConfiguration& config, int encode_size, - int decode_size) { - size_t i; - - grpc_arg server_arg; - grpc_channel_args server_args; - grpc_arg client_arg; - grpc_channel_args client_args; - - server_arg.type = GRPC_ARG_INTEGER; - server_arg.key = const_cast(GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER); - server_arg.value.integer = decode_size; - server_args.num_args = 1; - server_args.args = &server_arg; - - client_arg.type = GRPC_ARG_INTEGER; - client_arg.key = const_cast(GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER); - client_arg.value.integer = encode_size; - client_args.num_args = 1; - client_args.args = &client_arg; - - std::string name = - absl::StrFormat("test_size:e=%d:d=%d", encode_size, decode_size); - auto f = begin_test(config, name.c_str(), - encode_size != 4096 ? &client_args : nullptr, - decode_size != 4096 ? &server_args : nullptr); - for (i = 0; i < 4 * GPR_ARRAY_SIZE(hobbits); i++) { - simple_request_body(config, f.get(), i); +void HpackSize(CoreEnd2endTest& test, int encode_size, int decode_size) { + // TODO(ctiller): right now the hpack encoder isn't compressing these, so this + // test doesn't do what we want - which is to test overflow the hpack table + // slot count. + test.InitServer( + ChannelArgs().Set(GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER, decode_size)); + test.InitClient( + ChannelArgs().Set(GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER, encode_size)); + for (size_t i = 0; i < 4 * hobbits->size(); i++) { + SimpleRequestBody(test, i); } } -void hpack_size(const CoreTestConfiguration& config) { - static const int interesting_sizes[] = {4096, 0, 100, - 1000, 32768, 4 * 1024 * 1024}; - size_t i, j; - - for (i = 0; i < GPR_ARRAY_SIZE(interesting_sizes); i++) { - for (j = 0; j < GPR_ARRAY_SIZE(interesting_sizes); j++) { - test_size(config, interesting_sizes[i], interesting_sizes[j]); - } - } +TEST_P(Http2SingleHopTest, Encode0Decode0) { HpackSize(*this, 0, 0); } +TEST_P(Http2SingleHopTest, Encode0Decode100) { HpackSize(*this, 0, 100); } +TEST_P(Http2SingleHopTest, Encode0Decode1000) { HpackSize(*this, 0, 1000); } +TEST_P(Http2SingleHopTest, Encode0Decode4096) { HpackSize(*this, 0, 4096); } +TEST_P(Http2SingleHopTest, Encode0Decode32768) { HpackSize(*this, 0, 32768); } +TEST_P(Http2SingleHopTest, Encode0Decode4194304) { + HpackSize(*this, 0, 4194304); +} +TEST_P(Http2SingleHopTest, Encode100Decode0) { HpackSize(*this, 100, 0); } +TEST_P(Http2SingleHopTest, Encode100Decode100) { HpackSize(*this, 100, 100); } +TEST_P(Http2SingleHopTest, Encode100Decode1000) { HpackSize(*this, 100, 1000); } +TEST_P(Http2SingleHopTest, Encode100Decode4096) { HpackSize(*this, 100, 4096); } +TEST_P(Http2SingleHopTest, Encode100Decode32768) { + HpackSize(*this, 100, 32768); +} +TEST_P(Http2SingleHopTest, Encode100Decode4194304) { + HpackSize(*this, 100, 4194304); +} +TEST_P(Http2SingleHopTest, Encode1000Decode0) { HpackSize(*this, 1000, 0); } +TEST_P(Http2SingleHopTest, Encode1000Decode100) { HpackSize(*this, 1000, 100); } +TEST_P(Http2SingleHopTest, Encode1000Decode1000) { + HpackSize(*this, 1000, 1000); +} +TEST_P(Http2SingleHopTest, Encode1000Decode4096) { + HpackSize(*this, 1000, 4096); +} +TEST_P(Http2SingleHopTest, Encode1000Decode32768) { + HpackSize(*this, 1000, 32768); +} +TEST_P(Http2SingleHopTest, Encode1000Decode4194304) { + HpackSize(*this, 1000, 4194304); +} +TEST_P(Http2SingleHopTest, Encode4096Decode0) { HpackSize(*this, 4096, 0); } +TEST_P(Http2SingleHopTest, Encode4096Decode100) { HpackSize(*this, 4096, 100); } +TEST_P(Http2SingleHopTest, Encode4096Decode1000) { + HpackSize(*this, 4096, 1000); +} +TEST_P(Http2SingleHopTest, Encode4096Decode4096) { + HpackSize(*this, 4096, 4096); +} +TEST_P(Http2SingleHopTest, Encode4096Decode32768) { + HpackSize(*this, 4096, 32768); +} +TEST_P(Http2SingleHopTest, Encode4096Decode4194304) { + HpackSize(*this, 4096, 4194304); +} +TEST_P(Http2SingleHopTest, Encode32768Decode0) { HpackSize(*this, 32768, 0); } +TEST_P(Http2SingleHopTest, Encode32768Decode100) { + HpackSize(*this, 32768, 100); +} +TEST_P(Http2SingleHopTest, Encode32768Decode1000) { + HpackSize(*this, 32768, 1000); +} +TEST_P(Http2SingleHopTest, Encode32768Decode4096) { + HpackSize(*this, 32768, 4096); +} +TEST_P(Http2SingleHopTest, Encode32768Decode32768) { + HpackSize(*this, 32768, 32768); +} +TEST_P(Http2SingleHopTest, Encode32768Decode4194304) { + HpackSize(*this, 32768, 4194304); +} +TEST_P(Http2SingleHopTest, Encode4194304Decode0) { + HpackSize(*this, 4194304, 0); +} +TEST_P(Http2SingleHopTest, Encode4194304Decode100) { + HpackSize(*this, 4194304, 100); +} +TEST_P(Http2SingleHopTest, Encode4194304Decode1000) { + HpackSize(*this, 4194304, 1000); +} +TEST_P(Http2SingleHopTest, Encode4194304Decode4096) { + HpackSize(*this, 4194304, 4096); +} +TEST_P(Http2SingleHopTest, Encode4194304Decode32768) { + HpackSize(*this, 4194304, 32768); +} +TEST_P(Http2SingleHopTest, Encode4194304Decode4194304) { + HpackSize(*this, 4194304, 4194304); } -void hpack_size_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/invoke_large_request.cc b/test/core/end2end/tests/invoke_large_request.cc index 8b91583c697a2..de6f6ffb3f28e 100644 --- a/test/core/end2end/tests/invoke_large_request.cc +++ b/test/core/end2end/tests/invoke_large_request.cc @@ -18,218 +18,60 @@ #include -#include -#include -#include -#include +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/debug/event_log.h" -#include "src/core/lib/gpr/useful.h" -#include "src/core/lib/gprpp/no_destruct.h" #include "src/core/lib/gprpp/time.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/slice/slice.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; +namespace grpc_core { +namespace { + +TEST_P(Http2SingleHopTest, InvokeLargeRequest) { + const size_t kMessageSize = 10 * 1024 * 1024; + InitServer( + ChannelArgs().Set(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, kMessageSize)); + InitClient( + ChannelArgs().Set(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, kMessageSize)); + auto c = NewClientCall("/foo").Timeout(Duration::Minutes(5)).Create(); + auto send_from_client = RandomSlice(kMessageSize); + auto send_from_server = RandomSlice(kMessageSize); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage(send_from_client.Ref()) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(Duration::Minutes(1)); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).SendInitialMetadata({}).RecvMessage(client_message); + Expect(102, true); + Step(Duration::Minutes(1)); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(103) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .SendMessage(send_from_server.Ref()) + .RecvCloseOnServer(client_close); + Expect(103, true); + Expect(1, true); + Step(Duration::Minutes(1)); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(client_message.payload(), send_from_client); + EXPECT_EQ(server_message.payload(), send_from_server); } -static grpc_slice make_slice(int message_size) { - GPR_ASSERT(message_size > 0); - grpc_slice slice = grpc_slice_malloc(message_size); - memset(GRPC_SLICE_START_PTR(slice), 'x', GRPC_SLICE_LENGTH(slice)); - return slice; -} - -static void test_invoke_large_request(const CoreTestConfiguration& config, - int message_size) { - grpc_arg args[1]; - args[0].type = GRPC_ARG_INTEGER; - args[0].key = const_cast(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH); - args[0].value.integer = message_size; - grpc_channel_args channel_args = {GPR_ARRAY_SIZE(args), args}; - - auto f = begin_test(config, "test_invoke_large_request", &channel_args, - &channel_args); - - grpc_slice request_payload_slice = make_slice(message_size); - grpc_slice response_payload_slice = make_slice(message_size); - grpc_call* c; - grpc_call* s; - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(300); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(grpc_core::Duration::Seconds(60)); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(grpc_core::Duration::Seconds(60)); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(grpc_core::Duration::Seconds(60)); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - grpc_slice_unref(request_payload_slice); - grpc_slice_unref(response_payload_slice); -} - -static grpc_core::NoDestruct g_event_log; - -void invoke_large_request(const CoreTestConfiguration& config) { - { - grpc_core::ExecCtx exec_ctx; - g_event_log->BeginCollection(); - } - test_invoke_large_request(config, 10 * 1024 * 1024); - std::vector events; - grpc_core::ExecCtx exec_ctx; - gpr_log(GPR_ERROR, "event_log:\n%s", - g_event_log - ->EndCollectionAndReportCsv({"logging", "tcp-write-outstanding"}) - .c_str()); -} - -void invoke_large_request_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/keepalive_timeout.cc b/test/core/end2end/tests/keepalive_timeout.cc index 278fbd8640998..164de997702fc 100644 --- a/test/core/end2end/tests/keepalive_timeout.cc +++ b/test/core/end2end/tests/keepalive_timeout.cc @@ -16,320 +16,96 @@ // // -#include - -#include -#include - #include "absl/strings/string_view.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include "src/core/ext/transport/chttp2/transport/frame_ping.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/config/config_vars.h" -#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/iomgr/port.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "%s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Client sends a request, then waits for the keepalive watchdog timeouts before // returning status. -static void test_keepalive_timeout(const CoreTestConfiguration& config) { - grpc_call* c; - - grpc_arg keepalive_arg_elems[3]; - keepalive_arg_elems[0].type = GRPC_ARG_INTEGER; - keepalive_arg_elems[0].key = const_cast(GRPC_ARG_KEEPALIVE_TIME_MS); - keepalive_arg_elems[0].value.integer = 10; - keepalive_arg_elems[1].type = GRPC_ARG_INTEGER; - keepalive_arg_elems[1].key = const_cast(GRPC_ARG_KEEPALIVE_TIMEOUT_MS); - keepalive_arg_elems[1].value.integer = 0; - keepalive_arg_elems[2].type = GRPC_ARG_INTEGER; - keepalive_arg_elems[2].key = const_cast(GRPC_ARG_HTTP2_BDP_PROBE); - keepalive_arg_elems[2].value.integer = 0; - grpc_channel_args keepalive_args = {GPR_ARRAY_SIZE(keepalive_arg_elems), - keepalive_arg_elems}; - - auto f = begin_test(config, "keepalive_timeout", &keepalive_args, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - +TEST_P(Http2SingleHopTest, KeepaliveTimeout) { // Disable ping ack to trigger the keepalive timeout - grpc_set_disable_ping_ack(true); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "keepalive watchdog timeout")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - - grpc_call_unref(c); + InitServer(ChannelArgs().Set("grpc.http2.ack_pings", false)); + InitClient(ChannelArgs() + .Set(GRPC_ARG_KEEPALIVE_TIME_MS, 10) + .Set(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 0) + .Set(GRPC_ARG_HTTP2_BDP_PROBE, false)); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNAVAILABLE); + EXPECT_EQ(server_status.message(), "keepalive watchdog timeout"); } // Verify that reads reset the keepalive ping timer. The client sends 30 pings // with a sleep of 10ms in between. It has a configured keepalive timer of // 200ms. In the success case, each ping ack should reset the keepalive timer so // that the keepalive ping is never sent. -static void test_read_delays_keepalive(const CoreTestConfiguration& config) { +TEST_P(Http2SingleHopTest, ReadDelaysKeepalive) { #ifdef GRPC_POSIX_SOCKET - /* It is hard to get the timing right for the polling engine poll. */ - if (grpc_core::ConfigVars::Get().PollStrategy() == "poll") return; + // It is hard to get the timing right for the polling engine poll. + if (ConfigVars::Get().PollStrategy() == "poll") { + GTEST_SKIP() << "Skipping test under poll poller"; + } #endif // GRPC_POSIX_SOCKET - const int kPingIntervalMS = 100; - grpc_arg keepalive_arg_elems[3]; - keepalive_arg_elems[0].type = GRPC_ARG_INTEGER; - keepalive_arg_elems[0].key = const_cast(GRPC_ARG_KEEPALIVE_TIME_MS); - keepalive_arg_elems[0].value.integer = 20 * kPingIntervalMS; - keepalive_arg_elems[1].type = GRPC_ARG_INTEGER; - keepalive_arg_elems[1].key = const_cast(GRPC_ARG_KEEPALIVE_TIMEOUT_MS); - keepalive_arg_elems[1].value.integer = 0; - keepalive_arg_elems[2].type = GRPC_ARG_INTEGER; - keepalive_arg_elems[2].key = const_cast(GRPC_ARG_HTTP2_BDP_PROBE); - keepalive_arg_elems[2].value.integer = 0; - grpc_channel_args keepalive_args = {GPR_ARRAY_SIZE(keepalive_arg_elems), - keepalive_arg_elems}; - auto f = begin_test(config, "test_read_delays_keepalive", &keepalive_args, - nullptr); + const auto kPingInterval = Duration::Milliseconds(100); // Disable ping ack to trigger the keepalive timeout - grpc_set_disable_ping_ack(true); - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - grpc_byte_buffer* request_payload; - grpc_byte_buffer* request_payload_recv; - grpc_byte_buffer* response_payload; - grpc_byte_buffer* response_payload_recv; - int i; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_slice response_payload_slice = - grpc_slice_from_copied_string("hello you"); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(100)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(100), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(101), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - for (i = 0; i < 30; i++) { - request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - response_payload = grpc_raw_byte_buffer_create(&response_payload_slice, 1); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); + InitServer(ChannelArgs().Set("grpc.http2.ack_pings", false)); + InitClient(ChannelArgs() + .Set(GRPC_ARG_KEEPALIVE_TIME_MS, (20 * kPingInterval).millis()) + .Set(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 0) + .Set(GRPC_ARG_HTTP2_BDP_PROBE, false)); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(100); + Expect(100, true); + Step(); + IncomingCloseOnServer client_close; + s.NewBatch(101).SendInitialMetadata({}).RecvCloseOnServer(client_close); + for (int i = 0; i < 30; i++) { + IncomingMessage server_message; + IncomingMessage client_message; + c.NewBatch(2).SendMessage("hello world").RecvMessage(server_message); + s.NewBatch(102).RecvMessage(client_message); + Expect(102, true); + Step(); + s.NewBatch(103).SendMessage("hello you"); + Expect(103, true); + Expect(2, true); + Step(); // Sleep for a short interval to check if the client sends any pings - gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(kPingIntervalMS)); + Step(kPingInterval); } - - grpc_slice_unref(request_payload_slice); - grpc_slice_unref(response_payload_slice); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(104), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Expect(grpc_core::CqVerifier::tag(104), true); - cqv.Verify(); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_slice_unref(details); -} - -void keepalive_timeout(const CoreTestConfiguration& config) { - test_keepalive_timeout(config); - test_read_delays_keepalive(config); + c.NewBatch(3).SendCloseFromClient(); + s.NewBatch(104).SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}); + Expect(1, true); + Expect(3, true); + Expect(101, true); + Expect(104, true); + Step(); } -void keepalive_timeout_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/large_metadata.cc b/test/core/end2end/tests/large_metadata.cc index 28cef445a0d9a..6ae8adb512a0e 100644 --- a/test/core/end2end/tests/large_metadata.cc +++ b/test/core/end2end/tests/large_metadata.cc @@ -17,544 +17,211 @@ // #include -#include -#include -#include -#include +#include + +#include "absl/strings/string_view.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static grpc_status_code send_metadata(CoreTestFixture* f, - const size_t metadata_size, - grpc_slice* client_details) { - grpc_core::CqVerifier cqv(f->cq()); - grpc_call* c; - grpc_call* s; - grpc_metadata meta; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - int was_cancelled = 2; - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - // Add metadata of size `metadata_size`. - meta.key = grpc_slice_from_static_string("key"); - meta.value = grpc_slice_malloc(metadata_size); - memset(GRPC_SLICE_START_PTR(meta.value), 'a', metadata_size); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - // Client: wait on initial metadata from server. - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = client_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - // Server: send metadata of size `metadata_size`. - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 1; - op->data.send_initial_metadata.metadata = &meta; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_slice_unref(meta.value); - - return status; -} +namespace grpc_core { +namespace { + +class LargeMetadataTest { + public: + LargeMetadataTest(Http2SingleHopTest& test, const ChannelArgs& args) + : test_(test) { + test_.InitClient(args); + test_.InitServer(args); + } + + int PerformRequests(size_t metadata_size, int count) { + int num_requests_accepted = 0; + for (int i = 0; i < count; ++i) { + auto status = PerformOneRequest(metadata_size); + if (status.status() == GRPC_STATUS_RESOURCE_EXHAUSTED) { + EXPECT_THAT( + status.message(), + ::testing::StartsWith("received initial metadata size exceeds")); + } else { + num_requests_accepted++; + EXPECT_EQ(status.status(), GRPC_STATUS_OK); + EXPECT_EQ(status.message(), "xyz"); + } + } + return num_requests_accepted; + } + + private: + CoreEnd2endTest::IncomingStatusOnClient PerformOneRequest( + const size_t metadata_size) { + auto c = test_.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test_.RequestCall(101); + test_.Expect(101, true); + test_.Step(); + // Server: send metadata of size `metadata_size`. + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({{"key", std::string(metadata_size, 'a')}}) + .RecvCloseOnServer(client_close) + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + test_.Expect(102, true); + test_.Expect(1, true); + test_.Step(); + return server_status; + } + + Http2SingleHopTest& test_; +}; // Server responds with metadata under soft limit of what client accepts. No // requests should be rejected. -static void test_request_with_large_metadata_under_soft_limit( - const CoreTestConfiguration& config) { +TEST_P(Http2SingleHopTest, RequestWithLargeMetadataUnderSoftLimit) { const size_t soft_limit = 32 * 1024; const size_t hard_limit = 45 * 1024; const size_t metadata_size = soft_limit; - grpc_arg arg[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_MAX_METADATA_SIZE), soft_limit + 1024), - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE), - hard_limit + 1024)}; - grpc_channel_args args = {GPR_ARRAY_SIZE(arg), arg}; - auto f = - begin_test(config, "test_request_with_large_metadata_under_soft_limit", - &args, &args); - for (int i = 0; i < 100; i++) { - grpc_slice client_details; - auto status = send_metadata(f.get(), metadata_size, &client_details); - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(client_details, "xyz")); - grpc_slice_unref(client_details); - } + LargeMetadataTest test( + *this, ChannelArgs() + .Set(GRPC_ARG_MAX_METADATA_SIZE, soft_limit + 1024) + .Set(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE, hard_limit + 1024)); + EXPECT_EQ(test.PerformRequests(metadata_size, 100), 100); } // Server responds with metadata between soft and hard limits of what client // accepts. Some requests should be rejected. -static void test_request_with_large_metadata_between_soft_and_hard_limits( - const CoreTestConfiguration& config) { +TEST_P(Http2SingleHopTest, RequestWithLargeMetadataBetweenSoftAndHardLimits) { const size_t soft_limit = 32 * 1024; const size_t hard_limit = 45 * 1024; const size_t metadata_size = (soft_limit + hard_limit) / 2; - grpc_arg arg[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_MAX_METADATA_SIZE), soft_limit + 1024), - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE), - hard_limit + 1024)}; - grpc_channel_args args = {GPR_ARRAY_SIZE(arg), arg}; - auto f = begin_test( - config, "test_request_with_large_metadata_between_soft_and_hard_limits", - &args, &args); - - int num_requests_rejected = 0; - for (int i = 0; i < 100; i++) { - grpc_slice client_details; - auto status = send_metadata(f.get(), metadata_size, &client_details); - if (status == GRPC_STATUS_RESOURCE_EXHAUSTED) { - num_requests_rejected++; - const char* expected_error = - "received initial metadata size exceeds soft limit"; - grpc_slice actual_error = - grpc_slice_split_head(&client_details, strlen(expected_error)); - GPR_ASSERT(0 == grpc_slice_str_cmp(actual_error, expected_error)); - grpc_slice_unref(actual_error); - } else { - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(client_details, "xyz")); - } - grpc_slice_unref(client_details); - } - - // Check that some requests were rejected. - GPR_ASSERT(abs(num_requests_rejected - 50) <= 45); + LargeMetadataTest test( + *this, ChannelArgs() + .Set(GRPC_ARG_MAX_METADATA_SIZE, soft_limit + 1024) + .Set(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE, hard_limit + 1024)); + EXPECT_THAT(test.PerformRequests(metadata_size, 100), + ::testing::AllOf(::testing::Ge(5), ::testing::Le(95))); } // Server responds with metadata above hard limit of what the client accepts. // All requests should be rejected. -static void test_request_with_large_metadata_above_hard_limit( - const CoreTestConfiguration& config) { +TEST_P(Http2SingleHopTest, RequestWithLargeMetadataAboveHardLimit) { const size_t soft_limit = 32 * 1024; const size_t hard_limit = 45 * 1024; - const size_t metadata_size = hard_limit * 1.5; - grpc_arg arg[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_MAX_METADATA_SIZE), soft_limit + 1024), - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE), - hard_limit + 1024)}; - grpc_channel_args args = {GPR_ARRAY_SIZE(arg), arg}; - auto f = - begin_test(config, "test_request_with_large_metadata_above_hard_limit", - &args, &args); - - for (int i = 0; i < 100; i++) { - grpc_slice client_details; - auto status = send_metadata(f.get(), metadata_size, &client_details); - GPR_ASSERT(status == GRPC_STATUS_RESOURCE_EXHAUSTED); - const char* expected_error = - "received initial metadata size exceeds hard limit"; - grpc_slice actual_error = - grpc_slice_split_head(&client_details, strlen(expected_error)); - GPR_ASSERT(0 == grpc_slice_str_cmp(actual_error, expected_error)); - grpc_slice_unref(actual_error); - grpc_slice_unref(client_details); - } + const size_t metadata_size = hard_limit * 3 / 2; + LargeMetadataTest test( + *this, ChannelArgs() + .Set(GRPC_ARG_MAX_METADATA_SIZE, soft_limit + 1024) + .Set(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE, hard_limit + 1024)); + EXPECT_EQ(test.PerformRequests(metadata_size, 100), 0); } // Set soft limit higher than hard limit. All requests above hard limit should // be rejected, all requests below hard limit should be accepted (soft limit // should not be respected). -static void test_request_with_large_metadata_soft_limit_above_hard_limit( - const CoreTestConfiguration& config) { +TEST_P(Http2SingleHopTest, RequestWithLargeMetadataSoftLimitAboveHardLimit) { const size_t soft_limit = 64 * 1024; const size_t hard_limit = 32 * 1024; const size_t metadata_size_below_hard_limit = hard_limit; const size_t metadata_size_above_hard_limit = hard_limit * 2; - grpc_arg arg[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_MAX_METADATA_SIZE), soft_limit + 1024), - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE), - hard_limit + 1024)}; - grpc_channel_args args = {GPR_ARRAY_SIZE(arg), arg}; - auto f = begin_test( - config, "test_request_with_large_metadata_soft_limit_above_hard_limit", - &args, &args); - + LargeMetadataTest test( + *this, ChannelArgs() + .Set(GRPC_ARG_MAX_METADATA_SIZE, soft_limit + 1024) + .Set(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE, hard_limit + 1024)); // Send 50 requests below hard limit. Should be accepted. - for (int i = 0; i < 50; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_below_hard_limit, &client_details); - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(client_details, "xyz")); - grpc_slice_unref(client_details); - } - + EXPECT_EQ(test.PerformRequests(metadata_size_below_hard_limit, 50), 50); // Send 50 requests above hard limit. Should be rejected. - for (int i = 0; i < 50; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_above_hard_limit, &client_details); - GPR_ASSERT(status == GRPC_STATUS_RESOURCE_EXHAUSTED); - const char* expected_error = - "received initial metadata size exceeds hard limit"; - grpc_slice actual_error = - grpc_slice_split_head(&client_details, strlen(expected_error)); - GPR_ASSERT(0 == grpc_slice_str_cmp(actual_error, expected_error)); - grpc_slice_unref(actual_error); - grpc_slice_unref(client_details); - } + EXPECT_EQ(test.PerformRequests(metadata_size_above_hard_limit, 50), 0); } // Set soft limit * 1.25 higher than default hard limit and do not set hard // limit. Soft limit * 1.25 should be used as hard limit. -static void test_request_with_large_metadata_soft_limit_overrides_default_hard( - const CoreTestConfiguration& config) { +TEST_P(Http2SingleHopTest, + RequestWithLargeMetadataSoftLimitOverridesDefaultHard) { const size_t soft_limit = 64 * 1024; const size_t metadata_size_below_soft_limit = soft_limit; const size_t metadata_size_above_hard_limit = soft_limit * 1.5; const size_t metadata_size_between_limits = (soft_limit + soft_limit * 1.25) / 2; - grpc_arg arg[] = {grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_MAX_METADATA_SIZE), soft_limit + 1024)}; - grpc_channel_args args = {GPR_ARRAY_SIZE(arg), arg}; - auto f = begin_test( - config, - "test_request_with_large_metadata_soft_limit_overrides_default_hard", - &args, &args); - + LargeMetadataTest test( + *this, ChannelArgs().Set(GRPC_ARG_MAX_METADATA_SIZE, soft_limit + 1024)); // Send 50 requests below soft limit. Should be accepted. - for (int i = 0; i < 50; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_below_soft_limit, &client_details); - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(client_details, "xyz")); - grpc_slice_unref(client_details); - } - + EXPECT_EQ(test.PerformRequests(metadata_size_below_soft_limit, 50), 50); // Send 100 requests between soft and hard limits. Some should be rejected. - int num_requests_rejected = 0; - for (int i = 0; i < 100; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_between_limits, &client_details); - if (status == GRPC_STATUS_RESOURCE_EXHAUSTED) { - num_requests_rejected++; - const char* expected_error = - "received initial metadata size exceeds soft limit"; - grpc_slice actual_error = - grpc_slice_split_head(&client_details, strlen(expected_error)); - GPR_ASSERT(0 == grpc_slice_str_cmp(actual_error, expected_error)); - grpc_slice_unref(actual_error); - } else { - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(client_details, "xyz")); - } - grpc_slice_unref(client_details); - } - // Check that some requests were rejected. - GPR_ASSERT(abs(num_requests_rejected - 50) <= 45); - + EXPECT_THAT(test.PerformRequests(metadata_size_between_limits, 100), + ::testing::AllOf(::testing::Ge(5), ::testing::Le(95))); // Send 50 requests above hard limit. Should be rejected. - for (int i = 0; i < 50; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_above_hard_limit, &client_details); - GPR_ASSERT(status == GRPC_STATUS_RESOURCE_EXHAUSTED); - const char* expected_error = - "received initial metadata size exceeds hard limit"; - grpc_slice actual_error = - grpc_slice_split_head(&client_details, strlen(expected_error)); - GPR_ASSERT(0 == grpc_slice_str_cmp(actual_error, expected_error)); - grpc_slice_unref(actual_error); - grpc_slice_unref(client_details); - } + EXPECT_EQ(test.PerformRequests(metadata_size_above_hard_limit, 50), 0); } // Set hard limit * 0.8 higher than default soft limit and do not set soft // limit. Hard limit * 0.8 should be used as soft limit. -static void test_request_with_large_metadata_hard_limit_overrides_default_soft( - const CoreTestConfiguration& config) { +TEST_P(Http2SingleHopTest, + RequestWithLargeMetadataHardLimitOverridsDefaultSoft) { const size_t hard_limit = 45 * 1024; const size_t metadata_size_below_soft_limit = hard_limit * 0.5; const size_t metadata_size_above_hard_limit = hard_limit * 1.5; const size_t metadata_size_between_limits = (hard_limit * 0.8 + hard_limit) / 2; - grpc_arg arg[] = {grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE), - hard_limit + 1024)}; - grpc_channel_args args = {GPR_ARRAY_SIZE(arg), arg}; - auto f = begin_test( - config, - "test_request_with_large_metadata_hard_limit_overrides_default_soft", - &args, &args); - + LargeMetadataTest test(*this, + ChannelArgs().Set(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE, + hard_limit + 1024)); // Send 50 requests below soft limit. Should be accepted. - for (int i = 0; i < 50; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_below_soft_limit, &client_details); - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(client_details, "xyz")); - grpc_slice_unref(client_details); - } - + EXPECT_EQ(test.PerformRequests(metadata_size_below_soft_limit, 50), 50); // Send 100 requests between soft and hard limits. Some should be rejected. - int num_requests_rejected = 0; - for (int i = 0; i < 100; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_between_limits, &client_details); - if (status == GRPC_STATUS_RESOURCE_EXHAUSTED) { - num_requests_rejected++; - const char* expected_error = - "received initial metadata size exceeds soft limit"; - grpc_slice actual_error = - grpc_slice_split_head(&client_details, strlen(expected_error)); - GPR_ASSERT(0 == grpc_slice_str_cmp(actual_error, expected_error)); - grpc_slice_unref(actual_error); - } else { - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(client_details, "xyz")); - } - grpc_slice_unref(client_details); - } - // Check that some requests were rejected. - GPR_ASSERT(abs(num_requests_rejected - 50) <= 45); - + EXPECT_THAT(test.PerformRequests(metadata_size_between_limits, 100), + ::testing::AllOf(::testing::Ge(5), ::testing::Le(95))); // Send 50 requests above hard limit. Should be rejected. - for (int i = 0; i < 50; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_above_hard_limit, &client_details); - GPR_ASSERT(status == GRPC_STATUS_RESOURCE_EXHAUSTED); - const char* expected_error = - "received initial metadata size exceeds hard limit"; - grpc_slice actual_error = - grpc_slice_split_head(&client_details, strlen(expected_error)); - GPR_ASSERT(0 == grpc_slice_str_cmp(actual_error, expected_error)); - grpc_slice_unref(actual_error); - grpc_slice_unref(client_details); - } + EXPECT_EQ(test.PerformRequests(metadata_size_above_hard_limit, 50), 0); } // Set hard limit lower than default hard limit and ensure new limit is // respected. Default soft limit is not respected since hard limit is lower than // soft limit. -static void test_request_with_large_metadata_hard_limit_below_default_hard( - const CoreTestConfiguration& config) { +TEST_P(Http2SingleHopTest, RequestWithLargeMetadataHardLimitBelowDefaultHard) { const size_t hard_limit = 4 * 1024; const size_t metadata_size_below_hard_limit = hard_limit; const size_t metadata_size_above_hard_limit = hard_limit * 2; - grpc_arg arg[] = {grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE), - hard_limit + 1024)}; - grpc_channel_args args = {GPR_ARRAY_SIZE(arg), arg}; - auto f = begin_test( - config, "test_request_with_large_metadata_hard_limit_below_default_hard", - &args, &args); - + LargeMetadataTest test(*this, + ChannelArgs().Set(GRPC_ARG_ABSOLUTE_MAX_METADATA_SIZE, + hard_limit + 1024)); // Send 50 requests below hard limit. Should be accepted. - for (int i = 0; i < 50; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_below_hard_limit, &client_details); - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(client_details, "xyz")); - grpc_slice_unref(client_details); - } - + EXPECT_EQ(test.PerformRequests(metadata_size_below_hard_limit, 50), 50); // Send 50 requests above hard limit. Should be rejected. - for (int i = 0; i < 50; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_above_hard_limit, &client_details); - GPR_ASSERT(status == GRPC_STATUS_RESOURCE_EXHAUSTED); - const char* expected_error = - "received initial metadata size exceeds hard limit"; - grpc_slice actual_error = - grpc_slice_split_head(&client_details, strlen(expected_error)); - GPR_ASSERT(0 == grpc_slice_str_cmp(actual_error, expected_error)); - grpc_slice_unref(actual_error); - grpc_slice_unref(client_details); - } + EXPECT_EQ(test.PerformRequests(metadata_size_above_hard_limit, 50), 0); } // Set soft limit lower than default soft limit and ensure new limit is // respected. Hard limit should be default hard since this is greater than 2 * // soft limit. -static void test_request_with_large_metadata_soft_limit_below_default_soft( - const CoreTestConfiguration& config) { +TEST_P(Http2SingleHopTest, RequestWithLargeMetadataSoftLimitBelowDefaultSoft) { const size_t soft_limit = 1 * 1024; const size_t metadata_size_below_soft_limit = soft_limit; // greater than 2 * soft, less than default hard const size_t metadata_size_between_limits = 10 * 1024; const size_t metadata_size_above_hard_limit = 75 * 1024; - grpc_arg arg[] = {grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_MAX_METADATA_SIZE), soft_limit + 1024)}; - grpc_channel_args args = {GPR_ARRAY_SIZE(arg), arg}; - auto f = begin_test( - config, "test_request_with_large_metadata_soft_limit_below_default_soft", - &args, &args); - + LargeMetadataTest test( + *this, ChannelArgs().Set(GRPC_ARG_MAX_METADATA_SIZE, soft_limit + 1024)); // Send 50 requests below soft limit. Should be accepted. - for (int i = 0; i < 50; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_below_soft_limit, &client_details); - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(client_details, "xyz")); - grpc_slice_unref(client_details); - } - + EXPECT_EQ(test.PerformRequests(metadata_size_below_soft_limit, 50), 50); // Send 100 requests between soft and hard limits. Some should be rejected. - int num_requests_rejected = 0; - for (int i = 0; i < 100; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_between_limits, &client_details); - if (status == GRPC_STATUS_RESOURCE_EXHAUSTED) { - num_requests_rejected++; - const char* expected_error = - "received initial metadata size exceeds soft limit"; - grpc_slice actual_error = - grpc_slice_split_head(&client_details, strlen(expected_error)); - GPR_ASSERT(0 == grpc_slice_str_cmp(actual_error, expected_error)); - grpc_slice_unref(actual_error); - } else { - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(client_details, "xyz")); - } - grpc_slice_unref(client_details); - } - // Check that some requests were rejected. - GPR_ASSERT((abs(num_requests_rejected - 50) <= 49)); - + EXPECT_THAT(test.PerformRequests(metadata_size_between_limits, 100), + ::testing::AllOf(::testing::Ge(1), ::testing::Le(99))); // Send 50 requests above hard limit. Should be rejected. - for (int i = 0; i < 50; i++) { - grpc_slice client_details; - auto status = - send_metadata(f.get(), metadata_size_above_hard_limit, &client_details); - GPR_ASSERT(status == GRPC_STATUS_RESOURCE_EXHAUSTED); - const char* expected_error = - "received initial metadata size exceeds hard limit"; - grpc_slice actual_error = - grpc_slice_split_head(&client_details, strlen(expected_error)); - GPR_ASSERT(0 == grpc_slice_str_cmp(actual_error, expected_error)); - grpc_slice_unref(actual_error); - grpc_slice_unref(client_details); - } -} - -void large_metadata(const CoreTestConfiguration& config) { - test_request_with_large_metadata_under_soft_limit(config); - // TODO(yashykt): Maybe add checks for metadata size in inproc transport too. - if (strcmp(config.name, "inproc") != 0) { - test_request_with_large_metadata_between_soft_and_hard_limits(config); - test_request_with_large_metadata_above_hard_limit(config); - test_request_with_large_metadata_soft_limit_above_hard_limit(config); - test_request_with_large_metadata_soft_limit_overrides_default_hard(config); - test_request_with_large_metadata_hard_limit_overrides_default_soft(config); - test_request_with_large_metadata_hard_limit_below_default_hard(config); - test_request_with_large_metadata_soft_limit_below_default_soft(config); - } + EXPECT_EQ(test.PerformRequests(metadata_size_above_hard_limit, 50), 0); } -void large_metadata_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/load_reporting_hook.cc b/test/core/end2end/tests/load_reporting_hook.cc deleted file mode 100644 index 4ad8bf33ab29a..0000000000000 --- a/test/core/end2end/tests/load_reporting_hook.cc +++ /dev/null @@ -1,266 +0,0 @@ -// -// -// Copyright 2016 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 - -#include -#include -#include -#include -#include -#include - -#include "src/core/ext/filters/load_reporting/server_load_reporting_filter.h" -#include "src/core/ext/filters/load_reporting/server_load_reporting_plugin.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gprpp/crash.h" -#include "test/core/end2end/cq_verifier.h" -#include "test/core/end2end/end2end_tests.h" - -enum { TIMEOUT = 200000 }; - -static void* tag(intptr_t t) { return (void*)t; } - -typedef struct { - gpr_mu mu; - intptr_t channel_id; - intptr_t call_id; - - char* initial_md_str; - char* trailing_md_str; - char* method_name; - - uint64_t incoming_bytes; - uint64_t outgoing_bytes; - - grpc_status_code call_final_status; - - bool fully_processed; -} load_reporting_data; - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - - return f; -} - -static void request_response_with_payload( - const CoreTestConfiguration& config, CoreTestFixture f, - const char* method_name, const char* request_msg, const char* response_msg, - grpc_metadata* initial_lr_metadata, grpc_metadata* trailing_lr_metadata) { - grpc_slice request_payload_slice = grpc_slice_from_static_string(request_msg); - grpc_slice response_payload_slice = - grpc_slice_from_static_string(response_msg); - grpc_call* c; - grpc_call* s; - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call( - f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, f->cq(), - grpc_slice_from_static_string(method_name), nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - GPR_ASSERT(initial_lr_metadata != nullptr); - op->data.send_initial_metadata.count = 1; - op->data.send_initial_metadata.metadata = initial_lr_metadata; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - GPR_ASSERT(trailing_lr_metadata != nullptr); - op->data.send_status_from_server.trailing_metadata_count = 1; - op->data.send_status_from_server.trailing_metadata = trailing_lr_metadata; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); -} - -// override the default for testing purposes -extern void (*g_load_reporting_fn)( - const grpc_load_reporting_call_data* call_data); - -static void test_load_reporting_hook(const CoreTestConfiguration& config) { - // TODO(dgq): this test is currently a noop until LR is fully defined. - // Leaving the rest here, as it'll likely be reusable. - - // Introduce load reporting for the server through its arguments - grpc_arg arg = grpc_load_reporting_enable_arg(); - grpc_channel_args* lr_server_args = - grpc_channel_args_copy_and_add(nullptr, &arg, 1); - - auto f = - begin_test(config, "test_load_reporting_hook", nullptr, lr_server_args); - - const char* method_name = "/gRPCFTW"; - const char* request_msg = "the msg from the client"; - const char* response_msg = "... and the response from the server"; - - grpc_metadata initial_lr_metadata; - grpc_metadata trailing_lr_metadata; - - initial_lr_metadata.key = GRPC_MDSTR_LB_TOKEN; - initial_lr_metadata.value = grpc_slice_from_static_string("client-token"); - memset(&initial_lr_metadata.internal_data, 0, - sizeof(initial_lr_metadata.internal_data)); - - trailing_lr_metadata.key = GRPC_MDSTR_LB_COST_BIN; - trailing_lr_metadata.value = grpc_slice_from_static_string("server-token"); - memset(&trailing_lr_metadata.internal_data, 0, - sizeof(trailing_lr_metadata.internal_data)); - - request_response_with_payload(config, f, method_name, request_msg, - response_msg, &initial_lr_metadata, - &trailing_lr_metadata); - - { - grpc_core::ExecCtx exec_ctx; - grpc_channel_args_destroy(lr_server_args); - } -} - -void load_reporting_hook(const CoreTestConfiguration& config) { - test_load_reporting_hook(config); -} - -void load_reporting_hook_pre_init(void) {} diff --git a/test/core/end2end/tests/max_concurrent_streams.cc b/test/core/end2end/tests/max_concurrent_streams.cc index 24915ccd726a0..aa16ad9d1dc64 100644 --- a/test/core/end2end/tests/max_concurrent_streams.cc +++ b/test/core/end2end/tests/max_concurrent_streams.cc @@ -16,769 +16,222 @@ // // -#include -#include - -#include -#include +#include "gtest/gtest.h" #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static void simple_request_body(const CoreTestConfiguration& /*config*/, - CoreTestFixture* f) { - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); +namespace grpc_core { +namespace { + +void SimpleRequestBody(CoreEnd2endTest& test) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); } -static void test_max_concurrent_streams(const CoreTestConfiguration& config) { - grpc_arg server_arg; - grpc_channel_args server_args; - grpc_call* c1; - grpc_call* c2; - grpc_call* s1; - grpc_call* s2; - int live_call; - gpr_timespec deadline; - grpc_event ev; - grpc_call_details call_details; - grpc_metadata_array request_metadata_recv; - grpc_metadata_array initial_metadata_recv1; - grpc_metadata_array trailing_metadata_recv1; - grpc_metadata_array initial_metadata_recv2; - grpc_metadata_array trailing_metadata_recv2; - grpc_status_code status1; - grpc_call_error error; - grpc_slice details1; - grpc_status_code status2; - grpc_slice details2; - grpc_op ops[6]; - grpc_op* op; - int was_cancelled; - int got_client_start; - int got_server_start; - - server_arg.key = const_cast(GRPC_ARG_MAX_CONCURRENT_STREAMS); - server_arg.type = GRPC_ARG_INTEGER; - server_arg.value.integer = 1; - - server_args.num_args = 1; - server_args.args = &server_arg; - - auto f = - begin_test(config, "test_max_concurrent_streams", nullptr, &server_args); - grpc_core::CqVerifier cqv(f->cq()); - - grpc_metadata_array_init(&request_metadata_recv); - grpc_metadata_array_init(&initial_metadata_recv1); - grpc_metadata_array_init(&trailing_metadata_recv1); - grpc_metadata_array_init(&initial_metadata_recv2); - grpc_metadata_array_init(&trailing_metadata_recv2); - grpc_call_details_init(&call_details); - +TEST_P(Http2SingleHopTest, MaxConcurrentStreams) { + SKIP_IF_MINSTACK(); + InitServer(ChannelArgs().Set(GRPC_ARG_MAX_CONCURRENT_STREAMS, 1)); + InitClient(ChannelArgs()); // perform a ping-pong to ensure that settings have had a chance to round // trip - simple_request_body(config, f.get()); + SimpleRequestBody(*this); // perform another one to make sure that the one stream case still works - simple_request_body(config, f.get()); - + SimpleRequestBody(*this); // start two requests - ensuring that the second is not accepted until // the first completes - deadline = grpc_timeout_seconds_to_deadline(1000); - c1 = grpc_channel_create_call( - f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, f->cq(), - grpc_slice_from_static_string("/alpha"), nullptr, deadline, nullptr); - GPR_ASSERT(c1); - c2 = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/beta"), - nullptr, deadline, nullptr); - GPR_ASSERT(c2); - - GPR_ASSERT(GRPC_CALL_OK == - grpc_server_request_call(f->server(), &s1, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101))); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c1, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(301), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv1; - op->data.recv_status_on_client.status = &status1; - op->data.recv_status_on_client.status_details = &details1; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = - &initial_metadata_recv1; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c1, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(302), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c2, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(401), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv2; - op->data.recv_status_on_client.status = &status2; - op->data.recv_status_on_client.status_details = &details2; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = - &initial_metadata_recv1; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c2, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(402), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - got_client_start = 0; - got_server_start = 0; - live_call = -1; - while (!got_client_start || !got_server_start) { - ev = grpc_completion_queue_next( - f->cq(), grpc_timeout_seconds_to_deadline(3), nullptr); - GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); - GPR_ASSERT(ev.success); - if (ev.tag == grpc_core::CqVerifier::tag(101)) { - GPR_ASSERT(!got_server_start); - got_server_start = 1; - } else { - GPR_ASSERT(!got_client_start); - GPR_ASSERT(ev.tag == grpc_core::CqVerifier::tag(301) || - ev.tag == grpc_core::CqVerifier::tag(401)); - // The /alpha or /beta calls started above could be invoked (but NOT - // both); - // check this here - // We'll get tag 303 or 403, we want 300, 400 - live_call = (static_cast(reinterpret_cast(ev.tag))) - 1; - got_client_start = 1; - } + auto c1 = NewClientCall("/alpha").Timeout(Duration::Seconds(1000)).Create(); + auto c2 = NewClientCall("/beta").Timeout(Duration::Seconds(1000)).Create(); + auto s1 = RequestCall(101); + c1.NewBatch(301).SendInitialMetadata({}).SendCloseFromClient(); + IncomingMetadata server_initial_metadata1; + IncomingStatusOnClient server_status1; + c1.NewBatch(302) + .RecvStatusOnClient(server_status1) + .RecvInitialMetadata(server_initial_metadata1); + c2.NewBatch(401).SendInitialMetadata({}).SendCloseFromClient(); + IncomingMetadata server_initial_metadata2; + IncomingStatusOnClient server_status2; + c2.NewBatch(402) + .RecvStatusOnClient(server_status2) + .RecvInitialMetadata(server_initial_metadata2); + bool got_client_start = false; + bool got_server_start = false; + int live_call; + Expect(101, MaybePerformAction{[&got_server_start](bool ok) { + EXPECT_TRUE(ok); + got_server_start = ok; + }}); + Expect(301, MaybePerformAction{[&got_client_start, &live_call](bool ok) { + EXPECT_FALSE(got_client_start); + EXPECT_TRUE(ok); + got_client_start = ok; + live_call = 300; + }}); + Expect(401, MaybePerformAction{[&got_client_start, &live_call](bool ok) { + EXPECT_FALSE(got_client_start); + EXPECT_TRUE(ok); + got_client_start = ok; + live_call = 400; + }}); + Step(); + if (got_client_start && !got_server_start) { + Expect(101, true); + Step(); + got_server_start = true; + } else if (got_server_start && !got_client_start) { + Expect(301, MaybePerformAction{[&got_client_start, &live_call](bool ok) { + EXPECT_FALSE(got_client_start); + EXPECT_TRUE(ok); + got_client_start = ok; + live_call = 300; + }}); + Expect(401, MaybePerformAction{[&got_client_start, &live_call](bool ok) { + EXPECT_FALSE(got_client_start); + EXPECT_TRUE(ok); + got_client_start = ok; + live_call = 400; + }}); + Step(); + EXPECT_TRUE(got_client_start); } - GPR_ASSERT(live_call == 300 || live_call == 400); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s1, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(live_call + 2), true); + IncomingCloseOnServer client_close; + s1.NewBatch(102) + .SendInitialMetadata({}) + .RecvCloseOnServer(client_close) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}); + Expect(102, true); + Expect(live_call + 2, true); // first request is finished, we should be able to start the second live_call = (live_call == 300) ? 400 : 300; - cqv.Expect(grpc_core::CqVerifier::tag(live_call + 1), true); - cqv.Verify(); - - grpc_call_details_destroy(&call_details); - - GPR_ASSERT(GRPC_CALL_OK == - grpc_server_request_call(f->server(), &s2, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201))); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s2, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(202), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(live_call + 2), true); - cqv.Expect(grpc_core::CqVerifier::tag(202), true); - cqv.Verify(); - - grpc_call_unref(c1); - grpc_call_unref(s1); - grpc_call_unref(c2); - grpc_call_unref(s2); - - grpc_slice_unref(details1); - grpc_slice_unref(details2); - grpc_metadata_array_destroy(&initial_metadata_recv1); - grpc_metadata_array_destroy(&trailing_metadata_recv1); - grpc_metadata_array_destroy(&initial_metadata_recv2); - grpc_metadata_array_destroy(&trailing_metadata_recv2); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); + Expect(live_call + 1, true); + Step(); + auto s2 = RequestCall(201); + Expect(201, true); + Step(); + s2.NewBatch(202) + .SendInitialMetadata({}) + .RecvCloseOnServer(client_close) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}); + Expect(live_call + 2, true); + Expect(202, true); + Step(); } -static void test_max_concurrent_streams_with_timeout_on_first( - const CoreTestConfiguration& config) { - grpc_arg server_arg; - grpc_channel_args server_args; - grpc_call* c1; - grpc_call* c2; - grpc_call* s1; - grpc_call* s2; - grpc_call_details call_details; - grpc_metadata_array request_metadata_recv; - grpc_metadata_array initial_metadata_recv1; - grpc_metadata_array trailing_metadata_recv1; - grpc_metadata_array initial_metadata_recv2; - grpc_metadata_array trailing_metadata_recv2; - grpc_status_code status1; - grpc_call_error error; - grpc_slice details1 = grpc_empty_slice(); - grpc_status_code status2; - grpc_slice details2 = grpc_empty_slice(); - grpc_op ops[6]; - grpc_op* op; - int was_cancelled; - - server_arg.key = const_cast(GRPC_ARG_MAX_CONCURRENT_STREAMS); - server_arg.type = GRPC_ARG_INTEGER; - server_arg.value.integer = 1; - - server_args.num_args = 1; - server_args.args = &server_arg; - - auto f = - begin_test(config, "test_max_concurrent_streams_with_timeout_on_first", - nullptr, &server_args); - grpc_core::CqVerifier cqv(f->cq()); - - grpc_metadata_array_init(&request_metadata_recv); - grpc_metadata_array_init(&initial_metadata_recv1); - grpc_metadata_array_init(&trailing_metadata_recv1); - grpc_metadata_array_init(&initial_metadata_recv2); - grpc_metadata_array_init(&trailing_metadata_recv2); - grpc_call_details_init(&call_details); - +TEST_P(Http2SingleHopTest, MaxConcurrentStreamsTimeoutOnFirst) { + SKIP_IF_MINSTACK(); + InitServer(ChannelArgs().Set(GRPC_ARG_MAX_CONCURRENT_STREAMS, 1)); + InitClient(ChannelArgs()); // perform a ping-pong to ensure that settings have had a chance to round // trip - simple_request_body(config, f.get()); + SimpleRequestBody(*this); // perform another one to make sure that the one stream case still works - simple_request_body(config, f.get()); - + SimpleRequestBody(*this); // start two requests - ensuring that the second is not accepted until // the first completes - c1 = grpc_channel_create_call( - f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, f->cq(), - grpc_slice_from_static_string("/alpha"), nullptr, - grpc_timeout_seconds_to_deadline(3), nullptr); - GPR_ASSERT(c1); - c2 = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/beta"), - nullptr, grpc_timeout_seconds_to_deadline(1000), - nullptr); - GPR_ASSERT(c2); - - GPR_ASSERT(GRPC_CALL_OK == - grpc_server_request_call(f->server(), &s1, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101))); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c1, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(301), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv1; - op->data.recv_status_on_client.status = &status1; - op->data.recv_status_on_client.status_details = &details1; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = - &initial_metadata_recv1; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c1, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(302), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Expect(grpc_core::CqVerifier::tag(301), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c2, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(401), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv2; - op->data.recv_status_on_client.status = &status2; - op->data.recv_status_on_client.status_details = &details2; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = - &initial_metadata_recv2; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c2, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(402), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - GPR_ASSERT(GRPC_CALL_OK == - grpc_server_request_call(f->server(), &s2, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201))); - - cqv.Expect(grpc_core::CqVerifier::tag(302), true); + auto c1 = NewClientCall("/alpha").Timeout(Duration::Seconds(3)).Create(); + auto c2 = NewClientCall("/beta").Timeout(Duration::Seconds(1000)).Create(); + auto s1 = RequestCall(101); + c1.NewBatch(301).SendInitialMetadata({}).SendCloseFromClient(); + IncomingMetadata server_initial_metadata1; + IncomingStatusOnClient server_status1; + c1.NewBatch(302) + .RecvStatusOnClient(server_status1) + .RecvInitialMetadata(server_initial_metadata1); + Expect(101, true); + Expect(301, true); + Step(); + c2.NewBatch(401).SendInitialMetadata({}).SendCloseFromClient(); + IncomingMetadata server_initial_metadata2; + IncomingStatusOnClient server_status2; + c2.NewBatch(402) + .RecvStatusOnClient(server_status2) + .RecvInitialMetadata(server_initial_metadata2); + auto s2 = RequestCall(201); + Expect(302, true); // first request is finished, we should be able to start the second - cqv.Expect(grpc_core::CqVerifier::tag(401), true); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s2, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(202), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(402), true); - cqv.Expect(grpc_core::CqVerifier::tag(202), true); - cqv.Verify(); - - grpc_call_unref(c1); - grpc_call_unref(s1); - grpc_call_unref(c2); - grpc_call_unref(s2); - - grpc_slice_unref(details1); - grpc_slice_unref(details2); - grpc_metadata_array_destroy(&initial_metadata_recv1); - grpc_metadata_array_destroy(&trailing_metadata_recv1); - grpc_metadata_array_destroy(&initial_metadata_recv2); - grpc_metadata_array_destroy(&trailing_metadata_recv2); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); + Expect(401, true); + Expect(201, true); + Step(); + IncomingCloseOnServer client_close2; + s2.NewBatch(202) + .SendInitialMetadata({}) + .RecvCloseOnServer(client_close2) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}); + Expect(402, true); + Expect(202, true); + Step(); } -static void test_max_concurrent_streams_with_timeout_on_second( - const CoreTestConfiguration& config) { - grpc_arg server_arg; - grpc_channel_args server_args; - grpc_call* c1; - grpc_call* c2; - grpc_call* s1; - grpc_call_details call_details; - grpc_metadata_array request_metadata_recv; - grpc_metadata_array initial_metadata_recv1; - grpc_metadata_array trailing_metadata_recv1; - grpc_metadata_array initial_metadata_recv2; - grpc_metadata_array trailing_metadata_recv2; - grpc_status_code status1; - grpc_call_error error; - grpc_slice details1 = grpc_empty_slice(); - grpc_status_code status2; - grpc_slice details2 = grpc_empty_slice(); - grpc_op ops[6]; - grpc_op* op; - int was_cancelled; - - server_arg.key = const_cast(GRPC_ARG_MAX_CONCURRENT_STREAMS); - server_arg.type = GRPC_ARG_INTEGER; - server_arg.value.integer = 1; - - server_args.num_args = 1; - server_args.args = &server_arg; - - auto f = - begin_test(config, "test_max_concurrent_streams_with_timeout_on_second", - nullptr, &server_args); - grpc_core::CqVerifier cqv(f->cq()); - - grpc_metadata_array_init(&request_metadata_recv); - grpc_metadata_array_init(&initial_metadata_recv1); - grpc_metadata_array_init(&trailing_metadata_recv1); - grpc_metadata_array_init(&initial_metadata_recv2); - grpc_metadata_array_init(&trailing_metadata_recv2); - grpc_call_details_init(&call_details); - +TEST_P(Http2SingleHopTest, MaxConcurrentStreamsTimeoutOnSecond) { + SKIP_IF_MINSTACK(); + InitServer(ChannelArgs().Set(GRPC_ARG_MAX_CONCURRENT_STREAMS, 1)); + InitClient(ChannelArgs()); // perform a ping-pong to ensure that settings have had a chance to round // trip - simple_request_body(config, f.get()); + SimpleRequestBody(*this); // perform another one to make sure that the one stream case still works - simple_request_body(config, f.get()); - + SimpleRequestBody(*this); // start two requests - ensuring that the second is not accepted until // the first completes , and the second request will timeout in the // concurrent_list - c1 = grpc_channel_create_call( - f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, f->cq(), - grpc_slice_from_static_string("/alpha"), nullptr, - grpc_timeout_seconds_to_deadline(1000), nullptr); - GPR_ASSERT(c1); - c2 = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/beta"), - nullptr, grpc_timeout_seconds_to_deadline(3), - nullptr); - GPR_ASSERT(c2); - - GPR_ASSERT(GRPC_CALL_OK == - grpc_server_request_call(f->server(), &s1, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101))); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c1, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(301), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv1; - op->data.recv_status_on_client.status = &status1; - op->data.recv_status_on_client.status_details = &details1; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = - &initial_metadata_recv1; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c1, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(302), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Expect(grpc_core::CqVerifier::tag(301), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c2, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(401), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv2; - op->data.recv_status_on_client.status = &status2; - op->data.recv_status_on_client.status_details = &details2; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = - &initial_metadata_recv2; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c2, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(402), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + auto c1 = NewClientCall("/alpha").Timeout(Duration::Seconds(1000)).Create(); + auto c2 = NewClientCall("/beta").Timeout(Duration::Seconds(3)).Create(); + auto s1 = RequestCall(101); + c1.NewBatch(301).SendInitialMetadata({}).SendCloseFromClient(); + IncomingMetadata server_initial_metadata1; + IncomingStatusOnClient server_status1; + c1.NewBatch(302) + .RecvStatusOnClient(server_status1) + .RecvInitialMetadata(server_initial_metadata1); + Expect(101, true); + Expect(301, true); + Step(); + c2.NewBatch(401).SendInitialMetadata({}).SendCloseFromClient(); + IncomingMetadata server_initial_metadata2; + IncomingStatusOnClient server_status2; + c2.NewBatch(402) + .RecvStatusOnClient(server_status2) + .RecvInitialMetadata(server_initial_metadata2); // the second request is time out - cqv.Expect(grpc_core::CqVerifier::tag(401), false); - cqv.Expect(grpc_core::CqVerifier::tag(402), true); - cqv.Verify(); - - // second request is finished because of time out, so destroy the second call - // - grpc_call_unref(c2); - + Expect(401, false); + Expect(402, true); + Step(); // now reply the first call - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s1, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(302), true); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - grpc_call_unref(c1); - grpc_call_unref(s1); - - grpc_slice_unref(details1); - grpc_slice_unref(details2); - grpc_metadata_array_destroy(&initial_metadata_recv1); - grpc_metadata_array_destroy(&trailing_metadata_recv1); - grpc_metadata_array_destroy(&initial_metadata_recv2); - grpc_metadata_array_destroy(&trailing_metadata_recv2); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); -} - -void max_concurrent_streams(const CoreTestConfiguration& config) { - test_max_concurrent_streams_with_timeout_on_first(config); - test_max_concurrent_streams_with_timeout_on_second(config); - test_max_concurrent_streams(config); + IncomingCloseOnServer client_close1; + s1.NewBatch(102) + .SendInitialMetadata({}) + .RecvCloseOnServer(client_close1) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}); + Expect(302, true); + Expect(102, true); + Step(); } -void max_concurrent_streams_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/max_connection_age.cc b/test/core/end2end/tests/max_connection_age.cc index 0e4c1c433c5a5..4b45b9ba44c11 100644 --- a/test/core/end2end/tests/max_connection_age.cc +++ b/test/core/end2end/tests/max_connection_age.cc @@ -17,21 +17,14 @@ // #include -#include -#include -#include +#include "gtest/gtest.h" #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/gprpp/time.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/util/test_config.h" @@ -52,311 +45,124 @@ // The grace period for the test to observe the channel shutdown process #define IMMEDIATE_SHUTDOWN_GRACE_TIME_MS 3000 -static void test_max_age_forcibly_close(const CoreTestConfiguration& config) { - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - auto cqv = std::make_unique(f->cq()); - auto server_args = - grpc_core::ChannelArgs() - .Set(GRPC_ARG_MAX_CONNECTION_AGE_MS, MAX_CONNECTION_AGE_MS) - .Set(GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS, - MAX_CONNECTION_AGE_GRACE_MS) - .Set(GRPC_ARG_MAX_CONNECTION_IDLE_MS, MAX_CONNECTION_IDLE_MS); - - f->InitClient(grpc_core::ChannelArgs()); - f->InitServer(server_args); - - grpc_call* c; - grpc_call* s = nullptr; - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(CALL_DEADLINE_S); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - - grpc_event ev = grpc_completion_queue_next( - f->cq(), gpr_inf_future(GPR_CLOCK_MONOTONIC), nullptr); - GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); - GPR_ASSERT(ev.tag == grpc_core::CqVerifier::tag(1) || - ev.tag == grpc_core::CqVerifier::tag(101)); - - if (ev.tag == grpc_core::CqVerifier::tag(101)) { +namespace grpc_core { +namespace { + +TEST_P(Http2Test, MaxAgeForciblyClose) { + SKIP_IF_MINSTACK(); + InitClient(ChannelArgs()); + InitServer(ChannelArgs() + .Set(GRPC_ARG_MAX_CONNECTION_AGE_MS, MAX_CONNECTION_AGE_MS) + .Set(GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS, + MAX_CONNECTION_AGE_GRACE_MS) + .Set(GRPC_ARG_MAX_CONNECTION_IDLE_MS, MAX_CONNECTION_IDLE_MS)); + auto c = NewClientCall("/foo") + .Timeout(Duration::Seconds(CALL_DEADLINE_S)) + .Create(); + const auto expect_shutdown_time = + Timestamp::FromTimespecRoundUp(grpc_timeout_milliseconds_to_deadline( + static_cast(MAX_CONNECTION_AGE_MS * + MAX_CONNECTION_AGE_JITTER_MULTIPLIER) + + MAX_CONNECTION_AGE_GRACE_MS + IMMEDIATE_SHUTDOWN_GRACE_TIME_MS)); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + bool got_client = false; + bool got_server = false; + Expect(1, Maybe{&got_client}); + Expect(101, Maybe{&got_server}); + Step(); + if (got_server) { // Request got through to the server before connection timeout - // Wait for the channel to reach its max age - cqv->VerifyEmpty( - grpc_core::Duration::Seconds(CQ_MAX_CONNECTION_AGE_WAIT_TIME_S)); - + Step(Duration::Seconds(CQ_MAX_CONNECTION_AGE_WAIT_TIME_S)); // After the channel reaches its max age, we still do nothing here. And wait // for it to use up its max age grace period. - cqv->Expect(grpc_core::CqVerifier::tag(1), true); - cqv->Verify(); - - gpr_timespec expect_shutdown_time = grpc_timeout_milliseconds_to_deadline( - static_cast(MAX_CONNECTION_AGE_MS * - MAX_CONNECTION_AGE_JITTER_MULTIPLIER) + - MAX_CONNECTION_AGE_GRACE_MS + IMMEDIATE_SHUTDOWN_GRACE_TIME_MS); - - gpr_timespec channel_shutdown_time = gpr_now(GPR_CLOCK_MONOTONIC); - GPR_ASSERT(gpr_time_cmp(channel_shutdown_time, expect_shutdown_time) < 0); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv->Expect(grpc_core::CqVerifier::tag(102), true); - cqv->Verify(); - - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 1); + Expect(1, true); + Step(); + EXPECT_LT(Timestamp::Now(), expect_shutdown_time); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Step(); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_TRUE(client_close.was_cancelled()); } else { // Request failed before getting to the server } - - grpc_server_shutdown_and_notify(f->server(), f->cq(), - grpc_core::CqVerifier::tag(0xdead)); - cqv->Expect(grpc_core::CqVerifier::tag(0xdead), true); - if (s == nullptr) { - cqv->Expect(grpc_core::CqVerifier::tag(101), false); - } - cqv->Verify(); - - if (s != nullptr) { - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); + ShutdownServerAndNotify(1000); + Expect(1000, true); + if (got_client) { + Expect(101, false); } - + Step(); // The connection should be closed immediately after the max age grace period, // the in-progress RPC should fail. - GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_unref(c); - cqv.reset(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNAVAILABLE); } -static void test_max_age_gracefully_close(const CoreTestConfiguration& config) { - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - auto cqv = std::make_unique(f->cq()); - auto server_args = - grpc_core::ChannelArgs() - .Set(GRPC_ARG_MAX_CONNECTION_AGE_MS, MAX_CONNECTION_AGE_MS) - .Set(GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS, INT_MAX) - .Set(GRPC_ARG_MAX_CONNECTION_IDLE_MS, MAX_CONNECTION_IDLE_MS); - - f->InitClient(grpc_core::ChannelArgs()); - f->InitServer(server_args); - - grpc_call* c; - grpc_call* s = nullptr; - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(CALL_DEADLINE_S); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - - grpc_event ev = grpc_completion_queue_next( - f->cq(), gpr_inf_future(GPR_CLOCK_MONOTONIC), nullptr); - GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); - GPR_ASSERT(ev.tag == grpc_core::CqVerifier::tag(1) || - ev.tag == grpc_core::CqVerifier::tag(101)); - - if (ev.tag == grpc_core::CqVerifier::tag(101)) { +TEST_P(Http2Test, MaxAgeGracefullyClose) { + SKIP_IF_MINSTACK(); + InitClient(ChannelArgs()); + InitServer(ChannelArgs() + .Set(GRPC_ARG_MAX_CONNECTION_AGE_MS, MAX_CONNECTION_AGE_MS) + .Set(GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS, INT_MAX) + .Set(GRPC_ARG_MAX_CONNECTION_IDLE_MS, MAX_CONNECTION_IDLE_MS)); + auto c = NewClientCall("/foo") + .Timeout(Duration::Seconds(CALL_DEADLINE_S)) + .Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + bool got_client = false; + bool got_server = false; + Expect(1, Maybe{&got_client}); + Expect(101, Maybe{&got_server}); + Step(); + if (got_server) { // Request got through to the server before connection timeout - // Wait for the channel to reach its max age - cqv->VerifyEmpty( - grpc_core::Duration::Seconds(CQ_MAX_CONNECTION_AGE_WAIT_TIME_S)); - + Step(Duration::Seconds(CQ_MAX_CONNECTION_AGE_WAIT_TIME_S)); // The connection is shutting down gracefully. In-progress rpc should not be // closed, hence the completion queue should see nothing here. - cqv->VerifyEmpty( - grpc_core::Duration::Seconds(CQ_MAX_CONNECTION_AGE_GRACE_WAIT_TIME_S)); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv->Expect(grpc_core::CqVerifier::tag(102), true); - cqv->Expect(grpc_core::CqVerifier::tag(1), true); - cqv->Verify(); - - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); + Step(Duration::Seconds(CQ_MAX_CONNECTION_AGE_GRACE_WAIT_TIME_S)); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Step(); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); } else { // Request failed before getting to the server } - - grpc_server_shutdown_and_notify(f->server(), f->cq(), - grpc_core::CqVerifier::tag(0xdead)); - cqv->Expect(grpc_core::CqVerifier::tag(0xdead), true); - if (s == nullptr) { - cqv->Expect(grpc_core::CqVerifier::tag(101), false); - } - cqv->Verify(); - - if (s != nullptr) { - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); + ShutdownServerAndNotify(1000); + Expect(1000, true); + if (got_client) { + Expect(101, false); } - + Step(); // The connection is closed gracefully with goaway, the rpc should still be // completed. - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_unref(c); - cqv.reset(); -} - -void max_connection_age(const CoreTestConfiguration& config) { - test_max_age_forcibly_close(config); - test_max_age_gracefully_close(config); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); } -void max_connection_age_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/max_connection_idle.cc b/test/core/end2end/tests/max_connection_idle.cc index 7b4357777550b..3af51f7cdee17 100644 --- a/test/core/end2end/tests/max_connection_idle.cc +++ b/test/core/end2end/tests/max_connection_idle.cc @@ -16,205 +16,94 @@ // // -#include - -#include -#include +#include "absl/types/optional.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -#define MAX_CONNECTION_IDLE_MS 2000 -#define MAX_CONNECTION_AGE_MS 9999 - -static void simple_request_body(const CoreTestConfiguration& /*config*/, - CoreTestFixture* f) { - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(30); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY | - GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); +namespace grpc_core { +namespace { + +void SimpleRequestBody(CoreEnd2endTest& test) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(30)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata( + {}, GRPC_INITIAL_METADATA_WAIT_FOR_READY | + GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); } -static void test_max_connection_idle(const CoreTestConfiguration& config) { - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - grpc_connectivity_state state = GRPC_CHANNEL_IDLE; - grpc_core::CqVerifier cqv(f->cq()); - - auto client_args = grpc_core::ChannelArgs() - .Set(GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS, 1000) - .Set(GRPC_ARG_MAX_RECONNECT_BACKOFF_MS, 1000) - .Set(GRPC_ARG_MIN_RECONNECT_BACKOFF_MS, 5000); - auto server_args = - grpc_core::ChannelArgs() - .Set(GRPC_ARG_MAX_CONNECTION_IDLE_MS, MAX_CONNECTION_IDLE_MS) - .Set(GRPC_ARG_MAX_CONNECTION_AGE_MS, MAX_CONNECTION_AGE_MS); - - f->InitClient(client_args); - f->InitServer(server_args); - +TEST_P(RetryHttp2Test, MaxConnectionIdle) { + const auto kMaxConnectionIdle = Duration::Seconds(2); + const auto kMaxConnectionAge = Duration::Seconds(10); + InitClient( + ChannelArgs() + .Set(GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS, + Duration::Seconds(1).millis()) + .Set(GRPC_ARG_MAX_RECONNECT_BACKOFF_MS, Duration::Seconds(1).millis()) + .Set(GRPC_ARG_MIN_RECONNECT_BACKOFF_MS, + Duration::Seconds(5).millis())); + InitServer( + ChannelArgs() + .Set(GRPC_ARG_MAX_CONNECTION_IDLE_MS, kMaxConnectionIdle.millis()) + .Set(GRPC_ARG_MAX_CONNECTION_AGE_MS, kMaxConnectionAge.millis())); // check that we're still in idle, and start connecting - GPR_ASSERT(grpc_channel_check_connectivity_state(f->client(), 1) == - GRPC_CHANNEL_IDLE); + grpc_connectivity_state state = CheckConnectivityState(true); + EXPECT_EQ(state, GRPC_CHANNEL_IDLE); // we'll go through some set of transitions (some might be missed), until // READY is reached while (state != GRPC_CHANNEL_READY) { - grpc_channel_watch_connectivity_state( - f->client(), state, grpc_timeout_seconds_to_deadline(10), f->cq(), - grpc_core::CqVerifier::tag(99)); - cqv.Expect(grpc_core::CqVerifier::tag(99), true); - cqv.Verify(); - state = grpc_channel_check_connectivity_state(f->client(), 0); - GPR_ASSERT(state == GRPC_CHANNEL_READY || - state == GRPC_CHANNEL_CONNECTING || - state == GRPC_CHANNEL_TRANSIENT_FAILURE); + WatchConnectivityState(state, Duration::Seconds(10), 99); + Expect(99, true); + Step(); + state = CheckConnectivityState(false); + EXPECT_THAT(state, + ::testing::AnyOf(GRPC_CHANNEL_READY, GRPC_CHANNEL_CONNECTING, + GRPC_CHANNEL_TRANSIENT_FAILURE)); } - // Use a simple request to cancel and reset the max idle timer - simple_request_body(config, f.get()); - + SimpleRequestBody(*this); // wait for the channel to reach its maximum idle time - grpc_channel_watch_connectivity_state( - f->client(), GRPC_CHANNEL_READY, - gpr_time_add(grpc_timeout_milliseconds_to_deadline(3000), - gpr_time_from_millis(MAX_CONNECTION_IDLE_MS, GPR_TIMESPAN)), - f->cq(), grpc_core::CqVerifier::tag(99)); - cqv.Expect(grpc_core::CqVerifier::tag(99), true); - cqv.Verify(); - state = grpc_channel_check_connectivity_state(f->client(), 0); - GPR_ASSERT(state == GRPC_CHANNEL_TRANSIENT_FAILURE || - state == GRPC_CHANNEL_CONNECTING || state == GRPC_CHANNEL_IDLE); - - grpc_server_shutdown_and_notify(f->server(), f->cq(), - grpc_core::CqVerifier::tag(0xdead)); - cqv.Expect(grpc_core::CqVerifier::tag(0xdead), true); - cqv.Verify(); -} - -void max_connection_idle(const CoreTestConfiguration& config) { - test_max_connection_idle(config); + WatchConnectivityState(GRPC_CHANNEL_READY, + Duration::Seconds(3) + kMaxConnectionIdle, 99); + Expect(99, true); + Step(); + state = CheckConnectivityState(false); + EXPECT_THAT(state, + ::testing::AnyOf(GRPC_CHANNEL_IDLE, GRPC_CHANNEL_CONNECTING, + GRPC_CHANNEL_TRANSIENT_FAILURE)); + ShutdownServerAndNotify(1000); + Expect(1000, true); + Step(); } -void max_connection_idle_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/max_message_length.cc b/test/core/end2end/tests/max_message_length.cc index e57cc949cfe8a..cada91eddb455 100644 --- a/test/core/end2end/tests/max_message_length.cc +++ b/test/core/end2end/tests/max_message_length.cc @@ -16,731 +16,285 @@ // // -#include - -#include -#include #include -#include "absl/strings/match.h" +#include "absl/strings/string_view.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/slice/slice_internal.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "\n\n\nRunning test: %s/%s client_args=%s server_args=%s", - test_name, config.name, - grpc_core::ChannelArgs::FromC(client_args).ToString().c_str(), - grpc_core::ChannelArgs::FromC(server_args).ToString().c_str()); - // We intentionally do not pass the client and server args to - // create_fixture(), since we don't want the limit enforced on the - // proxy, only on the backend server. - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; +using testing::StartsWith; + +namespace grpc_core { +namespace { + +void TestMaxMessageLengthOnClientOnRequest(CoreEnd2endTest& test) { + auto c = test.NewClientCall("/service/method").Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello world") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_RESOURCE_EXHAUSTED); + EXPECT_EQ(server_status.message(), "Sent message larger than max (11 vs. 5)"); } -// Test with request larger than the limit. -// If send_limit is true, applies send limit on client; otherwise, applies -// recv limit on server. -static void test_max_message_length_on_request( - const CoreTestConfiguration& config, bool send_limit, - bool use_service_config, bool use_string_json_value) { - gpr_log(GPR_INFO, - "testing request with send_limit=%d use_service_config=%d " - "use_string_json_value=%d", - send_limit, use_service_config, use_string_json_value); - - grpc_call* c = nullptr; - grpc_call* s = nullptr; - grpc_op ops[6]; - grpc_op* op; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* recv_payload = nullptr; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - grpc_slice expect_in_details = grpc_slice_from_copied_string( - send_limit ? "Sent message larger than max (11 vs. 5)" - : "Received message larger than max (11 vs. 5)"); - int was_cancelled = 2; - - grpc_channel_args* client_args = nullptr; - grpc_channel_args* server_args = nullptr; - if (use_service_config) { - // We don't currently support service configs on the server side. - GPR_ASSERT(send_limit); - grpc_arg arg; - arg.type = GRPC_ARG_STRING; - arg.key = const_cast(GRPC_ARG_SERVICE_CONFIG); - arg.value.string = - use_string_json_value - ? const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"maxRequestMessageBytes\": \"5\"\n" - " } ]\n" - "}") - : const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"maxRequestMessageBytes\": 5\n" - " } ]\n" - "}"); - client_args = grpc_channel_args_copy_and_add(nullptr, &arg, 1); - } else { - // Set limit via channel args. - grpc_arg arg; - arg.key = send_limit - ? const_cast(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH) - : const_cast(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH); - arg.type = GRPC_ARG_INTEGER; - arg.value.integer = 5; - grpc_channel_args* args = grpc_channel_args_copy_and_add(nullptr, &arg, 1); - if (send_limit) { - client_args = args; - } else { - server_args = args; - } - } - - auto f = begin_test(config, "test_max_request_message_length", client_args, - server_args); - { - grpc_core::ExecCtx exec_ctx; - if (client_args != nullptr) grpc_channel_args_destroy(client_args); - if (server_args != nullptr) grpc_channel_args_destroy(server_args); - } - - grpc_core::CqVerifier cqv(f->cq()); - - c = grpc_channel_create_call( - f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, f->cq(), - grpc_slice_from_static_string("/service/method"), nullptr, - gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - if (send_limit) { - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - goto done; - } - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &recv_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 1); - -done: - GPR_ASSERT(status == GRPC_STATUS_RESOURCE_EXHAUSTED); - GPR_ASSERT(grpc_slice_slice(details, expect_in_details) >= 0); - - grpc_slice_unref(details); - grpc_slice_unref(expect_in_details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(recv_payload); - - grpc_call_unref(c); - if (s != nullptr) grpc_call_unref(s); +void TestMaxMessageLengthOnServerOnRequest(CoreEnd2endTest& test) { + auto c = test.NewClientCall("/service/method").Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello world") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).RecvCloseOnServer(client_close).RecvMessage(client_message); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_TRUE(client_close.was_cancelled()); + EXPECT_EQ(server_status.status(), GRPC_STATUS_RESOURCE_EXHAUSTED); + EXPECT_EQ(server_status.message(), + "Received message larger than max (11 vs. 5)"); } -// Test with response larger than the limit. -// If send_limit is true, applies send limit on server; otherwise, applies -// recv limit on client. -static void test_max_message_length_on_response( - const CoreTestConfiguration& config, bool send_limit, - bool use_service_config, bool use_string_json_value) { - gpr_log(GPR_INFO, - "testing response with send_limit=%d use_service_config=%d " - "use_string_json_value=%d", - send_limit, use_service_config, use_string_json_value); - - grpc_call* c = nullptr; - grpc_call* s = nullptr; - grpc_op ops[6]; - grpc_op* op; - grpc_slice response_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* recv_payload = nullptr; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - grpc_slice expect_in_details = grpc_slice_from_copied_string( - send_limit ? "Sent message larger than max (11 vs. 5)" - : "Received message larger than max (11 vs. 5)"); - int was_cancelled = 2; - - grpc_channel_args* client_args = nullptr; - grpc_channel_args* server_args = nullptr; - if (use_service_config) { - // We don't currently support service configs on the server side. - GPR_ASSERT(!send_limit); - grpc_arg arg; - arg.type = GRPC_ARG_STRING; - arg.key = const_cast(GRPC_ARG_SERVICE_CONFIG); - arg.value.string = const_cast( - use_string_json_value - ? "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"maxResponseMessageBytes\": \"5\"\n" - " } ]\n" - "}" - : "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"maxResponseMessageBytes\": 5\n" - " } ]\n" - "}"); - client_args = grpc_channel_args_copy_and_add(nullptr, &arg, 1); - } else { - // Set limit via channel args. - grpc_arg arg; - arg.key = send_limit - ? const_cast(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH) - : const_cast(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH); - arg.type = GRPC_ARG_INTEGER; - arg.value.integer = 5; - grpc_channel_args* args = grpc_channel_args_copy_and_add(nullptr, &arg, 1); - if (send_limit) { - server_args = args; - } else { - client_args = args; - } - } - - auto f = begin_test(config, "test_max_response_message_length", client_args, - server_args); - { - grpc_core::ExecCtx exec_ctx; - if (client_args != nullptr) grpc_channel_args_destroy(client_args); - if (server_args != nullptr) grpc_channel_args_destroy(server_args); - } - grpc_core::CqVerifier cqv(f->cq()); - - c = grpc_channel_create_call( - f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, f->cq(), - grpc_slice_from_static_string("/service/method"), nullptr, - gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &recv_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); +void TestMaxMessageLengthOnClientOnResponse(CoreEnd2endTest& test) { + auto c = test.NewClientCall("/service/method").Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .RecvCloseOnServer(client_close) + .SendMessage("hello world") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_EQ(server_status.status(), GRPC_STATUS_RESOURCE_EXHAUSTED); + EXPECT_EQ(server_status.message(), + "Received message larger than max (11 vs. 5)"); +} - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); +void TestMaxMessageLengthOnServerOnResponse(CoreEnd2endTest& test) { + auto c = test.NewClientCall("/service/method").Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .RecvCloseOnServer(client_close) + .SendMessage("hello world") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_EQ(server_status.status(), GRPC_STATUS_RESOURCE_EXHAUSTED); + EXPECT_EQ(server_status.message(), "Sent message larger than max (11 vs. 5)"); +} - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); +TEST_P(CoreEnd2endTest, MaxMessageLengthOnClientOnRequestViaChannelArg) { + SKIP_IF_MINSTACK(); + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, 5)); + TestMaxMessageLengthOnClientOnRequest(*this); +} - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); +TEST_P(CoreEnd2endTest, + MaxMessageLengthOnClientOnRequestViaServiceConfigWithStringJsonValue) { + SKIP_IF_MINSTACK(); + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"maxRequestMessageBytes\": \"5\"\n" + " } ]\n" + "}")); + TestMaxMessageLengthOnClientOnRequest(*this); +} - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(status == GRPC_STATUS_RESOURCE_EXHAUSTED); - GPR_ASSERT(grpc_slice_slice(details, expect_in_details) >= 0); +TEST_P(CoreEnd2endTest, + MaxMessageLengthOnClientOnRequestViaServiceConfigWithIntegerJsonValue) { + SKIP_IF_MINSTACK(); + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"maxRequestMessageBytes\": 5\n" + " } ]\n" + "}")); + TestMaxMessageLengthOnClientOnRequest(*this); +} - grpc_slice_unref(details); - grpc_slice_unref(expect_in_details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(recv_payload); +TEST_P(CoreEnd2endTest, MaxMessageLengthOnServerOnRequestViaChannelArg) { + SKIP_IF_MINSTACK(); + InitServer(ChannelArgs().Set(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, 5)); + InitClient(ChannelArgs()); + TestMaxMessageLengthOnServerOnRequest(*this); +} - grpc_call_unref(c); - if (s != nullptr) grpc_call_unref(s); +TEST_P(CoreEnd2endTest, MaxMessageLengthOnClientOnResponseViaChannelArg) { + SKIP_IF_MINSTACK(); + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, 5)); + TestMaxMessageLengthOnClientOnResponse(*this); } -static grpc_metadata gzip_compression_override() { - grpc_metadata gzip_compression_override; - gzip_compression_override.key = - grpc_slice_from_static_string("grpc-internal-encoding-request"); - gzip_compression_override.value = grpc_slice_from_static_string("gzip"); - memset(&gzip_compression_override.internal_data, 0, - sizeof(gzip_compression_override.internal_data)); - return gzip_compression_override; +TEST_P(CoreEnd2endTest, + MaxMessageLengthOnClientOnResponseViaServiceConfigWithStringJsonValue) { + SKIP_IF_MINSTACK(); + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"maxResponseMessageBytes\": \"5\"\n" + " } ]\n" + "}")); + TestMaxMessageLengthOnClientOnResponse(*this); } -// Test receive message limit with compressed request larger than the limit -static void test_max_receive_message_length_on_compressed_request( - const CoreTestConfiguration& config) { - gpr_log(GPR_INFO, "test max receive message length on compressed request"); +TEST_P(CoreEnd2endTest, + MaxMessageLengthOnClientOnResponseViaServiceConfigWithIntegerJsonValue) { + SKIP_IF_MINSTACK(); + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"maxResponseMessageBytes\": 5\n" + " } ]\n" + "}")); + TestMaxMessageLengthOnClientOnResponse(*this); +} - grpc_call* c = nullptr; - grpc_call* s = nullptr; - grpc_op ops[6]; - grpc_op* op; - grpc_slice request_payload_slice = grpc_slice_malloc(1024); - memset(GRPC_SLICE_START_PTR(request_payload_slice), 'a', 1024); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* recv_payload = nullptr; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; +TEST_P(CoreEnd2endTest, MaxMessageLengthOnServerOnResponseViaChannelArg) { + SKIP_IF_MINSTACK(); + InitServer(ChannelArgs().Set(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, 5)); + InitClient(ChannelArgs()); + TestMaxMessageLengthOnServerOnResponse(*this); +} +TEST_P(Http2Test, MaxMessageLengthOnServerOnRequestWithCompression) { + SKIP_IF_MINSTACK(); // Set limit via channel args. - grpc_arg arg[1]; - arg[0] = grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH), 5); - grpc_channel_args* server_args = - grpc_channel_args_copy_and_add(nullptr, arg, 1); - - auto f = begin_test(config, "test_max_request_message_length", nullptr, - server_args); - { - grpc_core::ExecCtx exec_ctx; - grpc_channel_args_destroy(server_args); - } - grpc_core::CqVerifier cqv(f->cq()); - c = grpc_channel_create_call( - f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, f->cq(), - grpc_slice_from_static_string("/service/method"), nullptr, - gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - grpc_metadata compression_md = gzip_compression_override(); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 1; - op->data.send_initial_metadata.metadata = &compression_md; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &recv_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + InitServer(ChannelArgs().Set(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, 5)); + InitClient(ChannelArgs()); + auto c = NewClientCall("/service/method").Create(); + IncomingStatusOnClient server_status; + IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({{"grpc-internal-encoding-request", "gzip"}}) + .SendMessage(std::string(1024, 'a')) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + IncomingMessage client_message; + IncomingCloseOnServer client_close; + s.NewBatch(102).RecvMessage(client_message); + s.NewBatch(103).RecvCloseOnServer(client_close); // WARNING!! // It's believed the following line (and the associated batch) is the only // test we have for failing a receive operation in a batch. - cqv.Expect(grpc_core::CqVerifier::tag(102), false); - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 1); - GPR_ASSERT(status == GRPC_STATUS_RESOURCE_EXHAUSTED); - GPR_ASSERT(absl::StartsWith(grpc_core::StringViewFromSlice(details), - "Received message larger than max")); - grpc_slice_unref(details); - grpc_slice_unref(request_payload_slice); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(recv_payload); - grpc_call_unref(c); - if (s != nullptr) grpc_call_unref(s); + Expect(102, false); + Expect(103, true); + Expect(1, true); + Step(); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_TRUE(client_close.was_cancelled()); + EXPECT_EQ(server_status.status(), GRPC_STATUS_RESOURCE_EXHAUSTED); + EXPECT_THAT(server_status.message(), + StartsWith("Received message larger than max")); } -// Test receive message limit with compressed response larger than the limit. -static void test_max_receive_message_length_on_compressed_response( - const CoreTestConfiguration& config) { - gpr_log(GPR_INFO, - "testing max receive message length on compressed response"); - - grpc_call* c = nullptr; - grpc_call* s = nullptr; - grpc_op ops[6]; - grpc_op* op; - grpc_slice response_payload_slice = grpc_slice_malloc(1024); - memset(GRPC_SLICE_START_PTR(response_payload_slice), 'a', 1024); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* recv_payload = nullptr; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - +TEST_P(Http2Test, MaxMessageLengthOnClientOnResponseWithCompression) { + SKIP_IF_MINSTACK(); // Set limit via channel args. - grpc_arg arg[1]; - arg[0] = grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH), 5); - grpc_channel_args* client_args = - grpc_channel_args_copy_and_add(nullptr, arg, 1); - - auto f = begin_test(config, "test_max_response_message_length", client_args, - nullptr); - { - grpc_core::ExecCtx exec_ctx; - grpc_channel_args_destroy(client_args); - } - grpc_core::CqVerifier cqv(f->cq()); - - c = grpc_channel_create_call( - f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, f->cq(), - grpc_slice_from_static_string("/service/method"), nullptr, - gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &recv_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - grpc_metadata compression_md = gzip_compression_override(); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 1; - op->data.send_initial_metadata.metadata = &compression_md; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(status == GRPC_STATUS_RESOURCE_EXHAUSTED); - GPR_ASSERT(absl::StartsWith(grpc_core::StringViewFromSlice(details), - "Received message larger than max")); - grpc_slice_unref(details); - grpc_slice_unref(response_payload_slice); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(recv_payload); - - grpc_call_unref(c); - if (s != nullptr) grpc_call_unref(s); -} - -void max_message_length(const CoreTestConfiguration& config) { - test_max_message_length_on_request(config, false /* send_limit */, - false /* use_service_config */, - false /* use_string_json_value */); - test_max_message_length_on_request(config, true /* send_limit */, - false /* use_service_config */, - false /* use_string_json_value */); - test_max_message_length_on_response(config, false /* send_limit */, - false /* use_service_config */, - false /* use_string_json_value */); - test_max_message_length_on_response(config, true /* send_limit */, - false /* use_service_config */, - false /* use_string_json_value */); - test_max_message_length_on_request(config, true /* send_limit */, - true /* use_service_config */, - false /* use_string_json_value */); - test_max_message_length_on_request(config, true /* send_limit */, - true /* use_service_config */, - true /* use_string_json_value */); - test_max_message_length_on_response(config, false /* send_limit */, - true /* use_service_config */, - false /* use_string_json_value */); - test_max_message_length_on_response(config, false /* send_limit */, - true /* use_service_config */, - true /* use_string_json_value */); - // The following tests are not useful for inproc transport and do not work - // with our simple proxy. - if (strcmp(config.name, "inproc") != 0 && - (config.feature_mask & FEATURE_MASK_SUPPORTS_REQUEST_PROXYING) == 0) { - test_max_receive_message_length_on_compressed_request(config); - test_max_receive_message_length_on_compressed_response(config); - } + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, 5)); + auto c = NewClientCall("/service/method").Create(); + IncomingStatusOnClient server_status; + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({{"grpc-internal-encoding-request", "gzip"}}) + .RecvCloseOnServer(client_close) + .SendMessage(std::string(1024, 'a')) + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + Expect(102, true); + Expect(1, true); + Step(); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_EQ(server_status.status(), GRPC_STATUS_RESOURCE_EXHAUSTED); + EXPECT_THAT(server_status.message(), + StartsWith("Received message larger than max")); } -void max_message_length_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/negative_deadline.cc b/test/core/end2end/tests/negative_deadline.cc index 1c0935ec9a2b7..e46cbd24a1fe3 100644 --- a/test/core/end2end/tests/negative_deadline.cc +++ b/test/core/end2end/tests/negative_deadline.cc @@ -16,109 +16,30 @@ // // -#include -#include -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static void simple_request_body(const CoreTestConfiguration& /*config*/, - CoreTestFixture* f, size_t num_ops) { - grpc_call* c; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - - gpr_log(GPR_DEBUG, "test with %" PRIuPTR " ops", num_ops); - - gpr_timespec deadline = gpr_inf_past(GPR_CLOCK_REALTIME); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - GPR_ASSERT(num_ops <= (size_t)(op - ops)); - error = grpc_call_start_batch(c, ops, num_ops, grpc_core::CqVerifier::tag(1), - nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_DEADLINE_EXCEEDED); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - - grpc_call_unref(c); -} - -static void test_invoke_simple_request(const CoreTestConfiguration& config, - size_t num_ops) { - auto f = begin_test(config, "test_invoke_simple_request", nullptr, nullptr); - simple_request_body(config, f.get(), num_ops); -} - -void negative_deadline(const CoreTestConfiguration& config) { - size_t i; - for (i = 1; i <= 4; i++) { - test_invoke_simple_request(config, i); - } +namespace grpc_core { +namespace { + +TEST_P(CoreDeadlineTest, NegativeDeadline) { + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(-1)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .RecvStatusOnClient(server_status) + .RecvInitialMetadata(server_initial_metadata) + .SendInitialMetadata({}) + .SendCloseFromClient(); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_DEADLINE_EXCEEDED); } -void negative_deadline_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/no_logging.cc b/test/core/end2end/tests/no_logging.cc index 6cde13c039d44..9efc6a6edb491 100644 --- a/test/core/end2end/tests/no_logging.cc +++ b/test/core/end2end/tests/no_logging.cc @@ -16,234 +16,113 @@ // // -#include -#include - -#include -#include +#include #include #include "absl/strings/str_cat.h" +#include "absl/types/optional.h" +#include "gtest/gtest.h" #include -#include -#include #include -#include -#include #include -#include -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gprpp/env.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -enum { TIMEOUT = 200000 }; void gpr_default_log(gpr_log_func_args* args); -static void test_no_log(gpr_log_func_args* args) { - std::string message = absl::StrCat("Unwanted log: ", args->message); - args->message = message.c_str(); - gpr_default_log(args); - abort(); -} - -static void test_no_error_log(gpr_log_func_args* args) { - if (args->severity == GPR_LOG_SEVERITY_ERROR) { - test_no_log(args); +namespace grpc_core { + +class Verifier { + public: + Verifier() { + if (gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { + saved_severity_ = GPR_LOG_SEVERITY_DEBUG; + } else if (gpr_should_log(GPR_LOG_SEVERITY_INFO)) { + saved_severity_ = GPR_LOG_SEVERITY_INFO; + } else if (gpr_should_log(GPR_LOG_SEVERITY_ERROR)) { + saved_severity_ = GPR_LOG_SEVERITY_ERROR; + } else { + saved_severity_ = + static_cast(GPR_LOG_SEVERITY_ERROR + 1); + } + grpc_tracer_set_enabled("all", 0); + gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG); + gpr_set_log_function(DispatchLog); } -} - -static gpr_atm g_log_func = reinterpret_cast(gpr_default_log); - -static void log_dispatcher_func(gpr_log_func_args* args) { - gpr_log_func log_func = - reinterpret_cast(gpr_atm_no_barrier_load(&g_log_func)); - log_func(args); -} - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} + ~Verifier() { + gpr_set_log_function(gpr_default_log); + saved_trace_flags_.Restore(); + gpr_set_log_verbosity(saved_severity_); + } + Verifier(const Verifier&) = delete; + Verifier& operator=(const Verifier&) = delete; -static void simple_request_body(const CoreTestConfiguration& /*config*/, - CoreTestFixture* f) { - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); -} + void FailOnAnyLog() { g_log_func_.store(NoLog); } + void FailOnNonErrorLog() { g_log_func_.store(NoErrorLog); } -static void test_invoke_simple_request(const CoreTestConfiguration& config) { - auto f = - begin_test(config, "test_invoke_simple_request_with_no_error_logging", - nullptr, nullptr); - simple_request_body(config, f.get()); -} + private: + static void DispatchLog(gpr_log_func_args* args) { g_log_func_.load()(args); } -static void test_invoke_10_simple_requests( - const CoreTestConfiguration& config) { - int i; - auto f = - begin_test(config, "test_invoke_10_simple_requests_with_no_error_logging", - nullptr, nullptr); - for (i = 0; i < 10; i++) { - simple_request_body(config, f.get()); - gpr_log(GPR_INFO, "Passed simple request %d", i); + static void NoLog(gpr_log_func_args* args) { + std::string message = absl::StrCat("Unwanted log: ", args->message); + args->message = message.c_str(); + gpr_default_log(args); + GTEST_FAIL(); } - simple_request_body(config, f.get()); -} -static void test_no_error_logging_in_entire_process( - const CoreTestConfiguration& config) { - int i; - gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)test_no_error_log); - for (i = 0; i < 10; i++) { - test_invoke_simple_request(config); + static void NoErrorLog(gpr_log_func_args* args) { + if (args->severity == GPR_LOG_SEVERITY_ERROR) { + NoLog(args); + } } - test_invoke_10_simple_requests(config); - gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)gpr_default_log); -} -static void test_no_logging_in_one_request( - const CoreTestConfiguration& config) { - int i; - auto f = - begin_test(config, "test_no_logging_in_last_request", nullptr, nullptr); - for (i = 0; i < 10; i++) { - simple_request_body(config, f.get()); - } - gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)test_no_log); - simple_request_body(config, f.get()); - gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)gpr_default_log); + gpr_log_severity saved_severity_; + SavedTraceFlags saved_trace_flags_; + static std::atomic g_log_func_; +}; + +std::atomic Verifier::g_log_func_(gpr_default_log); + +void SimpleRequest(CoreEnd2endTest& test) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + EXPECT_NE(s.GetPeer(), absl::nullopt); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); } -void no_logging(const CoreTestConfiguration& config) { - grpc_core::SetEnv("GRPC_TRACE", ""); - gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG); - grpc_tracer_set_enabled("all", 0); - gpr_set_log_function(log_dispatcher_func); - test_no_logging_in_one_request(config); - test_no_error_logging_in_entire_process(config); - gpr_set_log_function(gpr_default_log); +TEST_P(NoLoggingTest, NoLoggingTest) { + Verifier verifier; + verifier.FailOnNonErrorLog(); + for (int i = 0; i < 10; i++) { + SimpleRequest(*this); + } + verifier.FailOnAnyLog(); + SimpleRequest(*this); } -void no_logging_pre_init(void) {} +} // namespace grpc_core diff --git a/test/core/end2end/tests/no_op.cc b/test/core/end2end/tests/no_op.cc index 66c20748347ce..21e0255b38f3c 100644 --- a/test/core/end2end/tests/no_op.cc +++ b/test/core/end2end/tests/no_op.cc @@ -16,30 +16,10 @@ // // -#include -#include +#include "gtest/gtest.h" -#include -#include - -#include "src/core/lib/channel/channel_args.h" #include "test/core/end2end/end2end_tests.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static void test_no_op(const CoreTestConfiguration& config) { - auto f = begin_test(config, "no-op", nullptr, nullptr); -} - -void no_op(const CoreTestConfiguration& config) { test_no_op(config); } - -void no_op_pre_init(void) {} +namespace grpc_core { +TEST_P(CoreEnd2endTest, NoOp) {} +} // namespace grpc_core diff --git a/test/core/end2end/tests/payload.cc b/test/core/end2end/tests/payload.cc index a4b3cfd498e93..6c9dc35050702 100644 --- a/test/core/end2end/tests/payload.cc +++ b/test/core/end2end/tests/payload.cc @@ -16,230 +16,74 @@ // // -#include -#include -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include -#include #include -#include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" +#include "src/core/lib/slice/slice.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} -// Creates and returns a grpc_slice containing random alphanumeric characters. -// -static grpc_slice generate_random_slice() { - size_t i; - static const char chars[] = "abcdefghijklmnopqrstuvwxyz1234567890"; - char* output; - const size_t output_size = 1024 * 1024; - output = static_cast(gpr_malloc(output_size)); - for (i = 0; i < output_size - 1; ++i) { - output[i] = chars[rand() % static_cast(sizeof(chars) - 1)]; - } - output[output_size - 1] = '\0'; - grpc_slice out = grpc_slice_from_copied_string(output); - gpr_free(output); - return out; -} +namespace grpc_core { -static void request_response_with_payload( - const CoreTestConfiguration& /*config*/, CoreTestFixture* f) { +namespace { +void RequestResponseWithPayload(CoreEnd2endTest& test) { // Create large request and response bodies. These are big enough to require // multiple round trips to deliver to the peer, and their exact contents of // will be verified on completion. - grpc_slice request_payload_slice = generate_random_slice(); - grpc_slice response_payload_slice = generate_random_slice(); - - grpc_call* c; - grpc_call* s; - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(60); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice)); - GPR_ASSERT( - byte_buffer_eq_slice(response_payload_recv, response_payload_slice)); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); + auto request_slice = RandomSlice(1024 * 1024); + auto response_slice = RandomSlice(1024 * 1024); + + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(60)).Create(); + + CoreEnd2endTest::IncomingMetadata server_initial_md; + CoreEnd2endTest::IncomingMessage server_message; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage(request_slice.Ref()) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_md) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + + CoreEnd2endTest::IncomingCall s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).SendInitialMetadata({}).RecvMessage(client_message); + test.Expect(102, true); + test.Step(); + + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(103) + .RecvCloseOnServer(client_close) + .SendMessage(response_slice.Ref()) + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + test.Expect(103, true); + test.Expect(1, true); + test.Step(); + + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(client_message.payload(), request_slice); + EXPECT_EQ(server_message.payload(), response_slice); } +} // namespace // Client sends a request with payload, server reads then returns a response // payload and status. -static void test_invoke_request_response_with_payload( - const CoreTestConfiguration& config) { - auto f = begin_test(config, "test_invoke_request_response_with_payload", - nullptr, nullptr); - request_response_with_payload(config, f.get()); +TEST_P(CoreLargeSendTest, RequestResponseWithPayload) { + RequestResponseWithPayload(*this); } -static void test_invoke_10_request_response_with_payload( - const CoreTestConfiguration& config) { - int i; - auto f = begin_test(config, "test_invoke_10_request_response_with_payload", - nullptr, nullptr); - for (i = 0; i < 10; i++) { - request_response_with_payload(config, f.get()); +TEST_P(CoreLargeSendTest, RequestResponseWithPayload10Times) { + for (int i = 0; i < 10; i++) { + RequestResponseWithPayload(*this); } } -void payload(const CoreTestConfiguration& config) { - test_invoke_request_response_with_payload(config); - test_invoke_10_request_response_with_payload(config); -} - -void payload_pre_init(void) {} +} // namespace grpc_core diff --git a/test/core/end2end/tests/ping.cc b/test/core/end2end/tests/ping.cc index 83abe51648a98..e34b624180a62 100644 --- a/test/core/end2end/tests/ping.cc +++ b/test/core/end2end/tests/ping.cc @@ -16,81 +16,52 @@ // // -#include -#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/surface/channel.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -#define PING_NUM 5 +namespace grpc_core { +namespace { -static void test_ping(const CoreTestConfiguration& config, - int min_time_between_pings_ms) { - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - grpc_core::CqVerifier cqv(f->cq()); +TEST_P(RetryHttp2Test, Ping) { + const int kPingCount = 5; grpc_connectivity_state state = GRPC_CHANNEL_IDLE; - int i; - - auto client_args = grpc_core::ChannelArgs() - .Set(GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA, 0) - .Set(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1); - auto server_args = - grpc_core::ChannelArgs() - .Set(GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS, 0) - .Set(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1); - - f->InitClient(client_args); - f->InitServer(server_args); - - grpc_channel_ping(f->client(), f->cq(), grpc_core::CqVerifier::tag(0), - nullptr); - cqv.Expect(grpc_core::CqVerifier::tag(0), false); - + InitClient(ChannelArgs() + .Set(GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA, 0) + .Set(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1)); + InitServer(ChannelArgs() + .Set(GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS, 0) + .Set(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1)); + PingServerFromClient(0); + Expect(0, false); + Step(); // check that we're still in idle, and start connecting - GPR_ASSERT(grpc_channel_check_connectivity_state(f->client(), 1) == - GRPC_CHANNEL_IDLE); + EXPECT_EQ(CheckConnectivityState(true), GRPC_CHANNEL_IDLE); // we'll go through some set of transitions (some might be missed), until // READY is reached while (state != GRPC_CHANNEL_READY) { - grpc_channel_watch_connectivity_state( - f->client(), state, - gpr_time_add(grpc_timeout_seconds_to_deadline(3), - gpr_time_from_millis(min_time_between_pings_ms * PING_NUM, - GPR_TIMESPAN)), - f->cq(), grpc_core::CqVerifier::tag(99)); - cqv.Expect(grpc_core::CqVerifier::tag(99), true); - cqv.Verify(); - state = grpc_channel_check_connectivity_state(f->client(), 0); - GPR_ASSERT(state == GRPC_CHANNEL_READY || - state == GRPC_CHANNEL_CONNECTING || - state == GRPC_CHANNEL_TRANSIENT_FAILURE); + WatchConnectivityState(state, Duration::Seconds(3), 99); + Expect(99, true); + Step(); + state = CheckConnectivityState(false); + EXPECT_THAT(state, + ::testing::AnyOf(GRPC_CHANNEL_READY, GRPC_CHANNEL_CONNECTING, + GRPC_CHANNEL_TRANSIENT_FAILURE)); } - - for (i = 1; i <= PING_NUM; i++) { - grpc_channel_ping(f->client(), f->cq(), grpc_core::CqVerifier::tag(i), - nullptr); - cqv.Expect(grpc_core::CqVerifier::tag(i), true); - cqv.Verify(); + for (int i = 1; i <= kPingCount; i++) { + PingServerFromClient(i); + Expect(i, true); + Step(); } - - grpc_server_shutdown_and_notify(f->server(), f->cq(), - grpc_core::CqVerifier::tag(0xdead)); - cqv.Expect(grpc_core::CqVerifier::tag(0xdead), true); - cqv.Verify(); -} - -void ping(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION); - test_ping(config, 0); - test_ping(config, 100); + ShutdownServerAndNotify(1000); + Expect(1000, true); + Step(); } -void ping_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/ping_pong_streaming.cc b/test/core/end2end/tests/ping_pong_streaming.cc index 394da3aae04bc..dbbb4de1d1e11 100644 --- a/test/core/end2end/tests/ping_pong_streaming.cc +++ b/test/core/end2end/tests/ping_pong_streaming.cc @@ -16,221 +16,57 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" +#include "src/core/lib/slice/slice.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { // Client pings and server pongs. Repeat messages rounds before finishing. -static void test_pingpong_streaming(const CoreTestConfiguration& config, - int messages) { - auto f = begin_test(config, "test_pingpong_streaming", nullptr, nullptr); - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - grpc_byte_buffer* request_payload; - grpc_byte_buffer* request_payload_recv; - grpc_byte_buffer* response_payload; - grpc_byte_buffer* response_payload_recv; - int i; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_slice response_payload_slice = - grpc_slice_from_copied_string("hello you"); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(100)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(100), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(101), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - for (i = 0; i < messages; i++) { - request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - response_payload = grpc_raw_byte_buffer_create(&response_payload_slice, 1); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); +void PingPongStreaming(CoreEnd2endTest& test, int num_messages) { + auto request_slice = RandomSlice(20); + auto response_slice = RandomSlice(15); + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_md; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .RecvInitialMetadata(server_initial_md) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(100); + test.Expect(100, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(101).SendInitialMetadata({}).RecvCloseOnServer(client_close); + for (int i = 0; i < num_messages; i++) { + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(2).SendMessage(request_slice.Ref()).RecvMessage(server_message); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).RecvMessage(client_message); + test.Expect(102, true); + test.Step(); + s.NewBatch(103).SendMessage(response_slice.Ref()); + test.Expect(2, true); + test.Expect(103, true); + test.Step(); } - - grpc_slice_unref(request_payload_slice); - grpc_slice_unref(response_payload_slice); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(104), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Expect(grpc_core::CqVerifier::tag(104), true); - cqv.Verify(); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_slice_unref(details); + c.NewBatch(3).SendCloseFromClient(); + s.NewBatch(104).SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}); + test.Expect(1, true); + test.Expect(3, true); + test.Expect(101, true); + test.Expect(104, true); + test.Step(); } -void ping_pong_streaming(const CoreTestConfiguration& config) { - int i; +TEST_P(CoreEnd2endTest, PingPongStreaming1) { PingPongStreaming(*this, 1); } - for (i = 1; i < 10; i++) { - test_pingpong_streaming(config, i); - } -} +TEST_P(CoreEnd2endTest, PingPongStreaming3) { PingPongStreaming(*this, 3); } + +TEST_P(CoreEnd2endTest, PingPongStreaming10) { PingPongStreaming(*this, 10); } -void ping_pong_streaming_pre_init(void) {} +TEST_P(CoreEnd2endTest, PingPongStreaming30) { PingPongStreaming(*this, 30); } +} // namespace grpc_core diff --git a/test/core/end2end/tests/proxy_auth.cc b/test/core/end2end/tests/proxy_auth.cc index 2ce8e0c2e8e91..354088aa8ec01 100644 --- a/test/core/end2end/tests/proxy_auth.cc +++ b/test/core/end2end/tests/proxy_auth.cc @@ -16,168 +16,51 @@ // // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/end2end/fixtures/http_proxy_fixture.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static void simple_request_body(const CoreTestConfiguration& /*config*/, - CoreTestFixture* f) { - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); +namespace grpc_core { +namespace { - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -static void test_invoke_proxy_auth(const CoreTestConfiguration& config) { +TEST_P(ProxyAuthTest, InvokeProxyAuth) { // Indicate that the proxy requires user auth - grpc_arg client_arg; - client_arg.type = GRPC_ARG_STRING; - client_arg.key = const_cast(GRPC_ARG_HTTP_PROXY_AUTH_CREDS); - client_arg.value.string = const_cast(GRPC_TEST_HTTP_PROXY_AUTH_CREDS); - grpc_channel_args client_args = {1, &client_arg}; - auto f = begin_test(config, "test_invoke_proxy_auth", &client_args, nullptr); - simple_request_body(config, f.get()); -} - -void proxy_auth(const CoreTestConfiguration& config) { - test_invoke_proxy_auth(config); + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set(GRPC_ARG_HTTP_PROXY_AUTH_CREDS, + GRPC_TEST_HTTP_PROXY_AUTH_CREDS)); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); } -void proxy_auth_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/registered_call.cc b/test/core/end2end/tests/registered_call.cc index 6d0b4c8355999..45336728ba084 100644 --- a/test/core/end2end/tests/registered_call.cc +++ b/test/core/end2end/tests/registered_call.cc @@ -16,161 +16,53 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static void simple_request_body(const CoreTestConfiguration& /*config*/, - CoreTestFixture* f, void* rc) { - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_registered_call(f->client(), nullptr, - GRPC_PROPAGATE_DEFAULTS, f->cq(), rc, - deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); +namespace grpc_core { +namespace { + +void SimpleRequestBody(CoreEnd2endTest& test, + CoreEnd2endTest::RegisteredCall rc) { + auto c = test.NewClientCall(rc).Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); } -static void test_invoke_simple_request(const CoreTestConfiguration& config) { - auto f = begin_test(config, "test_invoke_simple_request", nullptr, nullptr); - void* rc = grpc_channel_register_call(f->client(), "/foo", nullptr, nullptr); - - simple_request_body(config, f.get(), rc); +TEST_P(CoreEnd2endTest, InvokeRegisteredCall) { + SimpleRequestBody(*this, RegisterCallOnClient("/foo", nullptr)); } -static void test_invoke_10_simple_requests( - const CoreTestConfiguration& config) { - int i; - auto f = - begin_test(config, "test_invoke_10_simple_requests", nullptr, nullptr); - void* rc = grpc_channel_register_call(f->client(), "/foo", nullptr, nullptr); - - for (i = 0; i < 10; i++) { - simple_request_body(config, f.get(), rc); - gpr_log(GPR_INFO, "Passed simple request %d", i); +TEST_P(CoreEnd2endTest, Invoke10RegisteredCalls) { + auto rc = RegisterCallOnClient("/foo", nullptr); + for (int i = 0; i < 10; i++) { + SimpleRequestBody(*this, rc); } } -void registered_call(const CoreTestConfiguration& config) { - test_invoke_simple_request(config); - test_invoke_10_simple_requests(config); -} - -void registered_call_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/request_with_flags.cc b/test/core/end2end/tests/request_with_flags.cc index 408307fba9835..5798d97d60f53 100644 --- a/test/core/end2end/tests/request_with_flags.cc +++ b/test/core/end2end/tests/request_with_flags.cc @@ -19,167 +19,152 @@ #include #include -#include -#include -#include +#include +#include -#include "absl/strings/str_cat.h" +#include "absl/types/optional.h" +#include "gtest/gtest.h" #include #include -#include #include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/transport/transport.h" #include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} -static gpr_timespec one_second_from_now(void) { - return grpc_timeout_seconds_to_deadline(1); -} +namespace grpc_core { +namespace { -static void test_invoke_request_with_flags( - const CoreTestConfiguration& config, uint32_t* flags_for_op, - grpc_call_error call_start_batch_expected_result) { - grpc_call* c; +void InvokeRequestWithFlags(CoreEnd2endTest& test, + std::map flags_for_op, + grpc_call_error call_start_batch_expected_result) { grpc_slice request_payload_slice = grpc_slice_from_copied_string("hello world"); grpc_byte_buffer* request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - auto f = begin_test( - config, - absl::StrCat("test_invoke_request_with_flags[", - absl::Hex(flags_for_op[GRPC_OP_SEND_INITIAL_METADATA]), ",", - absl::Hex(flags_for_op[GRPC_OP_SEND_MESSAGE]), ",", - absl::Hex(flags_for_op[GRPC_OP_SEND_CLOSE_FROM_CLIENT]), ",", - absl::Hex(flags_for_op[GRPC_OP_RECV_INITIAL_METADATA]), ",", - absl::Hex(flags_for_op[GRPC_OP_RECV_STATUS_ON_CLIENT]), - "]=>", - grpc_call_error_to_string(call_start_batch_expected_result)) - .c_str(), - nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); grpc_op ops[6]; grpc_op* op; grpc_metadata_array initial_metadata_recv; grpc_metadata_array trailing_metadata_recv; grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; grpc_call_details call_details; grpc_status_code status; grpc_call_error error; grpc_slice details; - grpc_call_error expectation; - gpr_timespec deadline = one_second_from_now(); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); + auto get_flags = [flags_for_op](grpc_op_type op_type) -> uint32_t { + auto it = flags_for_op.find(op_type); + if (it == flags_for_op.end()) return 0; + return it->second; + }; grpc_metadata_array_init(&initial_metadata_recv); grpc_metadata_array_init(&trailing_metadata_recv); grpc_metadata_array_init(&request_metadata_recv); grpc_call_details_init(&call_details); + absl::optional c = + test.NewClientCall("/foo").Timeout(Duration::Seconds(1)).Create(); + memset(ops, 0, sizeof(ops)); op = ops; op->op = GRPC_OP_SEND_INITIAL_METADATA; op->data.send_initial_metadata.count = 0; - op->flags = flags_for_op[op->op]; + op->flags = get_flags(op->op); op->reserved = nullptr; op++; op->op = GRPC_OP_SEND_MESSAGE; op->data.send_message.send_message = request_payload; - op->flags = flags_for_op[op->op]; + op->flags = get_flags(op->op); op->reserved = nullptr; op++; op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = flags_for_op[op->op]; + op->flags = get_flags(op->op); op->reserved = nullptr; op++; op->op = GRPC_OP_RECV_INITIAL_METADATA; op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = flags_for_op[op->op]; + op->flags = get_flags(op->op); op->reserved = nullptr; op++; op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; op->data.recv_status_on_client.status = &status; op->data.recv_status_on_client.status_details = &details; - op->flags = flags_for_op[op->op]; + op->flags = get_flags(op->op); op->reserved = nullptr; op++; - expectation = call_start_batch_expected_result; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(expectation == error); - - if (expectation == GRPC_CALL_OK) { - if (config.feature_mask & FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES) { - GPR_ASSERT(GRPC_CALL_OK == grpc_call_cancel(c, nullptr)); + error = grpc_call_start_batch(c->c_call(), ops, static_cast(op - ops), + CqVerifier::tag(1), nullptr); + EXPECT_EQ(error, call_start_batch_expected_result); + if (error == GRPC_CALL_OK) { + if (test.GetParam()->feature_mask & FEATURE_MASK_IS_MINSTACK) { + c->Cancel(); } - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); + test.Expect(1, true); + test.Step(); grpc_slice_unref(details); } + c.reset(); + grpc_metadata_array_destroy(&initial_metadata_recv); grpc_metadata_array_destroy(&trailing_metadata_recv); grpc_metadata_array_destroy(&request_metadata_recv); grpc_call_details_destroy(&call_details); + grpc_byte_buffer_destroy(request_payload); +} - grpc_call_unref(c); +TEST_P(CoreEnd2endTest, BadFlagsOnSendInitialMetadata) { + InvokeRequestWithFlags(*this, {{GRPC_OP_SEND_INITIAL_METADATA, 0xdeadbeef}}, + GRPC_CALL_ERROR_INVALID_FLAGS); +} - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request_payload_recv); +TEST_P(CoreEnd2endTest, BadFlagsOnSendMessage) { + InvokeRequestWithFlags(*this, {{GRPC_OP_SEND_MESSAGE, 0xdeadbeef}}, + GRPC_CALL_ERROR_INVALID_FLAGS); } -void request_with_flags(const CoreTestConfiguration& config) { - size_t i; - uint32_t flags_for_op[GRPC_OP_RECV_CLOSE_ON_SERVER + 1]; - - { - // check that all grpc_op_types fail when their flag value is set to an - // invalid value - int indices[] = {GRPC_OP_SEND_INITIAL_METADATA, GRPC_OP_SEND_MESSAGE, - GRPC_OP_SEND_CLOSE_FROM_CLIENT, - GRPC_OP_RECV_INITIAL_METADATA, - GRPC_OP_RECV_STATUS_ON_CLIENT}; - for (i = 0; i < GPR_ARRAY_SIZE(indices); ++i) { - memset(flags_for_op, 0, sizeof(flags_for_op)); - flags_for_op[indices[i]] = 0xDEADBEEF; - test_invoke_request_with_flags(config, flags_for_op, - GRPC_CALL_ERROR_INVALID_FLAGS); - } - } - { - // check valid operation with allowed flags for GRPC_OP_SEND_BUFFER - uint32_t flags[] = {GRPC_WRITE_BUFFER_HINT, GRPC_WRITE_NO_COMPRESS, - GRPC_WRITE_INTERNAL_COMPRESS}; - for (i = 0; i < GPR_ARRAY_SIZE(flags); ++i) { - memset(flags_for_op, 0, sizeof(flags_for_op)); - flags_for_op[GRPC_OP_SEND_MESSAGE] = flags[i]; - test_invoke_request_with_flags(config, flags_for_op, GRPC_CALL_OK); - } - } +TEST_P(CoreEnd2endTest, BadFlagsOnSendCloseFromClient) { + InvokeRequestWithFlags(*this, {{GRPC_OP_SEND_CLOSE_FROM_CLIENT, 0xdeadbeef}}, + GRPC_CALL_ERROR_INVALID_FLAGS); +} + +TEST_P(CoreEnd2endTest, BadFlagsOnRecvInitialMetadata) { + InvokeRequestWithFlags(*this, {{GRPC_OP_RECV_INITIAL_METADATA, 0xdeadbeef}}, + GRPC_CALL_ERROR_INVALID_FLAGS); +} + +TEST_P(CoreEnd2endTest, BadFlagsOnRecvStatusOnClient) { + InvokeRequestWithFlags(*this, {{GRPC_OP_RECV_STATUS_ON_CLIENT, 0xdeadbeef}}, + GRPC_CALL_ERROR_INVALID_FLAGS); +} + +TEST_P(CoreEnd2endTest, WriteBufferIntAcceptedOnSendMessage) { + InvokeRequestWithFlags( + *this, {{GRPC_OP_SEND_MESSAGE, GRPC_WRITE_BUFFER_HINT}}, GRPC_CALL_OK); +} + +TEST_P(CoreEnd2endTest, WriteNoCompressAcceptedOnSendMessage) { + InvokeRequestWithFlags( + *this, {{GRPC_OP_SEND_MESSAGE, GRPC_WRITE_NO_COMPRESS}}, GRPC_CALL_OK); +} + +TEST_P(CoreEnd2endTest, WriteBufferHintAndNoCompressAcceptedOnSendMessage) { + InvokeRequestWithFlags( + *this, + {{GRPC_OP_SEND_MESSAGE, GRPC_WRITE_BUFFER_HINT | GRPC_WRITE_NO_COMPRESS}}, + GRPC_CALL_OK); +} + +TEST_P(CoreEnd2endTest, WriteInternalCompressAcceptedOnSendMessage) { + InvokeRequestWithFlags(*this, + {{GRPC_OP_SEND_MESSAGE, GRPC_WRITE_INTERNAL_COMPRESS}}, + GRPC_CALL_OK); } -void request_with_flags_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/request_with_payload.cc b/test/core/end2end/tests/request_with_payload.cc index 9c74ab19e99b1..0b1c2c1ee4415 100644 --- a/test/core/end2end/tests/request_with_payload.cc +++ b/test/core/end2end/tests/request_with_payload.cc @@ -16,172 +16,46 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Client sends a request with payload, server reads then returns status. -static void test_invoke_request_with_payload( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - auto f = - begin_test(config, "test_invoke_request_with_payload", nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - GPR_ASSERT(GRPC_CALL_OK == - grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101))); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request_payload_recv); -} -void request_with_payload(const CoreTestConfiguration& config) { - test_invoke_request_with_payload(config); +namespace grpc_core { +namespace { + +TEST_P(CoreEnd2endTest, RequestWithPayload) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("hello world") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + IncomingMessage client_message; + s.NewBatch(102).SendInitialMetadata({}).RecvMessage(client_message); + Expect(102, true); + Step(); + IncomingCloseOnServer client_close; + s.NewBatch(103) + .RecvCloseOnServer(client_close) + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + Expect(103, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(client_message.payload(), "hello world"); } -void request_with_payload_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/resource_quota_server.cc b/test/core/end2end/tests/resource_quota_server.cc index d4092f79482c9..fa642ed71cad6 100644 --- a/test/core/end2end/tests/resource_quota_server.cc +++ b/test/core/end2end/tests/resource_quota_server.cc @@ -16,329 +16,123 @@ // // -#include -#include -#include -#include - -#include +#include #include -#include +#include #include "absl/strings/str_format.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include #include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/gprpp/crash.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" +#include "src/core/lib/slice/slice.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} -// Creates and returns a grpc_slice containing random alphanumeric characters. -// -static grpc_slice generate_random_slice() { - size_t i; - static const char chars[] = "abcdefghijklmnopqrstuvwxyz1234567890"; - char* output; - const size_t output_size = 1024 * 1024; - output = static_cast(gpr_malloc(output_size)); - for (i = 0; i < output_size - 1; ++i) { - output[i] = chars[rand() % static_cast(sizeof(chars) - 1)]; +namespace grpc_core { +namespace { + +const int kNumCalls = 8; +const int kClientBaseTag = 1000; +const int kServerStartBaseTag = 2000; +const int kServerRecvBaseTag = 3000; +const int kServerEndBaseTag = 4000; + +template +auto MakeVec(F init) { + std::vector v; + v.reserve(kNumCalls); + for (int i = 0; i < kNumCalls; ++i) { + v.push_back(init(i)); } - output[output_size - 1] = '\0'; - grpc_slice out = grpc_slice_from_copied_string(output); - gpr_free(output); - return out; + return v; } -void resource_quota_server(const CoreTestConfiguration& config) { +TEST_P(ResourceQuotaTest, ResourceQuota) { grpc_resource_quota* resource_quota = grpc_resource_quota_create("test_server"); - grpc_resource_quota_resize(resource_quota, 5 * 1024 * 1024); - -#define NUM_CALLS 100 -#define CLIENT_BASE_TAG 0x1000 -#define SERVER_START_BASE_TAG 0x2000 -#define SERVER_RECV_BASE_TAG 0x3000 -#define SERVER_END_BASE_TAG 0x4000 - - grpc_arg arg; - arg.key = const_cast(GRPC_ARG_RESOURCE_QUOTA); - arg.type = GRPC_ARG_POINTER; - arg.value.pointer.p = resource_quota; - arg.value.pointer.vtable = grpc_resource_quota_arg_vtable(); - grpc_channel_args args = {1, &arg}; - - auto f = begin_test(config, "resource_quota_server", nullptr, &args); - + grpc_resource_quota_resize(resource_quota, 1024 * 1024); + InitServer(ChannelArgs().Set( + GRPC_ARG_RESOURCE_QUOTA, + ChannelArgs::Pointer(resource_quota, grpc_resource_quota_arg_vtable()))); + InitClient(ChannelArgs()); // Create large request and response bodies. These are big enough to require // multiple round trips to deliver to the peer, and their exact contents of // will be verified on completion. - grpc_slice request_payload_slice = generate_random_slice(); + auto requests = MakeVec([](int) { return RandomSlice(128 * 1024); }); + auto server_calls = + MakeVec([this](int i) { return RequestCall(kServerRecvBaseTag + i); }); + std::vector server_metadata(kNumCalls); + std::vector server_status(kNumCalls); + std::vector client_message(kNumCalls); + std::vector client_close(kNumCalls); + auto client_calls = + MakeVec([this, &requests, &server_metadata, &server_status](int i) { + auto c = NewClientCall("/foo").Timeout(Duration::Minutes(1)).Create(); + c.NewBatch(kClientBaseTag + i) + .SendInitialMetadata({}, GRPC_INITIAL_METADATA_WAIT_FOR_READY) + .SendMessage(requests[i].Ref()) + .SendCloseFromClient() + .RecvInitialMetadata(server_metadata[i]) + .RecvStatusOnClient(server_status[i]); + return c; + }); + for (int i = 0; i < kNumCalls; i++) { + Expect(kClientBaseTag + i, true); + Expect(kServerRecvBaseTag + i, + PerformAction{[&server_calls, &client_message, i](bool success) { + EXPECT_TRUE(success); + server_calls[i] + .NewBatch(kServerStartBaseTag + i) + .RecvMessage(client_message[i]) + .SendInitialMetadata({}); + }}); + Expect(kServerStartBaseTag + i, + PerformAction{[&server_calls, &client_close, i](bool) { + server_calls[i] + .NewBatch(kServerEndBaseTag + i) + .RecvCloseOnServer(client_close[i]) + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + }}); + Expect(kServerEndBaseTag + i, true); + } + Step(Duration::Seconds(30)); - grpc_call** client_calls = - static_cast(malloc(sizeof(grpc_call*) * NUM_CALLS)); - grpc_call** server_calls = - static_cast(malloc(sizeof(grpc_call*) * NUM_CALLS)); - grpc_metadata_array* initial_metadata_recv = - static_cast( - malloc(sizeof(grpc_metadata_array) * NUM_CALLS)); - grpc_metadata_array* trailing_metadata_recv = - static_cast( - malloc(sizeof(grpc_metadata_array) * NUM_CALLS)); - grpc_metadata_array* request_metadata_recv = - static_cast( - malloc(sizeof(grpc_metadata_array) * NUM_CALLS)); - grpc_call_details* call_details = static_cast( - malloc(sizeof(grpc_call_details) * NUM_CALLS)); - grpc_status_code* status = static_cast( - malloc(sizeof(grpc_status_code) * NUM_CALLS)); - grpc_slice* details = - static_cast(malloc(sizeof(grpc_slice) * NUM_CALLS)); - grpc_byte_buffer** request_payload = static_cast( - malloc(sizeof(grpc_byte_buffer*) * NUM_CALLS)); - grpc_byte_buffer** request_payload_recv = static_cast( - malloc(sizeof(grpc_byte_buffer*) * NUM_CALLS)); - int* was_cancelled = static_cast(malloc(sizeof(int) * NUM_CALLS)); - grpc_call_error error; - int pending_client_calls = 0; - int pending_server_start_calls = 0; - int pending_server_recv_calls = 0; - int pending_server_end_calls = 0; int cancelled_calls_on_client = 0; int cancelled_calls_on_server = 0; int deadline_exceeded = 0; int unavailable = 0; - - grpc_op ops[6]; - grpc_op* op; - - for (int i = 0; i < NUM_CALLS; i++) { - grpc_metadata_array_init(&initial_metadata_recv[i]); - grpc_metadata_array_init(&trailing_metadata_recv[i]); - grpc_metadata_array_init(&request_metadata_recv[i]); - grpc_call_details_init(&call_details[i]); - request_payload[i] = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - request_payload_recv[i] = nullptr; - was_cancelled[i] = 0; - } - - for (int i = 0; i < NUM_CALLS; i++) { - error = grpc_server_request_call( - f->server(), &server_calls[i], &call_details[i], - &request_metadata_recv[i], f->cq(), f->cq(), - grpc_core::CqVerifier::tag(SERVER_START_BASE_TAG + i)); - GPR_ASSERT(GRPC_CALL_OK == error); - - pending_server_start_calls++; - } - - for (int i = 0; i < NUM_CALLS; i++) { - client_calls[i] = grpc_channel_create_call( - f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, f->cq(), - grpc_slice_from_static_string("/foo"), nullptr, - grpc_timeout_seconds_to_deadline(60), nullptr); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload[i]; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = - &initial_metadata_recv[i]; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = - &trailing_metadata_recv[i]; - op->data.recv_status_on_client.status = &status[i]; - op->data.recv_status_on_client.status_details = &details[i]; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch( - client_calls[i], ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(CLIENT_BASE_TAG + i), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - pending_client_calls++; - } - - while (pending_client_calls + pending_server_recv_calls + - pending_server_end_calls > - 0) { - grpc_event ev = grpc_completion_queue_next( - f->cq(), grpc_timeout_seconds_to_deadline(60), nullptr); - GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); - - int ev_tag = static_cast(reinterpret_cast(ev.tag)); - if (ev_tag < CLIENT_BASE_TAG) { - abort(); // illegal tag - } else if (ev_tag < SERVER_START_BASE_TAG) { - // client call finished - int call_id = ev_tag - CLIENT_BASE_TAG; - GPR_ASSERT(call_id >= 0); - GPR_ASSERT(call_id < NUM_CALLS); - switch (status[call_id]) { - case GRPC_STATUS_RESOURCE_EXHAUSTED: - cancelled_calls_on_client++; - break; - case GRPC_STATUS_DEADLINE_EXCEEDED: - deadline_exceeded++; - break; - case GRPC_STATUS_UNAVAILABLE: - unavailable++; - break; - case GRPC_STATUS_OK: - break; - default: - grpc_core::Crash( - absl::StrFormat("Unexpected status code: %d", status[call_id])); - } - GPR_ASSERT(pending_client_calls > 0); - - grpc_metadata_array_destroy(&initial_metadata_recv[call_id]); - grpc_metadata_array_destroy(&trailing_metadata_recv[call_id]); - grpc_call_unref(client_calls[call_id]); - grpc_slice_unref(details[call_id]); - grpc_byte_buffer_destroy(request_payload[call_id]); - - pending_client_calls--; - } else if (ev_tag < SERVER_RECV_BASE_TAG) { - // new incoming call to the server - int call_id = ev_tag - SERVER_START_BASE_TAG; - GPR_ASSERT(call_id >= 0); - GPR_ASSERT(call_id < NUM_CALLS); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv[call_id]; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch( - server_calls[call_id], ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(SERVER_RECV_BASE_TAG + call_id), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - GPR_ASSERT(pending_server_start_calls > 0); - pending_server_start_calls--; - pending_server_recv_calls++; - - grpc_call_details_destroy(&call_details[call_id]); - grpc_metadata_array_destroy(&request_metadata_recv[call_id]); - } else if (ev_tag < SERVER_END_BASE_TAG) { - // finished read on the server - int call_id = ev_tag - SERVER_RECV_BASE_TAG; - GPR_ASSERT(call_id >= 0); - GPR_ASSERT(call_id < NUM_CALLS); - - if (ev.success) { - if (request_payload_recv[call_id] != nullptr) { - grpc_byte_buffer_destroy(request_payload_recv[call_id]); - request_payload_recv[call_id] = nullptr; - } - } else { - GPR_ASSERT(request_payload_recv[call_id] == nullptr); - } - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled[call_id]; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch( - server_calls[call_id], ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(SERVER_END_BASE_TAG + call_id), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - GPR_ASSERT(pending_server_recv_calls > 0); - pending_server_recv_calls--; - pending_server_end_calls++; - } else { - int call_id = ev_tag - SERVER_END_BASE_TAG; - GPR_ASSERT(call_id >= 0); - GPR_ASSERT(call_id < NUM_CALLS); - - if (was_cancelled[call_id]) { - cancelled_calls_on_server++; - } - GPR_ASSERT(pending_server_end_calls > 0); - pending_server_end_calls--; - - grpc_call_unref(server_calls[call_id]); + for (int i = 0; i < kNumCalls; i++) { + switch (server_status[i].status()) { + case GRPC_STATUS_RESOURCE_EXHAUSTED: + cancelled_calls_on_client++; + break; + case GRPC_STATUS_DEADLINE_EXCEEDED: + deadline_exceeded++; + break; + case GRPC_STATUS_UNAVAILABLE: + unavailable++; + break; + case GRPC_STATUS_OK: + break; + default: + Crash(absl::StrFormat("Unexpected status code: %d", + server_status[i].status())); + } + if (client_close[i].was_cancelled()) { + cancelled_calls_on_server++; } } - gpr_log(GPR_INFO, "Done. %d total calls: %d cancelled at server, %d cancelled at " "client, %d timed out, %d unavailable.", - NUM_CALLS, cancelled_calls_on_server, cancelled_calls_on_client, + kNumCalls, cancelled_calls_on_server, cancelled_calls_on_client, deadline_exceeded, unavailable); - - f->ShutdownServer(); - - grpc_slice_unref(request_payload_slice); - grpc_resource_quota_unref(resource_quota); - - free(client_calls); - free(server_calls); - free(initial_metadata_recv); - free(trailing_metadata_recv); - free(request_metadata_recv); - free(call_details); - free(status); - free(details); - free(request_payload); - free(request_payload_recv); - free(was_cancelled); } -void resource_quota_server_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry.cc b/test/core/end2end/tests/retry.cc index 46d650ad38fc6..5e725ac3ab657 100644 --- a/test/core/end2end/tests/retry.cc +++ b/test/core/end2end/tests/retry.cc @@ -16,267 +16,100 @@ // // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { // Tests a basic retry scenario: // - 2 retries allowed for ABORTED status // - first attempt returns ABORTED // - second attempt returns OK -static void test_retry(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); +TEST_P(RetryTest, Retry) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingStatusOnClient server_status; + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); // Make sure the "grpc-previous-rpc-attempts" header was not sent in the // initial attempt. - for (size_t i = 0; i < request_metadata_recv.count; ++i) { - GPR_ASSERT(!grpc_slice_eq( - request_metadata_recv.metadata[i].key, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"))); - } - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); + EXPECT_EQ(s.GetInitialMetadata("grpc-previous-rpc-attempts"), absl::nullopt); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Step(); - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); + auto s2 = RequestCall(201); + Expect(201, true); + Step(); // Make sure the "grpc-previous-rpc-attempts" header was sent in the retry. - bool found_retry_header = false; - for (size_t i = 0; i < request_metadata_recv.count; ++i) { - if (grpc_slice_eq( - request_metadata_recv.metadata[i].key, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"))) { - GPR_ASSERT(grpc_slice_eq(request_metadata_recv.metadata[i].value, - grpc_slice_from_static_string("1"))); - found_retry_header = true; - break; - } - } - GPR_ASSERT(found_retry_header); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(202), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(203), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(202), true); - cqv.Expect(grpc_core::CqVerifier::tag(203), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry(config); + EXPECT_EQ(s2.GetInitialMetadata("grpc-previous-rpc-attempts"), "1"); + + EXPECT_NE(s2.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + + IncomingMessage server_message2; + s2.NewBatch(202) + .SendInitialMetadata({}) + .RecvMessage(server_message2) + .SendMessage("bar"); + IncomingCloseOnServer client_close2; + s2.NewBatch(203) + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}) + .RecvCloseOnServer(client_close2); + Expect(202, true); + Expect(203, true); + Expect(1, true); + Step(); + + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close2.was_cancelled()); } -void retry_pre_init(void) {} +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_cancel_after_first_attempt_starts.cc b/test/core/end2end/tests/retry_cancel_after_first_attempt_starts.cc index 52fbaf9843369..c1372806f1783 100644 --- a/test/core/end2end/tests/retry_cancel_after_first_attempt_starts.cc +++ b/test/core/end2end/tests/retry_cancel_after_first_attempt_starts.cc @@ -14,158 +14,65 @@ // limitations under the License. // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we can unref a call after the first attempt starts but // before any ops complete. This should not cause a memory leak. -static void test_retry_cancel_after_first_attempt_starts( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - // Make sure to dynamically allocate the request payload slice, so - // that the test will fail under ASAN if we don't clean up properly. - const char request_string[] = "foo"; - grpc_slice request_payload_slice = - grpc_slice_malloc_large(sizeof(request_string)); - memcpy(GRPC_SLICE_START_PTR(request_payload_slice), request_string, - sizeof(request_string)); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_slice_unref(request_payload_slice); - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_slice details; - grpc_call_error error; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_cancel_after_first_attempt_starts", - &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - +TEST_P(RetryTest, RetryCancelAfterFirstAttemptStarts) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + absl::optional c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); // Client starts send ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + c->NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .SendCloseFromClient(); // Client starts recv_initial_metadata and recv_message, but not // recv_trailing_metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + c->NewBatch(2) + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message); // Client starts recv_trailing_metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingStatusOnClient server_status; + c->NewBatch(3).RecvStatusOnClient(server_status); // Client unrefs the call without starting recv_trailing_metadata. // This should trigger a cancellation. - grpc_call_unref(c); - + c.reset(); // The send ops batch and the first recv ops batch will fail in most // fixtures but will pass in the proxy fixtures on some platforms. - cqv.Expect(grpc_core::CqVerifier::tag(1), grpc_core::CqVerifier::AnyStatus()); - cqv.Expect(grpc_core::CqVerifier::tag(2), grpc_core::CqVerifier::AnyStatus()); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Verify(); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload_recv); -} - -void retry_cancel_after_first_attempt_starts( - const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_cancel_after_first_attempt_starts(config); + Expect(1, AnyStatus()); + Expect(2, AnyStatus()); + Expect(3, true); + Step(); } -void retry_cancel_after_first_attempt_starts_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_cancel_during_delay.cc b/test/core/end2end/tests/retry_cancel_during_delay.cc index 09e279c1018ac..b88a7e2e5c802 100644 --- a/test/core/end2end/tests/retry_cancel_during_delay.cc +++ b/test/core/end2end/tests/retry_cancel_during_delay.cc @@ -14,232 +14,104 @@ // limitations under the License. // -#include - -#include #include #include -#include -#include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" -#include "absl/time/time.h" +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "src/core/lib/gprpp/time_util.h" -#include "src/core/lib/slice/slice_internal.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/end2end/tests/cancel_test_helpers.h" #include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Tests retry cancellation during backoff. -static void test_retry_cancel_during_delay(const CoreTestConfiguration& config, - cancellation_mode mode) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - std::string service_config = absl::StrFormat( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"%ds\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}", - 10 * grpc_test_slowdown_factor()); - - grpc_arg args[] = { - grpc_channel_arg_string_create(const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast(service_config.c_str())), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - std::string name = absl::StrCat("retry_cancel_during_delay/", mode.name); - auto f = begin_test(config, name.c_str(), &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec expect_finish_before = grpc_timeout_seconds_to_deadline(10); - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - +namespace grpc_core { +namespace { + +void TestRetryCancelDuringDelay( + CoreEnd2endTest& test, + std::unique_ptr cancellation_mode) { + test.InitServer(ChannelArgs()); + test.InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + absl::StrFormat( + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"%ds\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}", + 10 * grpc_test_slowdown_factor()))); + auto expect_finish_before = + test.TimestampAfterDuration(Duration::Seconds(10)); + auto c = test.NewClientCall("/service/method") + .Timeout(Duration::Seconds(5)) + .Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Client starts a batch with all 6 ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); // Server gets a call and fails with retryable status. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + test.Expect(102, true); + test.Step(); // Server should never get a second call, because the initial retry // delay is longer than the call's deadline. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - + auto s2 = test.RequestCall(201); // Initiate cancellation. - GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c, nullptr)); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - gpr_timespec finish_time = gpr_now(GPR_CLOCK_MONOTONIC); - - gpr_log(GPR_INFO, "status=%d expected=%d", status, mode.expect_status); - gpr_log(GPR_INFO, "message=\"%s\"", - std::string(grpc_core::StringViewFromSlice(details)).c_str()); - GPR_ASSERT(status == mode.expect_status); - GPR_ASSERT(was_cancelled == 0); - + cancellation_mode->Apply(c); + test.Expect(1, true); + test.Step(); + auto finish_time = Timestamp::Now(); + EXPECT_EQ(server_status.status(), cancellation_mode->ExpectedStatus()) + << server_status.message(); + EXPECT_FALSE(client_close.was_cancelled()); // Make sure we didn't wait the full deadline before failing. - gpr_log( - GPR_INFO, "Expect completion before: %s", - absl::FormatTime(grpc_core::ToAbslTime(expect_finish_before)).c_str()); - GPR_ASSERT(gpr_time_cmp(finish_time, expect_finish_before) < 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); + EXPECT_LT(finish_time, expect_finish_before); + // Shutdown the server to gc the requested call. + test.ShutdownServerAndNotify(1000); + test.Expect(1000, true); + test.Expect(201, false); + test.Step(); +} - grpc_call_unref(c); +TEST_P(RetryTest, CancelDuringDelay) { + TestRetryCancelDuringDelay(*this, std::make_unique()); } -void retry_cancel_during_delay(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - for (size_t i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); ++i) { - test_retry_cancel_during_delay(config, cancellation_modes[i]); - } +TEST_P(RetryTest, DeadlineDuringDelay) { + TestRetryCancelDuringDelay(*this, + std::make_unique()); } -void retry_cancel_during_delay_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_cancel_with_multiple_send_batches.cc b/test/core/end2end/tests/retry_cancel_with_multiple_send_batches.cc index bb48dab92e5a0..c61522adc709c 100644 --- a/test/core/end2end/tests/retry_cancel_with_multiple_send_batches.cc +++ b/test/core/end2end/tests/retry_cancel_with_multiple_send_batches.cc @@ -16,190 +16,92 @@ // // -#include - -#include #include #include #include -#include #include "absl/status/status.h" -#include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/config/core_configuration.h" -#include "src/core/lib/gpr/useful.h" #include "src/core/lib/gprpp/status_helper.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/iomgr/call_combiner.h" #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/surface/channel_stack_type.h" #include "src/core/lib/transport/transport.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/end2end/tests/cancel_test_helpers.h" #include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests cancellation with multiple send op batches. -static void test_retry_cancel_with_multiple_send_batches( - const CoreTestConfiguration& config, cancellation_mode mode) { - grpc_call* c; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - char* peer; - - std::string service_config_string = absl::StrFormat( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"%ds\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}", - 5 * grpc_test_slowdown_factor()); - - grpc_arg args[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ENABLE_RETRIES), 1), - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast(service_config_string.c_str())), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - std::string name = - absl::StrCat("retry_cancel_with_multiple_send_batches/", mode.name); - auto f = begin_test(config, name.c_str(), &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(3); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - +void TestRetryCancelWithMultipleSendBatches( + CoreEnd2endTest& test, std::unique_ptr mode) { + test.InitServer(ChannelArgs()); + test.InitClient( + ChannelArgs() + .Set( + GRPC_ARG_SERVICE_CONFIG, + absl::StrFormat( + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"%ds\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}", + 5 * grpc_test_slowdown_factor())) + // TODO(roth): do we need this now? + .Set(GRPC_ARG_ENABLE_RETRIES, true)); + auto c = test.NewClientCall("/service/method") + .Timeout(Duration::Seconds(3)) + .Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Start a batch containing send_initial_metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + c.NewBatch(1).SendInitialMetadata({}); // Start a batch containing send_message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + c.NewBatch(2).SendMessage("foo"); // Start a batch containing send_trailing_metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + c.NewBatch(3).SendCloseFromClient(); // Start a batch containing recv ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(4), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + CoreEnd2endTest::IncomingMessage server_message; + CoreEnd2endTest::IncomingMetadata server_incoming_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(4) + .RecvInitialMetadata(server_incoming_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); // Initiate cancellation. - GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c, nullptr)); - + mode->Apply(c); // Client ops should now complete. - cqv.Expect(grpc_core::CqVerifier::tag(1), false); - cqv.Expect(grpc_core::CqVerifier::tag(2), false); - cqv.Expect(grpc_core::CqVerifier::tag(3), false); - cqv.Expect(grpc_core::CqVerifier::tag(4), true); - cqv.Verify(); - - gpr_log(GPR_INFO, "status=%d expected=%d", status, mode.expect_status); - GPR_ASSERT(status == mode.expect_status); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); + test.Expect(1, false); + test.Expect(2, false); + test.Expect(3, false); + test.Expect(4, true); + test.Step(); + EXPECT_EQ(server_status.status(), mode->ExpectedStatus()); } -namespace { - // A filter that fails all batches with send ops. class FailSendOpsFilter { public: @@ -230,7 +132,7 @@ class FailSendOpsFilter { batch, grpc_error_set_int( GRPC_ERROR_CREATE("FailSendOpsFilter failing batch"), - grpc_core::StatusIntProperty::kRpcStatus, GRPC_STATUS_ABORTED), + StatusIntProperty::kRpcStatus, GRPC_STATUS_ABORTED), calld->call_combiner_); return; } @@ -241,7 +143,7 @@ class FailSendOpsFilter { explicit CallData(const grpc_call_element_args* args) : call_combiner_(args->call_combiner) {} - grpc_core::CallCombiner* call_combiner_; + CallCombiner* call_combiner_; }; static grpc_error_handle Init(grpc_channel_element* elem, @@ -272,7 +174,7 @@ grpc_channel_filter FailSendOpsFilter::kFilterVtable = { "FailSendOpsFilter", }; -bool MaybeAddFilter(grpc_core::ChannelStackBuilder* builder) { +bool MaybeAddFilter(ChannelStackBuilder* builder) { // Skip on proxy (which explicitly disables retries). if (!builder->channel_args() .GetBool(GRPC_ARG_ENABLE_RETRIES) @@ -284,23 +186,24 @@ bool MaybeAddFilter(grpc_core::ChannelStackBuilder* builder) { return true; } -} // namespace +void RegisterFilter() { + CoreConfiguration::RegisterBuilder([](CoreConfiguration::Builder* builder) { + builder->channel_init()->RegisterStage(GRPC_CLIENT_SUBCHANNEL, 0, + MaybeAddFilter); + }); +} -void retry_cancel_with_multiple_send_batches( - const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - grpc_core::CoreConfiguration::RunWithSpecialConfiguration( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::BuildCoreConfiguration(builder); - builder->channel_init()->RegisterStage(GRPC_CLIENT_SUBCHANNEL, 0, - MaybeAddFilter); - }, - [&config]() { - for (size_t i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); ++i) { - test_retry_cancel_with_multiple_send_batches(config, - cancellation_modes[i]); - } - }); +TEST_P(RetryTest, RetryCancelWithMultipleSendBatches) { + RegisterFilter(); + TestRetryCancelWithMultipleSendBatches( + *this, std::make_unique()); } -void retry_cancel_with_multiple_send_batches_pre_init(void) {} +TEST_P(RetryTest, RetryDeadlineWithMultipleSendBatches) { + RegisterFilter(); + TestRetryCancelWithMultipleSendBatches( + *this, std::make_unique()); +} + +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_cancellation.cc b/test/core/end2end/tests/retry_cancellation.cc index e91c9e8887af2..60759ed887b1c 100644 --- a/test/core/end2end/tests/retry_cancellation.cc +++ b/test/core/end2end/tests/retry_cancellation.cc @@ -16,215 +16,91 @@ // // -#include - -#include #include -#include -#include "absl/strings/str_cat.h" +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/end2end/tests/cancel_test_helpers.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests retry cancellation. -static void test_retry_cancellation(const CoreTestConfiguration& config, - cancellation_mode mode) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 5,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " },\n" - " \"timeout\": \"10s\"\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - std::string name = absl::StrCat("retry_cancellation/", mode.name); - auto f = begin_test(config, name.c_str(), &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - +void TestRetryCancellation(CoreEnd2endTest& test, + std::unique_ptr mode) { + test.InitServer(ChannelArgs()); + test.InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 5,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " },\n" + " \"timeout\": \"10s\"\n" + " } ]\n" + "}")); + auto c = test.NewClientCall("/service/method") + .Timeout(Duration::Seconds(5)) + .Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Client starts a batch with all 6 ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); // Server gets a call and fails with retryable status. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - + absl::optional s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + EXPECT_NE(s->GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s->NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + test.Expect(102, true); + test.Step(); + s.reset(); // Server gets a second call (the retry). - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - + s.emplace(test.RequestCall(201)); + test.Expect(201, true); + test.Step(); // Initiate cancellation. - GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c, nullptr)); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == mode.expect_status); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); + mode->Apply(c); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), mode->ExpectedStatus()); + EXPECT_FALSE(client_close.was_cancelled()); +} - grpc_call_unref(c); - grpc_call_unref(s); +TEST_P(RetryTest, RetryCancellation) { + TestRetryCancellation(*this, std::make_unique()); } -void retry_cancellation(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - for (size_t i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); ++i) { - test_retry_cancellation(config, cancellation_modes[i]); - } +TEST_P(RetryTest, RetryDeadline) { + TestRetryCancellation(*this, std::make_unique()); } -void retry_cancellation_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_disabled.cc b/test/core/end2end/tests/retry_disabled.cc index f0257e3a3ba96..223f8f505b294 100644 --- a/test/core/end2end/tests/retry_disabled.cc +++ b/test/core/end2end/tests/retry_disabled.cc @@ -16,193 +16,75 @@ // // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we don't retry when retries are disabled via the // GRPC_ARG_ENABLE_RETRIES channel arg, even when there is retry // configuration in the service config. // - 1 retry allowed for ABORTED status // - first attempt returns ABORTED but does not retry -static void test_retry_disabled(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ENABLE_RETRIES), 0), - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_disabled", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_disabled(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_disabled(config); +TEST_P(RetryTest, RetryDisabled) { + InitServer(ChannelArgs()); + InitClient( + ChannelArgs() + .Set(GRPC_ARG_ENABLE_RETRIES, false) + .Set(GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } -void retry_disabled_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_exceeds_buffer_size_in_delay.cc b/test/core/end2end/tests/retry_exceeds_buffer_size_in_delay.cc index 04ab3ede62026..f6cfa31f21be9 100644 --- a/test/core/end2end/tests/retry_exceeds_buffer_size_in_delay.cc +++ b/test/core/end2end/tests/retry_exceeds_buffer_size_in_delay.cc @@ -14,37 +14,20 @@ // limitations under the License. // -#include -#include +#include -#include -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests the case where the retry buffer size is exceeded during backoff. // - 1 retry allowed for ABORTED status @@ -53,217 +36,80 @@ static std::unique_ptr begin_test( // - server sends ABORTED, client goes into backoff delay // - client sends a 100 KiB message, thus exceeding the buffer size limit // - retry attempt gets ABORTED but is not retried -static void test_retry_exceeds_buffer_size_in_delay( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - const size_t buf_size = 102401; - char* buf = static_cast(gpr_malloc(buf_size * sizeof(*buf))); - memset(buf, 'a', buf_size - 1); - buf[buf_size - 1] = '\0'; - grpc_slice request_payload_slice = grpc_slice_from_static_string(buf); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"2s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE), 102400), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_exceeds_buffer_size_in_delay", - &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_milliseconds_to_deadline(15000); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details1 = grpc_slice_from_static_string("message1"); - grpc_slice status_details2 = grpc_slice_from_static_string("message2"); - +TEST_P(RetryTest, RetryExceedsBufferSizeInDelay) { + InitServer(ChannelArgs()); + InitClient( + ChannelArgs() + .Set(GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"2s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}") + .Set(GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE, 102400)); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(15)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Client sends initial metadata and starts the recv ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingMessage server_message; + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .RecvMessage(server_message) + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); // Server gets a call. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - + absl::optional s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s->GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Server sends ABORTED. This tells the client to retry. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details1; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - + IncomingCloseOnServer client_close; + s->NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "message1", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Step(); + s.reset(); // Do a bit more polling, to make sure the client sees status from the // first attempt. (Note: This polls for 1s, which is less than the // retry initial backoff time of 2s from the service config above.) - cqv.VerifyEmpty(); - + Step(Duration::Seconds(1)); // Client sends a message that puts it over the buffer size limit. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - + c.NewBatch(2).SendMessage(std::string(102401, 'a')).SendCloseFromClient(); + Expect(2, true); + Step(); // Server gets another call. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - + auto s2 = RequestCall(201); + Expect(201, true); + Step(); // Server again sends ABORTED. But this time, the client won't retry, // since the call has been committed by exceeding the buffer size. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details2; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(202), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(202), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "message2")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); - - gpr_free(buf); -} - -void retry_exceeds_buffer_size_in_delay(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_exceeds_buffer_size_in_delay(config); + IncomingCloseOnServer client_close2; + s2.NewBatch(202) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "message2", {}) + .RecvCloseOnServer(client_close2); + Expect(202, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "message2"); + EXPECT_EQ(s2.method(), "/service/method"); + EXPECT_FALSE(client_close2.was_cancelled()); } -void retry_exceeds_buffer_size_in_delay_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc b/test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc index 06889773bed59..5a3109c5582b5 100644 --- a/test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc +++ b/test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc @@ -16,36 +16,18 @@ // // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we don't make any further attempts after we exceed the // max buffer size. @@ -53,160 +35,57 @@ static std::unique_ptr begin_test( // - buffer size set to 2 bytes // - client sends a 3-byte message // - first attempt gets ABORTED but is not retried -static void test_retry_exceeds_buffer_size_in_initial_batch( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE), 2), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_exceeds_buffer_size_in_initial_batch", - &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_exceeds_buffer_size_in_initial_batch( - const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_exceeds_buffer_size_in_initial_batch(config); +TEST_P(RetryTest, RetryExceedsBufferSizeInInitialBatch) { + InitServer(ChannelArgs()); + InitClient( + ChannelArgs() + .Set(GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}") + .Set(GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE, 2)); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingMessage server_message; + IncomingStatusOnClient server_status; + IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } -void retry_exceeds_buffer_size_in_initial_batch_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc b/test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc index a8f8c99f43da2..feab394120637 100644 --- a/test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc +++ b/test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc @@ -16,37 +16,20 @@ // // -#include -#include +#include -#include -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Similar to the retry_exceeds_buffer_size_in_initial_batch test, but we // don't exceed the buffer size until the second batch. @@ -54,175 +37,59 @@ static std::unique_ptr begin_test( // - buffer size set to 100 KiB (larger than initial metadata) // - client sends a 100 KiB message // - first attempt gets ABORTED but is not retried -static void test_retry_exceeds_buffer_size_in_subsequent_batch( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - const size_t buf_size = 102401; - char* buf = static_cast(gpr_malloc(buf_size * sizeof(*buf))); - memset(buf, 'a', buf_size - 1); - buf[buf_size - 1] = '\0'; - // TODO(markdroth): buf is not a static string, so fix the next line - grpc_slice request_payload_slice = grpc_slice_from_static_string(buf); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE), 102400), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_exceeds_buffer_size_in_subsequent_batch", - &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); - - gpr_free(buf); -} - -void retry_exceeds_buffer_size_in_subsequent_batch( - const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_exceeds_buffer_size_in_subsequent_batch(config); +TEST_P(RetryTest, RetryExceedsBufferSizeInSubsequentBatch) { + InitServer(ChannelArgs()); + InitClient( + ChannelArgs() + .Set(GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}") + .Set(GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE, 102400)); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + c.NewBatch(1).SendInitialMetadata({}); + Expect(1, true); + Step(); + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + IncomingStatusOnClient server_status; + c.NewBatch(2) + .SendMessage(std::string(102400, 'a')) + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(2, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } -void retry_exceeds_buffer_size_in_subsequent_batch_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_lb_drop.cc b/test/core/end2end/tests/retry_lb_drop.cc index 4b2a0b0049984..88d48f5f95dae 100644 --- a/test/core/end2end/tests/retry_lb_drop.cc +++ b/test/core/end2end/tests/retry_lb_drop.cc @@ -14,11 +14,7 @@ // limitations under the License. // -#include -#include - #include -#include #include #include #include @@ -26,26 +22,21 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include #include -#include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/config/core_configuration.h" -#include "src/core/lib/gpr/useful.h" #include "src/core/lib/gprpp/orphanable.h" #include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/json/json.h" #include "src/core/lib/load_balancing/lb_policy.h" #include "src/core/lib/load_balancing/lb_policy_factory.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" #include "test/core/util/test_lb_policies.h" namespace grpc_core { @@ -105,139 +96,62 @@ void RegisterDropPolicy(CoreConfiguration::Builder* builder) { std::make_unique()); } -} // namespace -} // namespace grpc_core - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - // Tests that we don't retry when the LB policy drops a call, // even when there is retry configuration in the service config. // - 1 retry allowed for UNAVAILABLE status // - first attempt returns UNAVAILABLE due to LB drop but does not retry -static void test_retry_lb_drop(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - - std::vector pick_args_seen; - grpc_core::g_pick_args_vector = &pick_args_seen; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"loadBalancingConfig\": [ {\n" - " \"test_pick_args_lb\": {}\n" - " } ],\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"UNAVAILABLE\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_lb_drop", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE); - GPR_ASSERT(0 == - grpc_slice_str_cmp(details, "Call dropped by drop LB policy")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - - gpr_log(GPR_INFO, "NUMBER OF LB PICKS: %" PRIuPTR, pick_args_seen.size()); - GPR_ASSERT(pick_args_seen.size() == 1); - - grpc_core::g_pick_args_vector = nullptr; -} - -void retry_lb_drop(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_lb_drop(config); +TEST_P(RetryTest, RetryLbDrop) { + CoreConfiguration::RegisterBuilder([](CoreConfiguration::Builder* builder) { + RegisterTestPickArgsLoadBalancingPolicy( + builder, + [](const PickArgsSeen& pick_args) { + GPR_ASSERT(g_pick_args_vector != nullptr); + g_pick_args_vector->push_back(pick_args); + }, + kDropPolicyName); + }); + CoreConfiguration::RegisterBuilder(RegisterDropPolicy); + std::vector pick_args_seen; + g_pick_args_vector = &pick_args_seen; + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"loadBalancingConfig\": [ {\n" + " \"test_pick_args_lb\": {}\n" + " } ],\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"UNAVAILABLE\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + IncomingMessage server_message; + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNAVAILABLE); + EXPECT_EQ(server_status.message(), "Call dropped by drop LB policy"); + EXPECT_EQ(pick_args_seen.size(), 1); + g_pick_args_vector = nullptr; } -void retry_lb_drop_pre_init(void) { - grpc_core::CoreConfiguration::RegisterBuilder( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::RegisterTestPickArgsLoadBalancingPolicy( - builder, - [](const grpc_core::PickArgsSeen& pick_args) { - GPR_ASSERT(grpc_core::g_pick_args_vector != nullptr); - grpc_core::g_pick_args_vector->push_back(pick_args); - }, - grpc_core::kDropPolicyName); - }); - grpc_core::CoreConfiguration::RegisterBuilder(grpc_core::RegisterDropPolicy); -} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_lb_fail.cc b/test/core/end2end/tests/retry_lb_fail.cc index b3ebde65e82da..931a01aad5340 100644 --- a/test/core/end2end/tests/retry_lb_fail.cc +++ b/test/core/end2end/tests/retry_lb_fail.cc @@ -14,159 +14,71 @@ // limitations under the License. // -#include - #include -#include -#include #include "absl/status/status.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/config/core_configuration.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" #include "test/core/util/test_lb_policies.h" +namespace grpc_core { namespace { - std::atomic g_num_lb_picks; -} // namespace - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - // Tests that we retry properly when the LB policy fails the call before // it ever gets to the transport, even if recv_trailing_metadata isn't // started by the application until after the LB pick fails. // - 1 retry allowed for ABORTED status // - on first attempt, LB policy fails with ABORTED before application // starts recv_trailing_metadata op -static void test_retry_lb_fail(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - +TEST_P(RetryTest, RetryLbFail) { + CoreConfiguration::RegisterBuilder([](CoreConfiguration::Builder* builder) { + RegisterFailLoadBalancingPolicy( + builder, absl::UnavailableError("LB pick failed"), &g_num_lb_picks); + }); g_num_lb_picks.store(0, std::memory_order_relaxed); - - grpc_arg args[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ENABLE_RETRIES), 1), - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"loadBalancingConfig\": [ {\n" - " \"fail_lb\": {}\n" - " } ],\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"UNAVAILABLE\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_lb_fail", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(1), false); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "LB pick failed")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - - int num_picks = g_num_lb_picks.load(std::memory_order_relaxed); - gpr_log(GPR_INFO, "NUM LB PICKS: %d", num_picks); - GPR_ASSERT(num_picks == 2); -} - -void retry_lb_fail(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_lb_fail(config); + InitServer(ChannelArgs()); + InitClient( + ChannelArgs() + .Set(GRPC_ARG_ENABLE_RETRIES, true) + .Set(GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"loadBalancingConfig\": [ {\n" + " \"fail_lb\": {}\n" + " } ],\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"UNAVAILABLE\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + c.NewBatch(1).SendInitialMetadata({}); + Expect(1, false); + Step(); + IncomingStatusOnClient server_status; + c.NewBatch(2).RecvStatusOnClient(server_status); + Expect(2, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNAVAILABLE); + EXPECT_EQ(server_status.message(), "LB pick failed"); + EXPECT_EQ(g_num_lb_picks.load(std::memory_order_relaxed), 2); } -void retry_lb_fail_pre_init(void) { - grpc_core::CoreConfiguration::RegisterBuilder( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::RegisterFailLoadBalancingPolicy( - builder, absl::UnavailableError("LB pick failed"), &g_num_lb_picks); - }); -} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_non_retriable_status.cc b/test/core/end2end/tests/retry_non_retriable_status.cc index ee36fbe716d5e..b968c5e2169a9 100644 --- a/test/core/end2end/tests/retry_non_retriable_status.cc +++ b/test/core/end2end/tests/retry_non_retriable_status.cc @@ -16,191 +16,70 @@ // // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we don't retry for non-retryable status codes. // - 1 retry allowed for ABORTED status // - first attempt gets INVALID_ARGUMENT, so no retry is done -static void test_retry_non_retriable_status( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = - begin_test(config, "retry_non_retriable_status", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_INVALID_ARGUMENT; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_INVALID_ARGUMENT); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_non_retriable_status(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_non_retriable_status(config); +TEST_P(RetryTest, RetryNonRetriableStatus) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingMessage server_message; + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_INVALID_ARGUMENT, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_INVALID_ARGUMENT); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } - -void retry_non_retriable_status_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc b/test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc index c1c8e4d79bb37..a813df51a756a 100644 --- a/test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc +++ b/test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc @@ -16,206 +16,73 @@ // // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we don't retry for non-retryable status codes, even if // status is received before the recv_trailing_metadata op is started. // - 1 retry allowed for ABORTED status // - first attempt gets INVALID_ARGUMENT, so no retry is done -static void -test_retry_non_retriable_status_before_recv_trailing_metadata_started( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test( - config, - "retry_non_retriable_status_before_recv_trailing_metadata_started", - &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_INVALID_ARGUMENT; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_INVALID_ARGUMENT); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); +TEST_P(RetryTest, RetryNonRetriableStatusBeforeRecvTrailingMetadataStarted) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_INVALID_ARGUMENT, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Step(); + IncomingMessage server_message; + IncomingStatusOnClient server_status; + c.NewBatch(2).RecvMessage(server_message).RecvStatusOnClient(server_status); + Expect(2, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_INVALID_ARGUMENT); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } -void retry_non_retriable_status_before_recv_trailing_metadata_started( - const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_non_retriable_status_before_recv_trailing_metadata_started(config); -} - -void retry_non_retriable_status_before_recv_trailing_metadata_started_pre_init() { -} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_per_attempt_recv_timeout.cc b/test/core/end2end/tests/retry_per_attempt_recv_timeout.cc index 39e1debd7b657..9aa4779d7a103 100644 --- a/test/core/end2end/tests/retry_per_attempt_recv_timeout.cc +++ b/test/core/end2end/tests/retry_per_attempt_recv_timeout.cc @@ -14,299 +14,114 @@ // limitations under the License. // -#include - -#include #include -#include -#include #include "absl/strings/str_format.h" +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests perAttemptRecvTimeout: // - 2 retries allowed for ABORTED status // - first attempt does not receive a response until after perAttemptRecvTimeout // - second attempt returns ABORTED // - third attempt returns OK -static void test_retry_per_attempt_recv_timeout( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_call* s0; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - std::string service_config = absl::StrFormat( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"perAttemptRecvTimeout\": \"%ds\",\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}", - 2 * grpc_test_slowdown_factor()); - - grpc_arg args[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING), 1), - grpc_channel_arg_string_create(const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast(service_config.c_str())), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "test_retry_per_attempt_recv_timeout", - &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(10); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - +TEST_P(RetryTest, RetryPerAttemptRecvTimeout) { + InitServer(ChannelArgs()); + InitClient( + ChannelArgs() + .Set(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING, true) + .Set( + GRPC_ARG_SERVICE_CONFIG, + absl::StrFormat( + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"perAttemptRecvTimeout\": \"%ds\",\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}", + 2 * grpc_test_slowdown_factor()))); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(10)).Create(); + IncomingMessage server_message; + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); // Server gets a call but does not respond to the call. - error = grpc_server_request_call(f->server(), &s0, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - + absl::optional s0 = RequestCall(101); + Expect(101, true); + Step(); // Make sure the "grpc-previous-rpc-attempts" header was not sent in the // initial attempt. - for (size_t i = 0; i < request_metadata_recv.count; ++i) { - GPR_ASSERT(!grpc_slice_eq( - request_metadata_recv.metadata[i].key, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"))); - } - - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - + EXPECT_EQ(s0->GetInitialMetadata("grpc-previous-rpc-attempts"), + absl::nullopt); // Server gets a second call. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - + absl::optional s1 = RequestCall(201); + Expect(201, true); + Step(); // Now we can unref the first call. - grpc_call_unref(s0); - + s0.reset(); // Make sure the "grpc-previous-rpc-attempts" header was sent in the retry. - bool found_retry_header = false; - for (size_t i = 0; i < request_metadata_recv.count; ++i) { - if (grpc_slice_eq( - request_metadata_recv.metadata[i].key, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"))) { - GPR_ASSERT(grpc_slice_eq(request_metadata_recv.metadata[i].value, - grpc_slice_from_static_string("1"))); - found_retry_header = true; - break; - } - } - GPR_ASSERT(found_retry_header); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - + EXPECT_EQ(s1->GetInitialMetadata("grpc-previous-rpc-attempts"), "1"); + EXPECT_NE(s1->GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Server sends status ABORTED. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(202), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(202), true); - cqv.Verify(); - - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - + IncomingCloseOnServer client_close1; + s1->NewBatch(202) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close1); + Expect(202, true); + Step(); + s1.reset(); // Server gets a third call. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(301)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(301), true); - cqv.Verify(); - + auto s2 = RequestCall(301); + Expect(301, true); + Step(); // Make sure the "grpc-previous-rpc-attempts" header was sent in the retry. - found_retry_header = false; - for (size_t i = 0; i < request_metadata_recv.count; ++i) { - if (grpc_slice_eq( - request_metadata_recv.metadata[i].key, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"))) { - GPR_ASSERT(grpc_slice_eq(request_metadata_recv.metadata[i].value, - grpc_slice_from_static_string("2"))); - found_retry_header = true; - break; - } - } - GPR_ASSERT(found_retry_header); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(302), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + EXPECT_EQ(s2.GetInitialMetadata("grpc-previous-rpc-attempts"), "2"); + IncomingMessage client_message2; + s2.NewBatch(302).RecvMessage(client_message2); // Server sends OK status. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(303), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(302), true); - cqv.Expect(grpc_core::CqVerifier::tag(303), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); + IncomingCloseOnServer client_close2; + s2.NewBatch(303) + .SendInitialMetadata({}) + .SendMessage("bar") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}) + .RecvCloseOnServer(client_close2); + Expect(302, true); + Expect(303, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s2.method(), "/service/method"); + EXPECT_FALSE(client_close2.was_cancelled()); } - -void retry_per_attempt_recv_timeout(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_per_attempt_recv_timeout(config); -} - -void retry_per_attempt_recv_timeout_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_per_attempt_recv_timeout_on_last_attempt.cc b/test/core/end2end/tests/retry_per_attempt_recv_timeout_on_last_attempt.cc index bdb1696f86c90..4bbc3efbd54c3 100644 --- a/test/core/end2end/tests/retry_per_attempt_recv_timeout_on_last_attempt.cc +++ b/test/core/end2end/tests/retry_per_attempt_recv_timeout_on_last_attempt.cc @@ -14,207 +14,85 @@ // limitations under the License. // -#include - -#include #include -#include -#include #include "absl/strings/str_format.h" +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests perAttemptRecvTimeout: // - 1 retry allowed for ABORTED status // - both attempts do not receive a response until after perAttemptRecvTimeout -static void test_retry_per_attempt_recv_timeout_on_last_attempt( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_call* s0; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - - std::string service_config = absl::StrFormat( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"perAttemptRecvTimeout\": \"%ds\",\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}", - 2 * grpc_test_slowdown_factor()); - - grpc_arg args[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING), 1), - grpc_channel_arg_string_create(const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast(service_config.c_str())), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = - begin_test(config, "test_retry_per_attempt_recv_timeout_on_last_attempt", - &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(10); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - +TEST_P(RetryTest, RetryPerAttemptRecvTimeoutOnLastAttempt) { + InitServer(ChannelArgs()); + InitClient( + ChannelArgs() + .Set(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING, true) + .Set( + GRPC_ARG_SERVICE_CONFIG, + absl::StrFormat( + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"perAttemptRecvTimeout\": \"%ds\",\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}", + 2 * grpc_test_slowdown_factor()))); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(10)).Create(); + IncomingMessage server_message; + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); // Server gets a call but does not respond to the call. - error = grpc_server_request_call(f->server(), &s0, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - + absl::optional s0 = RequestCall(101); + Expect(101, true); + Step(); // Make sure the "grpc-previous-rpc-attempts" header was not sent in the // initial attempt. - for (size_t i = 0; i < request_metadata_recv.count; ++i) { - GPR_ASSERT(!grpc_slice_eq( - request_metadata_recv.metadata[i].key, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"))); - } - - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - + EXPECT_EQ(s0->GetInitialMetadata("grpc-previous-rpc-attempts"), + absl::nullopt); // Server gets a second call, which it also does not respond to. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - + absl::optional s1 = RequestCall(201); + Expect(201, true); + Step(); // Now we can unref the first call. - grpc_call_unref(s0); - + s0.reset(); // Make sure the "grpc-previous-rpc-attempts" header was sent in the retry. - bool found_retry_header = false; - for (size_t i = 0; i < request_metadata_recv.count; ++i) { - if (grpc_slice_eq( - request_metadata_recv.metadata[i].key, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"))) { - GPR_ASSERT(grpc_slice_eq(request_metadata_recv.metadata[i].value, - grpc_slice_from_static_string("1"))); - found_retry_header = true; - break; - } - } - GPR_ASSERT(found_retry_header); - + EXPECT_EQ(s1->GetInitialMetadata("grpc-previous-rpc-attempts"), "1"); // Client sees call completion. - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_CANCELLED); - GPR_ASSERT( - 0 == grpc_slice_str_cmp(details, "retry perAttemptRecvTimeout exceeded")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_per_attempt_recv_timeout_on_last_attempt( - const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_per_attempt_recv_timeout_on_last_attempt(config); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_CANCELLED); + EXPECT_EQ(server_status.message(), "retry perAttemptRecvTimeout exceeded"); + EXPECT_EQ(s1->method(), "/service/method"); } -void retry_per_attempt_recv_timeout_on_last_attempt_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_recv_initial_metadata.cc b/test/core/end2end/tests/retry_recv_initial_metadata.cc index 426512db1f099..76affef977a8d 100644 --- a/test/core/end2end/tests/retry_recv_initial_metadata.cc +++ b/test/core/end2end/tests/retry_recv_initial_metadata.cc @@ -16,153 +16,60 @@ // // -#include +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that receiving initial metadata commits the call. // - 1 retry allowed for ABORTED status // - first attempt receives initial metadata before trailing metadata, // so no retry is done even though status was ABORTED -static void test_retry_recv_initial_metadata( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - grpc_metadata initial_metadata_from_server = { - grpc_slice_from_static_string("key1"), - grpc_slice_from_static_string("val1"), - {{nullptr, nullptr, nullptr, nullptr}}}; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = - begin_test(config, "retry_recv_initial_metadata", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - +TEST_P(RetryTest, RetryRecvInitialMetadata) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingMessage server_message; + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Server sends initial metadata in its own batch, before sending // trailing metadata. // Ideally, this would not require actually sending any metadata @@ -170,59 +77,21 @@ static void test_retry_recv_initial_metadata( // tests, where the proxy may wind up combining the batches, depending // on timing. Sending a metadata entry ensures that the transport // won't send a Trailers-Only response, even if the batches are combined. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 1; - op->data.send_initial_metadata.metadata = &initial_metadata_from_server; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_recv_initial_metadata(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_recv_initial_metadata(config); + s.NewBatch(102).SendInitialMetadata({{"key1", "val1"}}); + Expect(102, true); + Step(); + IncomingCloseOnServer client_close; + s.NewBatch(103) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(103, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } -void retry_recv_initial_metadata_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_recv_message.cc b/test/core/end2end/tests/retry_recv_message.cc index f28804c8665b6..13a9a49eb1e8a 100644 --- a/test/core/end2end/tests/retry_recv_message.cc +++ b/test/core/end2end/tests/retry_recv_message.cc @@ -16,193 +16,73 @@ // // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that receiving a message commits the call. // - 1 retry allowed for ABORTED status // - first attempt receives a message and therefore does not retry even // though the final status is ABORTED -static void test_retry_recv_message(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_recv_message", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_recv_message(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_recv_message(config); +TEST_P(RetryTest, RetryRecvMessage) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingMessage server_message; + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s.NewBatch(103) + .SendInitialMetadata({}) + .SendMessage("bar") + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(103, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } -void retry_recv_message_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_recv_message_replay.cc b/test/core/end2end/tests/retry_recv_message_replay.cc index ca3ad5b1b7ff2..3e77b28aeb053 100644 --- a/test/core/end2end/tests/retry_recv_message_replay.cc +++ b/test/core/end2end/tests/retry_recv_message_replay.cc @@ -16,213 +16,30 @@ // // -#include - -#include -#include #include #include "absl/status/status.h" #include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/config/core_configuration.h" -#include "src/core/lib/gpr/useful.h" #include "src/core/lib/gprpp/status_helper.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/iomgr/call_combiner.h" #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/surface/channel_stack_type.h" #include "src/core/lib/transport/transport.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Tests the fix for a bug found in real-world code where recv_message -// was incorrectly replayed on a call attempt that it was already sent -// to when the recv_message completion had already been returned but was -// deferred at the point where recv_trailing_metadata was started from -// the surface. This resulted in ASAN failures caused by not unreffing -// a grpc_error. -static void test_retry_recv_message_replay( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = - begin_test(config, "retry_recv_message_replay", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - // Start a batch containing send_initial_metadata and recv_initial_metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // Start a batch containing recv_message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // Start a batch containing recv_trailing_metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // Server should get a call. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - // Server fails with status ABORTED. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // In principle, the server batch should complete before the client - // batches, but in the proxy fixtures, there are multiple threads - // involved, so the completion order tends to be a little racy. - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} +namespace grpc_core { namespace { // A filter that, for the first call it sees, will fail the batch @@ -260,7 +77,7 @@ class FailFirstSendOpFilter { batch, grpc_error_set_int( GRPC_ERROR_CREATE("FailFirstSendOpFilter failing batch"), - grpc_core::StatusIntProperty::kRpcStatus, GRPC_STATUS_ABORTED), + StatusIntProperty::kRpcStatus, GRPC_STATUS_ABORTED), calld->call_combiner_); return; } @@ -271,7 +88,7 @@ class FailFirstSendOpFilter { explicit CallData(const grpc_call_element_args* args) : call_combiner_(args->call_combiner) {} - grpc_core::CallCombiner* call_combiner_; + CallCombiner* call_combiner_; bool fail_ = false; }; @@ -305,28 +122,80 @@ grpc_channel_filter FailFirstSendOpFilter::kFilterVtable = { "FailFirstSendOpFilter", }; -} // namespace - -void retry_recv_message_replay(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - grpc_core::CoreConfiguration::RunWithSpecialConfiguration( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::BuildCoreConfiguration(builder); - builder->channel_init()->RegisterStage( - GRPC_CLIENT_SUBCHANNEL, 0, - [](grpc_core::ChannelStackBuilder* builder) { - // Skip on proxy (which explicitly disables retries). - if (!builder->channel_args() - .GetBool(GRPC_ARG_ENABLE_RETRIES) - .value_or(true)) { - return true; - } - // Install filter. - builder->PrependFilter(&FailFirstSendOpFilter::kFilterVtable); - return true; - }); - }, - [config] { test_retry_recv_message_replay(config); }); +// Tests the fix for a bug found in real-world code where recv_message +// was incorrectly replayed on a call attempt that it was already sent +// to when the recv_message completion had already been returned but was +// deferred at the point where recv_trailing_metadata was started from +// the surface. This resulted in ASAN failures caused by not unreffing +// a grpc_error. +TEST_P(RetryTest, RetryRecvMessageReplay) { + CoreConfiguration::RegisterBuilder([](CoreConfiguration::Builder* builder) { + builder->channel_init()->RegisterStage( + GRPC_CLIENT_SUBCHANNEL, 0, [](ChannelStackBuilder* builder) { + // Skip on proxy (which explicitly disables retries). + if (!builder->channel_args() + .GetBool(GRPC_ARG_ENABLE_RETRIES) + .value_or(true)) { + return true; + } + // Install filter. + builder->PrependFilter(&FailFirstSendOpFilter::kFilterVtable); + return true; + }); + }); + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + // Start a batch containing send_initial_metadata and recv_initial_metadata. + IncomingMetadata server_initial_metadata; + c.NewBatch(1).SendInitialMetadata({}).RecvInitialMetadata( + server_initial_metadata); + // Start a batch containing recv_message. + IncomingMessage server_message; + c.NewBatch(2).RecvMessage(server_message); + // Start a batch containing recv_trailing_metadata. + IncomingStatusOnClient server_status; + c.NewBatch(3).RecvStatusOnClient(server_status); + // Server should get a call. + auto s = RequestCall(101); + Expect(101, true); + Step(); + // Server fails with status ABORTED. + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + // In principle, the server batch should complete before the client + // batches, but in the proxy fixtures, there are multiple threads + // involved, so the completion order tends to be a little racy. + Expect(102, true); + Expect(1, true); + Expect(2, true); + Expect(3, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } -void retry_recv_message_replay_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_recv_trailing_metadata_error.cc b/test/core/end2end/tests/retry_recv_trailing_metadata_error.cc index 7fbccf2fcbbfa..26750545ba033 100644 --- a/test/core/end2end/tests/retry_recv_trailing_metadata_error.cc +++ b/test/core/end2end/tests/retry_recv_trailing_metadata_error.cc @@ -14,214 +14,30 @@ // limitations under the License. // -#include - -#include -#include #include #include "absl/status/status.h" #include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/config/core_configuration.h" -#include "src/core/lib/gpr/useful.h" #include "src/core/lib/gprpp/debug_location.h" #include "src/core/lib/gprpp/status_helper.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/surface/channel_stack_type.h" #include "src/core/lib/transport/transport.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Tests that we honor the error passed to recv_trailing_metadata_ready -// when determining the call's status, even if the op completion runs before -// the recv_trailing_metadata op is started from the surface. -// - 1 retry allowed for ABORTED status -// - server returns ABORTED, but filter overwrites to INVALID_ARGUMENT, -// so no retry is done -static void test_retry_recv_trailing_metadata_error( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_recv_trailing_metadata_error", - &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_INVALID_ARGUMENT); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "injected error")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} +namespace grpc_core { namespace { // A filter that returns recv_trailing_metadata_ready with an error. @@ -266,11 +82,11 @@ class InjectStatusFilter { static void RecvTrailingMetadataReady(void* arg, grpc_error_handle /*error*/) { auto* calld = static_cast(arg); - grpc_core::Closure::Run( - DEBUG_LOCATION, calld->original_recv_trailing_metadata_ready_, - grpc_error_set_int(GRPC_ERROR_CREATE("injected error"), - grpc_core::StatusIntProperty::kRpcStatus, - GRPC_STATUS_INVALID_ARGUMENT)); + Closure::Run(DEBUG_LOCATION, + calld->original_recv_trailing_metadata_ready_, + grpc_error_set_int(GRPC_ERROR_CREATE("injected error"), + StatusIntProperty::kRpcStatus, + GRPC_STATUS_INVALID_ARGUMENT)); } grpc_closure recv_trailing_metadata_ready_; @@ -301,7 +117,7 @@ grpc_channel_filter InjectStatusFilter::kFilterVtable = { "InjectStatusFilter", }; -bool AddFilter(grpc_core::ChannelStackBuilder* builder) { +bool AddFilter(ChannelStackBuilder* builder) { // Skip on proxy (which explicitly disables retries). if (!builder->channel_args() .GetBool(GRPC_ARG_ENABLE_RETRIES) @@ -313,17 +129,67 @@ bool AddFilter(grpc_core::ChannelStackBuilder* builder) { return true; } -} // namespace - -void retry_recv_trailing_metadata_error(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - grpc_core::CoreConfiguration::RunWithSpecialConfiguration( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::BuildCoreConfiguration(builder); - builder->channel_init()->RegisterStage(GRPC_CLIENT_SUBCHANNEL, 0, - AddFilter); - }, - [config] { test_retry_recv_trailing_metadata_error(config); }); +// Tests that we honor the error passed to recv_trailing_metadata_ready +// when determining the call's status, even if the op completion runs before +// the recv_trailing_metadata op is started from the surface. +// - 1 retry allowed for ABORTED status +// - server returns ABORTED, but filter overwrites to INVALID_ARGUMENT, +// so no retry is done +TEST_P(RetryTest, RetryRecvTrailingMetadataError) { + CoreConfiguration::RegisterBuilder([](CoreConfiguration::Builder* builder) { + builder->channel_init()->RegisterStage(GRPC_CLIENT_SUBCHANNEL, 0, + AddFilter); + }); + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingMessage server_message; + IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Step(); + IncomingStatusOnClient server_status; + c.NewBatch(2).RecvStatusOnClient(server_status); + Expect(2, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_INVALID_ARGUMENT); + EXPECT_EQ(server_status.message(), "injected error"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } -void retry_recv_trailing_metadata_error_pre_init() {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_send_initial_metadata_refs.cc b/test/core/end2end/tests/retry_send_initial_metadata_refs.cc index cf138afdcd2ac..373ed57d87f13 100644 --- a/test/core/end2end/tests/retry_send_initial_metadata_refs.cc +++ b/test/core/end2end/tests/retry_send_initial_metadata_refs.cc @@ -16,303 +16,112 @@ // // -#include - -#include -#include #include -#include +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "gtest/gtest.h" + #include -#include #include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we hold refs to send_initial_metadata payload while // cached, even after the caller has released its refs: // - 2 retries allowed for ABORTED status // - first attempt returns ABORTED // - second attempt returns OK -static void test_retry_send_initial_metadata_refs( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array client_send_initial_metadata; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_send_initial_metadata_refs", &client_args, - nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&client_send_initial_metadata); - client_send_initial_metadata.count = 2; - client_send_initial_metadata.metadata = static_cast( - gpr_malloc(client_send_initial_metadata.count * sizeof(grpc_metadata))); - // First element is short enough for slices to be inlined. - client_send_initial_metadata.metadata[0].key = - grpc_slice_from_copied_string(std::string("foo").c_str()); - client_send_initial_metadata.metadata[0].value = - grpc_slice_from_copied_string(std::string("bar").c_str()); - // Second element requires slice allocation. - client_send_initial_metadata.metadata[1].key = grpc_slice_from_copied_string( - std::string(GRPC_SLICE_INLINED_SIZE + 1, 'x').c_str()); - client_send_initial_metadata.metadata[1].value = - grpc_slice_from_copied_string( - std::string(GRPC_SLICE_INLINED_SIZE + 1, 'y').c_str()); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = client_send_initial_metadata.count; - op->data.send_initial_metadata.metadata = - client_send_initial_metadata.metadata; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - for (size_t i = 0; i < client_send_initial_metadata.count; ++i) { - grpc_slice_unref(client_send_initial_metadata.metadata[i].key); - grpc_slice_unref(client_send_initial_metadata.metadata[i].value); - } - grpc_metadata_array_destroy(&client_send_initial_metadata); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - +TEST_P(RetryTest, RetrySendInitialMetadataRefs) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + c.NewBatch(1) + .SendInitialMetadata( + {// First element is short enough for slices to be inlined. + {"foo", "bar"}, + // Second element requires slice allocation. + {std::string(GRPC_SLICE_INLINED_SIZE + 1, 'x'), + std::string(GRPC_SLICE_INLINED_SIZE + 1, 'y')}}) + .SendMessage("foo") + .SendCloseFromClient(); + Expect(1, true); + Step(); + IncomingMessage server_message; + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(2) + .RecvMessage(server_message) + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); // Make sure the "grpc-previous-rpc-attempts" header was not sent in the // initial attempt. - for (size_t i = 0; i < request_metadata_recv.count; ++i) { - GPR_ASSERT(!grpc_slice_eq( - request_metadata_recv.metadata[i].key, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"))); - } - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - + EXPECT_EQ(s.GetInitialMetadata("grpc-previous-rpc-attempts"), absl::nullopt); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Step(); + auto s2 = RequestCall(201); + Expect(201, true); + Step(); // Make sure the "grpc-previous-rpc-attempts" header was sent in the retry. - GPR_ASSERT(contains_metadata_slices( - &request_metadata_recv, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"), - grpc_slice_from_static_string("1"))); + EXPECT_EQ(s2.GetInitialMetadata("grpc-previous-rpc-attempts"), "1"); // It should also contain the initial metadata, even though the client // freed it already. - GPR_ASSERT(contains_metadata(&request_metadata_recv, "foo", "bar")); - GPR_ASSERT( - contains_metadata(&request_metadata_recv, - std::string(GRPC_SLICE_INLINED_SIZE + 1, 'x').c_str(), - std::string(GRPC_SLICE_INLINED_SIZE + 1, 'y').c_str())); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(202), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(203), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(202), true); - cqv.Expect(grpc_core::CqVerifier::tag(203), true); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_send_initial_metadata_refs(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_send_initial_metadata_refs(config); + EXPECT_EQ(s2.GetInitialMetadata("foo"), "bar"); + EXPECT_EQ( + s2.GetInitialMetadata(std::string(GRPC_SLICE_INLINED_SIZE + 1, 'x')), + std::string(GRPC_SLICE_INLINED_SIZE + 1, 'y')); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingMessage client_message; + s2.NewBatch(202).RecvMessage(client_message); + IncomingCloseOnServer client_close2; + s2.NewBatch(203) + .SendInitialMetadata({}) + .SendMessage("bar") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}) + .RecvCloseOnServer(client_close2); + Expect(202, true); + Expect(203, true); + Expect(2, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } -void retry_send_initial_metadata_refs_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_send_op_fails.cc b/test/core/end2end/tests/retry_send_op_fails.cc index 46bc7fdfda0af..befd60ff5c6f4 100644 --- a/test/core/end2end/tests/retry_send_op_fails.cc +++ b/test/core/end2end/tests/retry_send_op_fails.cc @@ -16,226 +16,30 @@ // // -#include - -#include -#include #include #include "absl/status/status.h" #include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/config/core_configuration.h" -#include "src/core/lib/gpr/useful.h" #include "src/core/lib/gprpp/status_helper.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/iomgr/call_combiner.h" #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/surface/channel_stack_type.h" #include "src/core/lib/transport/transport.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Tests failure on a send op batch: -// - 2 retries allowed for ABORTED status -// - on the first call attempt, the batch containing the -// send_initial_metadata op fails, and then the call returns ABORTED, -// all without ever going out on the wire -// - second attempt returns ABORTED but does not retry, because only 2 -// attempts are allowed -static void test_retry_send_op_fails(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_send_op_fails", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - // Start a batch containing send ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // Start a batch containing recv ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // Client send ops should now complete. - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - // Server should get a call. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - // Server fails with status ABORTED. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // In principle, the server batch should complete before the client - // recv ops batch, but in the proxy fixtures, there are multiple threads - // involved, so the completion order tends to be a little racy. - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - // Make sure the "grpc-previous-rpc-attempts" header was sent in the retry. - bool found_retry_header = false; - for (size_t i = 0; i < request_metadata_recv.count; ++i) { - if (grpc_slice_eq( - request_metadata_recv.metadata[i].key, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"))) { - GPR_ASSERT(grpc_slice_eq(request_metadata_recv.metadata[i].value, - grpc_slice_from_static_string("1"))); - found_retry_header = true; - break; - } - } - GPR_ASSERT(found_retry_header); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} +namespace grpc_core { namespace { // A filter that, for the first call it sees, will fail all batches except @@ -274,7 +78,7 @@ class FailFirstCallFilter { batch, grpc_error_set_int( GRPC_ERROR_CREATE("FailFirstCallFilter failing batch"), - grpc_core::StatusIntProperty::kRpcStatus, GRPC_STATUS_ABORTED), + StatusIntProperty::kRpcStatus, GRPC_STATUS_ABORTED), calld->call_combiner_); return; } @@ -285,7 +89,7 @@ class FailFirstCallFilter { explicit CallData(const grpc_call_element_args* args) : call_combiner_(args->call_combiner) {} - grpc_core::CallCombiner* call_combiner_; + CallCombiner* call_combiner_; bool fail_ = false; }; @@ -319,28 +123,87 @@ grpc_channel_filter FailFirstCallFilter::kFilterVtable = { "FailFirstCallFilter", }; -} // namespace - -void retry_send_op_fails(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - grpc_core::CoreConfiguration::RunWithSpecialConfiguration( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::BuildCoreConfiguration(builder); - builder->channel_init()->RegisterStage( - GRPC_CLIENT_SUBCHANNEL, 0, - [](grpc_core::ChannelStackBuilder* builder) { - // Skip on proxy (which explicitly disables retries). - if (!builder->channel_args() - .GetBool(GRPC_ARG_ENABLE_RETRIES) - .value_or(true)) { - return true; - } - // Install filter. - builder->PrependFilter(&FailFirstCallFilter::kFilterVtable); - return true; - }); - }, - [config] { test_retry_send_op_fails(config); }); +// Tests failure on a send op batch: +// - 2 retries allowed for ABORTED status +// - on the first call attempt, the batch containing the +// send_initial_metadata op fails, and then the call returns ABORTED, +// all without ever going out on the wire +// - second attempt returns ABORTED but does not retry, because only 2 +// attempts are allowed +TEST_P(RetryTest, RetrySendOpFails) { + CoreConfiguration::RegisterBuilder([](CoreConfiguration::Builder* builder) { + builder->channel_init()->RegisterStage( + GRPC_CLIENT_SUBCHANNEL, 0, [](ChannelStackBuilder* builder) { + // Skip on proxy (which explicitly disables retries). + if (!builder->channel_args() + .GetBool(GRPC_ARG_ENABLE_RETRIES) + .value_or(true)) { + return true; + } + // Install filter. + builder->PrependFilter(&FailFirstCallFilter::kFilterVtable); + return true; + }); + }); + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + // Start a batch containing send ops. + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .SendCloseFromClient(); + // Start a batch containing recv ops. + IncomingMessage server_message; + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(2) + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + // Client send ops should now complete. + Expect(1, true); + Step(); + // Server should get a call. + auto s = RequestCall(101); + Expect(101, true); + Step(); + // Server fails with status ABORTED. + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + // In principle, the server batch should complete before the client + // recv ops batch, but in the proxy fixtures, there are multiple threads + // involved, so the completion order tends to be a little racy. + Expect(102, true); + Expect(2, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); + // Make sure the "grpc-previous-rpc-attempts" header was sent in the retry. + EXPECT_EQ(s.GetInitialMetadata("grpc-previous-rpc-attempts"), "1"); } -void retry_send_op_fails_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_send_recv_batch.cc b/test/core/end2end/tests/retry_send_recv_batch.cc index 779942b70746f..4ec186d5ff7ea 100644 --- a/test/core/end2end/tests/retry_send_recv_batch.cc +++ b/test/core/end2end/tests/retry_send_recv_batch.cc @@ -14,191 +14,72 @@ // limitations under the License. // -#include +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests a scenario where there is a batch containing both a send op and // a recv op, where the send op completes but the recv op does not, and // then a subsequent recv op is started. This ensures that we do not // incorrectly attempt to replay the send op. -static void test_retry_send_recv_batch(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_send_recv_batch", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - +TEST_P(RetryTest, RetrySendRecvBatch) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); // Client starts batch with send_initial_metadata and recv_initial_metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingMetadata server_initial_metadata; + c.NewBatch(1).SendInitialMetadata({}).RecvInitialMetadata( + server_initial_metadata); // Client starts a batch with send_message and recv_trailing_metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingStatusOnClient server_status; + c.NewBatch(2).SendMessage("hello").RecvStatusOnClient(server_status); // Server gets a call. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - + auto s = RequestCall(101); + Expect(101, true); + Step(); // Client starts a batch containing recv_message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingMessage server_message; + c.NewBatch(3).RecvMessage(server_message); // Server fails the call with a non-retriable status. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_PERMISSION_DENIED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_PERMISSION_DENIED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_send_recv_batch(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_send_recv_batch(config); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_PERMISSION_DENIED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Expect(2, true); + Expect(3, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_PERMISSION_DENIED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } -void retry_send_recv_batch_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_server_pushback_delay.cc b/test/core/end2end/tests/retry_server_pushback_delay.cc index e645ba11c38bf..b58f1934167cc 100644 --- a/test/core/end2end/tests/retry_server_pushback_delay.cc +++ b/test/core/end2end/tests/retry_server_pushback_delay.cc @@ -16,262 +16,93 @@ // // -#include -#include - -#include -#include -#include - #include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" #include "src/core/lib/gprpp/time.h" -#include "src/core/lib/slice/slice_internal.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we honor server push-back delay. // - 2 retries allowed for ABORTED status // - first attempt gets ABORTED with a long delay // - second attempt succeeds -static void test_retry_server_pushback_delay( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_metadata pushback_md; - memset(&pushback_md, 0, sizeof(pushback_md)); - pushback_md.key = grpc_slice_from_static_string("grpc-retry-pushback-ms"); - pushback_md.value = grpc_slice_from_static_string("2000"); - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = - begin_test(config, "retry_server_pushback_delay", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details1 = grpc_slice_from_static_string("message1"); - grpc_slice status_details2 = grpc_slice_from_static_string("message2"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(grpc_core::Duration::Seconds(20)); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 1; - op->data.send_status_from_server.trailing_metadata = &pushback_md; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details1; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - gpr_timespec before_retry = gpr_now(GPR_CLOCK_MONOTONIC); - - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - - gpr_timespec after_retry = gpr_now(GPR_CLOCK_MONOTONIC); - gpr_timespec retry_delay = gpr_time_sub(after_retry, before_retry); +TEST_P(RetryTest, RetryServerPushbackDelay) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingMessage server_message; + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + absl::optional s = RequestCall(101); + Expect(101, true); + Step(Duration::Seconds(20)); + EXPECT_NE(s->GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s->NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "message1", + {{"grpc-retry-pushback-ms", "2000"}}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Step(); + const auto before_retry = Timestamp::Now(); + s.reset(); + s.emplace(RequestCall(201)); + Expect(201, true); + Step(); + const auto after_retry = Timestamp::Now(); + const auto retry_delay = after_retry - before_retry; // Configured back-off was 1 second, server push-back said 2 seconds. // To avoid flakiness, we allow some fudge factor here. - gpr_log(GPR_INFO, "retry delay was {.tv_sec=%" PRId64 ", .tv_nsec=%d}", - retry_delay.tv_sec, retry_delay.tv_nsec); - GPR_ASSERT(retry_delay.tv_sec >= 1); - if (retry_delay.tv_sec == 1) { - GPR_ASSERT(retry_delay.tv_nsec >= 800000000); - } - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - op->data.send_status_from_server.status_details = &status_details2; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(202), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(202), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - gpr_log(GPR_INFO, "status=%d message=\"%s\"", status, - std::string(grpc_core::StringViewFromSlice(details)).c_str()); - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "message2")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_server_pushback_delay(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_server_pushback_delay(config); + EXPECT_GE(retry_delay, Duration::Milliseconds(1800)); + EXPECT_NE(s->GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close2; + s->NewBatch(202) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_OK, "message2", {}) + .RecvCloseOnServer(client_close2); + Expect(202, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "message2"); + EXPECT_EQ(s->method(), "/service/method"); + EXPECT_FALSE(client_close2.was_cancelled()); } -void retry_server_pushback_delay_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_server_pushback_disabled.cc b/test/core/end2end/tests/retry_server_pushback_disabled.cc index 43dfcd4d998be..eb9dc962ce4ce 100644 --- a/test/core/end2end/tests/retry_server_pushback_disabled.cc +++ b/test/core/end2end/tests/retry_server_pushback_disabled.cc @@ -16,240 +16,87 @@ // // -#include +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we don't retry when disabled by server push-back. // - 2 retries allowed for ABORTED status // - first attempt gets ABORTED // - second attempt gets ABORTED but server push back disables retrying -static void test_retry_server_pushback_disabled( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_metadata pushback_md; - memset(&pushback_md, 0, sizeof(pushback_md)); - pushback_md.key = grpc_slice_from_static_string("grpc-retry-pushback-ms"); - pushback_md.value = grpc_slice_from_static_string("-1"); - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_server_pushback_disabled", &client_args, - nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 1; - op->data.send_status_from_server.trailing_metadata = &pushback_md; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(202), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(202), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_server_pushback_disabled(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_server_pushback_disabled(config); +TEST_P(RetryTest, RetryServerPushbackDisabled) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + absl::optional s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s->GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s->NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Step(); + s.reset(); + s.emplace(RequestCall(201)); + Expect(201, true); + Step(); + EXPECT_NE(s->GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close2; + s->NewBatch(202) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", + {{"grpc-retry-pushback-ms", "-1"}}) + .RecvCloseOnServer(client_close2); + Expect(202, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s->method(), "/service/method"); + EXPECT_FALSE(client_close2.was_cancelled()); } -void retry_server_pushback_disabled_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_streaming.cc b/test/core/end2end/tests/retry_streaming.cc index 6a4f8e0a47ca3..f6c366f3fe919 100644 --- a/test/core/end2end/tests/retry_streaming.cc +++ b/test/core/end2end/tests/retry_streaming.cc @@ -16,39 +16,25 @@ // // -#include - -#include -#include #include -#include +#include "absl/types/optional.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channelz.h" -#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/surface/channel.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +using testing::HasSubstr; + +namespace grpc_core { +namespace { // Tests retrying a streaming RPC. This is the same as // the basic retry test, except that the client sends two messages on the @@ -59,345 +45,133 @@ static std::unique_ptr begin_test( // replayed ops happen under the hood -- they are not surfaced to the // C-core API, and therefore we have no way to inject the commit at the // right point. -static void test_retry_streaming(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice request2_payload_slice = grpc_slice_from_static_string("bar"); - grpc_slice request3_payload_slice = grpc_slice_from_static_string("baz"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("quux"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* request2_payload = - grpc_raw_byte_buffer_create(&request2_payload_slice, 1); - grpc_byte_buffer* request3_payload = - grpc_raw_byte_buffer_create(&request3_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* request2_payload_recv = nullptr; - grpc_byte_buffer* request3_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE), - 1024 * 8), - grpc_channel_arg_integer_create( - const_cast(GRPC_ARG_ENABLE_CHANNELZ), true), - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}"))}; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_streaming", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - grpc_core::channelz::ChannelNode* channelz_channel = - grpc_channel_get_channelz_node(f->client()); - - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - +TEST_P(RetryTest, RetryStreaming) { + InitServer(ChannelArgs()); + InitClient( + ChannelArgs() + .Set(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE, 1024 * 8) + .Set(GRPC_ARG_ENABLE_CHANNELZ, true) + .Set(GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + channelz::ChannelNode* channelz_channel = + grpc_channel_get_channelz_node(client()); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Client starts a batch for receiving initial metadata, a message, // and trailing metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingStatusOnClient server_status; + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + c.NewBatch(1) + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); // Client sends initial metadata and a message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - + c.NewBatch(2).SendInitialMetadata({}).SendMessage("foo"); + Expect(2, true); + Step(); // Server gets a call with received initial metadata. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Server receives a message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - + IncomingMessage client_message; + s.NewBatch(102).RecvMessage(client_message); + Expect(102, true); + Step(); // Client sends a second message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request2_payload; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Verify(); - + c.NewBatch(3).SendMessage("bar"); + Expect(3, true); + Step(); // Server receives the second message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request2_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Verify(); - + IncomingMessage client_message2; + s.NewBatch(103).RecvMessage(client_message2); + Expect(103, true); + Step(); // Server sends both initial and trailing metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(104), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(104), true); - cqv.Verify(); - + IncomingCloseOnServer client_close; + s.NewBatch(104) + .RecvCloseOnServer(client_close) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}); + Expect(104, true); + Step(); // Clean up from first attempt. - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice)); - grpc_byte_buffer_destroy(request_payload_recv); - request_payload_recv = nullptr; - GPR_ASSERT( - byte_buffer_eq_slice(request2_payload_recv, request2_payload_slice)); - grpc_byte_buffer_destroy(request2_payload_recv); - request2_payload_recv = nullptr; - + EXPECT_EQ(client_message.payload(), "foo"); + EXPECT_EQ(client_message2.payload(), "bar"); // Server gets a second call (the retry). - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - + auto s2 = RequestCall(201); + Expect(201, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Server receives a message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(202), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(202), true); - cqv.Verify(); - + IncomingMessage client_message3; + s2.NewBatch(202).RecvMessage(client_message3); + Expect(202, true); + Step(); // Server receives a second message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request2_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(203), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(203), true); - cqv.Verify(); - + IncomingMessage client_message4; + s2.NewBatch(203).RecvMessage(client_message4); + Expect(203, true); + Step(); // Client sends a third message and a close. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request3_payload; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(4), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(4), true); - cqv.Verify(); - + c.NewBatch(4).SendMessage("baz").SendCloseFromClient(); + Expect(4, true); + Step(); // Server receives a third message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request3_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(204), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(204), true); - cqv.Verify(); - + IncomingMessage client_message5; + s2.NewBatch(204).RecvMessage(client_message5); + Expect(204, true); + Step(); // Server receives a close and sends initial metadata, a message, and // trailing metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - // Returning a retriable code, but because we are also sending a - // message, the client will commit instead of retrying again. - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(205), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(205), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - GPR_ASSERT(channelz_channel != nullptr); + IncomingCloseOnServer client_close2; + s2.NewBatch(205) + .RecvCloseOnServer(client_close2) + .SendInitialMetadata({}) + .SendMessage("quux") + // Returning a retriable code, but because we are also sending a + // message, the client will commit instead of retrying again. + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}); + Expect(205, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_NE(channelz_channel, nullptr); + // TODO(roth): consider using a regex check here. std::string json = channelz_channel->RenderJsonString(); - gpr_log(GPR_INFO, "%s", json.c_str()); - GPR_ASSERT(json.find("\"trace\"") != json.npos); - GPR_ASSERT(json.find("\"description\":\"Channel created\"") != json.npos); - GPR_ASSERT(json.find("\"severity\":\"CT_INFO\"") != json.npos); - GPR_ASSERT(json.find("Resolution event") != json.npos); - GPR_ASSERT(json.find("Created new LB policy") != json.npos); - GPR_ASSERT(json.find("Service config changed") != json.npos); - GPR_ASSERT(json.find("Address list became non-empty") != json.npos); - GPR_ASSERT(json.find("Channel state change to CONNECTING") != json.npos); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request2_payload); - grpc_byte_buffer_destroy(request3_payload); - grpc_byte_buffer_destroy(response_payload); - GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice)); - grpc_byte_buffer_destroy(request_payload_recv); - GPR_ASSERT( - byte_buffer_eq_slice(request2_payload_recv, request2_payload_slice)); - grpc_byte_buffer_destroy(request2_payload_recv); - GPR_ASSERT( - byte_buffer_eq_slice(request3_payload_recv, request3_payload_slice)); - grpc_byte_buffer_destroy(request3_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_streaming(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - - test_retry_streaming(config); + EXPECT_THAT(json, HasSubstr("\"trace\"")); + EXPECT_THAT(json, HasSubstr("\"description\":\"Channel created\"")); + EXPECT_THAT(json, HasSubstr("\"severity\":\"CT_INFO\"")); + EXPECT_THAT(json, HasSubstr("Resolution event")); + EXPECT_THAT(json, HasSubstr("Created new LB policy")); + EXPECT_THAT(json, HasSubstr("Service config changed")); + EXPECT_THAT(json, HasSubstr("Address list became non-empty")); + EXPECT_THAT(json, HasSubstr("Channel state change to CONNECTING")); + EXPECT_EQ(client_message3.payload(), "foo"); + EXPECT_EQ(client_message4.payload(), "bar"); + EXPECT_EQ(client_message5.payload(), "baz"); } -void retry_streaming_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_streaming_after_commit.cc b/test/core/end2end/tests/retry_streaming_after_commit.cc index d42c61c35589f..897600cee6445 100644 --- a/test/core/end2end/tests/retry_streaming_after_commit.cc +++ b/test/core/end2end/tests/retry_streaming_after_commit.cc @@ -16,295 +16,108 @@ // // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we can continue to send/recv messages on a streaming call // after retries are committed. -static void test_retry_streaming_after_commit( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice request2_payload_slice = grpc_slice_from_static_string("bar"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("baz"); - grpc_slice response2_payload_slice = grpc_slice_from_static_string("quux"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* request2_payload = - grpc_raw_byte_buffer_create(&request2_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* response2_payload = - grpc_raw_byte_buffer_create(&response2_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* request2_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_byte_buffer* response2_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = - begin_test(config, "retry_streaming_after_commit", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - +TEST_P(RetryTest, RetryStreamingAfterCommit) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Client starts a batch for receiving initial metadata and a message. // This will commit retries. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingMessage server_message; + IncomingMetadata server_initial_metadata; + c.NewBatch(2) + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message); // Client sends initial metadata and a message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Verify(); - + c.NewBatch(3).SendInitialMetadata({}).SendMessage("foo"); + Expect(3, true); + Step(); // Server gets a call with received initial metadata. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Server receives a message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - + IncomingMessage client_message; + s.NewBatch(102).RecvMessage(client_message); + Expect(102, true); + Step(); // Server sends initial metadata and a message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(103), true); + s.NewBatch(103).SendInitialMetadata({}).SendMessage("bar"); + Expect(103, true); // Client receives initial metadata and a message. - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - + Expect(2, true); + Step(); // Client sends a second message and a close. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request2_payload; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(4), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(4), true); - cqv.Verify(); - + c.NewBatch(4).SendMessage("baz").SendCloseFromClient(); + Expect(4, true); + Step(); // Server receives a second message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request2_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(104), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(104), true); - cqv.Verify(); - + IncomingMessage client_message2; + s.NewBatch(104).RecvMessage(client_message2); + Expect(104, true); + Step(); // Server receives a close, sends a second message, and sends status. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response2_payload; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; // Returning a retriable code, but because retries are already // committed, the client will not retry. - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(105), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(105), true); - cqv.Verify(); - + IncomingCloseOnServer client_close; + s.NewBatch(105) + .RecvCloseOnServer(client_close) + .SendMessage("quux") + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}); + Expect(105, true); + Step(); // Client receives a second message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response2_payload_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(5), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(5), true); - cqv.Verify(); - + IncomingMessage server_message2; + c.NewBatch(5).RecvMessage(server_message2); + Expect(5, true); + Step(); // Client receives status. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request2_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(response2_payload); - GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice)); - grpc_byte_buffer_destroy(request_payload_recv); - GPR_ASSERT( - byte_buffer_eq_slice(request2_payload_recv, request2_payload_slice)); - grpc_byte_buffer_destroy(request2_payload_recv); - GPR_ASSERT( - byte_buffer_eq_slice(response_payload_recv, response_payload_slice)); - grpc_byte_buffer_destroy(response_payload_recv); - GPR_ASSERT( - byte_buffer_eq_slice(response2_payload_recv, response2_payload_slice)); - grpc_byte_buffer_destroy(response2_payload_recv); - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_streaming_after_commit(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_streaming_after_commit(config); + IncomingStatusOnClient server_status; + c.NewBatch(1).RecvStatusOnClient(server_status); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(client_message.payload(), "foo"); + EXPECT_EQ(server_message.payload(), "bar"); + EXPECT_EQ(client_message2.payload(), "baz"); + EXPECT_EQ(server_message2.payload(), "quux"); } -void retry_streaming_after_commit_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc b/test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc index 395e77d396609..6ad20811a84d4 100644 --- a/test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc +++ b/test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc @@ -16,340 +16,124 @@ // // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we correctly clean up if the second attempt finishes // before we have finished replaying all of the send ops. -static void test_retry_streaming_succeeds_before_replay_finished( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice request2_payload_slice = grpc_slice_from_static_string("bar"); - grpc_slice request3_payload_slice = grpc_slice_from_static_string("baz"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("quux"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* request2_payload = - grpc_raw_byte_buffer_create(&request2_payload_slice, 1); - grpc_byte_buffer* request3_payload = - grpc_raw_byte_buffer_create(&request3_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* request2_payload_recv = nullptr; - grpc_byte_buffer* request3_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_streaming", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - +TEST_P(RetryTest, RetryStreamSucceedsBeforeReplayFinished) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Client starts a batch for receiving initial metadata, a message, // and trailing metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); // Client sends initial metadata and a message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - + c.NewBatch(2).SendInitialMetadata({}).SendMessage("foo"); + Expect(2, true); + Step(); // Server gets a call with received initial metadata. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - + absl::optional s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s->GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Server receives a message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - + IncomingMessage client_message; + s->NewBatch(102).RecvMessage(client_message); + Expect(102, true); + Step(); // Client sends a second message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request2_payload; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Verify(); - + c.NewBatch(3).SendMessage("bar"); + Expect(3, true); + Step(); // Server receives the second message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request2_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Verify(); - + IncomingMessage client_message2; + s->NewBatch(103).RecvMessage(client_message2); + Expect(103, true); + Step(); // Client sends a third message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request3_payload; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(4), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(4), true); - cqv.Verify(); - + c.NewBatch(4).SendMessage("baz"); + Expect(4, true); + Step(); // Server receives the third message. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request3_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(104), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(104), true); - cqv.Verify(); - + IncomingMessage client_message3; + s->NewBatch(104).RecvMessage(client_message3); + Expect(104, true); + Step(); // Server sends both initial and trailing metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(105), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(105), true); - cqv.Verify(); - + IncomingCloseOnServer client_close; + s->NewBatch(105) + .RecvCloseOnServer(client_close) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}); + Expect(105, true); + Step(); // Clean up from first attempt. - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice)); - grpc_byte_buffer_destroy(request_payload_recv); - request_payload_recv = nullptr; - GPR_ASSERT( - byte_buffer_eq_slice(request2_payload_recv, request2_payload_slice)); - grpc_byte_buffer_destroy(request2_payload_recv); - request2_payload_recv = nullptr; - GPR_ASSERT( - byte_buffer_eq_slice(request3_payload_recv, request3_payload_slice)); - grpc_byte_buffer_destroy(request3_payload_recv); - request3_payload_recv = nullptr; - + s.reset(); + EXPECT_EQ(client_message.payload(), "foo"); + EXPECT_EQ(client_message2.payload(), "bar"); + EXPECT_EQ(client_message3.payload(), "baz"); // Server gets a second call (the retry). - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - + s.emplace(RequestCall(201)); + Expect(201, true); + Step(); + EXPECT_NE(s->GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); // Server receives the first message (and does not receive any others). - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(202), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(202), true); - cqv.Verify(); - + IncomingMessage client_message4; + s->NewBatch(202).RecvMessage(client_message4); + Expect(202, true); + Step(); // Server sends initial metadata, a message, and trailing metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - // Returning a retriable code, but because we are also sending a - // message, the client will commit instead of retrying again. - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(205), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(205), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request2_payload); - grpc_byte_buffer_destroy(request3_payload); - grpc_byte_buffer_destroy(response_payload); - GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice)); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_streaming_succeeds_before_replay_finished( - const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_streaming_succeeds_before_replay_finished(config); + s->NewBatch(205) + .SendInitialMetadata({}) + .SendMessage("qux") + // Returning a retriable code, but because we are also sending a + // message, the client will commit instead of retrying again. + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}); + Expect(205, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s->method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(client_message4.payload(), "foo"); } -void retry_streaming_succeeds_before_replay_finished_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_throttled.cc b/test/core/end2end/tests/retry_throttled.cc index 5438dc74c0095..d8b5a2cb3b122 100644 --- a/test/core/end2end/tests/retry_throttled.cc +++ b/test/core/end2end/tests/retry_throttled.cc @@ -16,196 +16,78 @@ // // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we don't retry when throttled. // - 1 retry allowed for ABORTED status // - first attempt gets ABORTED but is over limit, so no retry is done -static void test_retry_throttled(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ],\n" - // A single failure will cause us to be throttled. - // (This is not a very realistic config, but it works for the - // purposes of this test.) - " \"retryThrottling\": {\n" - " \"maxTokens\": 2,\n" - " \"tokenRatio\": 1.0\n" - " }\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_throttled", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -void retry_throttled(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_throttled(config); +TEST_P(RetryTest, RetryThrottled) { + InitServer(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ],\n" + // A single failure will cause us to be throttled. + // (This is not a very realistic config, but it works for the + // purposes of this test.) + " \"retryThrottling\": {\n" + " \"maxTokens\": 2,\n" + " \"tokenRatio\": 1.0\n" + " }\n" + "}")); + InitClient(ChannelArgs()); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingStatusOnClient server_status; + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } -void retry_throttled_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_too_many_attempts.cc b/test/core/end2end/tests/retry_too_many_attempts.cc index f4d1bcaa0865a..5dceb7ba935de 100644 --- a/test/core/end2end/tests/retry_too_many_attempts.cc +++ b/test/core/end2end/tests/retry_too_many_attempts.cc @@ -16,232 +16,83 @@ // // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we stop retrying after the configured number of attempts. // - 1 retry allowed for ABORTED status // - first attempt gets ABORTED // - second attempt gets ABORTED but does not retry -static void test_retry_too_many_attempts(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_too_many_attempts", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_ABORTED; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(202), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(202), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_ABORTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); +TEST_P(RetryTest, RetryTooManyAttempts) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .RecvMessage(server_message) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Step(); + auto s2 = RequestCall(201); + + Expect(201, true); + Step(); + EXPECT_NE(s2.GetPeer(), absl::nullopt); + EXPECT_NE(c.GetPeer(), absl::nullopt); + CoreEnd2endTest::IncomingCloseOnServer client_close2; + s2.NewBatch(202) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_ABORTED, "xyz", {}) + .RecvCloseOnServer(client_close2); + Expect(202, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_ABORTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s2.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } - -void retry_too_many_attempts(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_too_many_attempts(config); -} - -void retry_too_many_attempts_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_transparent_goaway.cc b/test/core/end2end/tests/retry_transparent_goaway.cc index 1b6486d049077..368588a60355f 100644 --- a/test/core/end2end/tests/retry_transparent_goaway.cc +++ b/test/core/end2end/tests/retry_transparent_goaway.cc @@ -14,23 +14,14 @@ // limitations under the License. // -#include - -#include -#include #include #include "absl/status/status.h" #include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" @@ -38,6 +29,7 @@ #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/config/core_configuration.h" #include "src/core/lib/gprpp/status_helper.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/iomgr/call_combiner.h" #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/error.h" @@ -45,182 +37,9 @@ #include "src/core/lib/surface/channel_stack_type.h" #include "src/core/lib/transport/metadata_batch.h" #include "src/core/lib/transport/transport.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Tests transparent retries when the call was never sent out on the wire. -static void test_retry_transparent_goaway(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - auto f = begin_test(config, "retry_transparent_goaway", nullptr, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - // Start a batch containing send ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // Start a batch containing recv ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // Client send ops should now complete. - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - // Server should get a call. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - // Server receives the request. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - // Server sends a response with status OK. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - op->data.send_status_from_server.status_details = &status_details; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // In principle, the server batch should complete before the client - // recv ops batch, but in the proxy fixtures, there are multiple threads - // involved, so the completion order tends to be a little racy. - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice)); - GPR_ASSERT( - byte_buffer_eq_slice(response_payload_recv, response_payload_slice)); - - // Make sure the "grpc-previous-rpc-attempts" header was NOT sent, since - // we don't do that for transparent retries. - for (size_t i = 0; i < request_metadata_recv.count; ++i) { - GPR_ASSERT(!grpc_slice_eq( - request_metadata_recv.metadata[i].key, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"))); - } - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} +namespace grpc_core { namespace { // A filter that, for the first call it sees, will fail all batches except @@ -258,16 +77,15 @@ class FailFirstCallFilter { if (calld->fail_) { if (batch->recv_trailing_metadata) { batch->payload->recv_trailing_metadata.recv_trailing_metadata->Set( - grpc_core::GrpcStreamNetworkState(), - grpc_core::GrpcStreamNetworkState::kNotSeenByServer); + GrpcStreamNetworkState(), + GrpcStreamNetworkState::kNotSeenByServer); } if (!batch->cancel_stream) { grpc_transport_stream_op_batch_finish_with_failure( batch, grpc_error_set_int( GRPC_ERROR_CREATE("FailFirstCallFilter failing batch"), - grpc_core::StatusIntProperty::kRpcStatus, - GRPC_STATUS_UNAVAILABLE), + StatusIntProperty::kRpcStatus, GRPC_STATUS_UNAVAILABLE), calld->call_combiner_); return; } @@ -279,7 +97,7 @@ class FailFirstCallFilter { explicit CallData(const grpc_call_element_args* args) : call_combiner_(args->call_combiner) {} - grpc_core::CallCombiner* call_combiner_; + CallCombiner* call_combiner_; bool fail_ = false; }; @@ -313,28 +131,74 @@ grpc_channel_filter FailFirstCallFilter::kFilterVtable = { "FailFirstCallFilter", }; -} // namespace - -void retry_transparent_goaway(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - grpc_core::CoreConfiguration::RunWithSpecialConfiguration( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::BuildCoreConfiguration(builder); - builder->channel_init()->RegisterStage( - GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY + 1, - [](grpc_core::ChannelStackBuilder* builder) { - // Skip on proxy (which explicitly disables retries). - if (!builder->channel_args() - .GetBool(GRPC_ARG_ENABLE_RETRIES) - .value_or(true)) { - return true; - } - // Install filter. - builder->PrependFilter(&FailFirstCallFilter::kFilterVtable); - return true; - }); - }, - [config] { test_retry_transparent_goaway(config); }); +// Tests transparent retries when the call was never sent out on the wire. +TEST_P(RetryTest, TransparentGoaway) { + CoreConfiguration::RegisterBuilder([](CoreConfiguration::Builder* builder) { + builder->channel_init()->RegisterStage( + GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY + 1, + [](ChannelStackBuilder* builder) { + // Skip on proxy (which explicitly disables retries). + if (!builder->channel_args() + .GetBool(GRPC_ARG_ENABLE_RETRIES) + .value_or(true)) { + return true; + } + // Install filter. + builder->PrependFilter(&FailFirstCallFilter::kFilterVtable); + return true; + }); + }); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + // Start a batch containing send ops. + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .SendCloseFromClient(); + // Start a batch containing recv ops. + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(2) + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + // Client send ops should now complete. + Expect(1, true); + Step(); + // Server should get a call. + auto s = RequestCall(101); + Expect(101, true); + Step(); + // Server receives the request. + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102).RecvMessage(client_message); + Expect(102, true); + Step(); + // Server sends a response with status OK. + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(103) + .RecvCloseOnServer(client_close) + .SendInitialMetadata({}) + .SendMessage("bar") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + // In principle, the server batch should complete before the client + // recv ops batch, but in the proxy fixtures, there are multiple threads + // involved, so the completion order tends to be a little racy. + Expect(103, true); + Expect(2, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(server_message.payload(), "bar"); + EXPECT_EQ(client_message.payload(), "foo"); + // Make sure the "grpc-previous-rpc-attempts" header was NOT sent, since + // we don't do that for transparent retries. + EXPECT_EQ(s.GetInitialMetadata("grpc-previous-rpc-attempts"), absl::nullopt); } -void retry_transparent_goaway_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_transparent_max_concurrent_streams.cc b/test/core/end2end/tests/retry_transparent_max_concurrent_streams.cc index e75e94865a0ff..9aa13006fe6c5 100644 --- a/test/core/end2end/tests/retry_transparent_max_concurrent_streams.cc +++ b/test/core/end2end/tests/retry_transparent_max_concurrent_streams.cc @@ -14,35 +14,18 @@ // limitations under the License. // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - const grpc_core::ChannelArgs& client_args, - const grpc_core::ChannelArgs& server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(client_args, server_args); - f->InitServer(server_args); - f->InitClient(client_args); - return f; -} +namespace grpc_core { +namespace { // Tests transparent retries when the call was never sent out on the wire. // This is similar to retry_transparent_not_sent_on_wire, except that // instead of simulating the response with a filter, we actually have @@ -52,273 +35,97 @@ static std::unique_ptr begin_test( // Then, before the first call finishes, the server is shut down and // restarted. The second call will fail in that transport instance and // will be transparently retried after the server starts up again. -static void test_retry_transparent_max_concurrent_streams( - const CoreTestConfiguration& config) { - grpc_op ops[6]; - grpc_op* op; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - grpc_call_error error; - - auto server_args = - grpc_core::ChannelArgs().Set(GRPC_ARG_MAX_CONCURRENT_STREAMS, 1); - auto f = begin_test(config, "retry_transparent_max_concurrent_streams", - grpc_core::ChannelArgs(), server_args); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - - // Client starts a call. - grpc_call* c = grpc_channel_create_call( - f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, f->cq(), - grpc_slice_from_static_string("/service/method"), nullptr, deadline, - nullptr); - GPR_ASSERT(c); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array_init(&initial_metadata_recv); - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_status_code status; - grpc_slice details; - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - +TEST_P(RetryHttp2Test, RetryTransparentMaxConcurrentStreams) { + const auto server_args = + ChannelArgs().Set(GRPC_ARG_MAX_CONCURRENT_STREAMS, 1); + InitServer(server_args); + InitClient(ChannelArgs()); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + IncomingStatusOnClient server_status; + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); // Server should get a call. - grpc_call* s; - grpc_metadata_array request_metadata_recv; - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details call_details; - grpc_call_details_init(&call_details); - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - grpc_call_details_destroy(&call_details); - grpc_metadata_array_destroy(&request_metadata_recv); - + auto s = RequestCall(101); + Expect(101, true); + Step(); + EXPECT_EQ(s.method(), "/service/method"); // Client starts a second call. // We set wait_for_ready for this call, so that if it retries before // the server comes back up, it stays pending. - grpc_call* c2 = grpc_channel_create_call( - f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, f->cq(), - grpc_slice_from_static_string("/service/method"), nullptr, deadline, - nullptr); - GPR_ASSERT(c2); - grpc_byte_buffer* request_payload2 = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_metadata_array initial_metadata_recv2; - grpc_metadata_array_init(&initial_metadata_recv2); - grpc_byte_buffer* response_payload_recv2 = nullptr; - grpc_metadata_array trailing_metadata_recv2; - grpc_metadata_array_init(&trailing_metadata_recv2); - grpc_status_code status2; - grpc_slice details2; - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload2; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = - &initial_metadata_recv2; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv2; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv2; - op->data.recv_status_on_client.status = &status2; - op->data.recv_status_on_client.status_details = &details2; - op++; - error = grpc_call_start_batch(c2, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + auto c2 = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + IncomingStatusOnClient server_status2; + IncomingMetadata server_initial_metadata2; + IncomingMessage server_message2; + c2.NewBatch(2) + .SendInitialMetadata({}, GRPC_INITIAL_METADATA_WAIT_FOR_READY) + .SendMessage("bar") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata2) + .RecvMessage(server_message2) + .RecvStatusOnClient(server_status2); // Start server shutdown. - grpc_server_shutdown_and_notify(f->server(), f->cq(), - grpc_core::CqVerifier::tag(102)); - + ShutdownServerAndNotify(102); // Server handles the first call. - grpc_byte_buffer* request_payload_recv = nullptr; - int was_cancelled = 2; - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - op->data.send_status_from_server.status_details = &status_details; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(104), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingMessage client_message; + s.NewBatch(103).RecvMessage(client_message); + IncomingCloseOnServer client_close; + s.NewBatch(104) + .RecvCloseOnServer(client_close) + .SendInitialMetadata({}) + .SendMessage("baz") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); // Server completes first call and shutdown. // Client completes first call. - cqv.Expect(grpc_core::CqVerifier::tag(104), true); - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - + Expect(104, true); + Expect(103, true); + Expect(102, true); + Expect(1, true); + Step(); // Clean up from first call. - GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice)); - grpc_byte_buffer_destroy(request_payload_recv); - GPR_ASSERT(was_cancelled == 0); - grpc_byte_buffer_destroy(response_payload); - grpc_call_unref(s); - grpc_byte_buffer_destroy(request_payload); - grpc_metadata_array_destroy(&initial_metadata_recv); - GPR_ASSERT( - byte_buffer_eq_slice(response_payload_recv, response_payload_slice)); - grpc_byte_buffer_destroy(response_payload_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - grpc_slice_unref(details); - grpc_call_unref(c); - + EXPECT_EQ(client_message.payload(), "foo"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(server_message.payload(), "baz"); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); // Destroy server and then restart it. - f->DestroyServer(); - f->InitServer(server_args); - + InitServer(server_args); // Server should get the second call. - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(201)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(201), true); - cqv.Verify(); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - grpc_call_details_destroy(&call_details); + auto s2 = RequestCall(201); + Expect(201, true); + Step(); + EXPECT_EQ(s2.method(), "/service/method"); // Make sure the "grpc-previous-rpc-attempts" header was NOT sent, since // we don't do that for transparent retries. - for (size_t i = 0; i < request_metadata_recv.count; ++i) { - GPR_ASSERT(!grpc_slice_eq( - request_metadata_recv.metadata[i].key, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"))); - } - grpc_metadata_array_destroy(&request_metadata_recv); - + EXPECT_EQ(s2.GetInitialMetadata("grpc-previous-rpc-attempts"), absl::nullopt); // Server handles the second call. - request_payload_recv = nullptr; - was_cancelled = 2; - grpc_byte_buffer* response_payload2 = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(202), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload2; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - op->data.send_status_from_server.status_details = &status_details; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(203), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingMessage client_message2; + IncomingCloseOnServer client_close2; + s2.NewBatch(202).RecvMessage(client_message2); + s2.NewBatch(203) + .RecvCloseOnServer(client_close2) + .SendInitialMetadata({}) + .SendMessage("qux") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); // Second call completes. - cqv.Expect(grpc_core::CqVerifier::tag(203), true); - cqv.Expect(grpc_core::CqVerifier::tag(202), true); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - + Expect(203, true); + Expect(202, true); + Expect(2, true); + Step(); // Clean up from second call. - GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice)); - grpc_byte_buffer_destroy(request_payload_recv); - GPR_ASSERT(was_cancelled == 0); - grpc_byte_buffer_destroy(response_payload2); - grpc_call_unref(s); - grpc_byte_buffer_destroy(request_payload2); - grpc_metadata_array_destroy(&initial_metadata_recv2); - GPR_ASSERT( - byte_buffer_eq_slice(response_payload_recv2, response_payload_slice)); - grpc_byte_buffer_destroy(response_payload_recv2); - grpc_metadata_array_destroy(&trailing_metadata_recv2); - GPR_ASSERT(status2 == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details2, "xyz")); - grpc_slice_unref(details2); - grpc_call_unref(c2); + EXPECT_EQ(client_message2.payload(), "bar"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(server_message2.payload(), "qux"); + EXPECT_EQ(server_status2.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status2.message(), "xyz"); } - -void retry_transparent_max_concurrent_streams( - const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_transparent_max_concurrent_streams(config); -} - -void retry_transparent_max_concurrent_streams_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_transparent_not_sent_on_wire.cc b/test/core/end2end/tests/retry_transparent_not_sent_on_wire.cc index b7c1a1f77b71d..7b95bb0de973c 100644 --- a/test/core/end2end/tests/retry_transparent_not_sent_on_wire.cc +++ b/test/core/end2end/tests/retry_transparent_not_sent_on_wire.cc @@ -16,21 +16,14 @@ #include -#include -#include #include #include "absl/status/status.h" #include "absl/types/optional.h" +#include "gtest/gtest.h" -#include #include -#include -#include #include -#include -#include -#include #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" @@ -38,6 +31,7 @@ #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/config/core_configuration.h" #include "src/core/lib/gprpp/status_helper.h" +#include "src/core/lib/gprpp/time.h" #include "src/core/lib/iomgr/call_combiner.h" #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/error.h" @@ -45,184 +39,9 @@ #include "src/core/lib/surface/channel_stack_type.h" #include "src/core/lib/transport/metadata_batch.h" #include "src/core/lib/transport/transport.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Tests transparent retries when the call was never sent out on the wire. -static void test_retry_transparent_not_sent_on_wire( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - auto f = begin_test(config, "retry_transparent_not_sent_on_wire", nullptr, - nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - - // Start a batch containing send ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // Start a batch containing recv ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // Client send ops should now complete. - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - // Server should get a call. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - // Server receives the request. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - // Server sends a response with status OK. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - op->data.send_status_from_server.status_details = &status_details; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - // In principle, the server batch should complete before the client - // recv ops batch, but in the proxy fixtures, there are multiple threads - // involved, so the completion order tends to be a little racy. - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice)); - GPR_ASSERT( - byte_buffer_eq_slice(response_payload_recv, response_payload_slice)); - - // Make sure the "grpc-previous-rpc-attempts" header was NOT sent, since - // we don't do that for transparent retries. - for (size_t i = 0; i < request_metadata_recv.count; ++i) { - GPR_ASSERT(!grpc_slice_eq( - request_metadata_recv.metadata[i].key, - grpc_slice_from_static_string("grpc-previous-rpc-attempts"))); - } - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); - - grpc_call_unref(c); - grpc_call_unref(s); -} +namespace grpc_core { namespace { // A filter that, for the first 10 calls it sees, will fail all batches except @@ -258,16 +77,14 @@ class FailFirstTenCallsFilter { if (calld->fail_) { if (batch->recv_trailing_metadata) { batch->payload->recv_trailing_metadata.recv_trailing_metadata->Set( - grpc_core::GrpcStreamNetworkState(), - grpc_core::GrpcStreamNetworkState::kNotSentOnWire); + GrpcStreamNetworkState(), GrpcStreamNetworkState::kNotSentOnWire); } if (!batch->cancel_stream) { grpc_transport_stream_op_batch_finish_with_failure( batch, grpc_error_set_int( GRPC_ERROR_CREATE("FailFirstTenCallsFilter failing batch"), - grpc_core::StatusIntProperty::kRpcStatus, - GRPC_STATUS_UNAVAILABLE), + StatusIntProperty::kRpcStatus, GRPC_STATUS_UNAVAILABLE), calld->call_combiner_); return; } @@ -279,7 +96,7 @@ class FailFirstTenCallsFilter { explicit CallData(const grpc_call_element_args* args) : call_combiner_(args->call_combiner) {} - grpc_core::CallCombiner* call_combiner_; + CallCombiner* call_combiner_; bool fail_ = false; }; @@ -313,28 +130,74 @@ grpc_channel_filter FailFirstTenCallsFilter::kFilterVtable = { "FailFirstTenCallsFilter", }; -} // namespace - -void retry_transparent_not_sent_on_wire(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - grpc_core::CoreConfiguration::RunWithSpecialConfiguration( - [](grpc_core::CoreConfiguration::Builder* builder) { - grpc_core::BuildCoreConfiguration(builder); - builder->channel_init()->RegisterStage( - GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY + 1, - [](grpc_core::ChannelStackBuilder* builder) { - // Skip on proxy (which explicitly disables retries). - if (!builder->channel_args() - .GetBool(GRPC_ARG_ENABLE_RETRIES) - .value_or(true)) { - return true; - } - // Install filter. - builder->PrependFilter(&FailFirstTenCallsFilter::kFilterVtable); - return true; - }); - }, - [config] { test_retry_transparent_not_sent_on_wire(config); }); +// Tests transparent retries when the call was never sent out on the wire. +TEST_P(RetryTest, RetryTransparentNotSentOnWire) { + CoreConfiguration::RegisterBuilder([](CoreConfiguration::Builder* builder) { + builder->channel_init()->RegisterStage( + GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY + 1, + [](ChannelStackBuilder* builder) { + // Skip on proxy (which explicitly disables retries). + if (!builder->channel_args() + .GetBool(GRPC_ARG_ENABLE_RETRIES) + .value_or(true)) { + return true; + } + // Install filter. + builder->PrependFilter(&FailFirstTenCallsFilter::kFilterVtable); + return true; + }); + }); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + // Start a batch containing send ops. + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .SendCloseFromClient(); + // Start a batch containing recv ops. + IncomingStatusOnClient server_status; + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + c.NewBatch(2) + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + // Client send ops should now complete. + Expect(1, true); + Step(); + // Server should get a call. + auto s = RequestCall(101); + Expect(101, true); + Step(); + // Server receives the request. + IncomingMessage client_message; + s.NewBatch(102).RecvMessage(client_message); + Expect(102, true); + Step(); + // Server sends a response with status OK. + IncomingCloseOnServer client_close; + s.NewBatch(103) + .RecvCloseOnServer(client_close) + .SendInitialMetadata({}) + .SendMessage("bar") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + // In principle, the server batch should complete before the client + // recv ops batch, but in the proxy fixtures, there are multiple threads + // involved, so the completion order tends to be a little racy. + Expect(103, true); + Expect(2, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(client_message.payload(), "foo"); + EXPECT_EQ(server_message.payload(), "bar"); + // Make sure the "grpc-previous-rpc-attempts" header was NOT sent, since + // we don't do that for transparent retries. + EXPECT_EQ(s.GetInitialMetadata("grpc-previous-rpc-attempts"), absl::nullopt); } -void retry_transparent_not_sent_on_wire_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_unref_before_finish.cc b/test/core/end2end/tests/retry_unref_before_finish.cc index 7216ff1b2f602..f2782d8b5bba3 100644 --- a/test/core/end2end/tests/retry_unref_before_finish.cc +++ b/test/core/end2end/tests/retry_unref_before_finish.cc @@ -14,183 +14,70 @@ // limitations under the License. // -#include +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we can unref a call whose status is cached but not yet // requested by the application. This should not cause a memory leak. -static void test_retry_unref_before_finish( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_call_error error; - int was_cancelled = 2; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = - begin_test(config, "retry_unref_before_finish", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - +TEST_P(RetryTest, RetryUnrefBeforeFinish) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + auto c = + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create(); // Client starts send ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + c.NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .SendCloseFromClient(); // Client starts recv_initial_metadata and recv_message, but not // recv_trailing_metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + c.NewBatch(2) + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message); // Server gets a call and client send ops complete. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - + auto s = RequestCall(101); + Expect(1, true); + Expect(101, true); + Step(); // Server immediately sends FAILED_PRECONDITION status (not retriable). // This forces the retry filter to start a recv_trailing_metadata op // internally, since the application hasn't started it yet. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_FAILED_PRECONDITION; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_FAILED_PRECONDITION, "xyz", {}) + .RecvCloseOnServer(client_close); // Server ops complete and client recv ops complete. - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); - GPR_ASSERT(was_cancelled == 0); - - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - - // Client unrefs the call without starting recv_trailing_metadata. - // This should trigger a cancellation. - grpc_call_unref(c); - - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); -} - -void retry_unref_before_finish(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_unref_before_finish(config); + Expect(2, true); + Expect(102, true); + Step(); + EXPECT_EQ(s.method(), "/service/method"); + EXPECT_FALSE(client_close.was_cancelled()); } - -void retry_unref_before_finish_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/retry_unref_before_recv.cc b/test/core/end2end/tests/retry_unref_before_recv.cc index 7daacec288d74..0251c3a816d93 100644 --- a/test/core/end2end/tests/retry_unref_before_recv.cc +++ b/test/core/end2end/tests/retry_unref_before_recv.cc @@ -14,184 +14,81 @@ // limitations under the License. // -#include +#include "absl/types/optional.h" +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gpr/useful.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Tests that we can unref a call while recv ops are started but before // they complete. This ensures that we don't drop callbacks or cause a // memory leak. -static void test_retry_unref_before_recv(const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_slice request_payload_slice = grpc_slice_from_static_string("foo"); - grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_call_error error; - int was_cancelled = 2; - - grpc_arg args[] = { - grpc_channel_arg_string_create( - const_cast(GRPC_ARG_SERVICE_CONFIG), - const_cast( - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}")), - }; - grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args}; - auto f = begin_test(config, "retry_unref_before_recv", &client_args, nullptr); - - grpc_core::CqVerifier cqv(f->cq()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), - grpc_slice_from_static_string("/service/method"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - grpc_slice status_details = grpc_slice_from_static_string("xyz"); +TEST_P(RetryTest, UnrefBeforeRecv) { + InitServer(ChannelArgs()); + InitClient(ChannelArgs().Set( + GRPC_ARG_SERVICE_CONFIG, + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}")); + absl::optional c{ + NewClientCall("/service/method").Timeout(Duration::Seconds(5)).Create()}; // Client starts send ops. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + c->NewBatch(1) + .SendInitialMetadata({}) + .SendMessage("foo") + .SendCloseFromClient(); // Client starts recv_initial_metadata and recv_message, but not // recv_trailing_metadata. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingMetadata server_initial_metadata; + IncomingMessage server_message; + c->NewBatch(2) + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message); // Server gets a call and client send ops complete. - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - + auto s = RequestCall(101); + Expect(1, true); + Expect(101, true); + Step(); // Client unrefs the call without starting recv_trailing_metadata. // This should trigger a cancellation. - grpc_call_unref(c); - + c.reset(); // Server immediately sends FAILED_PRECONDITION status (not retriable). // This forces the retry filter to start a recv_trailing_metadata op // internally, since the application hasn't started it yet. - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_FAILED_PRECONDITION; - op->data.send_status_from_server.status_details = &status_details; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_FAILED_PRECONDITION, "xyz", {}) + .RecvCloseOnServer(client_close); // Server ops complete and client recv ops complete. - cqv.Expect(grpc_core::CqVerifier::tag(2), false); // Failure! - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); + Expect(2, false); // Failure! + Expect(102, true); + Step(); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); + EXPECT_EQ(s.method(), "/service/method"); // Note: Not checking the value of was_cancelled here, because it will // be flaky, depending on whether the server sent its response before // the client sent its cancellation. - - grpc_call_unref(s); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_call_details_init(&call_details); - - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); -} - -void retry_unref_before_recv(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); - test_retry_unref_before_recv(config); } -void retry_unref_before_recv_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/server_finishes_request.cc b/test/core/end2end/tests/server_finishes_request.cc index d12a73294482c..b0568071418b9 100644 --- a/test/core/end2end/tests/server_finishes_request.cc +++ b/test/core/end2end/tests/server_finishes_request.cc @@ -16,141 +16,46 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static void simple_request_body(const CoreTestConfiguration& /*config*/, - CoreTestFixture* f) { - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); +namespace grpc_core { +namespace { - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); +TEST_P(CoreEnd2endTest, ServerFinishesRequest) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); + auto s = RequestCall(101); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); + Expect(101, true); + Step(); - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}) + .RecvCloseOnServer(client_close); - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -static void test_invoke_simple_request(const CoreTestConfiguration& config) { - auto f = begin_test(config, "test_invoke_simple_request", nullptr, nullptr); - simple_request_body(config, f.get()); -} + Expect(102, true); + Expect(1, true); + Step(); -void server_finishes_request(const CoreTestConfiguration& config) { - test_invoke_simple_request(config); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); } -void server_finishes_request_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/server_streaming.cc b/test/core/end2end/tests/server_streaming.cc index 80e1e8a6d25b5..7367cc5b457aa 100644 --- a/test/core/end2end/tests/server_streaming.cc +++ b/test/core/end2end/tests/server_streaming.cc @@ -16,228 +16,86 @@ // // -#include +#include "gtest/gtest.h" -#include -#include -#include - -#include -#include -#include -#include #include #include -#include -#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args, - int num_messages) { - gpr_log(GPR_INFO, "%s\nRunning test: %s/%s/%d", std::string(100, '*').c_str(), - test_name, config.name, num_messages); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Client requests status along with the initial metadata. Server streams // messages and ends with a non-OK status. Client reads after server is done // writing, and expects to get the status after the messages. -static void test_server_streaming(const CoreTestConfiguration& config, - int num_messages) { - auto f = begin_test(config, "test_server_streaming", nullptr, nullptr, - num_messages); - grpc_call* c; - grpc_call* s; - auto cqv = std::make_unique(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - grpc_byte_buffer* request_payload_recv; - grpc_byte_buffer* response_payload; - grpc_slice response_payload_slice = - grpc_slice_from_copied_string("hello world"); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - // Client requests status early but should not receive status till all the - // messages are received. - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - +void ServerStreaming(CoreEnd2endTest& test, int num_messages) { + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .RecvInitialMetadata(server_initial_metadata) + // Client requests status early but should not receive status till all the + // messages are received. + .RecvStatusOnClient(server_status); // Client sends close early - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv->Expect(grpc_core::CqVerifier::tag(3), true); - cqv->Verify(); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(100)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv->Expect(grpc_core::CqVerifier::tag(100), true); - cqv->Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(101), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv->Expect(grpc_core::CqVerifier::tag(101), true); - cqv->Verify(); - + c.NewBatch(3).SendCloseFromClient(); + test.Expect(3, true); + test.Step(); + auto s = test.RequestCall(100); + test.Expect(100, true); + test.Step(); + s.NewBatch(101).SendInitialMetadata({}); + test.Expect(101, true); + test.Step(); // Server writes bunch of messages for (int i = 0; i < num_messages; i++) { - response_payload = grpc_raw_byte_buffer_create(&response_payload_slice, 1); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv->Expect(grpc_core::CqVerifier::tag(103), true); - cqv->Verify(); - - grpc_byte_buffer_destroy(response_payload); + s.NewBatch(103).SendMessage("hello world"); + test.Expect(103, true); + test.Step(); } - // Server sends status - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(104), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(104) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); bool seen_status = false; - cqv->Expect(grpc_core::CqVerifier::tag(1), - grpc_core::CqVerifier::Maybe{&seen_status}); - cqv->Expect(grpc_core::CqVerifier::tag(104), true); - cqv->Verify(); + test.Expect(1, CoreEnd2endTest::Maybe{&seen_status}); + test.Expect(104, true); + test.Step(); // Client keeps reading messages till it gets the status int num_messages_received = 0; while (true) { - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv->Expect(grpc_core::CqVerifier::tag(1), - grpc_core::CqVerifier::Maybe{&seen_status}); - cqv->Expect(grpc_core::CqVerifier::tag(102), true); - cqv->Verify(); - if (request_payload_recv == nullptr) { + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(102).RecvMessage(server_message); + test.Expect(1, CqVerifier::Maybe{&seen_status}); + test.Expect(102, true); + test.Step(); + if (server_message.is_end_of_stream()) { // The transport has received the trailing metadata. break; } - GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world")); - grpc_byte_buffer_destroy(request_payload_recv); + EXPECT_EQ(server_message.payload(), "hello world"); num_messages_received++; } GPR_ASSERT(num_messages_received == num_messages); if (!seen_status) { - cqv->Expect(grpc_core::CqVerifier::tag(1), true); - cqv->Verify(); + test.Expect(1, true); + test.Step(); } - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - - grpc_slice_unref(response_payload_slice); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); +} - grpc_call_unref(c); - grpc_call_unref(s); +TEST_P(Http2Test, ServerStreaming) { ServerStreaming(*this, 1); } - cqv.reset(); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - grpc_slice_unref(details); -} +TEST_P(Http2Test, ServerStreamingEmptyStream) { ServerStreaming(*this, 0); } -void server_streaming(const CoreTestConfiguration& config) { - test_server_streaming(config, 0); - test_server_streaming(config, 1); - test_server_streaming(config, 10); -} +TEST_P(Http2Test, ServerStreaming10Messages) { ServerStreaming(*this, 10); } -void server_streaming_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/shutdown_finishes_calls.cc b/test/core/end2end/tests/shutdown_finishes_calls.cc index 4d3e80a50b426..eb06c9156843f 100644 --- a/test/core/end2end/tests/shutdown_finishes_calls.cc +++ b/test/core/end2end/tests/shutdown_finishes_calls.cc @@ -16,109 +16,32 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include #include -#include #include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static void test_early_server_shutdown_finishes_inflight_calls( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - auto f = - begin_test(config, "test_early_server_shutdown_finishes_inflight_calls", - nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->data.send_initial_metadata.metadata = nullptr; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); +namespace grpc_core { +namespace { + +TEST_P(CoreEnd2endTest, EarlyServerShutdownFinishesInflightCalls) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102).RecvCloseOnServer(client_close); // Make sure we don't shutdown the server while HTTP/2 PING frames are still // being exchanged on the newly established connection. It can lead to @@ -128,33 +51,20 @@ static void test_early_server_shutdown_finishes_inflight_calls( gpr_sleep_until(grpc_timeout_seconds_to_deadline(1)); // shutdown and destroy the server - grpc_server_shutdown_and_notify(f->server(), f->cq(), - grpc_core::CqVerifier::tag(1000)); - grpc_server_cancel_all_calls(f->server()); - - cqv.Expect(grpc_core::CqVerifier::tag(1000), true); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); + ShutdownServerAndNotify(1000); + CancelAllCallsOnServer(); - f->DestroyServer(); + Expect(1000, true); + Expect(102, true); + Expect(1, true); + Step(); - GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 1); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); -} + DestroyServer(); -void shutdown_finishes_calls(const CoreTestConfiguration& config) { - test_early_server_shutdown_finishes_inflight_calls(config); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNAVAILABLE); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_TRUE(client_close.was_cancelled()); } -void shutdown_finishes_calls_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/shutdown_finishes_tags.cc b/test/core/end2end/tests/shutdown_finishes_tags.cc index f774759978b81..c9781e36b01dc 100644 --- a/test/core/end2end/tests/shutdown_finishes_tags.cc +++ b/test/core/end2end/tests/shutdown_finishes_tags.cc @@ -16,56 +16,23 @@ // // -#include -#include +#include "gtest/gtest.h" -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" #include "test/core/end2end/end2end_tests.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -static void test_early_server_shutdown_finishes_tags( - const CoreTestConfiguration& config) { - auto f = begin_test(config, "test_early_server_shutdown_finishes_tags", - nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_call* s = reinterpret_cast(1); - grpc_call_details call_details; - grpc_metadata_array request_metadata_recv; - - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); +namespace grpc_core { +namespace { +TEST_P(CoreEnd2endTest, ShutdownFinishesTags) { // upon shutdown, the server should finish all requested calls indicating // no new call - GPR_ASSERT(GRPC_CALL_OK == - grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101))); - f->ShutdownClient(); - grpc_server_shutdown_and_notify(f->server(), f->cq(), - grpc_core::CqVerifier::tag(1000)); - cqv.Expect(grpc_core::CqVerifier::tag(101), false); - cqv.Expect(grpc_core::CqVerifier::tag(1000), true); - cqv.Verify(); - GPR_ASSERT(s == nullptr); -} - -void shutdown_finishes_tags(const CoreTestConfiguration& config) { - test_early_server_shutdown_finishes_tags(config); + auto s = RequestCall(101); + ShutdownAndDestroyClient(); + ShutdownServerAndNotify(1000); + Expect(101, false); + Expect(1000, true); + Step(); } -void shutdown_finishes_tags_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/simple_delayed_request.cc b/test/core/end2end/tests/simple_delayed_request.cc index a5e307c6ffc9d..3e1719df50bf5 100644 --- a/test/core/end2end/tests/simple_delayed_request.cc +++ b/test/core/end2end/tests/simple_delayed_request.cc @@ -16,164 +16,48 @@ // // -#include - -#include -#include +#include "gtest/gtest.h" #include -#include -#include #include -#include -#include #include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static void simple_delayed_request_body(CoreTestFixture* f) { - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - f->InitClient(grpc_core::ChannelArgs()); - f->InitServer(grpc_core::ChannelArgs()); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); -} - -static void test_simple_delayed_request_short( - const CoreTestConfiguration& config) { - auto client_args = grpc_core::ChannelArgs() - .Set(GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS, 1000) - .Set(GRPC_ARG_MAX_RECONNECT_BACKOFF_MS, 1000) - .Set(GRPC_ARG_MIN_RECONNECT_BACKOFF_MS, 5000) - .ToC(); - gpr_log(GPR_INFO, "Running test: %s/%s", "test_simple_delayed_request_short", - config.name); - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - - simple_delayed_request_body(f.get()); -} - -static void test_simple_delayed_request_long( - const CoreTestConfiguration& config) { - auto client_args = grpc_core::ChannelArgs() - .Set(GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS, 1000) - .Set(GRPC_ARG_MAX_RECONNECT_BACKOFF_MS, 1000) - .Set(GRPC_ARG_MIN_RECONNECT_BACKOFF_MS, 5000) - .ToC(); - - gpr_log(GPR_INFO, "Running test: %s/%s", "test_simple_delayed_request_long", - config.name); - auto f = - config.create_fixture(grpc_core::ChannelArgs(), grpc_core::ChannelArgs()); - // This timeout should be longer than a single retry - simple_delayed_request_body(f.get()); -} -void simple_delayed_request(const CoreTestConfiguration& config) { - GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION); - test_simple_delayed_request_short(config); - test_simple_delayed_request_long(config); +namespace grpc_core { +namespace { + +TEST_P(CoreClientChannelTest, SimpleDelayedRequestShort) { + InitClient(ChannelArgs() + .Set(GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS, 1000) + .Set(GRPC_ARG_MAX_RECONNECT_BACKOFF_MS, 1000) + .Set(GRPC_ARG_MIN_RECONNECT_BACKOFF_MS, 5000)); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + IncomingMetadata server_initial_metadata; + IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}, GRPC_INITIAL_METADATA_WAIT_FOR_READY) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + InitServer(ChannelArgs()); + auto s = RequestCall(101); + Expect(101, true); + Step(); + IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + Expect(102, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); } -void simple_delayed_request_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/simple_metadata.cc b/test/core/end2end/tests/simple_metadata.cc index 79c3da0d39129..4574d7c7caa73 100644 --- a/test/core/end2end/tests/simple_metadata.cc +++ b/test/core/end2end/tests/simple_metadata.cc @@ -16,208 +16,59 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Request/response with metadata and payload. -static void test_request_response_with_metadata_and_payload( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_slice response_payload_slice = - grpc_slice_from_copied_string("hello you"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_metadata meta_c[2] = {{grpc_slice_from_static_string("key1"), - grpc_slice_from_static_string("val1"), - {{nullptr, nullptr, nullptr, nullptr}}}, - {grpc_slice_from_static_string("key2"), - grpc_slice_from_static_string("val2"), - {{nullptr, nullptr, nullptr, nullptr}}}}; - grpc_metadata meta_s[2] = {{grpc_slice_from_static_string("key3"), - grpc_slice_from_static_string("val3"), - {{nullptr, nullptr, nullptr, nullptr}}}, - {grpc_slice_from_static_string("key4"), - grpc_slice_from_static_string("val4"), - {{nullptr, nullptr, nullptr, nullptr}}}}; - auto f = begin_test(config, "test_request_response_with_metadata_and_payload", - nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 2; - op->data.send_initial_metadata.metadata = meta_c; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 2; - op->data.send_initial_metadata.metadata = meta_s; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world")); - GPR_ASSERT(byte_buffer_eq_string(response_payload_recv, "hello you")); - GPR_ASSERT(contains_metadata(&request_metadata_recv, "key1", "val1")); - GPR_ASSERT(contains_metadata(&request_metadata_recv, "key2", "val2")); - GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key3", "val3")); - GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key4", "val4")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); -} -void simple_metadata(const CoreTestConfiguration& config) { - test_request_response_with_metadata_and_payload(config); +namespace grpc_core { +namespace { + +TEST_P(CoreEnd2endTest, SimpleMetadata) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({{"key1", "val1"}, {"key2", "val2"}}) + .SendMessage("hello world") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102) + .SendInitialMetadata({{"key3", "val3"}, {"key4", "val4"}}) + .RecvMessage(client_message); + Expect(102, true); + Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(103) + .RecvCloseOnServer(client_close) + .SendMessage("hello you") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", + {{"key5", "val5"}, {"key6", "val6"}}); + Expect(103, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(server_message.payload(), "hello you"); + EXPECT_EQ(client_message.payload(), "hello world"); + EXPECT_EQ(s.GetInitialMetadata("key1"), "val1"); + EXPECT_EQ(s.GetInitialMetadata("key2"), "val2"); + EXPECT_EQ(server_initial_metadata.Get("key3"), "val3"); + EXPECT_EQ(server_initial_metadata.Get("key4"), "val4"); + EXPECT_EQ(server_status.GetTrailingMetadata("key5"), "val5"); + EXPECT_EQ(server_status.GetTrailingMetadata("key6"), "val6"); } -void simple_metadata_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/simple_request.cc b/test/core/end2end/tests/simple_request.cc index 2a236ceede13c..501cf397e687a 100644 --- a/test/core/end2end/tests/simple_request.cc +++ b/test/core/end2end/tests/simple_request.cc @@ -17,218 +17,93 @@ // #include -#include #include -#include #include #include -#include -#include -#include +#include "absl/strings/match.h" +#include "absl/types/optional.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + #include -#include #include -#include -#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/debug/stats.h" #include "src/core/lib/debug/stats_data.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "%s", std::string(100, '*').c_str()); - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +using testing::HasSubstr; +using testing::StartsWith; -static void check_peer(char* peer_name) { +namespace grpc_core { +namespace { +void CheckPeer(std::string peer_name) { // If the peer name is a uds path, then check if it is filled - if (strncmp(peer_name, "unix:/", strlen("unix:/")) == 0) { - GPR_ASSERT(strncmp(peer_name, "unix:/tmp/grpc_fullstack_test.", - strlen("unix:/tmp/grpc_fullstack_test.")) == 0); + if (absl::StartsWith(peer_name, "unix:/")) { + EXPECT_THAT(peer_name, StartsWith("unix:/tmp/grpc_fullstack_test.")); } } -static void simple_request_body(const CoreTestConfiguration& config, - CoreTestFixture* f) { - grpc_call* c; - grpc_call* s; - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_call_details call_details; - grpc_status_code status; - const char* error_string; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - char* peer; - - auto before = grpc_core::global_stats().Collect(); - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); - gpr_free(peer); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->data.recv_status_on_client.error_string = &error_string; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - peer = grpc_call_get_peer(s); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "server_peer=%s", peer); - check_peer(peer); - gpr_free(peer); - peer = grpc_call_get_peer(c); - GPR_ASSERT(peer != nullptr); - gpr_log(GPR_DEBUG, "client_peer=%s", peer); - check_peer(peer); - gpr_free(peer); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); +void SimpleRequestBody(CoreEnd2endTest& test) { + auto before = global_stats().Collect(); + auto c = test.NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + EXPECT_NE(c.GetPeer(), absl::nullopt); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = test.RequestCall(101); + test.Expect(101, true); + test.Step(); + EXPECT_NE(s.GetPeer(), absl::nullopt); + CheckPeer(*s.GetPeer()); + EXPECT_NE(c.GetPeer(), absl::nullopt); + CheckPeer(*c.GetPeer()); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(102) + .SendInitialMetadata({}) + .SendStatusFromServer(GRPC_STATUS_UNIMPLEMENTED, "xyz", {}) + .RecvCloseOnServer(client_close); + test.Expect(102, true); + test.Expect(1, true); + test.Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_UNIMPLEMENTED); + EXPECT_EQ(server_status.message(), "xyz"); // the following sanity check makes sure that the requested error string is // correctly populated by the core. It looks for certain substrings that are // not likely to change much. Some parts of the error, like time created, // obviously are not checked. - GPR_ASSERT(nullptr != strstr(error_string, "xyz")); - GPR_ASSERT(nullptr != strstr(error_string, "Error received from peer")); - GPR_ASSERT(nullptr != strstr(error_string, "grpc_message")); - GPR_ASSERT(nullptr != strstr(error_string, "grpc_status")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - - grpc_slice_unref(details); - gpr_free(const_cast(error_string)); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); - + EXPECT_THAT(server_status.error_string(), HasSubstr("xyz")); + EXPECT_THAT(server_status.error_string(), + HasSubstr("Error received from peer")); + EXPECT_THAT(server_status.error_string(), HasSubstr("grpc_message")); + EXPECT_THAT(server_status.error_string(), HasSubstr("grpc_status")); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); uint64_t expected_calls = 1; - if (config.feature_mask & FEATURE_MASK_SUPPORTS_REQUEST_PROXYING) { + if (test.GetParam()->feature_mask & FEATURE_MASK_SUPPORTS_REQUEST_PROXYING) { expected_calls *= 2; } - - auto after = grpc_core::global_stats().Collect(); - - gpr_log(GPR_DEBUG, "%s", grpc_core::StatsAsJson(after.get()).c_str()); - - GPR_ASSERT(after->client_calls_created - before->client_calls_created == - expected_calls); - GPR_ASSERT(after->server_calls_created - before->server_calls_created == - expected_calls); + auto after = global_stats().Collect(); + gpr_log(GPR_DEBUG, "%s", StatsAsJson(after.get()).c_str()); + EXPECT_EQ(after->client_calls_created - before->client_calls_created, + expected_calls); + EXPECT_EQ(after->server_calls_created - before->server_calls_created, + expected_calls); } -static void test_invoke_simple_request(const CoreTestConfiguration& config) { - auto f = begin_test(config, "test_invoke_simple_request", nullptr, nullptr); - simple_request_body(config, f.get()); -} +TEST_P(CoreEnd2endTest, SimpleRequest) { SimpleRequestBody(*this); } -static void test_invoke_10_simple_requests( - const CoreTestConfiguration& config) { - int i; - auto f = - begin_test(config, "test_invoke_10_simple_requests", nullptr, nullptr); - for (i = 0; i < 10; i++) { - simple_request_body(config, f.get()); - gpr_log(GPR_INFO, "Running test: Passed simple request %d", i); +TEST_P(CoreEnd2endTest, SimpleRequest10) { + for (int i = 0; i < 10; i++) { + SimpleRequestBody(*this); } } - -void simple_request(const CoreTestConfiguration& config) { - int i; - for (i = 0; i < 10; i++) { - test_invoke_simple_request(config); - } - test_invoke_10_simple_requests(config); -} - -void simple_request_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/streaming_error_response.cc b/test/core/end2end/tests/streaming_error_response.cc index 82f9a380801f3..347eede099dda 100644 --- a/test/core/end2end/tests/streaming_error_response.cc +++ b/test/core/end2end/tests/streaming_error_response.cc @@ -19,234 +19,135 @@ /// \file Verify that status ordering rules are obeyed. /// \ref doc/status_ordering.md -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args, - bool request_status_early) { - gpr_log(GPR_INFO, "Running test: %s/%s/request_status_early=%s", test_name, - config.name, request_status_early ? "true" : "false"); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { +namespace { // Client sends a request with payload, potentially requesting status early. The // server reads and streams responses. The client cancels the RPC to get an // error status. (Server sending a non-OK status is not considered an error // status.) -static void test(const CoreTestConfiguration& config, bool request_status_early, - bool recv_message_separately) { - grpc_call* c; - grpc_call* s; - grpc_slice response_payload1_slice = grpc_slice_from_copied_string("hello"); - grpc_byte_buffer* response_payload1 = - grpc_raw_byte_buffer_create(&response_payload1_slice, 1); - grpc_slice response_payload2_slice = grpc_slice_from_copied_string("world"); - grpc_byte_buffer* response_payload2 = - grpc_raw_byte_buffer_create(&response_payload2_slice, 1); - auto f = begin_test(config, "streaming_error_response", nullptr, nullptr, - request_status_early); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* response_payload1_recv = nullptr; - grpc_byte_buffer* response_payload2_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status = GRPC_STATUS_OK; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - GPR_ASSERT(!recv_message_separately || request_status_early); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op++; - if (!recv_message_separately) { - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload1_recv; - op++; - } - if (request_status_early) { - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - } - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - GPR_ASSERT(GRPC_CALL_OK == - grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101))); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload1; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - if (recv_message_separately) { - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload1_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(4), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - } - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - if (!request_status_early) { - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - } - if (recv_message_separately) { - cqv.Expect(grpc_core::CqVerifier::tag(4), true); - } - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload2; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); +TEST_P(CoreEnd2endTest, StreamingErrorResponse) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage response_payload1_recv; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(response_payload1_recv); + auto s = RequestCall(101); + Expect(101, true); + Step(); + s.NewBatch(102).SendInitialMetadata({}).SendMessage("hello"); + Expect(102, true); + Expect(1, true); + Step(); + s.NewBatch(103).SendMessage("world"); // The success of the op depends on whether the payload is written before the // transport sees the end of stream. If the stream has been write closed // before the write completes, it would fail, otherwise it would succeed. // Since this behavior is dependent on the transport implementation, we allow // any success status with this op. - cqv.Expect(grpc_core::CqVerifier::tag(103), - grpc_core::CqVerifier::AnyStatus()); - - if (!request_status_early) { - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload2_recv; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Verify(); - - GPR_ASSERT(response_payload2_recv != nullptr); - } - + Expect(103, AnyStatus()); + CoreEnd2endTest::IncomingMessage response_payload2_recv; + c.NewBatch(2).RecvMessage(response_payload2_recv); + Expect(2, true); + Step(); + EXPECT_FALSE(response_payload2_recv.is_end_of_stream()); // Cancel the call so that the client sets up an error status. - grpc_call_cancel(c, nullptr); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(104), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(104), true); - if (request_status_early) { - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - } - cqv.Verify(); - - if (!request_status_early) { - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Verify(); - - GPR_ASSERT(response_payload1_recv != nullptr); - GPR_ASSERT(response_payload2_recv != nullptr); - } - - GPR_ASSERT(status == GRPC_STATUS_CANCELLED); - GPR_ASSERT(was_cancelled == 1); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); + c.Cancel(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(104).RecvCloseOnServer(client_close); + Expect(104, true); + Step(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(3).RecvStatusOnClient(server_status); + Expect(3, true); + Step(); + EXPECT_FALSE(response_payload1_recv.is_end_of_stream()); + EXPECT_FALSE(response_payload2_recv.is_end_of_stream()); + EXPECT_EQ(server_status.status(), GRPC_STATUS_CANCELLED); + EXPECT_TRUE(client_close.was_cancelled()); +} - grpc_byte_buffer_destroy(response_payload1); - grpc_byte_buffer_destroy(response_payload2); - grpc_byte_buffer_destroy(response_payload1_recv); - grpc_byte_buffer_destroy(response_payload2_recv); +TEST_P(CoreEnd2endTest, StreamingErrorResponseRequestStatusEarly) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage response_payload1_recv; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(response_payload1_recv) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + s.NewBatch(102).SendInitialMetadata({}).SendMessage("hello"); + Expect(102, true); + Step(); + s.NewBatch(103).SendMessage("world"); + // The success of the op depends on whether the payload is written before the + // transport sees the end of stream. If the stream has been write closed + // before the write completes, it would fail, otherwise it would succeed. + // Since this behavior is dependent on the transport implementation, we allow + // any success status with this op. + Expect(103, AnyStatus()); + // Cancel the call so that the client sets up an error status. + c.Cancel(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(104).RecvCloseOnServer(client_close); + Expect(104, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_CANCELLED); + EXPECT_TRUE(client_close.was_cancelled()); } -void streaming_error_response(const CoreTestConfiguration& config) { - test(config, false, false); - test(config, true, false); - test(config, true, true); +TEST_P(CoreEnd2endTest, + StreamingErrorResponseRequestStatusEarlyAndRecvMessageSeparately) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(1) + .SendInitialMetadata({}) + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + s.NewBatch(102).SendInitialMetadata({}).SendMessage("hello"); + CoreEnd2endTest::IncomingMessage response_payload1_recv; + c.NewBatch(4).RecvMessage(response_payload1_recv); + Expect(102, true); + Expect(4, true); + Step(); + s.NewBatch(103).SendMessage("world"); + // The success of the op depends on whether the payload is written before the + // transport sees the end of stream. If the stream has been write closed + // before the write completes, it would fail, otherwise it would succeed. + // Since this behavior is dependent on the transport implementation, we allow + // any success status with this op. + Expect(103, AnyStatus()); + // Cancel the call so that the client sets up an error status. + c.Cancel(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(104).RecvCloseOnServer(client_close); + Expect(104, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_CANCELLED); + EXPECT_TRUE(client_close.was_cancelled()); } -void streaming_error_response_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/trailing_metadata.cc b/test/core/end2end/tests/trailing_metadata.cc index 55a1e0652c64b..16b942cb98b4d 100644 --- a/test/core/end2end/tests/trailing_metadata.cc +++ b/test/core/end2end/tests/trailing_metadata.cc @@ -16,216 +16,57 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include -#include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Request/response with metadata and payload. -static void test_request_response_with_metadata_and_payload( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_slice response_payload_slice = - grpc_slice_from_copied_string("hello you"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_byte_buffer* response_payload = - grpc_raw_byte_buffer_create(&response_payload_slice, 1); - grpc_metadata meta_c[2] = {{grpc_slice_from_static_string("key1"), - grpc_slice_from_static_string("val1"), - {{nullptr, nullptr, nullptr, nullptr}}}, - {grpc_slice_from_static_string("key2"), - grpc_slice_from_static_string("val2"), - {{nullptr, nullptr, nullptr, nullptr}}}}; - grpc_metadata meta_s[2] = {{grpc_slice_from_static_string("key3"), - grpc_slice_from_static_string("val3"), - {{nullptr, nullptr, nullptr, nullptr}}}, - {grpc_slice_from_static_string("key4"), - grpc_slice_from_static_string("val4"), - {{nullptr, nullptr, nullptr, nullptr}}}}; - grpc_metadata meta_t[2] = {{grpc_slice_from_static_string("key5"), - grpc_slice_from_static_string("val5"), - {{nullptr, nullptr, nullptr, nullptr}}}, - {grpc_slice_from_static_string("key6"), - grpc_slice_from_static_string("val6"), - {{nullptr, nullptr, nullptr, nullptr}}}}; - auto f = begin_test(config, "test_request_response_with_metadata_and_payload", - nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv = nullptr; - grpc_byte_buffer* response_payload_recv = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details; - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 2; - op->data.send_initial_metadata.metadata = meta_c; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &response_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - error = grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101)); - GPR_ASSERT(GRPC_CALL_OK == error); - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 2; - op->data.send_initial_metadata.metadata = meta_s; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = response_payload; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 2; - op->data.send_status_from_server.trailing_metadata = meta_t; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world")); - GPR_ASSERT(byte_buffer_eq_string(response_payload_recv, "hello you")); - GPR_ASSERT(contains_metadata(&request_metadata_recv, "key1", "val1")); - GPR_ASSERT(contains_metadata(&request_metadata_recv, "key2", "val2")); - GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key3", "val3")); - GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key4", "val4")); - GPR_ASSERT(contains_metadata(&trailing_metadata_recv, "key5", "val5")); - GPR_ASSERT(contains_metadata(&trailing_metadata_recv, "key6", "val6")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(response_payload); - grpc_byte_buffer_destroy(request_payload_recv); - grpc_byte_buffer_destroy(response_payload_recv); -} -void trailing_metadata(const CoreTestConfiguration& config) { - test_request_response_with_metadata_and_payload(config); +namespace grpc_core { +namespace { + +TEST_P(CoreEnd2endTest, TrailingMetadata) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + CoreEnd2endTest::IncomingStatusOnClient server_status; + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + CoreEnd2endTest::IncomingMessage server_message; + c.NewBatch(1) + .SendInitialMetadata({{"key1", "val1"}, {"key2", "val2"}}) + .SendMessage("hello world") + .SendCloseFromClient() + .RecvInitialMetadata(server_initial_metadata) + .RecvMessage(server_message) + .RecvStatusOnClient(server_status); + auto s = RequestCall(101); + Expect(101, true); + Step(); + CoreEnd2endTest::IncomingMessage client_message; + s.NewBatch(102) + .SendInitialMetadata({{"key3", "val3"}, {"key4", "val4"}}) + .RecvMessage(client_message); + Expect(102, true); + Step(); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(103) + .RecvCloseOnServer(client_close) + .SendMessage("hello you") + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", + {{"key5", "val5"}, {"key6", "val6"}}); + Expect(103, true); + Expect(1, true); + Step(); + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(client_message.payload(), "hello world"); + EXPECT_EQ(server_message.payload(), "hello you"); + EXPECT_EQ(s.GetInitialMetadata("key1"), "val1"); + EXPECT_EQ(s.GetInitialMetadata("key2"), "val2"); + EXPECT_EQ(server_initial_metadata.Get("key3"), "val3"); + EXPECT_EQ(server_initial_metadata.Get("key4"), "val4"); + EXPECT_EQ(server_status.GetTrailingMetadata("key5"), "val5"); + EXPECT_EQ(server_status.GetTrailingMetadata("key6"), "val6"); } -void trailing_metadata_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/end2end/tests/write_buffering.cc b/test/core/end2end/tests/write_buffering.cc index 9dde20ba137fd..c6cdc8cf117a2 100644 --- a/test/core/end2end/tests/write_buffering.cc +++ b/test/core/end2end/tests/write_buffering.cc @@ -16,228 +16,66 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} +namespace grpc_core { // Client sends a request with payload, server reads then returns status. -static void test_invoke_request_with_payload( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_slice request_payload_slice1 = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload1 = - grpc_raw_byte_buffer_create(&request_payload_slice1, 1); - grpc_slice request_payload_slice2 = grpc_slice_from_copied_string("abc123"); - grpc_byte_buffer* request_payload2 = - grpc_raw_byte_buffer_create(&request_payload_slice2, 1); - auto f = - begin_test(config, "test_invoke_request_with_payload", nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv1 = nullptr; - grpc_byte_buffer* request_payload_recv2 = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details = grpc_empty_slice(); - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - GPR_ASSERT(GRPC_CALL_OK == - grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101))); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); // send message is buffered - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload1; - op->flags = GRPC_WRITE_BUFFER_HINT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); +TEST_P(WriteBufferingTest, WriteBufferingWorks) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + c.NewBatch(1).SendInitialMetadata({}); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(2).RecvInitialMetadata(server_initial_metadata); + auto s = RequestCall(101); + Expect(1, true); // send message is buffered + Expect(101, true); + Step(); + c.NewBatch(3).SendMessage("hello world", GRPC_WRITE_BUFFER_HINT); + s.NewBatch(102).SendInitialMetadata({}); // recv message should not succeed yet - it's buffered at the client still - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv1; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); + CoreEnd2endTest::IncomingMessage request_payload_recv1; + s.NewBatch(103).RecvMessage(request_payload_recv1); + Expect(2, true); + Expect(3, true); + Expect(102, true); + Step(); // send another message, this time not buffered - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload2; - op->flags = 0; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(4), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + c.NewBatch(4).SendMessage("abc123"); // now the first send should match up with the first recv - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(4), true); - cqv.Verify(); + Expect(103, true); + Expect(4, true); + Step(); // and the next recv should be ready immediately also - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv2; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(104), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(104), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(4), nullptr); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(105), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(105), true); - cqv.Expect(grpc_core::CqVerifier::tag(4), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - GPR_ASSERT(byte_buffer_eq_string(request_payload_recv1, "hello world")); - GPR_ASSERT(byte_buffer_eq_string(request_payload_recv2, "abc123")); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_byte_buffer_destroy(request_payload1); - grpc_byte_buffer_destroy(request_payload_recv1); - grpc_byte_buffer_destroy(request_payload2); - grpc_byte_buffer_destroy(request_payload_recv2); + CoreEnd2endTest::IncomingMessage request_payload_recv2; + s.NewBatch(104).RecvMessage(request_payload_recv2); + Expect(104, true); + Step(); + + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(5).SendCloseFromClient().RecvStatusOnClient(server_status); + + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(105) + .RecvCloseOnServer(client_close) + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + + Expect(105, true); + Expect(5, true); + Step(); + + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(request_payload_recv1.payload(), "hello world"); + EXPECT_EQ(request_payload_recv2.payload(), "abc123"); } - -void write_buffering(const CoreTestConfiguration& config) { - test_invoke_request_with_payload(config); -} - -void write_buffering_pre_init(void) {} +} // namespace grpc_core diff --git a/test/core/end2end/tests/write_buffering_at_end.cc b/test/core/end2end/tests/write_buffering_at_end.cc index db4a1439a0886..354eee04dd284 100644 --- a/test/core/end2end/tests/write_buffering_at_end.cc +++ b/test/core/end2end/tests/write_buffering_at_end.cc @@ -16,217 +16,67 @@ // // -#include +#include "gtest/gtest.h" -#include -#include - -#include #include -#include -#include #include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "test/core/end2end/cq_verifier.h" +#include "src/core/lib/gprpp/time.h" #include "test/core/end2end/end2end_tests.h" -#include "test/core/util/test_config.h" - -static std::unique_ptr begin_test( - const CoreTestConfiguration& config, const char* test_name, - grpc_channel_args* client_args, grpc_channel_args* server_args) { - gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); - auto f = config.create_fixture(grpc_core::ChannelArgs::FromC(client_args), - grpc_core::ChannelArgs::FromC(server_args)); - f->InitServer(grpc_core::ChannelArgs::FromC(server_args)); - f->InitClient(grpc_core::ChannelArgs::FromC(client_args)); - return f; -} - -// Client sends a request with payload, server reads then returns status. -static void test_invoke_request_with_payload( - const CoreTestConfiguration& config) { - grpc_call* c; - grpc_call* s; - grpc_slice request_payload_slice = - grpc_slice_from_copied_string("hello world"); - grpc_byte_buffer* request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - auto f = - begin_test(config, "test_invoke_request_with_payload", nullptr, nullptr); - grpc_core::CqVerifier cqv(f->cq()); - grpc_op ops[6]; - grpc_op* op; - grpc_metadata_array initial_metadata_recv; - grpc_metadata_array trailing_metadata_recv; - grpc_metadata_array request_metadata_recv; - grpc_byte_buffer* request_payload_recv1 = nullptr; - grpc_byte_buffer* request_payload_recv2 = nullptr; - grpc_call_details call_details; - grpc_status_code status; - grpc_call_error error; - grpc_slice details = grpc_empty_slice(); - int was_cancelled = 2; - - gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); - c = grpc_channel_create_call(f->client(), nullptr, GRPC_PROPAGATE_DEFAULTS, - f->cq(), grpc_slice_from_static_string("/foo"), - nullptr, deadline, nullptr); - GPR_ASSERT(c); - - grpc_metadata_array_init(&initial_metadata_recv); - grpc_metadata_array_init(&trailing_metadata_recv); - grpc_metadata_array_init(&request_metadata_recv); - grpc_call_details_init(&call_details); - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(1), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); +namespace grpc_core { +namespace { - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(2), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); +TEST_P(WriteBufferingTest, WriteBufferingAtEnd) { + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); + c.NewBatch(1).SendInitialMetadata({}); + CoreEnd2endTest::IncomingMetadata server_initial_metadata; + c.NewBatch(2).RecvInitialMetadata(server_initial_metadata); - GPR_ASSERT(GRPC_CALL_OK == - grpc_server_request_call(f->server(), &s, &call_details, - &request_metadata_recv, f->cq(), f->cq(), - grpc_core::CqVerifier::tag(101))); - cqv.Expect(grpc_core::CqVerifier::tag(1), true); // send message is buffered - cqv.Expect(grpc_core::CqVerifier::tag(101), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = request_payload; - op->flags = GRPC_WRITE_BUFFER_HINT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(3), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(102), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); + auto s = RequestCall(101); + Expect(1, true); // send message is buffered + Expect(101, true); + Step(); + c.NewBatch(3).SendMessage("hello world", GRPC_WRITE_BUFFER_HINT); + s.NewBatch(102).SendInitialMetadata({}); // recv message should not succeed yet - it's buffered at the client still - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv1; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(103), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(2), true); - cqv.Expect(grpc_core::CqVerifier::tag(3), true); - cqv.Expect(grpc_core::CqVerifier::tag(102), true); - cqv.Verify(); + CoreEnd2endTest::IncomingMessage request_payload_recv1; + s.NewBatch(103).RecvMessage(request_payload_recv1); + Expect(2, true); + Expect(3, true); + Expect(102, true); + Step(); // send end of stream: should release the buffering - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(4), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - + c.NewBatch(4).SendCloseFromClient(); // now the first send should match up with the first recv - cqv.Expect(grpc_core::CqVerifier::tag(103), true); - cqv.Expect(grpc_core::CqVerifier::tag(4), true); - cqv.Verify(); + Expect(103, true); + Expect(4, true); + Step(); // and the next recv should be ready immediately also (and empty) - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &request_payload_recv2; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(104), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(104), true); - cqv.Verify(); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; - op->data.recv_status_on_client.status = &status; - op->data.recv_status_on_client.status_details = &details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(c, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(4), nullptr); - - memset(ops, 0, sizeof(ops)); - op = ops; - op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; - op->data.recv_close_on_server.cancelled = &was_cancelled; - op->flags = 0; - op->reserved = nullptr; - op++; - op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; - op->data.send_status_from_server.trailing_metadata_count = 0; - op->data.send_status_from_server.status = GRPC_STATUS_OK; - grpc_slice status_details = grpc_slice_from_static_string("xyz"); - op->data.send_status_from_server.status_details = &status_details; - op->flags = 0; - op->reserved = nullptr; - op++; - error = grpc_call_start_batch(s, ops, static_cast(op - ops), - grpc_core::CqVerifier::tag(105), nullptr); - GPR_ASSERT(GRPC_CALL_OK == error); - - cqv.Expect(grpc_core::CqVerifier::tag(105), true); - cqv.Expect(grpc_core::CqVerifier::tag(4), true); - cqv.Verify(); - - GPR_ASSERT(status == GRPC_STATUS_OK); - GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); - GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); - GPR_ASSERT(was_cancelled == 0); - GPR_ASSERT(byte_buffer_eq_string(request_payload_recv1, "hello world")); - GPR_ASSERT(request_payload_recv2 == nullptr); - - grpc_slice_unref(details); - grpc_metadata_array_destroy(&initial_metadata_recv); - grpc_metadata_array_destroy(&trailing_metadata_recv); - grpc_metadata_array_destroy(&request_metadata_recv); - grpc_call_details_destroy(&call_details); - - grpc_call_unref(c); - grpc_call_unref(s); - - grpc_byte_buffer_destroy(request_payload); - grpc_byte_buffer_destroy(request_payload_recv1); + CoreEnd2endTest::IncomingMessage request_payload_recv2; + s.NewBatch(104).RecvMessage(request_payload_recv2); + Expect(104, true); + Step(); + + CoreEnd2endTest::IncomingStatusOnClient server_status; + c.NewBatch(4).RecvStatusOnClient(server_status); + CoreEnd2endTest::IncomingCloseOnServer client_close; + s.NewBatch(105) + .RecvCloseOnServer(client_close) + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + Expect(105, true); + Expect(4, true); + Step(); + + EXPECT_EQ(server_status.status(), GRPC_STATUS_OK); + EXPECT_EQ(server_status.message(), "xyz"); + EXPECT_EQ(s.method(), "/foo"); + EXPECT_FALSE(client_close.was_cancelled()); + EXPECT_EQ(request_payload_recv1.payload(), "hello world"); + EXPECT_TRUE(request_payload_recv2.is_end_of_stream()); } - -void write_buffering_at_end(const CoreTestConfiguration& config) { - test_invoke_request_with_payload(config); -} - -void write_buffering_at_end_pre_init(void) {} +} // namespace +} // namespace grpc_core diff --git a/test/core/iomgr/ios/CFStreamTests/Podfile b/test/core/iomgr/ios/CFStreamTests/Podfile index 0da1b2a9512e1..176226696a142 100644 --- a/test/core/iomgr/ios/CFStreamTests/Podfile +++ b/test/core/iomgr/ios/CFStreamTests/Podfile @@ -9,7 +9,6 @@ GRPC_LOCAL_SRC = '../../../../..' # Install the dependencies in the main target plus all test targets. target 'CFStreamTests' do pod 'gRPC-Core', :path => GRPC_LOCAL_SRC - pod 'gRPC-Core/Tests', :path => GRPC_LOCAL_SRC pod 'BoringSSL-GRPC', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true end diff --git a/test/cpp/cocoapods/Podfile b/test/cpp/cocoapods/Podfile index e3ab369b31ae2..26e68de0b1777 100644 --- a/test/cpp/cocoapods/Podfile +++ b/test/cpp/cocoapods/Podfile @@ -12,7 +12,6 @@ GRPC_LOCAL_SRC = '../../..' ).each do |target_name| target target_name do pod 'gRPC-Core', :path => GRPC_LOCAL_SRC - pod 'gRPC-Core/Tests', :path => GRPC_LOCAL_SRC pod 'gRPC-C++', :path => GRPC_LOCAL_SRC pod 'BoringSSL-GRPC', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true end diff --git a/tools/buildgen/extract_metadata_from_bazel_xml.py b/tools/buildgen/extract_metadata_from_bazel_xml.py index 84aafd88e3b07..c8810193c0945 100755 --- a/tools/buildgen/extract_metadata_from_bazel_xml.py +++ b/tools/buildgen/extract_metadata_from_bazel_xml.py @@ -993,13 +993,6 @@ def _detect_and_print_issues(build_yaml_like: BuildYaml) -> None: '_RENAME': 'grpc++_test_util' }, - # end2end test support libraries - 'test/core/end2end:end2end_tests': { - 'language': 'c', - 'build': 'private', - '_RENAME': 'end2end_tests' - }, - # benchmark support libraries 'test/cpp/microbenchmarks:helpers': { 'language': 'c++', diff --git a/tools/buildgen/generate_build_additions.sh b/tools/buildgen/generate_build_additions.sh index 46ff7e9e51eb0..c890b5372fdfe 100755 --- a/tools/buildgen/generate_build_additions.sh +++ b/tools/buildgen/generate_build_additions.sh @@ -24,7 +24,6 @@ gen_build_yaml_dirs=" \ src/upb \ src/zlib \ src/c-ares \ - test/core/end2end \ test/cpp/naming" diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index a7264007bda87..a491456422eb1 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -1,120 +1,6 @@ [ - { - "args": [], - "benchmark": false, - "ci_platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "cpu_cost": 1.0, - "exclude_configs": [], - "exclude_iomgrs": [], - "flaky": false, - "gtest": false, - "language": "c", - "name": "bad_server_response_test", - "platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "uses_polling": true - }, - { - "args": [], - "benchmark": false, - "ci_platforms": [ - "linux", - "mac", - "posix" - ], - "cpu_cost": 1.0, - "exclude_configs": [], - "exclude_iomgrs": [], - "flaky": false, - "gtest": false, - "language": "c", - "name": "bad_ssl_alpn_test", - "platforms": [ - "linux", - "mac", - "posix" - ], - "uses_polling": true - }, - { - "args": [], - "benchmark": false, - "ci_platforms": [ - "linux", - "mac", - "posix" - ], - "cpu_cost": 1.0, - "exclude_configs": [], - "exclude_iomgrs": [], - "flaky": false, - "gtest": false, - "language": "c", - "name": "bad_ssl_cert_test", - "platforms": [ - "linux", - "mac", - "posix" - ], - "uses_polling": true - }, - { - "args": [], - "benchmark": false, - "ci_platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "cpu_cost": 1.0, - "exclude_configs": [], - "exclude_iomgrs": [], - "flaky": false, - "gtest": false, - "language": "c", - "name": "connection_refused_test", - "platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "uses_polling": true - }, - { - "args": [], - "benchmark": false, - "ci_platforms": [ - "linux", - "mac", - "posix" - ], - "cpu_cost": 1.0, - "exclude_configs": [], - "exclude_iomgrs": [], - "flaky": false, - "gtest": false, - "language": "c", - "name": "dualstack_socket_test", - "platforms": [ - "linux", - "mac", - "posix" - ], - "uses_polling": true - }, { "args": [], "benchmark": false, @@ -137,54 +23,6 @@ ], "uses_polling": true }, - { - "args": [], - "benchmark": false, - "ci_platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "cpu_cost": 1.0, - "exclude_configs": [], - "exclude_iomgrs": [], - "flaky": false, - "gtest": false, - "language": "c", - "name": "goaway_server_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": false, - "language": "c", - "name": "invalid_call_argument_test", - "platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "uses_polling": true - }, { "args": [], "benchmark": false, @@ -209,30 +47,6 @@ ], "uses_polling": true }, - { - "args": [], - "benchmark": false, - "ci_platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "cpu_cost": 1.0, - "exclude_configs": [], - "exclude_iomgrs": [], - "flaky": false, - "gtest": false, - "language": "c", - "name": "no_server_test", - "platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "uses_polling": true - }, { "args": [], "benchmark": false, @@ -1033,6 +847,74 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "bad_server_response_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "bad_ssl_alpn_test", + "platforms": [ + "linux", + "mac", + "posix" + ], + "uses_polling": true + }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "bad_ssl_cert_test", + "platforms": [ + "linux", + "mac", + "posix" + ], + "uses_polling": true + }, { "args": [], "benchmark": false, @@ -2203,6 +2085,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": "connection_refused_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false, @@ -2323,6 +2229,30 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "core_end2end_tests", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false, @@ -2537,6 +2467,28 @@ ], "uses_polling": true }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "dualstack_socket_test", + "platforms": [ + "linux", + "mac", + "posix" + ], + "uses_polling": true + }, { "args": [], "benchmark": false, @@ -3293,6 +3245,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": "goaway_server_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false, @@ -4243,6 +4219,30 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "invalid_call_argument_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false, @@ -4973,6 +4973,30 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "no_server_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false, From fd8e7e39fe02588a7207a5fc8d6e90d020fd3827 Mon Sep 17 00:00:00 2001 From: Eugene Ostroukhov Date: Tue, 4 Apr 2023 08:43:35 -0700 Subject: [PATCH 02/39] [PSM tests] Troubleshoot Ubuntu venv issue (#32786) --- tools/run_tests/xds_k8s_test_driver/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/run_tests/xds_k8s_test_driver/README.md b/tools/run_tests/xds_k8s_test_driver/README.md index 492e3926eef7d..f9385514a9d0a 100644 --- a/tools/run_tests/xds_k8s_test_driver/README.md +++ b/tools/run_tests/xds_k8s_test_driver/README.md @@ -30,6 +30,11 @@ changes to this codebase at the moment. `kubectl` can be installed via `gcloud components install kubectl`, or system package manager: https://kubernetes.io/docs/tasks/tools/#kubectl +Python3 venv tool may need to be installed from APT on some Ubuntu systems: +```shell +sudo apt-get install python3-venv +``` + ##### Getting Started 1. If you haven't, [initialize](https://cloud.google.com/sdk/docs/install-sdk) gcloud SDK @@ -174,7 +179,7 @@ export KUBE_CONTEXT="$(kubectl config current-context)" ```shell # Create python virtual environment -python3.7 -m venv venv +python3 -m venv venv # Activate virtual environment . ./venv/bin/activate From 1f0630fd910edbd78386ad3a2967535cb445082d Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 4 Apr 2023 10:07:38 -0700 Subject: [PATCH 03/39] [core-test] Ensure grpc is fully shutdown between e2e tests (#32797) --- test/core/end2end/end2end_tests.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/core/end2end/end2end_tests.cc b/test/core/end2end/end2end_tests.cc index 8c6ba5598f89d..016a5a86f4bf4 100644 --- a/test/core/end2end/end2end_tests.cc +++ b/test/core/end2end/end2end_tests.cc @@ -83,7 +83,14 @@ void CoreEnd2endTest::TearDown() { const bool do_shutdown = fixture_ != nullptr; cq_verifier_.reset(); fixture_.reset(); - if (do_shutdown) grpc_shutdown_blocking(); + if (do_shutdown) { + grpc_shutdown_blocking(); + // This will wait until gRPC shutdown has actually happened to make sure + // no gRPC resources (such as thread) are active. (timeout = 10s) + if (!grpc_wait_until_shutdown(10)) { + gpr_log(GPR_ERROR, "Timeout in waiting for gRPC shutdown"); + } + } initialized_ = false; } From 4e2f92bf9c1fcb5a70c0393701bfa08b6d50d834 Mon Sep 17 00:00:00 2001 From: Alisha Nanda Date: Tue, 4 Apr 2023 10:14:59 -0700 Subject: [PATCH 04/39] [metadata] Fix fuzzer bug with metadata arg. (#32787) Bug: b/276525236. --- .../chttp2/transport/chttp2_transport.cc | 2 +- ...case-minimized-api_fuzzer-4813636509761536 | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 test/core/end2end/fuzzers/api_fuzzer_corpus/clusterfuzz-testcase-minimized-api_fuzzer-4813636509761536 diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index 27e3342ecb26f..93abc3a8fee54 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -445,7 +445,7 @@ static void read_channel_args(grpc_chttp2_transport* t, // `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 < (INT_MAX / 1.25)) + const int value = (soft_limit >= 0 && soft_limit < (INT_MAX / 1.25)) ? static_cast(soft_limit * 1.25) : soft_limit; if (value > DEFAULT_MAX_HEADER_LIST_SIZE) { diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/clusterfuzz-testcase-minimized-api_fuzzer-4813636509761536 b/test/core/end2end/fuzzers/api_fuzzer_corpus/clusterfuzz-testcase-minimized-api_fuzzer-4813636509761536 new file mode 100644 index 0000000000000..b44c1cc02440a --- /dev/null +++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/clusterfuzz-testcase-minimized-api_fuzzer-4813636509761536 @@ -0,0 +1,21 @@ +actions { + create_channel { + target: "unix: {" + channel_args { + key: "grpc.max_metadata_size" + i: 1236454714901355 + } + channel_actions { + add_n_bytes_writable: 21 + add_n_bytes_readable: 18446744069414584320 + wait_ms: 8 + } + } +} +actions { + create_server { + } +} +actions { + check_connectivity: true +} From 6f81b87122aec1194059f91289c527b3c08dc025 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Tue, 4 Apr 2023 19:58:04 +0200 Subject: [PATCH 05/39] [protobuf] Add third_party/utf8_range as a subtree (#32794) This is a prerequisite for upgrading to protobuf 22.x (upb and protobuf now depend on utf8_range) Currently utf8_range isn't referenced by anything, but it's better to bring the subtree in advance to make the protobuf upgrade PR smaller. --- WORKSPACE | 8 + third_party/README.md | 11 + third_party/utf8_range/.bazelrc | 11 + .../.github/workflows/bazel_tests.yml | 36 ++ .../.github/workflows/cmake_tests.yml | 61 +++ third_party/utf8_range/.gitignore | 2 + third_party/utf8_range/BUILD.bazel | 55 +++ third_party/utf8_range/CMakeLists.txt | 81 ++++ third_party/utf8_range/CONTRIBUTING.md | 31 ++ third_party/utf8_range/LICENSE | 22 + third_party/utf8_range/README.md | 264 ++++++++++ third_party/utf8_range/UTF-8-demo.txt | 212 ++++++++ third_party/utf8_range/WORKSPACE | 31 ++ third_party/utf8_range/ascii.cpp | 222 +++++++++ third_party/utf8_range/boost.cpp | 15 + .../cmake/utf8_range-config.cmake.in | 7 + third_party/utf8_range/fuzz/BUILD.bazel | 9 + third_party/utf8_range/fuzz/utf8_fuzzer.dict | 7 + .../utf8_range/fuzz/utf8_validity_fuzzer.cc | 15 + third_party/utf8_range/lemire-avx2.c | 233 +++++++++ third_party/utf8_range/lemire-neon.c | 215 ++++++++ third_party/utf8_range/lemire-sse.c | 206 ++++++++ third_party/utf8_range/lookup.c | 41 ++ third_party/utf8_range/main.c | 405 ++++++++++++++++ third_party/utf8_range/naive.c | 92 ++++ third_party/utf8_range/range-avx2.c | 277 +++++++++++ third_party/utf8_range/range-neon.c | 228 +++++++++ third_party/utf8_range/range-sse.c | 255 ++++++++++ third_party/utf8_range/range2-neon.c | 157 ++++++ third_party/utf8_range/range2-sse.c | 170 +++++++ .../utf8_corpus_dir/utf8_corpus_durst.txt | 213 ++++++++ .../utf8_corpus_dir/utf8_corpus_kuhn.txt | Bin 0 -> 23349 bytes third_party/utf8_range/utf8_range.h | 21 + third_party/utf8_range/utf8_to_utf16/Makefile | 11 + third_party/utf8_range/utf8_to_utf16/iconv.c | 51 ++ third_party/utf8_range/utf8_to_utf16/main.c | 424 ++++++++++++++++ third_party/utf8_range/utf8_to_utf16/naive.c | 133 +++++ third_party/utf8_range/utf8_validity.cc | 458 ++++++++++++++++++ third_party/utf8_range/utf8_validity.h | 23 + third_party/utf8_range/utf8_validity_test.cc | 76 +++ third_party/utf8_range/workspace_deps.bzl | 11 + 41 files changed, 4800 insertions(+) create mode 100644 third_party/utf8_range/.bazelrc create mode 100644 third_party/utf8_range/.github/workflows/bazel_tests.yml create mode 100644 third_party/utf8_range/.github/workflows/cmake_tests.yml create mode 100644 third_party/utf8_range/.gitignore create mode 100644 third_party/utf8_range/BUILD.bazel create mode 100644 third_party/utf8_range/CMakeLists.txt create mode 100644 third_party/utf8_range/CONTRIBUTING.md create mode 100644 third_party/utf8_range/LICENSE create mode 100644 third_party/utf8_range/README.md create mode 100644 third_party/utf8_range/UTF-8-demo.txt create mode 100644 third_party/utf8_range/WORKSPACE create mode 100644 third_party/utf8_range/ascii.cpp create mode 100644 third_party/utf8_range/boost.cpp create mode 100644 third_party/utf8_range/cmake/utf8_range-config.cmake.in create mode 100644 third_party/utf8_range/fuzz/BUILD.bazel create mode 100644 third_party/utf8_range/fuzz/utf8_fuzzer.dict create mode 100644 third_party/utf8_range/fuzz/utf8_validity_fuzzer.cc create mode 100644 third_party/utf8_range/lemire-avx2.c create mode 100644 third_party/utf8_range/lemire-neon.c create mode 100644 third_party/utf8_range/lemire-sse.c create mode 100644 third_party/utf8_range/lookup.c create mode 100644 third_party/utf8_range/main.c create mode 100644 third_party/utf8_range/naive.c create mode 100644 third_party/utf8_range/range-avx2.c create mode 100644 third_party/utf8_range/range-neon.c create mode 100644 third_party/utf8_range/range-sse.c create mode 100644 third_party/utf8_range/range2-neon.c create mode 100644 third_party/utf8_range/range2-sse.c create mode 100644 third_party/utf8_range/utf8_corpus_dir/utf8_corpus_durst.txt create mode 100644 third_party/utf8_range/utf8_corpus_dir/utf8_corpus_kuhn.txt create mode 100644 third_party/utf8_range/utf8_range.h create mode 100644 third_party/utf8_range/utf8_to_utf16/Makefile create mode 100644 third_party/utf8_range/utf8_to_utf16/iconv.c create mode 100644 third_party/utf8_range/utf8_to_utf16/main.c create mode 100644 third_party/utf8_range/utf8_to_utf16/naive.c create mode 100644 third_party/utf8_range/utf8_validity.cc create mode 100644 third_party/utf8_range/utf8_validity.h create mode 100644 third_party/utf8_range/utf8_validity_test.cc create mode 100644 third_party/utf8_range/workspace_deps.bzl diff --git a/WORKSPACE b/WORKSPACE index 080f874f7173d..0b722da9f6f8c 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -51,6 +51,14 @@ local_repository( path = "./src/core/ext/transport/binder/java", ) +# Prevents bazel's '...' expansion from including the following folder. +# This is required to avoid triggering "Unable to find package for @rules_fuzzing//fuzzing:cc_defs.bzl" +# error. +local_repository( + name = "ignore_third_party_utf8_range_subtree", + path = "third_party/utf8_range", +) + load("@io_bazel_rules_python//python:pip.bzl", "pip_install") pip_install( diff --git a/third_party/README.md b/third_party/README.md index 505efe13e4958..7fc8e4356b8bc 100644 --- a/third_party/README.md +++ b/third_party/README.md @@ -134,6 +134,17 @@ Since upb is vendored in the gRPC repo, you cannot use submodule to update it. P 5. Run `tools/buildgen/generate_projects.sh` to regenerate the generated files 6. Run `tools/codegen/core/gen_upb_api.sh` to regenerate upb files. +### Updating third_party/utf8_range + +``` +# set to wherever your grpc repo lives +export GRPC_ROOT=~/git/grpc +wget https://github.com/protocolbuffers/utf8_range/archive/refs/heads/main.zip +rm -rf $GRPC_ROOT/third_party/utf8_range +unzip main.zip -d $GRPC_ROOT/third_party +mv $GRPC_ROOT/third_party/utf8_range-main $GRPC_ROOT/third_party/utf8_range +``` + ### Updating third_party/xxhash TODO(https://github.com/Cyan4973/xxHash/issues/548): revisit LICENSE diff --git a/third_party/utf8_range/.bazelrc b/third_party/utf8_range/.bazelrc new file mode 100644 index 0000000000000..aa13acf66a1f3 --- /dev/null +++ b/third_party/utf8_range/.bazelrc @@ -0,0 +1,11 @@ +build --cxxopt=-std=c++14 --host_cxxopt=-std=c++14 + +build:asan --copt=-fsanitize=address --linkopt=-fsanitize=address +build:msan --copt=-fsanitize=memory --linkopt=-fsanitize=memory +build:tsan --copt=-fsanitize=thread --linkopt=-fsanitize=thread +build:ubsan --copt=-fsanitize=undefined --linkopt=-fsanitize=undefined --action_env=UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 +# Workaround for the fact that Bazel links with $CC, not $CXX +# https://github.com/bazelbuild/bazel/issues/11122#issuecomment-613746748 +build:ubsan --copt=-fno-sanitize=function --copt=-fno-sanitize=vptr +# Workaround for https://bugs.llvm.org/show_bug.cgi?id=16404 +build:ubsan --linkopt=--rtlib=compiler-rt --linkopt=-lunwind diff --git a/third_party/utf8_range/.github/workflows/bazel_tests.yml b/third_party/utf8_range/.github/workflows/bazel_tests.yml new file mode 100644 index 0000000000000..ae3e970419cbc --- /dev/null +++ b/third_party/utf8_range/.github/workflows/bazel_tests.yml @@ -0,0 +1,36 @@ +name: Bazel Tests + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + + ubuntu: + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false # Don't cancel all jobs if one fails. + matrix: + include: + - { NAME: "Debug", CC: clang, os: ubuntu-20.04, flags: "-c dbg" } + - { NAME: "Optmized", CC: clang, os: ubuntu-20.04, flags: "-c opt" } + - { NAME: "GCC Optimized", CC: gcc, os: ubuntu-20.04, flags: "-c opt" } + - { NAME: "ASAN", CC: clang, os: ubuntu-20.04, flags: "--config=asan -c dbg" } + - { NAME: "UBSAN", CC: clang, os: ubuntu-20.04, flags: "--config=ubsan -c dbg", install: "libunwind-dev" } + - { NAME: "macOS", CC: clang, os: macos-11, flags: "" } + + name: Bazel ${{ matrix.NAME }} + + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: sudo apt update && sudo apt install -y ${{ matrix.install }} + if: matrix.install != '' + - name: Run tests + run: cd ${{ github.workspace }} && CC=${{ matrix.CC }} bazel test --test_output=errors ... ${{ matrix.flags }} diff --git a/third_party/utf8_range/.github/workflows/cmake_tests.yml b/third_party/utf8_range/.github/workflows/cmake_tests.yml new file mode 100644 index 0000000000000..1a0b3abf3eef9 --- /dev/null +++ b/third_party/utf8_range/.github/workflows/cmake_tests.yml @@ -0,0 +1,61 @@ +name: CMake Tests + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +env: + GOOGLETEST_PINNED_COMMIT: 4c9a3bb62bf3ba1f1010bf96f9c8ed767b363774 + ABSEIL_PINNED_COMMIT: 273292d1cfc0a94a65082ee350509af1d113344d + INSTALL_DIR: /tmp/install + +jobs: + install: + name: CMake + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Install Googletest + run: | + git clone --no-checkout https://github.com/google/googletest + git -C googletest reset --hard $GOOGLETEST_PINNED_COMMIT + cd googletest && cmake . -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR && make install -j20 + - name: Install Abseil + run: | + git clone --no-checkout https://github.com/abseil/abseil-cpp + git -C abseil-cpp reset --hard $ABSEIL_PINNED_COMMIT + cd abseil-cpp && cmake . -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR && make install -j20 + - name: Configure + run: cd ${{ github.workspace }} && cmake . -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR + - name: Build + run: cd ${{ github.workspace }} && cmake --build . -j20 + - name: Test + run: cd ${{ github.workspace }} && ctest + + test: + name: Cmake Install + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Install Googletest + run: | + git clone --no-checkout https://github.com/google/googletest + git -C googletest reset --hard $GOOGLETEST_PINNED_COMMIT + cd googletest && cmake . -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR && make install -j20 + - name: Download Abseil + run: | + git clone --no-checkout https://github.com/abseil/abseil-cpp /tmp/abseil-cpp + git -C /tmp/abseil-cpp reset --hard $ABSEIL_PINNED_COMMIT + - name: Configure + run: cd ${{ github.workspace }} && cmake . -DABSL_ROOT_DIR=/tmp/abseil-cpp -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR + - name: Build + run: cd ${{ github.workspace }} && cmake --build . -j20 + - name: Install + run: cd ${{ github.workspace }} && make install + - name: Test + run: cd ${{ github.workspace }} && ctest diff --git a/third_party/utf8_range/.gitignore b/third_party/utf8_range/.gitignore new file mode 100644 index 0000000000000..229542d23ce92 --- /dev/null +++ b/third_party/utf8_range/.gitignore @@ -0,0 +1,2 @@ +# Ignore the bazel symlinks +/bazel-* diff --git a/third_party/utf8_range/BUILD.bazel b/third_party/utf8_range/BUILD.bazel new file mode 100644 index 0000000000000..361baf0d014f4 --- /dev/null +++ b/third_party/utf8_range/BUILD.bazel @@ -0,0 +1,55 @@ +# Copyright 2022 Google LLC +# +# Use of this source code is governed by an MIT-style +# license that can be found in the LICENSE file or at +# https://opensource.org/licenses/MIT. + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +exports_files(["LICENSE"]) + +# TODO(b/252876197) Remove this once callers have been Bazelified. +filegroup( + name = "utf8_range_srcs", + srcs = [ + "naive.c", + "range2-neon.c", + "range2-sse.c", + "utf8_range.h", + ], + visibility = [ + "@com_google_protobuf//:__subpackages__", + "@upb//:__subpackages__", + ], +) + +cc_library( + name = "utf8_range", + srcs = [ + "naive.c", + "range2-neon.c", + "range2-sse.c", + ], + hdrs = ["utf8_range.h"], +) + +cc_library( + name = "utf8_validity", + srcs = ["utf8_validity.cc"], + hdrs = ["utf8_validity.h"], + deps = [ + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "utf8_validity_test", + srcs = ["utf8_validity_test.cc"], + deps = [ + ":utf8_validity", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/third_party/utf8_range/CMakeLists.txt b/third_party/utf8_range/CMakeLists.txt new file mode 100644 index 0000000000000..69a4d8990775f --- /dev/null +++ b/third_party/utf8_range/CMakeLists.txt @@ -0,0 +1,81 @@ +cmake_minimum_required (VERSION 3.5) +project (utf8_range C CXX) + +# option() honor variables +if (POLICY CMP0077) + cmake_policy(SET CMP0077 NEW) +endif (POLICY CMP0077) + +option (utf8_range_ENABLE_TESTS "Build test suite" ON) +option (utf8_range_ENABLE_INSTALL "Configure installation" ON) + +## +# Create the lightweight C library +add_library (utf8_range STATIC + naive.c + range2-neon.c + range2-sse.c +) + +## +# A heavier-weight C++ wrapper that supports Abseil. +add_library (utf8_validity STATIC utf8_validity.cc) + +# Load Abseil dependency. +if (NOT TARGET absl::strings) + if (NOT ABSL_ROOT_DIR) + find_package(absl REQUIRED CONFIG) + else () + set(ABSL_ENABLE_INSTALL ${utf8_range_ENABLE_INSTALL}) + set(ABSL_PROPAGATE_CXX_STD ON) + add_subdirectory(${ABSL_ROOT_DIR} third_party/abseil-cpp) + endif () +endif () +target_link_libraries(utf8_validity PUBLIC absl::strings) + +# Configure tests. +if (utf8_range_ENABLE_TESTS) + enable_testing() + + find_package(GTest REQUIRED) + + add_executable(tests utf8_validity_test.cc) + target_link_libraries(tests utf8_validity GTest::gmock_main) + + add_test(NAME utf8_validity_test COMMAND tests) + + add_custom_target(check + COMMAND tests + DEPENDS tests + ) +endif () + +# Configure installation. +if (utf8_range_ENABLE_INSTALL) + include(CMakePackageConfigHelpers) + include(GNUInstallDirs) + + install(EXPORT ${PROJECT_NAME}-targets + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + NAMESPACE utf8_range:: + ) + install(TARGETS utf8_validity utf8_range EXPORT ${PROJECT_NAME}-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + + configure_package_config_file( + cmake/${PROJECT_NAME}-config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + ) + install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + ) + + # Install public headers explicitly. + install(FILES utf8_range.h utf8_validity.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) +endif () diff --git a/third_party/utf8_range/CONTRIBUTING.md b/third_party/utf8_range/CONTRIBUTING.md new file mode 100644 index 0000000000000..3e5b62eebfe6b --- /dev/null +++ b/third_party/utf8_range/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# How to Contribute + +This repository is currently a read-only clone of internal Google code for use +in open-source projects. We don't currently have a mechanism to upstream +changes, but if you'd like to contribute, please reach out to us to discuss your +proposed changes. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement (CLA). You (or your employer) retain the copyright to your +contribution; this simply gives us permission to use and redistribute your +contributions as part of the project. Head over to + to see your current agreements on file or +to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code Reviews + +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. + +## Community Guidelines + +This project follows +[Google's Open Source Community Guidelines](https://opensource.google/conduct/). diff --git a/third_party/utf8_range/LICENSE b/third_party/utf8_range/LICENSE new file mode 100644 index 0000000000000..e59f14fa55de7 --- /dev/null +++ b/third_party/utf8_range/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2019 Yibo Cai +Copyright 2022 Google LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/utf8_range/README.md b/third_party/utf8_range/README.md new file mode 100644 index 0000000000000..30ff5b785b8fa --- /dev/null +++ b/third_party/utf8_range/README.md @@ -0,0 +1,264 @@ +[![Build Status](https://travis-ci.com/cyb70289/utf8.svg?branch=master)](https://travis-ci.com/cyb70289/utf8) + +# Fast UTF-8 validation with Range algorithm (NEON+SSE4+AVX2) + +This is a brand new algorithm to leverage SIMD for fast UTF-8 string validation. Both **NEON**(armv8a) and **SSE4** versions are implemented. **AVX2** implementation contributed by [ioioioio](https://github.com/ioioioio). + +Four UTF-8 validation methods are compared on both x86 and Arm platforms. Benchmark result shows range base algorithm is the best solution on Arm, and achieves same performance as [Lemire's approach](https://lemire.me/blog/2018/05/16/validating-utf-8-strings-using-as-little-as-0-7-cycles-per-byte/) on x86. + +* Range based algorithm + * range-neon.c: NEON version + * range-sse.c: SSE4 version + * range-avx2.c: AVX2 version + * range2-neon.c, range2-sse.c: Process two blocks in one iteration +* [Lemire's SIMD implementation](https://github.com/lemire/fastvalidate-utf-8) + * lemire-sse.c: SSE4 version + * lemire-avx2.c: AVX2 version + * lemire-neon.c: NEON porting +* naive.c: Naive UTF-8 validation byte by byte +* lookup.c: [Lookup-table method](http://bjoern.hoehrmann.de/utf-8/decoder/dfa/) + +## About the code + +* Run "make" to build. Built and tested with gcc-7.3. +* Run "./utf8" to see all command line options. +* Benchmark + * Run "./utf8 bench" to bechmark all algorithms with [default test file](https://raw.githubusercontent.com/cyb70289/utf8/master/UTF-8-demo.txt). + * Run "./utf8 bench size NUM" to benchmark specified string size. +* Run "./utf8 test" to test all algorithms with positive and negative test cases. +* To benchmark or test specific algorithm, run something like "./utf8 bench range". + +## Benchmark result (MB/s) + +### Method +1. Generate UTF-8 test buffer per [test file](https://raw.githubusercontent.com/cyb70289/utf8/master/UTF-8-demo.txt) or buffer size. +1. Call validation sub-routines in a loop until 1G bytes are checked. +1. Calculate speed(MB/s) of validating UTF-8 strings. + +### NEON(armv8a) +Test case | naive | lookup | lemire | range | range2 +:-------- | :---- | :----- | :----- | :---- | :----- +[UTF-demo.txt](https://raw.githubusercontent.com/cyb70289/utf8/master/UTF-8-demo.txt) | 562.25 | 412.84 | 1198.50 | 1411.72 | **1579.85** +32 bytes | 651.55 | 441.70 | 891.38 | 1003.95 | **1043.58** +33 bytes | 660.00 | 446.78 | 588.77 | 1009.31 | **1048.12** +129 bytes | 771.89 | 402.55 | 938.07 | 1283.77 | **1401.76** +1K bytes | 811.92 | 411.58 | 1188.96 | 1398.15 | **1560.23** +8K bytes | 812.25 | 412.74 | 1198.90 | 1412.18 | **1580.65** +64K bytes | 817.35 | 412.24 | 1200.20 | 1415.11 | **1583.86** +1M bytes | 815.70 | 411.93 | 1200.93 | 1415.65 | **1585.40** + +### SSE4(E5-2650) +Test case | naive | lookup | lemire | range | range2 +:-------- | :---- | :----- | :----- | :---- | :----- +[UTF-demo.txt](https://raw.githubusercontent.com/cyb70289/utf8/master/UTF-8-demo.txt) | 753.70 | 310.41 | 3954.74 | 3945.60 | **3986.13** +32 bytes | 1135.76 | 364.07 | **2890.52** | 2351.81 | 2173.02 +33 bytes | 1161.85 | 376.29 | 1352.95 | **2239.55** | 2041.43 +129 bytes | 1161.22 | 322.47 | 2742.49 | **3315.33** | 3249.35 +1K bytes | 1310.95 | 310.72 | 3755.88 | 3781.23 | **3874.17** +8K bytes | 1348.32 | 307.93 | 3860.71 | 3922.81 | **3968.93** +64K bytes | 1301.34 | 308.39 | 3935.15 | 3973.50 | **3983.44** +1M bytes | 1279.78 | 309.06 | 3923.51 | 3953.00 | **3960.49** + +## Range algorithm analysis + +Basic idea: +* Load 16 bytes +* Leverage SIMD to calculate value range for each byte efficiently +* Validate 16 bytes at once + +### UTF-8 coding format + +http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf, page 94 + +Table 3-7. Well-Formed UTF-8 Byte Sequences + +Code Points | First Byte | Second Byte | Third Byte | Fourth Byte | +:---------- | :--------- | :---------- | :--------- | :---------- | +U+0000..U+007F | 00..7F | | | | +U+0080..U+07FF | C2..DF | 80..BF | | | +U+0800..U+0FFF | E0 | ***A0***..BF| 80..BF | | +U+1000..U+CFFF | E1..EC | 80..BF | 80..BF | | +U+D000..U+D7FF | ED | 80..***9F***| 80..BF | | +U+E000..U+FFFF | EE..EF | 80..BF | 80..BF | | +U+10000..U+3FFFF | F0 | ***90***..BF| 80..BF | 80..BF | +U+40000..U+FFFFF | F1..F3 | 80..BF | 80..BF | 80..BF | +U+100000..U+10FFFF | F4 | 80..***8F***| 80..BF | 80..BF | + +To summarise UTF-8 encoding: +* Depending on First Byte, one legal character can be 1, 2, 3, 4 bytes + * For First Byte within C0..DF, character length = 2 + * For First Byte within E0..EF, character length = 3 + * For First Byte within F0..F4, character length = 4 +* C0, C1, F5..FF are not allowed +* Second,Third,Fourth Bytes must lie in 80..BF. +* There are four **special cases** for Second Byte, shown ***bold italic*** in above table. + +### Range table + +Range table maps range index 0 ~ 15 to minimal and maximum values allowed. Our task is to observe input string, find the pattern and set correct range index for each byte, then validate input string. + +Index | Min | Max | Byte type +:---- | :-- | :-- | :-------- +0 | 00 | 7F | First Byte, ASCII +1,2,3 | 80 | BF | Second, Third, Fourth Bytes +4 | A0 | BF | Second Byte after E0 +5 | 80 | 9F | Second Byte after ED +6 | 90 | BF | Second Byte after F0 +7 | 80 | 8F | Second Byte after F4 +8 | C2 | F4 | First Byte, non-ASCII +9..15(NEON) | FF | 00 | Illegal: unsigned char >= 255 && unsigned char <= 0 +9..15(SSE) | 7F | 80 | Illegal: signed char >= 127 && signed char <= -128 + +### Calculate byte ranges (ignore special cases) + +Ignoring the four special cases(E0,ED,F0,F4), how should we set range index for each byte? + +* Set range index to 0(00..7F) for all bytes by default +* Find non-ASCII First Byte (C0..FF), set their range index to 8(C2..F4) +* For First Byte within C0..DF, set next byte's range index to 1(80..BF) +* For First Byte within E0..EF, set next two byte's range index to 2,1(80..BF) in sequence +* For First Byte within F0..FF, set next three byte's range index to 3,2,1(80..BF) in sequence + +To implement above operations efficiently with SIMD: +* For 16 input bytes, use lookup table to map C0..DF to 1, E0..EF to 2, F0..FF to 3, others to 0. Save to first_len. +* Map C0..FF to 8, we get range indices for First Byte. +* Shift first_len one byte, we get range indices for Second Byte. +* Saturate substract first_len by one(3->2, 2->1, 1->0, 0->0), then shift two bytes, we get range indices for Third Byte. +* Saturate substract first_len by two(3->1, 2->0, 1->0, 0->0), then shift three bytes, we get range indices for Fourth Byte. + +Example(assume no previous data) + +Input | F1 | 80 | 80 | 80 | 80 | C2 | 80 | 80 | ... +:---- | :- | :- | :- | :- | :- | :- | :- | :- | :-- +*first_len* |*3* |*0* |*0* |*0* |*0* |*1* |*0* |*0* |*...* +First Byte | 8 | 0 | 0 | 0 | 0 | 8 | 0 | 0 | ... +Second Byte | 0 | 3 | 0 | 0 | 0 | 0 | 1 | 0 | ... +Third Byte | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | ... +Fourth Byte | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | ... +Range index | 8 | 3 | 2 | 1 | 0 | 8 | 1 | 0 | ... + +```c +Range_index = First_Byte | Second_Byte | Third_Byte | Fourth_Byte +``` + +#### Error handling + +* C0,C1,F5..FF are not included in range table and will always be detected. +* Illegal 80..BF will have range index 0(00..7F) and be detected. +* Based on First Byte, according Second, Third and Fourth Bytes will have range index 1/2/3, to make sure they must lie in 80..BF. +* If non-ASCII First Byte overlaps, above algorithm will set range index of the latter First Byte to 9,10,11, which are illegal ranges. E.g, Input = F1 80 C2 90 --> Range index = 8 3 10 1, where 10 indicates error. See table below. + +Overlapped non-ASCII First Byte + +Input | F1 | 80 | C2 | 90 +:---- | :- | :- | :- | :- +*first_len* |*3* |*0* |*1* |*0* +First Byte | 8 | 0 | 8 | 0 +Second Byte | 0 | 3 | 0 | 1 +Third Byte | 0 | 0 | 2 | 0 +Fourth Byte | 0 | 0 | 0 | 1 +Range index | 8 | 3 |***10***| 1 + +### Adjust Second Byte range for special cases + +Range index adjustment for four special cases + +First Byte | Second Byte | Before adjustment | Correct index | Adjustment | +:--------- | :---------- | :---------------- | :------------ | :--------- +E0 | A0..BF | 2 | 4 | **2** +ED | 80..9F | 2 | 5 | **3** +F0 | 90..BF | 3 | 6 | **3** +F4 | 80..8F | 3 | 7 | **4** + +Range index adjustment can be reduced to below problem: + +***Given 16 bytes, replace E0 with 2, ED with 3, F0 with 3, F4 with 4, others with 0.*** + +A naive SIMD approach: +1. Compare 16 bytes with E0, get the mask for eacy byte (FF if equal, 00 otherwise) +1. And the mask with 2 to get adjustment for E0 +1. Repeat step 1,2 for ED,F0,F4 + +At least **eight** operations are required for naive approach. + +Observing special bytes(E0,ED,F0,F4) are close to each other, we can do much better using lookup table. + +#### NEON + +NEON ```tbl``` instruction is very convenient for table lookup: +* Table can be up to 16x4 bytes in size +* Return zero if index is out of range + +Leverage these features, we can solve the problem with as few as **two** operations: +* Precreate a 16x2 lookup table, where table[0]=2, table[13]=3, table[16]=3, table[20]=4, table[others]=0. +* Substract input bytes with E0 (E0 -> 0, ED -> 13, F0 -> 16, F4 -> 20). +* Use the substracted byte as index of lookup table and get range adjustment directly. + * For indices less than 32, we get zero or required adjustment value per input byte + * For out of bound indices, we get zero per ```tbl``` behaviour + +#### SSE + +SSE ```pshufb``` instruction is not as friendly as NEON ```tbl``` in this case: +* Table can only be 16 bytes in size +* Out of bound indices are handled this way: + * If 7-th bit of index is 0, least four bits are used as index (E.g, index 0x73 returns 3rd element) + * If 7-th bit of index is 1, return 0 (E.g, index 0x83 returns 0) + +We can still leverage these features to solve the problem in **five** operations: +* Precreate two tables: + * table_df[1] = 2, table_df[14] = 3, table_df[others] = 0 + * table_ef[1] = 3, table_ef[5] = 4, table_ef[others] = 0 +* Substract input bytes with EF (E0 -> 241, ED -> 254, F0 -> 1, F4 -> 5) to get the temporary indices +* Get range index for E0,ED + * Saturate substract temporary indices with 240 (E0 -> 1, ED -> 14, all values below 240 becomes 0) + * Use substracted indices to look up table_df, get the correct adjustment +* Get range index for F0,F4 + * Saturate add temporary indices with 112(0x70) (F0 -> 0x71, F4 -> 0x75, all values above 16 will be larger than 128(7-th bit set)) + * Use added indices to look up table_ef, get the correct adjustment (index 0x71,0x75 returns 1st,5th elements, per ```pshufb``` behaviour) + +#### Error handling + +* For overlapped non-ASCII First Byte, range index before adjustment is 9,10,11. After adjustment (adds 2,3,4 or 0), the range index will be 9 to 15, which is still illegal in range table. So the error will be detected. + +### Handling remaining bytes + +For remaining input less than 16 bytes, we will fallback to naive byte by byte approach to validate them, which is actually faster than SIMD processing. +* Look back last 16 bytes buffer to find First Byte. At most three bytes need to look back. Otherwise we either happen to be at character boundray, or there are some errors we already detected. +* Validate string byte by byte starting from the First Byte. + +## Tests + +It's necessary to design test cases to cover corner cases as more as possible. + +### Positive cases + +1. Prepare correct characters +2. Validate correct characters +3. Validate long strings + * Round concatenate characters starting from first character to 1024 bytes + * Validate 1024 bytes string + * Shift 1 byte, validate 1025 bytes string + * Shift 2 bytes, Validate 1026 bytes string + * ... + * Shift 16 bytes, validate 1040 bytes string +4. Repeat step3, test buffer starting from second character +5. Repeat step3, test buffer starting from third character +6. ... + +### Negative cases + +1. Prepare bad characters and bad strings + * Bad character + * Bad character cross 16 bytes boundary + * Bad character cross last 16 bytes and remaining bytes boundary +2. Test long strings + * Prepare correct long strings same as positive cases + * Append bad characters + * Shift one byte for each iteration + * Validate each shift + +## Code breakdown + +Below table shows how 16 bytes input are processed step by step. See [range-neon.c](range-neon.c) for according code. + +![Range based UTF-8 validation algorithm](https://raw.githubusercontent.com/cyb70289/utf8/master/range.png) diff --git a/third_party/utf8_range/UTF-8-demo.txt b/third_party/utf8_range/UTF-8-demo.txt new file mode 100644 index 0000000000000..ff915b268aaa1 --- /dev/null +++ b/third_party/utf8_range/UTF-8-demo.txt @@ -0,0 +1,212 @@ + +UTF-8 encoded sample plain-text file +‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + +Markus Kuhn [ˈmaʳkʊs kuːn] — 2002-07-25 CC BY + + +The ASCII compatible UTF-8 encoding used in this plain-text file +is defined in Unicode, ISO 10646-1, and RFC 2279. + + +Using Unicode/UTF-8, you can write in emails and source code things such as + +Mathematics and sciences: + + ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫ + ⎪⎢⎜│a²+b³ ⎟⎥⎪ + ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β), ⎪⎢⎜│───── ⎟⎥⎪ + ⎪⎢⎜⎷ c₈ ⎟⎥⎪ + ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⎨⎢⎜ ⎟⎥⎬ + ⎪⎢⎜ ∞ ⎟⎥⎪ + ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫), ⎪⎢⎜ ⎲ ⎟⎥⎪ + ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪ + 2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm ⎩⎣⎝i=1 ⎠⎦⎭ + +Linguistics and dictionaries: + + ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn + Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ] + +APL: + + ((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈ + +Nicer typography in plain text files: + + ╔══════════════════════════════════════════╗ + ║ ║ + ║ • ‘single’ and “double” quotes ║ + ║ ║ + ║ • Curly apostrophes: “We’ve been here” ║ + ║ ║ + ║ • Latin-1 apostrophe and accents: '´` ║ + ║ ║ + ║ • ‚deutsche‘ „Anführungszeichen“ ║ + ║ ║ + ║ • †, ‡, ‰, •, 3–4, —, −5/+5, ™, … ║ + ║ ║ + ║ • ASCII safety test: 1lI|, 0OD, 8B ║ + ║ ╭─────────╮ ║ + ║ • the euro symbol: │ 14.95 € │ ║ + ║ ╰─────────╯ ║ + ╚══════════════════════════════════════════╝ + +Combining characters: + + STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑ + +Greek (in Polytonic): + + The Greek anthem: + + Σὲ γνωρίζω ἀπὸ τὴν κόψη + τοῦ σπαθιοῦ τὴν τρομερή, + σὲ γνωρίζω ἀπὸ τὴν ὄψη + ποὺ μὲ βία μετράει τὴ γῆ. + + ᾿Απ᾿ τὰ κόκκαλα βγαλμένη + τῶν ῾Ελλήνων τὰ ἱερά + καὶ σὰν πρῶτα ἀνδρειωμένη + χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά! + + From a speech of Demosthenes in the 4th century BC: + + Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι, + ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς + λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ + τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿ + εἰς τοῦτο προήκοντα, ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ + πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν + οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι, + οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν + ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον + τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι + γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν + προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους + σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ + τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ + τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς + τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον. + + Δημοσθένους, Γ´ ᾿Ολυνθιακὸς + +Georgian: + + From a Unicode conference invitation: + + გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო + კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს, + ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს + ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი, + ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება + ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში, + ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში. + +Russian: + + From a Unicode conference invitation: + + Зарегистрируйтесь сейчас на Десятую Международную Конференцию по + Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии. + Конференция соберет широкий круг экспертов по вопросам глобального + Интернета и Unicode, локализации и интернационализации, воплощению и + применению Unicode в различных операционных системах и программных + приложениях, шрифтах, верстке и многоязычных компьютерных системах. + +Thai (UCS Level 2): + + Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese + classic 'San Gua'): + + [----------------------------|------------------------] + ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่ + สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา + ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา + โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ + เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ + ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ + พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้ + ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ + + (The above is a two-column text. If combining characters are handled + correctly, the lines of the second column should be aligned with the + | character above.) + +Ethiopian: + + Proverbs in the Amharic language: + + ሰማይ አይታረስ ንጉሥ አይከሰስ። + ብላ ካለኝ እንደአባቴ በቆመጠኝ። + ጌጥ ያለቤቱ ቁምጥና ነው። + ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው። + የአፍ ወለምታ በቅቤ አይታሽም። + አይጥ በበላ ዳዋ ተመታ። + ሲተረጉሙ ይደረግሙ። + ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል። + ድር ቢያብር አንበሳ ያስር። + ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም። + እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም። + የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ። + ሥራ ከመፍታት ልጄን ላፋታት። + ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል። + የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ። + ተንጋሎ ቢተፉ ተመልሶ ባፉ። + ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው። + እግርህን በፍራሽህ ልክ ዘርጋ። + +Runes: + + ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ + + (Old English, which transcribed into Latin reads 'He cwaeth that he + bude thaem lande northweardum with tha Westsae.' and means 'He said + that he lived in the northern land near the Western Sea.') + +Braille: + + ⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌ + + ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞ + ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎ + ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂ + ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙ + ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑ + ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲ + + ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ + + ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹ + ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞ + ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕ + ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹ + ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎ + ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎ + ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳ + ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞ + ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ + + (The first couple of paragraphs of "A Christmas Carol" by Dickens) + +Compact font selection example text: + + ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789 + abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ + –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд + ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა + +Greetings in various languages: + + Hello world, Καλημέρα κόσμε, コンニチハ + +Box drawing alignment tests: █ + ▉ + ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳ + ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳ + ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳ + ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳ + ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎ + ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏ + ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█ + ▝▀▘▙▄▟ diff --git a/third_party/utf8_range/WORKSPACE b/third_party/utf8_range/WORKSPACE new file mode 100644 index 0000000000000..42708aff8242f --- /dev/null +++ b/third_party/utf8_range/WORKSPACE @@ -0,0 +1,31 @@ +workspace(name = "utf8_range") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("//:workspace_deps.bzl", "utf8_range_deps") + +utf8_range_deps() + +http_archive( + name = "com_google_googletest", + sha256 = "81964fe578e9bd7c94dfdb09c8e4d6e6759e19967e397dbea48d1c10e45d0df2", + strip_prefix = "googletest-release-1.12.1", + urls = [ + "https://mirror.bazel.build/github.com/google/googletest/archive/refs/tags/release-1.12.1.tar.gz", + "https://github.com/google/googletest/archive/refs/tags/release-1.12.1.tar.gz", + ], +) + +http_archive( + name = "rules_fuzzing", + sha256 = "d9002dd3cd6437017f08593124fdd1b13b3473c7b929ceb0e60d317cb9346118", + strip_prefix = "rules_fuzzing-0.3.2", + urls = ["https://github.com/bazelbuild/rules_fuzzing/archive/v0.3.2.zip"], +) + +load("@rules_fuzzing//fuzzing:repositories.bzl", "rules_fuzzing_dependencies") + +rules_fuzzing_dependencies() + +load("@rules_fuzzing//fuzzing:init.bzl", "rules_fuzzing_init") + +rules_fuzzing_init() diff --git a/third_party/utf8_range/ascii.cpp b/third_party/utf8_range/ascii.cpp new file mode 100644 index 0000000000000..9c5d183a4bfdb --- /dev/null +++ b/third_party/utf8_range/ascii.cpp @@ -0,0 +1,222 @@ +#include + +#include +#include +#include +#include +#include +#include + +static inline int ascii_std(const uint8_t *data, int len) { + return !std::any_of(data, data + len, [](int8_t b) { return b < 0; }); +} + +static inline int ascii_u64(const uint8_t *data, int len) { + uint8_t orall = 0; + + if (len >= 16) { + uint64_t or1 = 0, or2 = 0; + const uint8_t *data2 = data + 8; + + do { + or1 |= *(const uint64_t *)data; + or2 |= *(const uint64_t *)data2; + data += 16; + data2 += 16; + len -= 16; + } while (len >= 16); + + /* + * Idea from Benny Halevy + * - 7-th bit set ==> orall = !(non-zero) - 1 = 0 - 1 = 0xFF + * - 7-th bit clear ==> orall = !0 - 1 = 1 - 1 = 0x00 + */ + orall = !((or1 | or2) & 0x8080808080808080ULL) - 1; + } + + while (len--) orall |= *data++; + + return orall < 0x80; +} + +#if defined(__x86_64__) +#include + +static inline int ascii_simd(const uint8_t *data, int len) { + if (len >= 32) { + const uint8_t *data2 = data + 16; + + __m128i or1 = _mm_set1_epi8(0), or2 = or1; + + while (len >= 32) { + __m128i input1 = _mm_loadu_si128((const __m128i *)data); + __m128i input2 = _mm_loadu_si128((const __m128i *)data2); + + or1 = _mm_or_si128(or1, input1); + or2 = _mm_or_si128(or2, input2); + + data += 32; + data2 += 32; + len -= 32; + } + + or1 = _mm_or_si128(or1, or2); + if (_mm_movemask_epi8(_mm_cmplt_epi8(or1, _mm_set1_epi8(0)))) return 0; + } + + return ascii_u64(data, len); +} + +#elif defined(__aarch64__) +#include + +static inline int ascii_simd(const uint8_t *data, int len) { + if (len >= 32) { + const uint8_t *data2 = data + 16; + + uint8x16_t or1 = vdupq_n_u8(0), or2 = or1; + + while (len >= 32) { + const uint8x16_t input1 = vld1q_u8(data); + const uint8x16_t input2 = vld1q_u8(data2); + + or1 = vorrq_u8(or1, input1); + or2 = vorrq_u8(or2, input2); + + data += 32; + data2 += 32; + len -= 32; + } + + or1 = vorrq_u8(or1, or2); + if (vmaxvq_u8(or1) >= 0x80) return 0; + } + + return ascii_u64(data, len); +} + +#endif + +struct ftab { + const char *name; + int (*func)(const uint8_t *data, int len); +}; + +static const std::vector _f = { + { + .name = "std", + .func = ascii_std, + }, + { + .name = "u64", + .func = ascii_u64, + }, + { + .name = "simd", + .func = ascii_simd, + }, +}; + +static void load_test_buf(uint8_t *data, int len) { + uint8_t v = 0; + + for (int i = 0; i < len; ++i) { + data[i] = v++; + v &= 0x7F; + } +} + +static void bench(const struct ftab &f, const uint8_t *data, int len) { + const int loops = 1024 * 1024 * 1024 / len; + int ret = 1; + double time_aligned, time_unaligned, size; + struct timeval tv1, tv2; + + fprintf(stderr, "bench %s (%d bytes)... ", f.name, len); + + /* aligned */ + gettimeofday(&tv1, 0); + for (int i = 0; i < loops; ++i) ret &= f.func(data, len); + gettimeofday(&tv2, 0); + time_aligned = tv2.tv_usec - tv1.tv_usec; + time_aligned = time_aligned / 1000000 + tv2.tv_sec - tv1.tv_sec; + + /* unaligned */ + gettimeofday(&tv1, 0); + for (int i = 0; i < loops; ++i) ret &= f.func(data + 1, len); + gettimeofday(&tv2, 0); + time_unaligned = tv2.tv_usec - tv1.tv_usec; + time_unaligned = time_unaligned / 1000000 + tv2.tv_sec - tv1.tv_sec; + + printf("%s ", ret ? "pass" : "FAIL"); + + size = ((double)len * loops) / (1024 * 1024); + printf("%.0f/%.0f MB/s\n", size / time_aligned, size / time_unaligned); +} + +static void test(const struct ftab &f, uint8_t *data, int len) { + int error = 0; + + fprintf(stderr, "test %s (%d bytes)... ", f.name, len); + + /* positive */ + error |= !f.func(data, len); + + /* negative */ + if (len < 100 * 1024) { + for (int i = 0; i < len; ++i) { + data[i] += 0x80; + error |= f.func(data, len); + data[i] -= 0x80; + } + } + + printf("%s\n", error ? "FAIL" : "pass"); +} + +/* ./ascii [test|bench] [alg] */ +int main(int argc, const char *argv[]) { + int do_test = 1, do_bench = 1; + const char *alg = NULL; + + if (argc > 1) { + do_bench &= !!strcmp(argv[1], "test"); + do_test &= !!strcmp(argv[1], "bench"); + } + + if (do_bench && argc > 2) alg = argv[2]; + + const std::vector size = { + 9, 16 + 1, 32 - 1, 128 + 1, + 1024 + 15, 16 * 1024 + 1, 64 * 1024 + 15, 1024 * 1024}; + + int max_size = *std::max_element(size.begin(), size.end()); + uint8_t *_data = new uint8_t[max_size + 1]; + assert(((uintptr_t)_data & 7) == 0); + uint8_t *data = _data + 1; /* Unalign buffer address */ + + _data[0] = 0; + load_test_buf(data, max_size); + + if (do_test) { + printf("==================== Test ====================\n"); + for (int sz : size) { + for (auto &f : _f) { + test(f, data, sz); + } + } + } + + if (do_bench) { + printf("==================== Bench ====================\n"); + for (int sz : size) { + for (auto &f : _f) { + if (!alg || strcmp(alg, f.name) == 0) bench(f, _data, sz); + } + printf("-----------------------------------------------\n"); + } + } + + delete _data; + return 0; +} diff --git a/third_party/utf8_range/boost.cpp b/third_party/utf8_range/boost.cpp new file mode 100644 index 0000000000000..2954f612820b1 --- /dev/null +++ b/third_party/utf8_range/boost.cpp @@ -0,0 +1,15 @@ +#include + +using namespace std; + +/* Return 0 on sucess, -1 on error */ +extern "C" int utf8_boost(const unsigned char* data, int len) { + try { + boost::locale::conv::utf_to_utf(data, data + len, + boost::locale::conv::stop); + } catch (const boost::locale::conv::conversion_error& ex) { + return -1; + } + + return 0; +} diff --git a/third_party/utf8_range/cmake/utf8_range-config.cmake.in b/third_party/utf8_range/cmake/utf8_range-config.cmake.in new file mode 100644 index 0000000000000..e9b9331c93241 --- /dev/null +++ b/third_party/utf8_range/cmake/utf8_range-config.cmake.in @@ -0,0 +1,7 @@ +# Depend packages +if(NOT TARGET absl::strings) + find_package(absl CONFIG) +endif() + +# Imported targets +include("${CMAKE_CURRENT_LIST_DIR}/utf8_range-targets.cmake") diff --git a/third_party/utf8_range/fuzz/BUILD.bazel b/third_party/utf8_range/fuzz/BUILD.bazel new file mode 100644 index 0000000000000..742be2ee74957 --- /dev/null +++ b/third_party/utf8_range/fuzz/BUILD.bazel @@ -0,0 +1,9 @@ +load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test") + +cc_fuzz_test( + name = "utf8_validity_fuzzer", + testonly = 1, + srcs = ["utf8_validity_fuzzer.cc"], + dicts = ["utf8_fuzzer.dict"], + deps = ["//:utf8_validity"], +) diff --git a/third_party/utf8_range/fuzz/utf8_fuzzer.dict b/third_party/utf8_range/fuzz/utf8_fuzzer.dict new file mode 100644 index 0000000000000..33e80e1a0de89 --- /dev/null +++ b/third_party/utf8_range/fuzz/utf8_fuzzer.dict @@ -0,0 +1,7 @@ +# 1, 2, 3 and 4 letter unicode symbols. Also 16 byte non ascii symbols to faster +# test the SIMD case +"z" +"\xd1\x8f" +"\xe2\x8f\xa9" +"\xf0\x9f\x94\x8b" +"\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f\xd1\x8f" diff --git a/third_party/utf8_range/fuzz/utf8_validity_fuzzer.cc b/third_party/utf8_range/fuzz/utf8_validity_fuzzer.cc new file mode 100644 index 0000000000000..4578600d05d20 --- /dev/null +++ b/third_party/utf8_range/fuzz/utf8_validity_fuzzer.cc @@ -0,0 +1,15 @@ +// Copyright 2022 Google LLC +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +#include "utf8_validity.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + utf8_range::IsStructurallyValid( + absl::string_view(reinterpret_cast(data), size)); + utf8_range::SpanStructurallyValid( + absl::string_view(reinterpret_cast(data), size)); + return 0; +} diff --git a/third_party/utf8_range/lemire-avx2.c b/third_party/utf8_range/lemire-avx2.c new file mode 100644 index 0000000000000..ab61c2cc48f81 --- /dev/null +++ b/third_party/utf8_range/lemire-avx2.c @@ -0,0 +1,233 @@ +// Adapted from https://github.com/lemire/fastvalidate-utf-8 + +#ifdef __AVX2__ + +#include +#include +#include +#include +#include + +/* + * legal utf-8 byte sequence + * http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf - page 94 + * + * Code Points 1st 2s 3s 4s + * U+0000..U+007F 00..7F + * U+0080..U+07FF C2..DF 80..BF + * U+0800..U+0FFF E0 A0..BF 80..BF + * U+1000..U+CFFF E1..EC 80..BF 80..BF + * U+D000..U+D7FF ED 80..9F 80..BF + * U+E000..U+FFFF EE..EF 80..BF 80..BF + * U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + * U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + * U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + * + */ + +#if 0 +static void print256(const char *s, const __m256i v256) +{ + const unsigned char *v8 = (const unsigned char *)&v256; + if (s) + printf("%s:\t", s); + for (int i = 0; i < 32; i++) + printf("%02x ", v8[i]); + printf("\n"); +} +#endif + +static inline __m256i push_last_byte_of_a_to_b(__m256i a, __m256i b) { + return _mm256_alignr_epi8(b, _mm256_permute2x128_si256(a, b, 0x21), 15); +} + +static inline __m256i push_last_2bytes_of_a_to_b(__m256i a, __m256i b) { + return _mm256_alignr_epi8(b, _mm256_permute2x128_si256(a, b, 0x21), 14); +} + +// all byte values must be no larger than 0xF4 +static inline void avxcheckSmallerThan0xF4(__m256i current_bytes, + __m256i *has_error) { + // unsigned, saturates to 0 below max + *has_error = _mm256_or_si256( + *has_error, _mm256_subs_epu8(current_bytes, _mm256_set1_epi8(0xF4))); +} + +static inline __m256i avxcontinuationLengths(__m256i high_nibbles) { + return _mm256_shuffle_epi8( + _mm256_setr_epi8(1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII) + 0, 0, 0, 0, // 10xx (continuation) + 2, 2, // 110x + 3, // 1110 + 4, // 1111, next should be 0 (not checked here) + 1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII) + 0, 0, 0, 0, // 10xx (continuation) + 2, 2, // 110x + 3, // 1110 + 4 // 1111, next should be 0 (not checked here) + ), + high_nibbles); +} + +static inline __m256i avxcarryContinuations(__m256i initial_lengths, + __m256i previous_carries) { + + __m256i right1 = _mm256_subs_epu8( + push_last_byte_of_a_to_b(previous_carries, initial_lengths), + _mm256_set1_epi8(1)); + __m256i sum = _mm256_add_epi8(initial_lengths, right1); + + __m256i right2 = _mm256_subs_epu8( + push_last_2bytes_of_a_to_b(previous_carries, sum), _mm256_set1_epi8(2)); + return _mm256_add_epi8(sum, right2); +} + +static inline void avxcheckContinuations(__m256i initial_lengths, + __m256i carries, __m256i *has_error) { + + // overlap || underlap + // carry > length && length > 0 || !(carry > length) && !(length > 0) + // (carries > length) == (lengths > 0) + __m256i overunder = _mm256_cmpeq_epi8( + _mm256_cmpgt_epi8(carries, initial_lengths), + _mm256_cmpgt_epi8(initial_lengths, _mm256_setzero_si256())); + + *has_error = _mm256_or_si256(*has_error, overunder); +} + +// when 0xED is found, next byte must be no larger than 0x9F +// when 0xF4 is found, next byte must be no larger than 0x8F +// next byte must be continuation, ie sign bit is set, so signed < is ok +static inline void avxcheckFirstContinuationMax(__m256i current_bytes, + __m256i off1_current_bytes, + __m256i *has_error) { + __m256i maskED = + _mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xED)); + __m256i maskF4 = + _mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xF4)); + + __m256i badfollowED = _mm256_and_si256( + _mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x9F)), maskED); + __m256i badfollowF4 = _mm256_and_si256( + _mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x8F)), maskF4); + + *has_error = + _mm256_or_si256(*has_error, _mm256_or_si256(badfollowED, badfollowF4)); +} + +// map off1_hibits => error condition +// hibits off1 cur +// C => < C2 && true +// E => < E1 && < A0 +// F => < F1 && < 90 +// else false && false +static inline void avxcheckOverlong(__m256i current_bytes, + __m256i off1_current_bytes, __m256i hibits, + __m256i previous_hibits, + __m256i *has_error) { + __m256i off1_hibits = push_last_byte_of_a_to_b(previous_hibits, hibits); + __m256i initial_mins = _mm256_shuffle_epi8( + _mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, // 10xx => false + 0xC2, -128, // 110x + 0xE1, // 1110 + 0xF1, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, // 10xx => false + 0xC2, -128, // 110x + 0xE1, // 1110 + 0xF1), + off1_hibits); + + __m256i initial_under = _mm256_cmpgt_epi8(initial_mins, off1_current_bytes); + + __m256i second_mins = _mm256_shuffle_epi8( + _mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, // 10xx => false + 127, 127, // 110x => true + 0xA0, // 1110 + 0x90, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, // 10xx => false + 127, 127, // 110x => true + 0xA0, // 1110 + 0x90), + off1_hibits); + __m256i second_under = _mm256_cmpgt_epi8(second_mins, current_bytes); + *has_error = _mm256_or_si256(*has_error, + _mm256_and_si256(initial_under, second_under)); +} + +struct avx_processed_utf_bytes { + __m256i rawbytes; + __m256i high_nibbles; + __m256i carried_continuations; +}; + +static inline void avx_count_nibbles(__m256i bytes, + struct avx_processed_utf_bytes *answer) { + answer->rawbytes = bytes; + answer->high_nibbles = + _mm256_and_si256(_mm256_srli_epi16(bytes, 4), _mm256_set1_epi8(0x0F)); +} + +// check whether the current bytes are valid UTF-8 +// at the end of the function, previous gets updated +static struct avx_processed_utf_bytes +avxcheckUTF8Bytes(__m256i current_bytes, + struct avx_processed_utf_bytes *previous, + __m256i *has_error) { + struct avx_processed_utf_bytes pb; + avx_count_nibbles(current_bytes, &pb); + + avxcheckSmallerThan0xF4(current_bytes, has_error); + + __m256i initial_lengths = avxcontinuationLengths(pb.high_nibbles); + + pb.carried_continuations = + avxcarryContinuations(initial_lengths, previous->carried_continuations); + + avxcheckContinuations(initial_lengths, pb.carried_continuations, has_error); + + __m256i off1_current_bytes = + push_last_byte_of_a_to_b(previous->rawbytes, pb.rawbytes); + avxcheckFirstContinuationMax(current_bytes, off1_current_bytes, has_error); + + avxcheckOverlong(current_bytes, off1_current_bytes, pb.high_nibbles, + previous->high_nibbles, has_error); + return pb; +} + +/* Return 0 on success, -1 on error */ +int utf8_lemire_avx2(const unsigned char *src, int len) { + size_t i = 0; + __m256i has_error = _mm256_setzero_si256(); + struct avx_processed_utf_bytes previous = { + .rawbytes = _mm256_setzero_si256(), + .high_nibbles = _mm256_setzero_si256(), + .carried_continuations = _mm256_setzero_si256()}; + if (len >= 32) { + for (; i <= len - 32; i += 32) { + __m256i current_bytes = _mm256_loadu_si256((const __m256i *)(src + i)); + previous = avxcheckUTF8Bytes(current_bytes, &previous, &has_error); + } + } + + // last part + if (i < len) { + char buffer[32]; + memset(buffer, 0, 32); + memcpy(buffer, src + i, len - i); + __m256i current_bytes = _mm256_loadu_si256((const __m256i *)(buffer)); + previous = avxcheckUTF8Bytes(current_bytes, &previous, &has_error); + } else { + has_error = _mm256_or_si256( + _mm256_cmpgt_epi8(previous.carried_continuations, + _mm256_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 1)), + has_error); + } + + return _mm256_testz_si256(has_error, has_error) ? 0 : -1; +} + +#endif diff --git a/third_party/utf8_range/lemire-neon.c b/third_party/utf8_range/lemire-neon.c new file mode 100644 index 0000000000000..be419af731683 --- /dev/null +++ b/third_party/utf8_range/lemire-neon.c @@ -0,0 +1,215 @@ +// Adapted from https://github.com/lemire/fastvalidate-utf-8 + +#ifdef __aarch64__ + +#include +#include +#include +#include +#include +#include + +/* + * legal utf-8 byte sequence + * http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf - page 94 + * + * Code Points 1st 2s 3s 4s + * U+0000..U+007F 00..7F + * U+0080..U+07FF C2..DF 80..BF + * U+0800..U+0FFF E0 A0..BF 80..BF + * U+1000..U+CFFF E1..EC 80..BF 80..BF + * U+D000..U+D7FF ED 80..9F 80..BF + * U+E000..U+FFFF EE..EF 80..BF 80..BF + * U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + * U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + * U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + * + */ + +#if 0 +static void print128(const char *s, const int8x16_t *v128) +{ + int8_t v8[16]; + vst1q_s8(v8, *v128); + + if (s) + printf("%s:\t", s); + for (int i = 0; i < 16; ++i) + printf("%02x ", (unsigned char)v8[i]); + printf("\n"); +} +#endif + +// all byte values must be no larger than 0xF4 +static inline void checkSmallerThan0xF4(int8x16_t current_bytes, + int8x16_t *has_error) { + // unsigned, saturates to 0 below max + *has_error = vorrq_s8(*has_error, + vreinterpretq_s8_u8(vqsubq_u8(vreinterpretq_u8_s8(current_bytes), vdupq_n_u8(0xF4)))); +} + +static const int8_t _nibbles[] = { + 1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII) + 0, 0, 0, 0, // 10xx (continuation) + 2, 2, // 110x + 3, // 1110 + 4, // 1111, next should be 0 (not checked here) +}; + +static inline int8x16_t continuationLengths(int8x16_t high_nibbles) { + return vqtbl1q_s8(vld1q_s8(_nibbles), vreinterpretq_u8_s8(high_nibbles)); +} + +static inline int8x16_t carryContinuations(int8x16_t initial_lengths, + int8x16_t previous_carries) { + + int8x16_t right1 = + vreinterpretq_s8_u8(vqsubq_u8(vreinterpretq_u8_s8(vextq_s8(previous_carries, initial_lengths, 16 - 1)), + vdupq_n_u8(1))); + int8x16_t sum = vaddq_s8(initial_lengths, right1); + + int8x16_t right2 = vreinterpretq_s8_u8(vqsubq_u8(vreinterpretq_u8_s8(vextq_s8(previous_carries, sum, 16 - 2)), + vdupq_n_u8(2))); + return vaddq_s8(sum, right2); +} + +static inline void checkContinuations(int8x16_t initial_lengths, int8x16_t carries, + int8x16_t *has_error) { + + // overlap || underlap + // carry > length && length > 0 || !(carry > length) && !(length > 0) + // (carries > length) == (lengths > 0) + uint8x16_t overunder = + vceqq_u8(vcgtq_s8(carries, initial_lengths), + vcgtq_s8(initial_lengths, vdupq_n_s8(0))); + + *has_error = vorrq_s8(*has_error, vreinterpretq_s8_u8(overunder)); +} + +// when 0xED is found, next byte must be no larger than 0x9F +// when 0xF4 is found, next byte must be no larger than 0x8F +// next byte must be continuation, ie sign bit is set, so signed < is ok +static inline void checkFirstContinuationMax(int8x16_t current_bytes, + int8x16_t off1_current_bytes, + int8x16_t *has_error) { + uint8x16_t maskED = vceqq_s8(off1_current_bytes, vdupq_n_s8(0xED)); + uint8x16_t maskF4 = vceqq_s8(off1_current_bytes, vdupq_n_s8(0xF4)); + + uint8x16_t badfollowED = + vandq_u8(vcgtq_s8(current_bytes, vdupq_n_s8(0x9F)), maskED); + uint8x16_t badfollowF4 = + vandq_u8(vcgtq_s8(current_bytes, vdupq_n_s8(0x8F)), maskF4); + + *has_error = vorrq_s8(*has_error, vreinterpretq_s8_u8(vorrq_u8(badfollowED, badfollowF4))); +} + +static const int8_t _initial_mins[] = { + -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, // 10xx => false + 0xC2, -128, // 110x + 0xE1, // 1110 + 0xF1, +}; + +static const int8_t _second_mins[] = { + -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, // 10xx => false + 127, 127, // 110x => true + 0xA0, // 1110 + 0x90, +}; + +// map off1_hibits => error condition +// hibits off1 cur +// C => < C2 && true +// E => < E1 && < A0 +// F => < F1 && < 90 +// else false && false +static inline void checkOverlong(int8x16_t current_bytes, + int8x16_t off1_current_bytes, int8x16_t hibits, + int8x16_t previous_hibits, int8x16_t *has_error) { + int8x16_t off1_hibits = vextq_s8(previous_hibits, hibits, 16 - 1); + int8x16_t initial_mins = vqtbl1q_s8(vld1q_s8(_initial_mins), vreinterpretq_u8_s8(off1_hibits)); + + uint8x16_t initial_under = vcgtq_s8(initial_mins, off1_current_bytes); + + int8x16_t second_mins = vqtbl1q_s8(vld1q_s8(_second_mins), vreinterpretq_u8_s8(off1_hibits)); + uint8x16_t second_under = vcgtq_s8(second_mins, current_bytes); + *has_error = + vorrq_s8(*has_error, vreinterpretq_s8_u8(vandq_u8(initial_under, second_under))); +} + +struct processed_utf_bytes { + int8x16_t rawbytes; + int8x16_t high_nibbles; + int8x16_t carried_continuations; +}; + +static inline void count_nibbles(int8x16_t bytes, + struct processed_utf_bytes *answer) { + answer->rawbytes = bytes; + answer->high_nibbles = + vreinterpretq_s8_u8(vshrq_n_u8(vreinterpretq_u8_s8(bytes), 4)); +} + +// check whether the current bytes are valid UTF-8 +// at the end of the function, previous gets updated +static inline struct processed_utf_bytes +checkUTF8Bytes(int8x16_t current_bytes, struct processed_utf_bytes *previous, + int8x16_t *has_error) { + struct processed_utf_bytes pb; + count_nibbles(current_bytes, &pb); + + checkSmallerThan0xF4(current_bytes, has_error); + + int8x16_t initial_lengths = continuationLengths(pb.high_nibbles); + + pb.carried_continuations = + carryContinuations(initial_lengths, previous->carried_continuations); + + checkContinuations(initial_lengths, pb.carried_continuations, has_error); + + int8x16_t off1_current_bytes = + vextq_s8(previous->rawbytes, pb.rawbytes, 16 - 1); + checkFirstContinuationMax(current_bytes, off1_current_bytes, has_error); + + checkOverlong(current_bytes, off1_current_bytes, pb.high_nibbles, + previous->high_nibbles, has_error); + return pb; +} + +static const int8_t _verror[] = {9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1}; + +/* Return 0 on success, -1 on error */ +int utf8_lemire(const unsigned char *src, int len) { + size_t i = 0; + int8x16_t has_error = vdupq_n_s8(0); + struct processed_utf_bytes previous = {.rawbytes = vdupq_n_s8(0), + .high_nibbles = vdupq_n_s8(0), + .carried_continuations = + vdupq_n_s8(0)}; + if (len >= 16) { + for (; i <= len - 16; i += 16) { + int8x16_t current_bytes = vld1q_s8((int8_t*)(src + i)); + previous = checkUTF8Bytes(current_bytes, &previous, &has_error); + } + } + + // last part + if (i < len) { + char buffer[16]; + memset(buffer, 0, 16); + memcpy(buffer, src + i, len - i); + int8x16_t current_bytes = vld1q_s8((int8_t *)buffer); + previous = checkUTF8Bytes(current_bytes, &previous, &has_error); + } else { + has_error = + vorrq_s8(vreinterpretq_s8_u8(vcgtq_s8(previous.carried_continuations, + vld1q_s8(_verror))), + has_error); + } + + return vmaxvq_u8(vreinterpretq_u8_s8(has_error)) == 0 ? 0 : -1; +} + +#endif diff --git a/third_party/utf8_range/lemire-sse.c b/third_party/utf8_range/lemire-sse.c new file mode 100644 index 0000000000000..72a9ebe9d3fec --- /dev/null +++ b/third_party/utf8_range/lemire-sse.c @@ -0,0 +1,206 @@ +// Adapted from https://github.com/lemire/fastvalidate-utf-8 + +#ifdef __x86_64__ + +#include +#include +#include +#include +#include + +/* + * legal utf-8 byte sequence + * http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf - page 94 + * + * Code Points 1st 2s 3s 4s + * U+0000..U+007F 00..7F + * U+0080..U+07FF C2..DF 80..BF + * U+0800..U+0FFF E0 A0..BF 80..BF + * U+1000..U+CFFF E1..EC 80..BF 80..BF + * U+D000..U+D7FF ED 80..9F 80..BF + * U+E000..U+FFFF EE..EF 80..BF 80..BF + * U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + * U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + * U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + * + */ + +#if 0 +static void print128(const char *s, const __m128i *v128) +{ + const unsigned char *v8 = (const unsigned char *)v128; + if (s) + printf("%s: ", s); + for (int i = 0; i < 16; i++) + printf("%02x ", v8[i]); + printf("\n"); +} +#endif + +// all byte values must be no larger than 0xF4 +static inline void checkSmallerThan0xF4(__m128i current_bytes, + __m128i *has_error) { + // unsigned, saturates to 0 below max + *has_error = _mm_or_si128(*has_error, + _mm_subs_epu8(current_bytes, _mm_set1_epi8(0xF4))); +} + +static inline __m128i continuationLengths(__m128i high_nibbles) { + return _mm_shuffle_epi8( + _mm_setr_epi8(1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII) + 0, 0, 0, 0, // 10xx (continuation) + 2, 2, // 110x + 3, // 1110 + 4), // 1111, next should be 0 (not checked here) + high_nibbles); +} + +static inline __m128i carryContinuations(__m128i initial_lengths, + __m128i previous_carries) { + + __m128i right1 = + _mm_subs_epu8(_mm_alignr_epi8(initial_lengths, previous_carries, 16 - 1), + _mm_set1_epi8(1)); + __m128i sum = _mm_add_epi8(initial_lengths, right1); + + __m128i right2 = _mm_subs_epu8(_mm_alignr_epi8(sum, previous_carries, 16 - 2), + _mm_set1_epi8(2)); + return _mm_add_epi8(sum, right2); +} + +static inline void checkContinuations(__m128i initial_lengths, __m128i carries, + __m128i *has_error) { + + // overlap || underlap + // carry > length && length > 0 || !(carry > length) && !(length > 0) + // (carries > length) == (lengths > 0) + __m128i overunder = + _mm_cmpeq_epi8(_mm_cmpgt_epi8(carries, initial_lengths), + _mm_cmpgt_epi8(initial_lengths, _mm_setzero_si128())); + + *has_error = _mm_or_si128(*has_error, overunder); +} + +// when 0xED is found, next byte must be no larger than 0x9F +// when 0xF4 is found, next byte must be no larger than 0x8F +// next byte must be continuation, ie sign bit is set, so signed < is ok +static inline void checkFirstContinuationMax(__m128i current_bytes, + __m128i off1_current_bytes, + __m128i *has_error) { + __m128i maskED = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xED)); + __m128i maskF4 = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xF4)); + + __m128i badfollowED = + _mm_and_si128(_mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x9F)), maskED); + __m128i badfollowF4 = + _mm_and_si128(_mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x8F)), maskF4); + + *has_error = _mm_or_si128(*has_error, _mm_or_si128(badfollowED, badfollowF4)); +} + +// map off1_hibits => error condition +// hibits off1 cur +// C => < C2 && true +// E => < E1 && < A0 +// F => < F1 && < 90 +// else false && false +static inline void checkOverlong(__m128i current_bytes, + __m128i off1_current_bytes, __m128i hibits, + __m128i previous_hibits, __m128i *has_error) { + __m128i off1_hibits = _mm_alignr_epi8(hibits, previous_hibits, 16 - 1); + __m128i initial_mins = _mm_shuffle_epi8( + _mm_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, // 10xx => false + 0xC2, -128, // 110x + 0xE1, // 1110 + 0xF1), + off1_hibits); + + __m128i initial_under = _mm_cmpgt_epi8(initial_mins, off1_current_bytes); + + __m128i second_mins = _mm_shuffle_epi8( + _mm_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, // 10xx => false + 127, 127, // 110x => true + 0xA0, // 1110 + 0x90), + off1_hibits); + __m128i second_under = _mm_cmpgt_epi8(second_mins, current_bytes); + *has_error = + _mm_or_si128(*has_error, _mm_and_si128(initial_under, second_under)); +} + +struct processed_utf_bytes { + __m128i rawbytes; + __m128i high_nibbles; + __m128i carried_continuations; +}; + +static inline void count_nibbles(__m128i bytes, + struct processed_utf_bytes *answer) { + answer->rawbytes = bytes; + answer->high_nibbles = + _mm_and_si128(_mm_srli_epi16(bytes, 4), _mm_set1_epi8(0x0F)); +} + +// check whether the current bytes are valid UTF-8 +// at the end of the function, previous gets updated +static inline struct processed_utf_bytes +checkUTF8Bytes(__m128i current_bytes, struct processed_utf_bytes *previous, + __m128i *has_error) { + + struct processed_utf_bytes pb; + count_nibbles(current_bytes, &pb); + + checkSmallerThan0xF4(current_bytes, has_error); + + __m128i initial_lengths = continuationLengths(pb.high_nibbles); + + pb.carried_continuations = + carryContinuations(initial_lengths, previous->carried_continuations); + + checkContinuations(initial_lengths, pb.carried_continuations, has_error); + + __m128i off1_current_bytes = + _mm_alignr_epi8(pb.rawbytes, previous->rawbytes, 16 - 1); + checkFirstContinuationMax(current_bytes, off1_current_bytes, has_error); + + checkOverlong(current_bytes, off1_current_bytes, pb.high_nibbles, + previous->high_nibbles, has_error); + return pb; +} + +/* Return 0 on success, -1 on error */ +int utf8_lemire(const unsigned char *src, int len) { + size_t i = 0; + __m128i has_error = _mm_setzero_si128(); + struct processed_utf_bytes previous = {.rawbytes = _mm_setzero_si128(), + .high_nibbles = _mm_setzero_si128(), + .carried_continuations = + _mm_setzero_si128()}; + if (len >= 16) { + for (; i <= len - 16; i += 16) { + __m128i current_bytes = _mm_loadu_si128((const __m128i *)(src + i)); + previous = checkUTF8Bytes(current_bytes, &previous, &has_error); + } + } + + // last part + if (i < len) { + char buffer[16]; + memset(buffer, 0, 16); + memcpy(buffer, src + i, len - i); + __m128i current_bytes = _mm_loadu_si128((const __m128i *)(buffer)); + previous = checkUTF8Bytes(current_bytes, &previous, &has_error); + } else { + has_error = + _mm_or_si128(_mm_cmpgt_epi8(previous.carried_continuations, + _mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 1)), + has_error); + } + + return _mm_testz_si128(has_error, has_error) ? 0 : -1; +} + +#endif diff --git a/third_party/utf8_range/lookup.c b/third_party/utf8_range/lookup.c new file mode 100644 index 0000000000000..2eb892598dd40 --- /dev/null +++ b/third_party/utf8_range/lookup.c @@ -0,0 +1,41 @@ +#include + +/* http://bjoern.hoehrmann.de/utf-8/decoder/dfa */ +/* Optimized version based on Rich Felker's variant. */ +#define UTF8_ACCEPT 0 +#define UTF8_REJECT 12 + +static const unsigned char utf8d[] = { + /* The first part of the table maps bytes to character classes that + * to reduce the size of the transition table and create bitmasks. */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8 +}; +/* Note: Splitting the table improves performance on ARM due to its simpler + * addressing modes not being able to encode x[y + 256]. */ +static const unsigned char utf8s[] = { + /* The second part is a transition table that maps a combination + * of a state of the automaton and a character class to a state. */ + 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, + 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, + 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, + 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, + 12,36,12,12,12,12,12,12,12,12,12,12 +}; + +/* Return 0 on success, -1 on error */ +int utf8_lookup(const unsigned char *data, int len) +{ + int state = 0; + + while (len-- && state != UTF8_REJECT) + state = utf8s[state + utf8d[*data++]]; + + return state == UTF8_ACCEPT ? 0 : -1; +} diff --git a/third_party/utf8_range/main.c b/third_party/utf8_range/main.c new file mode 100644 index 0000000000000..214fd4ff5c9cb --- /dev/null +++ b/third_party/utf8_range/main.c @@ -0,0 +1,405 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int utf8_naive(const unsigned char *data, int len); +int utf8_lookup(const unsigned char *data, int len); +int utf8_boost(const unsigned char *data, int len); +int utf8_lemire(const unsigned char *data, int len); +int utf8_range(const unsigned char *data, int len); +int utf8_range2(const unsigned char *data, int len); +#ifdef __AVX2__ +int utf8_lemire_avx2(const unsigned char *data, int len); +int utf8_range_avx2(const unsigned char *data, int len); +#endif + +static struct ftab { + const char *name; + int (*func)(const unsigned char *data, int len); +} ftab[] = { + { + .name = "naive", + .func = utf8_naive, + }, + { + .name = "lookup", + .func = utf8_lookup, + }, + { + .name = "lemire", + .func = utf8_lemire, + }, + { + .name = "range", + .func = utf8_range, + }, + { + .name = "range2", + .func = utf8_range2, + }, +#ifdef __AVX2__ + { + .name = "lemire_avx2", + .func = utf8_lemire_avx2, + }, + { + .name = "range_avx2", + .func = utf8_range_avx2, + }, +#endif +#ifdef BOOST + { + .name = "boost", + .func = utf8_boost, + }, +#endif +}; + +static unsigned char *load_test_buf(int len) +{ + const char utf8[] = "\xF0\x90\xBF\x80"; + const int utf8_len = sizeof(utf8)/sizeof(utf8[0]) - 1; + + unsigned char *data = malloc(len); + unsigned char *p = data; + + while (len >= utf8_len) { + memcpy(p, utf8, utf8_len); + p += utf8_len; + len -= utf8_len; + } + + while (len--) + *p++ = 0x7F; + + return data; +} + +static unsigned char *load_test_file(int *len) +{ + unsigned char *data; + int fd; + struct stat stat; + + fd = open("./UTF-8-demo.txt", O_RDONLY); + if (fd == -1) { + printf("Failed to open UTF-8-demo.txt!\n"); + exit(1); + } + if (fstat(fd, &stat) == -1) { + printf("Failed to get file size!\n"); + exit(1); + } + + *len = stat.st_size; + data = malloc(*len); + if (read(fd, data, *len) != *len) { + printf("Failed to read file!\n"); + exit(1); + } + + utf8_range(data, *len); +#ifdef __AVX2__ + utf8_range_avx2(data, *len); +#endif + close(fd); + + return data; +} + +static void print_test(const unsigned char *data, int len) +{ + while (len--) + printf("\\x%02X", *data++); + + printf("\n"); +} + +struct test { + const unsigned char *data; + int len; +}; + +static void prepare_test_buf(unsigned char *buf, const struct test *pos, + int pos_len, int pos_idx) +{ + /* Round concatenate correct tokens to 1024 bytes */ + int buf_idx = 0; + while (buf_idx < 1024) { + int buf_len = 1024 - buf_idx; + + if (buf_len >= pos[pos_idx].len) { + memcpy(buf+buf_idx, pos[pos_idx].data, pos[pos_idx].len); + buf_idx += pos[pos_idx].len; + } else { + memset(buf+buf_idx, 0, buf_len); + buf_idx += buf_len; + } + + if (++pos_idx == pos_len) + pos_idx = 0; + } +} + +/* Return 0 on success, -1 on error */ +static int test_manual(const struct ftab *ftab) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpointer-sign" + /* positive tests */ + static const struct test pos[] = { + {"", 0}, + {"\x00", 1}, + {"\x66", 1}, + {"\x7F", 1}, + {"\x00\x7F", 2}, + {"\x7F\x00", 2}, + {"\xC2\x80", 2}, + {"\xDF\xBF", 2}, + {"\xE0\xA0\x80", 3}, + {"\xE0\xA0\xBF", 3}, + {"\xED\x9F\x80", 3}, + {"\xEF\x80\xBF", 3}, + {"\xF0\x90\xBF\x80", 4}, + {"\xF2\x81\xBE\x99", 4}, + {"\xF4\x8F\x88\xAA", 4}, + }; + + /* negative tests */ + static const struct test neg[] = { + {"\x80", 1}, + {"\xBF", 1}, + {"\xC0\x80", 2}, + {"\xC1\x00", 2}, + {"\xC2\x7F", 2}, + {"\xDF\xC0", 2}, + {"\xE0\x9F\x80", 3}, + {"\xE0\xC2\x80", 3}, + {"\xED\xA0\x80", 3}, + {"\xED\x7F\x80", 3}, + {"\xEF\x80\x00", 3}, + {"\xF0\x8F\x80\x80", 4}, + {"\xF0\xEE\x80\x80", 4}, + {"\xF2\x90\x91\x7F", 4}, + {"\xF4\x90\x88\xAA", 4}, + {"\xF4\x00\xBF\xBF", 4}, + {"\x00\x00\x00\x00\x00\xC2\x80\x00\x00\x00\xE1\x80\x80\x00\x00\xC2" \ + "\xC2\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + 32}, + {"\x00\x00\x00\x00\x00\xC2\xC2\x80\x00\x00\xE1\x80\x80\x00\x00\x00", + 16}, + {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xF1\x80", + 32}, + {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xF1", + 32}, + {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xF1\x80" \ + "\x80", 33}, + {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xF1\x80" \ + "\xC2\x80", 34}, + {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xF0" \ + "\x80\x80\x80", 35}, + }; +#pragma GCC diagnostic push + + /* Test single token */ + for (int i = 0; i < sizeof(pos)/sizeof(pos[0]); ++i) { + if (ftab->func(pos[i].data, pos[i].len) != 0) { + printf("FAILED positive test: "); + print_test(pos[i].data, pos[i].len); + return -1; + } + } + for (int i = 0; i < sizeof(neg)/sizeof(neg[0]); ++i) { + if (ftab->func(neg[i].data, neg[i].len) == 0) { + printf("FAILED negitive test: "); + print_test(neg[i].data, neg[i].len); + return -1; + } + } + + /* Test shifted buffer to cover 1k length */ + /* buffer size must be greater than 1024 + 16 + max(test string length) */ + const int max_size = 1024*2; + uint64_t buf64[max_size/8 + 2]; + /* Offset 8 bytes by 1 byte */ + unsigned char *buf = ((unsigned char *)buf64) + 1; + int buf_len; + + for (int i = 0; i < sizeof(pos)/sizeof(pos[0]); ++i) { + /* Positive test: shift 16 bytes, validate each shift */ + prepare_test_buf(buf, pos, sizeof(pos)/sizeof(pos[0]), i); + buf_len = 1024; + for (int j = 0; j < 16; ++j) { + if (ftab->func(buf, buf_len) != 0) { + printf("FAILED positive test: "); + print_test(buf, buf_len); + return -1; + } + for (int k = buf_len; k >= 1; --k) + buf[k] = buf[k-1]; + buf[0] = '\x55'; + ++buf_len; + } + + /* Negative test: trunk last non ascii */ + while (buf_len >= 1 && buf[buf_len-1] <= 0x7F) + --buf_len; + if (buf_len && ftab->func(buf, buf_len-1) == 0) { + printf("FAILED negitive test: "); + print_test(buf, buf_len); + return -1; + } + } + + /* Negative test */ + for (int i = 0; i < sizeof(neg)/sizeof(neg[0]); ++i) { + /* Append one error token, shift 16 bytes, validate each shift */ + int pos_idx = i % (sizeof(pos)/sizeof(pos[0])); + prepare_test_buf(buf, pos, sizeof(pos)/sizeof(pos[0]), pos_idx); + memcpy(buf+1024, neg[i].data, neg[i].len); + buf_len = 1024 + neg[i].len; + for (int j = 0; j < 16; ++j) { + if (ftab->func(buf, buf_len) == 0) { + printf("FAILED negative test: "); + print_test(buf, buf_len); + return -1; + } + for (int k = buf_len; k >= 1; --k) + buf[k] = buf[k-1]; + buf[0] = '\x66'; + ++buf_len; + } + } + + return 0; +} + +static int test(const unsigned char *data, int len, const struct ftab *ftab) +{ + int ret_standard = ftab->func(data, len); + int ret_manual = test_manual(ftab); + printf("%s\n", ftab->name); + printf("standard test: %s\n", ret_standard ? "FAIL" : "pass"); + printf("manual test: %s\n", ret_manual ? "FAIL" : "pass"); + + return ret_standard | ret_manual; +} + +static int bench(const unsigned char *data, int len, const struct ftab *ftab) +{ + const int loops = 1024*1024*1024/len; + int ret = 0; + double time, size; + struct timeval tv1, tv2; + + fprintf(stderr, "bench %s... ", ftab->name); + gettimeofday(&tv1, 0); + for (int i = 0; i < loops; ++i) + ret |= ftab->func(data, len); + gettimeofday(&tv2, 0); + printf("%s\n", ret?"FAIL":"pass"); + + time = tv2.tv_usec - tv1.tv_usec; + time = time / 1000000 + tv2.tv_sec - tv1.tv_sec; + size = ((double)len * loops) / (1024*1024); + printf("time: %.4f s\n", time); + printf("data: %.0f MB\n", size); + printf("BW: %.2f MB/s\n", size / time); + + return 0; +} + +static void usage(const char *bin) +{ + printf("Usage:\n"); + printf("%s test [alg] ==> test all or one algorithm\n", bin); + printf("%s bench [alg] ==> benchmark all or one algorithm\n", bin); + printf("%s bench size NUM ==> benchmark with specific buffer size\n", bin); + printf("alg = "); + for (int i = 0; i < sizeof(ftab)/sizeof(ftab[0]); ++i) + printf("%s ", ftab[i].name); + printf("\nNUM = buffer size in bytes, 1 ~ 67108864(64M)\n"); +} + +int main(int argc, char *argv[]) +{ + int len = 0; + unsigned char *data; + const char *alg = NULL; + int (*tb)(const unsigned char *data, int len, const struct ftab *ftab); + + tb = NULL; + if (argc >= 2) { + if (strcmp(argv[1], "test") == 0) + tb = test; + else if (strcmp(argv[1], "bench") == 0) + tb = bench; + if (argc >= 3) { + alg = argv[2]; + if (strcmp(alg, "size") == 0) { + if (argc < 4) { + tb = NULL; + } else { + alg = NULL; + len = atoi(argv[3]); + if (len <= 0 || len > 67108864) { + printf("Buffer size error!\n\n"); + tb = NULL; + } + } + } + } + } + + if (tb == NULL) { + usage(argv[0]); + return 1; + } + + /* Load UTF8 test buffer */ + if (len) + data = load_test_buf(len); + else + data = load_test_file(&len); + + int ret = 0; + if (tb == bench) + printf("=============== Bench UTF8 (%d bytes) ===============\n", len); + for (int i = 0; i < sizeof(ftab)/sizeof(ftab[0]); ++i) { + if (alg && strcmp(alg, ftab[i].name) != 0) + continue; + ret |= tb((const unsigned char *)data, len, &ftab[i]); + printf("\n"); + } + +#if 0 + if (tb == bench) { + printf("==================== Bench ASCII ====================\n"); + /* Change test buffer to ascii */ + for (int i = 0; i < len; i++) + data[i] &= 0x7F; + + for (int i = 0; i < sizeof(ftab)/sizeof(ftab[0]); ++i) { + if (alg && strcmp(alg, ftab[i].name) != 0) + continue; + tb((const unsigned char *)data, len, &ftab[i]); + printf("\n"); + } + } +#endif + + free(data); + + return ret; +} diff --git a/third_party/utf8_range/naive.c b/third_party/utf8_range/naive.c new file mode 100644 index 0000000000000..9b7e5bb4be9ae --- /dev/null +++ b/third_party/utf8_range/naive.c @@ -0,0 +1,92 @@ +#include + +/* + * http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf - page 94 + * + * Table 3-7. Well-Formed UTF-8 Byte Sequences + * + * +--------------------+------------+-------------+------------+-------------+ + * | Code Points | First Byte | Second Byte | Third Byte | Fourth Byte | + * +--------------------+------------+-------------+------------+-------------+ + * | U+0000..U+007F | 00..7F | | | | + * +--------------------+------------+-------------+------------+-------------+ + * | U+0080..U+07FF | C2..DF | 80..BF | | | + * +--------------------+------------+-------------+------------+-------------+ + * | U+0800..U+0FFF | E0 | A0..BF | 80..BF | | + * +--------------------+------------+-------------+------------+-------------+ + * | U+1000..U+CFFF | E1..EC | 80..BF | 80..BF | | + * +--------------------+------------+-------------+------------+-------------+ + * | U+D000..U+D7FF | ED | 80..9F | 80..BF | | + * +--------------------+------------+-------------+------------+-------------+ + * | U+E000..U+FFFF | EE..EF | 80..BF | 80..BF | | + * +--------------------+------------+-------------+------------+-------------+ + * | U+10000..U+3FFFF | F0 | 90..BF | 80..BF | 80..BF | + * +--------------------+------------+-------------+------------+-------------+ + * | U+40000..U+FFFFF | F1..F3 | 80..BF | 80..BF | 80..BF | + * +--------------------+------------+-------------+------------+-------------+ + * | U+100000..U+10FFFF | F4 | 80..8F | 80..BF | 80..BF | + * +--------------------+------------+-------------+------------+-------------+ + */ + +/* Return 0 - success, >0 - index(1 based) of first error char */ +int utf8_naive(const unsigned char *data, int len) +{ + int err_pos = 1; + + while (len) { + int bytes; + const unsigned char byte1 = data[0]; + + /* 00..7F */ + if (byte1 <= 0x7F) { + bytes = 1; + /* C2..DF, 80..BF */ + } else if (len >= 2 && byte1 >= 0xC2 && byte1 <= 0xDF && + (signed char)data[1] <= (signed char)0xBF) { + bytes = 2; + } else if (len >= 3) { + const unsigned char byte2 = data[1]; + + /* Is byte2, byte3 between 0x80 ~ 0xBF */ + const int byte2_ok = (signed char)byte2 <= (signed char)0xBF; + const int byte3_ok = (signed char)data[2] <= (signed char)0xBF; + + if (byte2_ok && byte3_ok && + /* E0, A0..BF, 80..BF */ + ((byte1 == 0xE0 && byte2 >= 0xA0) || + /* E1..EC, 80..BF, 80..BF */ + (byte1 >= 0xE1 && byte1 <= 0xEC) || + /* ED, 80..9F, 80..BF */ + (byte1 == 0xED && byte2 <= 0x9F) || + /* EE..EF, 80..BF, 80..BF */ + (byte1 >= 0xEE && byte1 <= 0xEF))) { + bytes = 3; + } else if (len >= 4) { + /* Is byte4 between 0x80 ~ 0xBF */ + const int byte4_ok = (signed char)data[3] <= (signed char)0xBF; + + if (byte2_ok && byte3_ok && byte4_ok && + /* F0, 90..BF, 80..BF, 80..BF */ + ((byte1 == 0xF0 && byte2 >= 0x90) || + /* F1..F3, 80..BF, 80..BF, 80..BF */ + (byte1 >= 0xF1 && byte1 <= 0xF3) || + /* F4, 80..8F, 80..BF, 80..BF */ + (byte1 == 0xF4 && byte2 <= 0x8F))) { + bytes = 4; + } else { + return err_pos; + } + } else { + return err_pos; + } + } else { + return err_pos; + } + + len -= bytes; + err_pos += bytes; + data += bytes; + } + + return 0; +} diff --git a/third_party/utf8_range/range-avx2.c b/third_party/utf8_range/range-avx2.c new file mode 100644 index 0000000000000..0791cf83ce1d0 --- /dev/null +++ b/third_party/utf8_range/range-avx2.c @@ -0,0 +1,277 @@ +#ifdef __AVX2__ + +#include +#include +#include + +int utf8_naive(const unsigned char *data, int len); + +#if 0 +static void print256(const char *s, const __m256i v256) +{ + const unsigned char *v8 = (const unsigned char *)&v256; + if (s) + printf("%s:\t", s); + for (int i = 0; i < 32; i++) + printf("%02x ", v8[i]); + printf("\n"); +} +#endif + +/* + * Map high nibble of "First Byte" to legal character length minus 1 + * 0x00 ~ 0xBF --> 0 + * 0xC0 ~ 0xDF --> 1 + * 0xE0 ~ 0xEF --> 2 + * 0xF0 ~ 0xFF --> 3 + */ +static const int8_t _first_len_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, +}; + +/* Map "First Byte" to 8-th item of range table (0xC2 ~ 0xF4) */ +static const int8_t _first_range_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, +}; + +/* + * Range table, map range index to min and max values + * Index 0 : 00 ~ 7F (First Byte, ascii) + * Index 1,2,3: 80 ~ BF (Second, Third, Fourth Byte) + * Index 4 : A0 ~ BF (Second Byte after E0) + * Index 5 : 80 ~ 9F (Second Byte after ED) + * Index 6 : 90 ~ BF (Second Byte after F0) + * Index 7 : 80 ~ 8F (Second Byte after F4) + * Index 8 : C2 ~ F4 (First Byte, non ascii) + * Index 9~15 : illegal: i >= 127 && i <= -128 + */ +static const int8_t _range_min_tbl[] = { + 0x00, 0x80, 0x80, 0x80, 0xA0, 0x80, 0x90, 0x80, + 0xC2, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, + 0x00, 0x80, 0x80, 0x80, 0xA0, 0x80, 0x90, 0x80, + 0xC2, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +}; +static const int8_t _range_max_tbl[] = { + 0x7F, 0xBF, 0xBF, 0xBF, 0xBF, 0x9F, 0xBF, 0x8F, + 0xF4, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x7F, 0xBF, 0xBF, 0xBF, 0xBF, 0x9F, 0xBF, 0x8F, + 0xF4, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +}; + +/* + * Tables for fast handling of four special First Bytes(E0,ED,F0,F4), after + * which the Second Byte are not 80~BF. It contains "range index adjustment". + * +------------+---------------+------------------+----------------+ + * | First Byte | original range| range adjustment | adjusted range | + * +------------+---------------+------------------+----------------+ + * | E0 | 2 | 2 | 4 | + * +------------+---------------+------------------+----------------+ + * | ED | 2 | 3 | 5 | + * +------------+---------------+------------------+----------------+ + * | F0 | 3 | 3 | 6 | + * +------------+---------------+------------------+----------------+ + * | F4 | 4 | 4 | 8 | + * +------------+---------------+------------------+----------------+ + */ +/* index1 -> E0, index14 -> ED */ +static const int8_t _df_ee_tbl[] = { + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, +}; +/* index1 -> F0, index5 -> F4 */ +static const int8_t _ef_fe_tbl[] = { + 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +#define RET_ERR_IDX 0 /* Define 1 to return index of first error char */ + +static inline __m256i push_last_byte_of_a_to_b(__m256i a, __m256i b) { + return _mm256_alignr_epi8(b, _mm256_permute2x128_si256(a, b, 0x21), 15); +} + +static inline __m256i push_last_2bytes_of_a_to_b(__m256i a, __m256i b) { + return _mm256_alignr_epi8(b, _mm256_permute2x128_si256(a, b, 0x21), 14); +} + +static inline __m256i push_last_3bytes_of_a_to_b(__m256i a, __m256i b) { + return _mm256_alignr_epi8(b, _mm256_permute2x128_si256(a, b, 0x21), 13); +} + +/* 5x faster than naive method */ +/* Return 0 - success, -1 - error, >0 - first error char(if RET_ERR_IDX = 1) */ +int utf8_range_avx2(const unsigned char *data, int len) +{ +#if RET_ERR_IDX + int err_pos = 1; +#endif + + if (len >= 32) { + __m256i prev_input = _mm256_set1_epi8(0); + __m256i prev_first_len = _mm256_set1_epi8(0); + + /* Cached tables */ + const __m256i first_len_tbl = + _mm256_loadu_si256((const __m256i *)_first_len_tbl); + const __m256i first_range_tbl = + _mm256_loadu_si256((const __m256i *)_first_range_tbl); + const __m256i range_min_tbl = + _mm256_loadu_si256((const __m256i *)_range_min_tbl); + const __m256i range_max_tbl = + _mm256_loadu_si256((const __m256i *)_range_max_tbl); + const __m256i df_ee_tbl = + _mm256_loadu_si256((const __m256i *)_df_ee_tbl); + const __m256i ef_fe_tbl = + _mm256_loadu_si256((const __m256i *)_ef_fe_tbl); + +#if !RET_ERR_IDX + __m256i error1 = _mm256_set1_epi8(0); + __m256i error2 = _mm256_set1_epi8(0); +#endif + + while (len >= 32) { + const __m256i input = _mm256_loadu_si256((const __m256i *)data); + + /* high_nibbles = input >> 4 */ + const __m256i high_nibbles = + _mm256_and_si256(_mm256_srli_epi16(input, 4), _mm256_set1_epi8(0x0F)); + + /* first_len = legal character length minus 1 */ + /* 0 for 00~7F, 1 for C0~DF, 2 for E0~EF, 3 for F0~FF */ + /* first_len = first_len_tbl[high_nibbles] */ + __m256i first_len = _mm256_shuffle_epi8(first_len_tbl, high_nibbles); + + /* First Byte: set range index to 8 for bytes within 0xC0 ~ 0xFF */ + /* range = first_range_tbl[high_nibbles] */ + __m256i range = _mm256_shuffle_epi8(first_range_tbl, high_nibbles); + + /* Second Byte: set range index to first_len */ + /* 0 for 00~7F, 1 for C0~DF, 2 for E0~EF, 3 for F0~FF */ + /* range |= (first_len, prev_first_len) << 1 byte */ + range = _mm256_or_si256( + range, push_last_byte_of_a_to_b(prev_first_len, first_len)); + + /* Third Byte: set range index to saturate_sub(first_len, 1) */ + /* 0 for 00~7F, 0 for C0~DF, 1 for E0~EF, 2 for F0~FF */ + __m256i tmp1, tmp2; + + /* tmp1 = (first_len, prev_first_len) << 2 bytes */ + tmp1 = push_last_2bytes_of_a_to_b(prev_first_len, first_len); + /* tmp2 = saturate_sub(tmp1, 1) */ + tmp2 = _mm256_subs_epu8(tmp1, _mm256_set1_epi8(1)); + + /* range |= tmp2 */ + range = _mm256_or_si256(range, tmp2); + + /* Fourth Byte: set range index to saturate_sub(first_len, 2) */ + /* 0 for 00~7F, 0 for C0~DF, 0 for E0~EF, 1 for F0~FF */ + /* tmp1 = (first_len, prev_first_len) << 3 bytes */ + tmp1 = push_last_3bytes_of_a_to_b(prev_first_len, first_len); + /* tmp2 = saturate_sub(tmp1, 2) */ + tmp2 = _mm256_subs_epu8(tmp1, _mm256_set1_epi8(2)); + /* range |= tmp2 */ + range = _mm256_or_si256(range, tmp2); + + /* + * Now we have below range indices caluclated + * Correct cases: + * - 8 for C0~FF + * - 3 for 1st byte after F0~FF + * - 2 for 1st byte after E0~EF or 2nd byte after F0~FF + * - 1 for 1st byte after C0~DF or 2nd byte after E0~EF or + * 3rd byte after F0~FF + * - 0 for others + * Error cases: + * 9,10,11 if non ascii First Byte overlaps + * E.g., F1 80 C2 90 --> 8 3 10 2, where 10 indicates error + */ + + /* Adjust Second Byte range for special First Bytes(E0,ED,F0,F4) */ + /* Overlaps lead to index 9~15, which are illegal in range table */ + __m256i shift1, pos, range2; + /* shift1 = (input, prev_input) << 1 byte */ + shift1 = push_last_byte_of_a_to_b(prev_input, input); + pos = _mm256_sub_epi8(shift1, _mm256_set1_epi8(0xEF)); + /* + * shift1: | EF F0 ... FE | FF 00 ... ... DE | DF E0 ... EE | + * pos: | 0 1 15 | 16 17 239| 240 241 255| + * pos-240: | 0 0 0 | 0 0 0 | 0 1 15 | + * pos+112: | 112 113 127| >= 128 | >= 128 | + */ + tmp1 = _mm256_subs_epu8(pos, _mm256_set1_epi8(240)); + range2 = _mm256_shuffle_epi8(df_ee_tbl, tmp1); + tmp2 = _mm256_adds_epu8(pos, _mm256_set1_epi8(112)); + range2 = _mm256_add_epi8(range2, _mm256_shuffle_epi8(ef_fe_tbl, tmp2)); + + range = _mm256_add_epi8(range, range2); + + /* Load min and max values per calculated range index */ + __m256i minv = _mm256_shuffle_epi8(range_min_tbl, range); + __m256i maxv = _mm256_shuffle_epi8(range_max_tbl, range); + + /* Check value range */ +#if RET_ERR_IDX + __m256i error = _mm256_cmpgt_epi8(minv, input); + error = _mm256_or_si256(error, _mm256_cmpgt_epi8(input, maxv)); + /* 5% performance drop from this conditional branch */ + if (!_mm256_testz_si256(error, error)) + break; +#else + error1 = _mm256_or_si256(error1, _mm256_cmpgt_epi8(minv, input)); + error2 = _mm256_or_si256(error2, _mm256_cmpgt_epi8(input, maxv)); +#endif + + prev_input = input; + prev_first_len = first_len; + + data += 32; + len -= 32; +#if RET_ERR_IDX + err_pos += 32; +#endif + } + +#if RET_ERR_IDX + /* Error in first 16 bytes */ + if (err_pos == 1) + goto do_naive; +#else + __m256i error = _mm256_or_si256(error1, error2); + if (!_mm256_testz_si256(error, error)) + return -1; +#endif + + /* Find previous token (not 80~BF) */ + int32_t token4 = _mm256_extract_epi32(prev_input, 7); + const int8_t *token = (const int8_t *)&token4; + int lookahead = 0; + if (token[3] > (int8_t)0xBF) + lookahead = 1; + else if (token[2] > (int8_t)0xBF) + lookahead = 2; + else if (token[1] > (int8_t)0xBF) + lookahead = 3; + + data -= lookahead; + len += lookahead; +#if RET_ERR_IDX + err_pos -= lookahead; +#endif + } + + /* Check remaining bytes with naive method */ +#if RET_ERR_IDX + int err_pos2; +do_naive: + err_pos2 = utf8_naive(data, len); + if (err_pos2) + return err_pos + err_pos2 - 1; + return 0; +#else + return utf8_naive(data, len); +#endif +} + +#endif diff --git a/third_party/utf8_range/range-neon.c b/third_party/utf8_range/range-neon.c new file mode 100644 index 0000000000000..99c2a24835a4a --- /dev/null +++ b/third_party/utf8_range/range-neon.c @@ -0,0 +1,228 @@ +#ifdef __aarch64__ + +#include +#include +#include + +int utf8_naive(const unsigned char *data, int len); + +#if 0 +static void print128(const char *s, const uint8x16_t v128) +{ + unsigned char v8[16]; + vst1q_u8(v8, v128); + + if (s) + printf("%s:\t", s); + for (int i = 0; i < 16; ++i) + printf("%02x ", v8[i]); + printf("\n"); +} +#endif + +/* + * Map high nibble of "First Byte" to legal character length minus 1 + * 0x00 ~ 0xBF --> 0 + * 0xC0 ~ 0xDF --> 1 + * 0xE0 ~ 0xEF --> 2 + * 0xF0 ~ 0xFF --> 3 + */ +static const uint8_t _first_len_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, +}; + +/* Map "First Byte" to 8-th item of range table (0xC2 ~ 0xF4) */ +static const uint8_t _first_range_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, +}; + +/* + * Range table, map range index to min and max values + * Index 0 : 00 ~ 7F (First Byte, ascii) + * Index 1,2,3: 80 ~ BF (Second, Third, Fourth Byte) + * Index 4 : A0 ~ BF (Second Byte after E0) + * Index 5 : 80 ~ 9F (Second Byte after ED) + * Index 6 : 90 ~ BF (Second Byte after F0) + * Index 7 : 80 ~ 8F (Second Byte after F4) + * Index 8 : C2 ~ F4 (First Byte, non ascii) + * Index 9~15 : illegal: u >= 255 && u <= 0 + */ +static const uint8_t _range_min_tbl[] = { + 0x00, 0x80, 0x80, 0x80, 0xA0, 0x80, 0x90, 0x80, + 0xC2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +}; +static const uint8_t _range_max_tbl[] = { + 0x7F, 0xBF, 0xBF, 0xBF, 0xBF, 0x9F, 0xBF, 0x8F, + 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +/* + * This table is for fast handling four special First Bytes(E0,ED,F0,F4), after + * which the Second Byte are not 80~BF. It contains "range index adjustment". + * - The idea is to minus byte with E0, use the result(0~31) as the index to + * lookup the "range index adjustment". Then add the adjustment to original + * range index to get the correct range. + * - Range index adjustment + * +------------+---------------+------------------+----------------+ + * | First Byte | original range| range adjustment | adjusted range | + * +------------+---------------+------------------+----------------+ + * | E0 | 2 | 2 | 4 | + * +------------+---------------+------------------+----------------+ + * | ED | 2 | 3 | 5 | + * +------------+---------------+------------------+----------------+ + * | F0 | 3 | 3 | 6 | + * +------------+---------------+------------------+----------------+ + * | F4 | 4 | 4 | 8 | + * +------------+---------------+------------------+----------------+ + * - Below is a uint8x16x2 table, data is interleaved in NEON register. So I'm + * putting it vertically. 1st column is for E0~EF, 2nd column for F0~FF. + */ +static const uint8_t _range_adjust_tbl[] = { + /* index -> 0~15 16~31 <- index */ + /* E0 -> */ 2, 3, /* <- F0 */ + 0, 0, + 0, 0, + 0, 0, + 0, 4, /* <- F4 */ + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + /* ED -> */ 3, 0, + 0, 0, + 0, 0, +}; + +/* 2x ~ 4x faster than naive method */ +/* Return 0 on success, -1 on error */ +int utf8_range(const unsigned char *data, int len) +{ + if (len >= 16) { + uint8x16_t prev_input = vdupq_n_u8(0); + uint8x16_t prev_first_len = vdupq_n_u8(0); + + /* Cached tables */ + const uint8x16_t first_len_tbl = vld1q_u8(_first_len_tbl); + const uint8x16_t first_range_tbl = vld1q_u8(_first_range_tbl); + const uint8x16_t range_min_tbl = vld1q_u8(_range_min_tbl); + const uint8x16_t range_max_tbl = vld1q_u8(_range_max_tbl); + const uint8x16x2_t range_adjust_tbl = vld2q_u8(_range_adjust_tbl); + + /* Cached values */ + const uint8x16_t const_1 = vdupq_n_u8(1); + const uint8x16_t const_2 = vdupq_n_u8(2); + const uint8x16_t const_e0 = vdupq_n_u8(0xE0); + + /* We use two error registers to remove a dependency. */ + uint8x16_t error1 = vdupq_n_u8(0); + uint8x16_t error2 = vdupq_n_u8(0); + + while (len >= 16) { + const uint8x16_t input = vld1q_u8(data); + + /* high_nibbles = input >> 4 */ + const uint8x16_t high_nibbles = vshrq_n_u8(input, 4); + + /* first_len = legal character length minus 1 */ + /* 0 for 00~7F, 1 for C0~DF, 2 for E0~EF, 3 for F0~FF */ + /* first_len = first_len_tbl[high_nibbles] */ + const uint8x16_t first_len = + vqtbl1q_u8(first_len_tbl, high_nibbles); + + /* First Byte: set range index to 8 for bytes within 0xC0 ~ 0xFF */ + /* range = first_range_tbl[high_nibbles] */ + uint8x16_t range = vqtbl1q_u8(first_range_tbl, high_nibbles); + + /* Second Byte: set range index to first_len */ + /* 0 for 00~7F, 1 for C0~DF, 2 for E0~EF, 3 for F0~FF */ + /* range |= (first_len, prev_first_len) << 1 byte */ + range = + vorrq_u8(range, vextq_u8(prev_first_len, first_len, 15)); + + /* Third Byte: set range index to saturate_sub(first_len, 1) */ + /* 0 for 00~7F, 0 for C0~DF, 1 for E0~EF, 2 for F0~FF */ + uint8x16_t tmp1, tmp2; + /* tmp1 = (first_len, prev_first_len) << 2 bytes */ + tmp1 = vextq_u8(prev_first_len, first_len, 14); + /* tmp1 = saturate_sub(tmp1, 1) */ + tmp1 = vqsubq_u8(tmp1, const_1); + /* range |= tmp1 */ + range = vorrq_u8(range, tmp1); + + /* Fourth Byte: set range index to saturate_sub(first_len, 2) */ + /* 0 for 00~7F, 0 for C0~DF, 0 for E0~EF, 1 for F0~FF */ + /* tmp2 = (first_len, prev_first_len) << 3 bytes */ + tmp2 = vextq_u8(prev_first_len, first_len, 13); + /* tmp2 = saturate_sub(tmp2, 2) */ + tmp2 = vqsubq_u8(tmp2, const_2); + /* range |= tmp2 */ + range = vorrq_u8(range, tmp2); + + /* + * Now we have below range indices caluclated + * Correct cases: + * - 8 for C0~FF + * - 3 for 1st byte after F0~FF + * - 2 for 1st byte after E0~EF or 2nd byte after F0~FF + * - 1 for 1st byte after C0~DF or 2nd byte after E0~EF or + * 3rd byte after F0~FF + * - 0 for others + * Error cases: + * 9,10,11 if non ascii First Byte overlaps + * E.g., F1 80 C2 90 --> 8 3 10 2, where 10 indicates error + */ + + /* Adjust Second Byte range for special First Bytes(E0,ED,F0,F4) */ + /* See _range_adjust_tbl[] definition for details */ + /* Overlaps lead to index 9~15, which are illegal in range table */ + uint8x16_t shift1 = vextq_u8(prev_input, input, 15); + uint8x16_t pos = vsubq_u8(shift1, const_e0); + range = vaddq_u8(range, vqtbl2q_u8(range_adjust_tbl, pos)); + + /* Load min and max values per calculated range index */ + uint8x16_t minv = vqtbl1q_u8(range_min_tbl, range); + uint8x16_t maxv = vqtbl1q_u8(range_max_tbl, range); + + /* Check value range */ + error1 = vorrq_u8(error1, vcltq_u8(input, minv)); + error2 = vorrq_u8(error2, vcgtq_u8(input, maxv)); + + prev_input = input; + prev_first_len = first_len; + + data += 16; + len -= 16; + } + /* Merge our error counters together */ + error1 = vorrq_u8(error1, error2); + + /* Delay error check till loop ends */ + if (vmaxvq_u8(error1)) + return -1; + + /* Find previous token (not 80~BF) */ + uint32_t token4; + vst1q_lane_u32(&token4, vreinterpretq_u32_u8(prev_input), 3); + + const int8_t *token = (const int8_t *)&token4; + int lookahead = 0; + if (token[3] > (int8_t)0xBF) + lookahead = 1; + else if (token[2] > (int8_t)0xBF) + lookahead = 2; + else if (token[1] > (int8_t)0xBF) + lookahead = 3; + + data -= lookahead; + len += lookahead; + } + + /* Check remaining bytes with naive method */ + return utf8_naive(data, len); +} + +#endif diff --git a/third_party/utf8_range/range-sse.c b/third_party/utf8_range/range-sse.c new file mode 100644 index 0000000000000..3dcde8e7b83b8 --- /dev/null +++ b/third_party/utf8_range/range-sse.c @@ -0,0 +1,255 @@ +#ifdef __x86_64__ + +#include +#include +#include + +int utf8_naive(const unsigned char *data, int len); + +#if 0 +static void print128(const char *s, const __m128i v128) +{ + const unsigned char *v8 = (const unsigned char *)&v128; + if (s) + printf("%s:\t", s); + for (int i = 0; i < 16; i++) + printf("%02x ", v8[i]); + printf("\n"); +} +#endif + +/* + * Map high nibble of "First Byte" to legal character length minus 1 + * 0x00 ~ 0xBF --> 0 + * 0xC0 ~ 0xDF --> 1 + * 0xE0 ~ 0xEF --> 2 + * 0xF0 ~ 0xFF --> 3 + */ +static const int8_t _first_len_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, +}; + +/* Map "First Byte" to 8-th item of range table (0xC2 ~ 0xF4) */ +static const int8_t _first_range_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, +}; + +/* + * Range table, map range index to min and max values + * Index 0 : 00 ~ 7F (First Byte, ascii) + * Index 1,2,3: 80 ~ BF (Second, Third, Fourth Byte) + * Index 4 : A0 ~ BF (Second Byte after E0) + * Index 5 : 80 ~ 9F (Second Byte after ED) + * Index 6 : 90 ~ BF (Second Byte after F0) + * Index 7 : 80 ~ 8F (Second Byte after F4) + * Index 8 : C2 ~ F4 (First Byte, non ascii) + * Index 9~15 : illegal: i >= 127 && i <= -128 + */ +static const int8_t _range_min_tbl[] = { + 0x00, 0x80, 0x80, 0x80, 0xA0, 0x80, 0x90, 0x80, + 0xC2, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +}; +static const int8_t _range_max_tbl[] = { + 0x7F, 0xBF, 0xBF, 0xBF, 0xBF, 0x9F, 0xBF, 0x8F, + 0xF4, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +}; + +/* + * Tables for fast handling of four special First Bytes(E0,ED,F0,F4), after + * which the Second Byte are not 80~BF. It contains "range index adjustment". + * +------------+---------------+------------------+----------------+ + * | First Byte | original range| range adjustment | adjusted range | + * +------------+---------------+------------------+----------------+ + * | E0 | 2 | 2 | 4 | + * +------------+---------------+------------------+----------------+ + * | ED | 2 | 3 | 5 | + * +------------+---------------+------------------+----------------+ + * | F0 | 3 | 3 | 6 | + * +------------+---------------+------------------+----------------+ + * | F4 | 4 | 4 | 8 | + * +------------+---------------+------------------+----------------+ + */ +/* index1 -> E0, index14 -> ED */ +static const int8_t _df_ee_tbl[] = { + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, +}; +/* index1 -> F0, index5 -> F4 */ +static const int8_t _ef_fe_tbl[] = { + 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +#define RET_ERR_IDX 0 /* Define 1 to return index of first error char */ + +/* 5x faster than naive method */ +/* Return 0 - success, -1 - error, >0 - first error char(if RET_ERR_IDX = 1) */ +int utf8_range(const unsigned char *data, int len) +{ +#if RET_ERR_IDX + int err_pos = 1; +#endif + + if (len >= 16) { + __m128i prev_input = _mm_set1_epi8(0); + __m128i prev_first_len = _mm_set1_epi8(0); + + /* Cached tables */ + const __m128i first_len_tbl = + _mm_loadu_si128((const __m128i *)_first_len_tbl); + const __m128i first_range_tbl = + _mm_loadu_si128((const __m128i *)_first_range_tbl); + const __m128i range_min_tbl = + _mm_loadu_si128((const __m128i *)_range_min_tbl); + const __m128i range_max_tbl = + _mm_loadu_si128((const __m128i *)_range_max_tbl); + const __m128i df_ee_tbl = + _mm_loadu_si128((const __m128i *)_df_ee_tbl); + const __m128i ef_fe_tbl = + _mm_loadu_si128((const __m128i *)_ef_fe_tbl); + + __m128i error = _mm_set1_epi8(0); + + while (len >= 16) { + const __m128i input = _mm_loadu_si128((const __m128i *)data); + + /* high_nibbles = input >> 4 */ + const __m128i high_nibbles = + _mm_and_si128(_mm_srli_epi16(input, 4), _mm_set1_epi8(0x0F)); + + /* first_len = legal character length minus 1 */ + /* 0 for 00~7F, 1 for C0~DF, 2 for E0~EF, 3 for F0~FF */ + /* first_len = first_len_tbl[high_nibbles] */ + __m128i first_len = _mm_shuffle_epi8(first_len_tbl, high_nibbles); + + /* First Byte: set range index to 8 for bytes within 0xC0 ~ 0xFF */ + /* range = first_range_tbl[high_nibbles] */ + __m128i range = _mm_shuffle_epi8(first_range_tbl, high_nibbles); + + /* Second Byte: set range index to first_len */ + /* 0 for 00~7F, 1 for C0~DF, 2 for E0~EF, 3 for F0~FF */ + /* range |= (first_len, prev_first_len) << 1 byte */ + range = _mm_or_si128( + range, _mm_alignr_epi8(first_len, prev_first_len, 15)); + + /* Third Byte: set range index to saturate_sub(first_len, 1) */ + /* 0 for 00~7F, 0 for C0~DF, 1 for E0~EF, 2 for F0~FF */ + __m128i tmp; + /* tmp = (first_len, prev_first_len) << 2 bytes */ + tmp = _mm_alignr_epi8(first_len, prev_first_len, 14); + /* tmp = saturate_sub(tmp, 1) */ + tmp = _mm_subs_epu8(tmp, _mm_set1_epi8(1)); + /* range |= tmp */ + range = _mm_or_si128(range, tmp); + + /* Fourth Byte: set range index to saturate_sub(first_len, 2) */ + /* 0 for 00~7F, 0 for C0~DF, 0 for E0~EF, 1 for F0~FF */ + /* tmp = (first_len, prev_first_len) << 3 bytes */ + tmp = _mm_alignr_epi8(first_len, prev_first_len, 13); + /* tmp = saturate_sub(tmp, 2) */ + tmp = _mm_subs_epu8(tmp, _mm_set1_epi8(2)); + /* range |= tmp */ + range = _mm_or_si128(range, tmp); + + /* + * Now we have below range indices caluclated + * Correct cases: + * - 8 for C0~FF + * - 3 for 1st byte after F0~FF + * - 2 for 1st byte after E0~EF or 2nd byte after F0~FF + * - 1 for 1st byte after C0~DF or 2nd byte after E0~EF or + * 3rd byte after F0~FF + * - 0 for others + * Error cases: + * 9,10,11 if non ascii First Byte overlaps + * E.g., F1 80 C2 90 --> 8 3 10 2, where 10 indicates error + */ + + /* Adjust Second Byte range for special First Bytes(E0,ED,F0,F4) */ + /* Overlaps lead to index 9~15, which are illegal in range table */ + __m128i shift1, pos, range2; + /* shift1 = (input, prev_input) << 1 byte */ + shift1 = _mm_alignr_epi8(input, prev_input, 15); + pos = _mm_sub_epi8(shift1, _mm_set1_epi8(0xEF)); + /* + * shift1: | EF F0 ... FE | FF 00 ... ... DE | DF E0 ... EE | + * pos: | 0 1 15 | 16 17 239| 240 241 255| + * pos-240: | 0 0 0 | 0 0 0 | 0 1 15 | + * pos+112: | 112 113 127| >= 128 | >= 128 | + */ + tmp = _mm_subs_epu8(pos, _mm_set1_epi8(0xF0)); + range2 = _mm_shuffle_epi8(df_ee_tbl, tmp); + tmp = _mm_adds_epu8(pos, _mm_set1_epi8(0x70)); + range2 = _mm_add_epi8(range2, _mm_shuffle_epi8(ef_fe_tbl, tmp)); + + range = _mm_add_epi8(range, range2); + + /* Load min and max values per calculated range index */ + __m128i minv = _mm_shuffle_epi8(range_min_tbl, range); + __m128i maxv = _mm_shuffle_epi8(range_max_tbl, range); + + /* Check value range */ +#if RET_ERR_IDX + error = _mm_cmplt_epi8(input, minv); + error = _mm_or_si128(error, _mm_cmpgt_epi8(input, maxv)); + /* 5% performance drop from this conditional branch */ + if (!_mm_testz_si128(error, error)) + break; +#else + /* error |= (input < minv) | (input > maxv) */ + tmp = _mm_or_si128( + _mm_cmplt_epi8(input, minv), + _mm_cmpgt_epi8(input, maxv) + ); + error = _mm_or_si128(error, tmp); +#endif + + prev_input = input; + prev_first_len = first_len; + + data += 16; + len -= 16; +#if RET_ERR_IDX + err_pos += 16; +#endif + } + +#if RET_ERR_IDX + /* Error in first 16 bytes */ + if (err_pos == 1) + goto do_naive; +#else + if (!_mm_testz_si128(error, error)) + return -1; +#endif + + /* Find previous token (not 80~BF) */ + int32_t token4 = _mm_extract_epi32(prev_input, 3); + const int8_t *token = (const int8_t *)&token4; + int lookahead = 0; + if (token[3] > (int8_t)0xBF) + lookahead = 1; + else if (token[2] > (int8_t)0xBF) + lookahead = 2; + else if (token[1] > (int8_t)0xBF) + lookahead = 3; + + data -= lookahead; + len += lookahead; +#if RET_ERR_IDX + err_pos -= lookahead; +#endif + } + + /* Check remaining bytes with naive method */ +#if RET_ERR_IDX + int err_pos2; +do_naive: + err_pos2 = utf8_naive(data, len); + if (err_pos2) + return err_pos + err_pos2 - 1; + return 0; +#else + return utf8_naive(data, len); +#endif +} + +#endif diff --git a/third_party/utf8_range/range2-neon.c b/third_party/utf8_range/range2-neon.c new file mode 100644 index 0000000000000..5367b6ebdcba9 --- /dev/null +++ b/third_party/utf8_range/range2-neon.c @@ -0,0 +1,157 @@ +/* + * Process 2x16 bytes in each iteration. + * Comments removed for brevity. See range-neon.c for details. + */ +#ifdef __aarch64__ + +#include +#include +#include + +int utf8_naive(const unsigned char *data, int len); + +static const uint8_t _first_len_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, +}; + +static const uint8_t _first_range_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, +}; + +static const uint8_t _range_min_tbl[] = { + 0x00, 0x80, 0x80, 0x80, 0xA0, 0x80, 0x90, 0x80, + 0xC2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +}; +static const uint8_t _range_max_tbl[] = { + 0x7F, 0xBF, 0xBF, 0xBF, 0xBF, 0x9F, 0xBF, 0x8F, + 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t _range_adjust_tbl[] = { + 2, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, +}; + +/* Return 0 on success, -1 on error */ +int utf8_range2(const unsigned char *data, int len) +{ + if (len >= 32) { + uint8x16_t prev_input = vdupq_n_u8(0); + uint8x16_t prev_first_len = vdupq_n_u8(0); + + const uint8x16_t first_len_tbl = vld1q_u8(_first_len_tbl); + const uint8x16_t first_range_tbl = vld1q_u8(_first_range_tbl); + const uint8x16_t range_min_tbl = vld1q_u8(_range_min_tbl); + const uint8x16_t range_max_tbl = vld1q_u8(_range_max_tbl); + const uint8x16x2_t range_adjust_tbl = vld2q_u8(_range_adjust_tbl); + + const uint8x16_t const_1 = vdupq_n_u8(1); + const uint8x16_t const_2 = vdupq_n_u8(2); + const uint8x16_t const_e0 = vdupq_n_u8(0xE0); + + uint8x16_t error1 = vdupq_n_u8(0); + uint8x16_t error2 = vdupq_n_u8(0); + uint8x16_t error3 = vdupq_n_u8(0); + uint8x16_t error4 = vdupq_n_u8(0); + + while (len >= 32) { + /******************* two blocks interleaved **********************/ + +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 8) + /* gcc doesn't support vldq1_u8_x2 until version 8 */ + const uint8x16_t input_a = vld1q_u8(data); + const uint8x16_t input_b = vld1q_u8(data + 16); +#else + /* Forces a double load on Clang */ + const uint8x16x2_t input_pair = vld1q_u8_x2(data); + const uint8x16_t input_a = input_pair.val[0]; + const uint8x16_t input_b = input_pair.val[1]; +#endif + + const uint8x16_t high_nibbles_a = vshrq_n_u8(input_a, 4); + const uint8x16_t high_nibbles_b = vshrq_n_u8(input_b, 4); + + const uint8x16_t first_len_a = + vqtbl1q_u8(first_len_tbl, high_nibbles_a); + const uint8x16_t first_len_b = + vqtbl1q_u8(first_len_tbl, high_nibbles_b); + + uint8x16_t range_a = vqtbl1q_u8(first_range_tbl, high_nibbles_a); + uint8x16_t range_b = vqtbl1q_u8(first_range_tbl, high_nibbles_b); + + range_a = + vorrq_u8(range_a, vextq_u8(prev_first_len, first_len_a, 15)); + range_b = + vorrq_u8(range_b, vextq_u8(first_len_a, first_len_b, 15)); + + uint8x16_t tmp1_a, tmp2_a, tmp1_b, tmp2_b; + tmp1_a = vextq_u8(prev_first_len, first_len_a, 14); + tmp1_a = vqsubq_u8(tmp1_a, const_1); + range_a = vorrq_u8(range_a, tmp1_a); + + tmp1_b = vextq_u8(first_len_a, first_len_b, 14); + tmp1_b = vqsubq_u8(tmp1_b, const_1); + range_b = vorrq_u8(range_b, tmp1_b); + + tmp2_a = vextq_u8(prev_first_len, first_len_a, 13); + tmp2_a = vqsubq_u8(tmp2_a, const_2); + range_a = vorrq_u8(range_a, tmp2_a); + + tmp2_b = vextq_u8(first_len_a, first_len_b, 13); + tmp2_b = vqsubq_u8(tmp2_b, const_2); + range_b = vorrq_u8(range_b, tmp2_b); + + uint8x16_t shift1_a = vextq_u8(prev_input, input_a, 15); + uint8x16_t pos_a = vsubq_u8(shift1_a, const_e0); + range_a = vaddq_u8(range_a, vqtbl2q_u8(range_adjust_tbl, pos_a)); + + uint8x16_t shift1_b = vextq_u8(input_a, input_b, 15); + uint8x16_t pos_b = vsubq_u8(shift1_b, const_e0); + range_b = vaddq_u8(range_b, vqtbl2q_u8(range_adjust_tbl, pos_b)); + + uint8x16_t minv_a = vqtbl1q_u8(range_min_tbl, range_a); + uint8x16_t maxv_a = vqtbl1q_u8(range_max_tbl, range_a); + + uint8x16_t minv_b = vqtbl1q_u8(range_min_tbl, range_b); + uint8x16_t maxv_b = vqtbl1q_u8(range_max_tbl, range_b); + + error1 = vorrq_u8(error1, vcltq_u8(input_a, minv_a)); + error2 = vorrq_u8(error2, vcgtq_u8(input_a, maxv_a)); + + error3 = vorrq_u8(error3, vcltq_u8(input_b, minv_b)); + error4 = vorrq_u8(error4, vcgtq_u8(input_b, maxv_b)); + + /************************ next iteration *************************/ + prev_input = input_b; + prev_first_len = first_len_b; + + data += 32; + len -= 32; + } + error1 = vorrq_u8(error1, error2); + error1 = vorrq_u8(error1, error3); + error1 = vorrq_u8(error1, error4); + + if (vmaxvq_u8(error1)) + return -1; + + uint32_t token4; + vst1q_lane_u32(&token4, vreinterpretq_u32_u8(prev_input), 3); + + const int8_t *token = (const int8_t *)&token4; + int lookahead = 0; + if (token[3] > (int8_t)0xBF) + lookahead = 1; + else if (token[2] > (int8_t)0xBF) + lookahead = 2; + else if (token[1] > (int8_t)0xBF) + lookahead = 3; + + data -= lookahead; + len += lookahead; + } + + return utf8_naive(data, len); +} + +#endif diff --git a/third_party/utf8_range/range2-sse.c b/third_party/utf8_range/range2-sse.c new file mode 100644 index 0000000000000..f3deb860061a5 --- /dev/null +++ b/third_party/utf8_range/range2-sse.c @@ -0,0 +1,170 @@ +/* + * Process 2x16 bytes in each iteration. + * Comments removed for brevity. See range-sse.c for details. + */ +#ifdef __SSE4_1__ + +#include +#include +#include + +int utf8_naive(const unsigned char *data, int len); + +static const int8_t _first_len_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, +}; + +static const int8_t _first_range_tbl[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, +}; + +static const int8_t _range_min_tbl[] = { + 0x00, 0x80, 0x80, 0x80, 0xA0, 0x80, 0x90, 0x80, + 0xC2, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +}; +static const int8_t _range_max_tbl[] = { + 0x7F, 0xBF, 0xBF, 0xBF, 0xBF, 0x9F, 0xBF, 0x8F, + 0xF4, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +}; + +static const int8_t _df_ee_tbl[] = { + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, +}; +static const int8_t _ef_fe_tbl[] = { + 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Return 0 on success, -1 on error */ +int utf8_range2(const unsigned char *data, int len) +{ + if (len >= 32) { + __m128i prev_input = _mm_set1_epi8(0); + __m128i prev_first_len = _mm_set1_epi8(0); + + const __m128i first_len_tbl = + _mm_loadu_si128((const __m128i *)_first_len_tbl); + const __m128i first_range_tbl = + _mm_loadu_si128((const __m128i *)_first_range_tbl); + const __m128i range_min_tbl = + _mm_loadu_si128((const __m128i *)_range_min_tbl); + const __m128i range_max_tbl = + _mm_loadu_si128((const __m128i *)_range_max_tbl); + const __m128i df_ee_tbl = + _mm_loadu_si128((const __m128i *)_df_ee_tbl); + const __m128i ef_fe_tbl = + _mm_loadu_si128((const __m128i *)_ef_fe_tbl); + + __m128i error = _mm_set1_epi8(0); + + while (len >= 32) { + /***************************** block 1 ****************************/ + const __m128i input_a = _mm_loadu_si128((const __m128i *)data); + + __m128i high_nibbles = + _mm_and_si128(_mm_srli_epi16(input_a, 4), _mm_set1_epi8(0x0F)); + + __m128i first_len_a = _mm_shuffle_epi8(first_len_tbl, high_nibbles); + + __m128i range_a = _mm_shuffle_epi8(first_range_tbl, high_nibbles); + + range_a = _mm_or_si128( + range_a, _mm_alignr_epi8(first_len_a, prev_first_len, 15)); + + __m128i tmp; + tmp = _mm_alignr_epi8(first_len_a, prev_first_len, 14); + tmp = _mm_subs_epu8(tmp, _mm_set1_epi8(1)); + range_a = _mm_or_si128(range_a, tmp); + + tmp = _mm_alignr_epi8(first_len_a, prev_first_len, 13); + tmp = _mm_subs_epu8(tmp, _mm_set1_epi8(2)); + range_a = _mm_or_si128(range_a, tmp); + + __m128i shift1, pos, range2; + shift1 = _mm_alignr_epi8(input_a, prev_input, 15); + pos = _mm_sub_epi8(shift1, _mm_set1_epi8(0xEF)); + tmp = _mm_subs_epu8(pos, _mm_set1_epi8(0xF0)); + range2 = _mm_shuffle_epi8(df_ee_tbl, tmp); + tmp = _mm_adds_epu8(pos, _mm_set1_epi8(0x70)); + range2 = _mm_add_epi8(range2, _mm_shuffle_epi8(ef_fe_tbl, tmp)); + + range_a = _mm_add_epi8(range_a, range2); + + __m128i minv = _mm_shuffle_epi8(range_min_tbl, range_a); + __m128i maxv = _mm_shuffle_epi8(range_max_tbl, range_a); + + tmp = _mm_or_si128( + _mm_cmplt_epi8(input_a, minv), + _mm_cmpgt_epi8(input_a, maxv) + ); + error = _mm_or_si128(error, tmp); + + /***************************** block 2 ****************************/ + const __m128i input_b = _mm_loadu_si128((const __m128i *)(data+16)); + + high_nibbles = + _mm_and_si128(_mm_srli_epi16(input_b, 4), _mm_set1_epi8(0x0F)); + + __m128i first_len_b = _mm_shuffle_epi8(first_len_tbl, high_nibbles); + + __m128i range_b = _mm_shuffle_epi8(first_range_tbl, high_nibbles); + + range_b = _mm_or_si128( + range_b, _mm_alignr_epi8(first_len_b, first_len_a, 15)); + + + tmp = _mm_alignr_epi8(first_len_b, first_len_a, 14); + tmp = _mm_subs_epu8(tmp, _mm_set1_epi8(1)); + range_b = _mm_or_si128(range_b, tmp); + + tmp = _mm_alignr_epi8(first_len_b, first_len_a, 13); + tmp = _mm_subs_epu8(tmp, _mm_set1_epi8(2)); + range_b = _mm_or_si128(range_b, tmp); + + shift1 = _mm_alignr_epi8(input_b, input_a, 15); + pos = _mm_sub_epi8(shift1, _mm_set1_epi8(0xEF)); + tmp = _mm_subs_epu8(pos, _mm_set1_epi8(0xF0)); + range2 = _mm_shuffle_epi8(df_ee_tbl, tmp); + tmp = _mm_adds_epu8(pos, _mm_set1_epi8(0x70)); + range2 = _mm_add_epi8(range2, _mm_shuffle_epi8(ef_fe_tbl, tmp)); + + range_b = _mm_add_epi8(range_b, range2); + + minv = _mm_shuffle_epi8(range_min_tbl, range_b); + maxv = _mm_shuffle_epi8(range_max_tbl, range_b); + + + tmp = _mm_or_si128( + _mm_cmplt_epi8(input_b, minv), + _mm_cmpgt_epi8(input_b, maxv) + ); + error = _mm_or_si128(error, tmp); + + /************************ next iteration **************************/ + prev_input = input_b; + prev_first_len = first_len_b; + + data += 32; + len -= 32; + } + + if (!_mm_testz_si128(error, error)) + return -1; + + int32_t token4 = _mm_extract_epi32(prev_input, 3); + const int8_t *token = (const int8_t *)&token4; + int lookahead = 0; + if (token[3] > (int8_t)0xBF) + lookahead = 1; + else if (token[2] > (int8_t)0xBF) + lookahead = 2; + else if (token[1] > (int8_t)0xBF) + lookahead = 3; + + data -= lookahead; + len += lookahead; + } + + return utf8_naive(data, len); +} + +#endif diff --git a/third_party/utf8_range/utf8_corpus_dir/utf8_corpus_durst.txt b/third_party/utf8_range/utf8_corpus_dir/utf8_corpus_durst.txt new file mode 100644 index 0000000000000..b8157dbf45d58 --- /dev/null +++ b/third_party/utf8_range/utf8_corpus_dir/utf8_corpus_durst.txt @@ -0,0 +1,213 @@ + + + UTF-8 test file + +

Original by Markus Kuhn, adapted for HTML by Martin Dürst.

+
+UTF-8 encoded sample plain-text file
+‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+Markus Kuhn [ˈmaʳkʊs kuːn] <mkuhn@acm.org> — 1999-08-20
+
+
+The ASCII compatible UTF-8 encoding of ISO 10646 and Unicode
+plain-text files is defined in RFC 2279 and in ISO 10646-1 Annex R.
+
+
+Using Unicode/UTF-8, you can write in emails and source code things such as
+
+Mathematics and Sciences:
+
+  ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i), ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),
+
+  ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (A ⇔ B),
+
+  2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm
+
+Linguistics and dictionaries:
+
+  ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn
+  Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]
+
+APL:
+
+  ((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈
+
+Nicer typography in plain text files:
+
+  ╔══════════════════════════════════════════╗
+  ║                                          ║
+  ║   • ‘single’ and “double” quotes         ║
+  ║                                          ║
+  ║   • Curly apostrophes: “We’ve been here” ║
+  ║                                          ║
+  ║   • Latin-1 apostrophe and accents: '´`  ║
+  ║                                          ║
+  ║   • ‚deutsche‘ „Anführungszeichen“       ║
+  ║                                          ║
+  ║   • †, ‡, ‰, •, 3–4, —, −5/+5, ™, …      ║
+  ║                                          ║
+  ║   • ASCII safety test: 1lI|, 0OD, 8B     ║
+  ║                      ╭─────────╮         ║
+  ║   • the euro symbol: │ 14.95 € │         ║
+  ║                      ╰─────────╯         ║
+  ╚══════════════════════════════════════════╝
+
+Greek (in Polytonic):
+
+  The Greek anthem:
+
+  Σὲ γνωρίζω ἀπὸ τὴν κόψη
+  τοῦ σπαθιοῦ τὴν τρομερή,
+  σὲ γνωρίζω ἀπὸ τὴν ὄψη
+  ποὺ μὲ βία μετράει τὴ γῆ.
+
+  ᾿Απ᾿ τὰ κόκκαλα βγαλμένη
+  τῶν ῾Ελλήνων τὰ ἱερά
+  καὶ σὰν πρῶτα ἀνδρειωμένη
+  χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!
+
+  From a speech of Demosthenes in the 4th century BC:
+
+  Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,
+  ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς
+  λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ
+  τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿ 
+  εἰς τοῦτο προήκοντα,  ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ
+  πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν
+  οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,
+  οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν
+  ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον
+  τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι
+  γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν
+  προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους
+  σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ
+  τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ
+  τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς
+  τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.
+
+  Δημοσθένους, Γ´ ᾿Ολυνθιακὸς
+
+Georgian:
+
+  From a Unicode conference invitation:
+
+  გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო
+  კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,
+  ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს
+  ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,
+  ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება
+  ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,
+  ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.
+
+Russian:
+
+  From a Unicode conference invitation:
+
+  Зарегистрируйтесь сейчас на Десятую Международную Конференцию по
+  Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.
+  Конференция соберет широкий круг экспертов по  вопросам глобального
+  Интернета и Unicode, локализации и интернационализации, воплощению и
+  применению Unicode в различных операционных системах и программных
+  приложениях, шрифтах, верстке и многоязычных компьютерных системах.
+
+Thai (UCS Level 2):
+
+  Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese
+  classic 'San Gua'):
+
+  [----------------------------|------------------------]
+    ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช  พระปกเกศกองบู๊กู้ขึ้นใหม่
+  สิบสองกษัตริย์ก่อนหน้าแลถัดไป       สององค์ไซร้โง่เขลาเบาปัญญา
+    ทรงนับถือขันทีเป็นที่พึ่ง           บ้านเมืองจึงวิปริตเป็นนักหนา
+  โฮจิ๋นเรียกทัพทั่วหัวเมืองมา         หมายจะฆ่ามดชั่วตัวสำคัญ
+    เหมือนขับไสไล่เสือจากเคหา      รับหมาป่าเข้ามาเลยอาสัญ
+  ฝ่ายอ้องอุ้นยุแยกให้แตกกัน          ใช้สาวนั้นเป็นชนวนชื่นชวนใจ
+    พลันลิฉุยกุยกีกลับก่อเหตุ          ช่างอาเพศจริงหนาฟ้าร้องไห้
+  ต้องรบราฆ่าฟันจนบรรลัย           ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ
+
+  (The above is a two-column text. If combining characters are handled
+  correctly, the lines of the second column should be aligned with the
+  | character above.)
+
+Ethiopian:
+
+  Proverbs in the Amharic language:
+
+  ሰማይ አይታረስ ንጉሥ አይከሰስ።
+  ብላ ካለኝ እንደአባቴ በቆመጠኝ።
+  ጌጥ ያለቤቱ ቁምጥና ነው።
+  ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።
+  የአፍ ወለምታ በቅቤ አይታሽም።
+  አይጥ በበላ ዳዋ ተመታ።
+  ሲተረጉሙ ይደረግሙ።
+  ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።
+  ድር ቢያብር አንበሳ ያስር።
+  ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።
+  እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።
+  የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።
+  ሥራ ከመፍታት ልጄን ላፋታት።
+  ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።
+  የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።
+  ተንጋሎ ቢተፉ ተመልሶ ባፉ።
+  ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።
+  እግርህን በፍራሽህ ልክ ዘርጋ።
+
+Runes:
+
+  ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ
+
+  (Old English, which transcribed into Latin reads 'He cwaeth that he
+  bude thaem lande northweardum with tha Westsae.' and means 'He said
+  that he lived in the northern land near the Western Sea.')
+
+Braille:
+
+  ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌
+
+  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞
+  ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎
+  ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂
+  ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙
+  ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑ 
+  ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲
+
+  ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
+
+  ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹
+  ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞
+  ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕
+  ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹ 
+  ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎ 
+  ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎
+  ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳
+  ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞
+  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
+
+  (The first couple of paragraphs of "A Christmas Carol" by Dickens)
+
+Compact font selection example text:
+
+  ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789
+  abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ
+  –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд
+  ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა
+
+Greetings in various languages:
+
+  Hello world, Καλημέρα κόσμε, コンニチハ
+
+Box drawing alignment tests:                                          █
+                                                                      ▉
+  ╔══╦══╗  ┌──┬──┐  ╭──┬──╮  ╭──┬──╮  ┏━━┳━━┓  ┎┒┏┑   ╷  ╻ ┏┯┓ ┌┰┐    ▊ ╱╲╱╲╳╳╳
+  ║┌─╨─┐║  │╔═╧═╗│  │╒═╪═╕│  │╓─╁─╖│  ┃┌─╂─┐┃  ┗╃╄┙  ╶┼╴╺╋╸┠┼┨ ┝╋┥    ▋ ╲╱╲╱╳╳╳
+  ║│╲ ╱│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╿ │┃  ┍╅╆┓   ╵  ╹ ┗┷┛ └┸┘    ▌ ╱╲╱╲╳╳╳
+  ╠╡ ╳ ╞╣  ├╢   ╟┤  ├┼─┼─┼┤  ├╫─╂─╫┤  ┣┿╾┼╼┿┫  ┕┛┖┚     ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
+  ║│╱ ╲│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╽ │┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▎
+  ║└─╥─┘║  │╚═╤═╝│  │╘═╪═╛│  │╙─╀─╜│  ┃└─╂─┘┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▏
+  ╚══╩══╝  └──┴──┘  ╰──┴──╯  ╰──┴──╯  ┗━━┻━━┛           └╌╌┘ ╎ ┗╍╍┛ ┋  ▁▂▃▄▅▆▇█
+
+
+ + diff --git a/third_party/utf8_range/utf8_corpus_dir/utf8_corpus_kuhn.txt b/third_party/utf8_range/utf8_corpus_dir/utf8_corpus_kuhn.txt new file mode 100644 index 0000000000000000000000000000000000000000..e8708178c168e2f1d38de6c9ea4f34dfd828c811 GIT binary patch literal 23349 zcmdU1X>;3FmYvW16?ZrvBDW<*n{6dEJyo$KOGv1YP# zcY4jfZ+7;rojsjBt^)p8pK~uZE|N)^s_CF?fTVzf`|kGcyYIet`N8d_4XL{|zwY*= z=5(E^*Yd&xaXNJg!k!xh61qWHTAK1GmF{(VyZu1^tKaO%zc<6Md*|)9@9*zdYOPAm zX;++DrN4Xo|F(B_SI^x3AF?E?%PVJ=mN%AGH}K`c1v&pKS+6XYN)I+Yd$%;amJ8ZC z(sWy0Iq>`9UUAx9$6b`Bzt10fa(SpBO_1MmgVLVU^L!8!9(3J6{D##0UeB$Ci_&&l z4ZqiR>x<&|#1ETpZ{G`C@j9W4R)=0^$1qEQ`pI+OE?HCG@50 ziUaTtF&*5jOsmMnQ0hMD;JsbI)_NZA>>Nnf>4jdc-*WKsepvI{F5&TNAfR2O2gL9# zvhN(=6{T|534$`IEjJvmRmMF<1}%!a=XUU;-`{ESd600$UN1p^X9sN(rvxGzZqKQL zp{dZTZqwNVmwMua5`Yltwj6El#V`=ZYlDnM@FfUc2iyWBnHWGpXIGpmTCk`ZLse1Y z!H^UcT%RBzzXNK51Aw+2Fao@5wICP|zvF>^ryum47T((JhhY5Hm9E?KCt@!_9TK)b*jR&~CCIfZ$to=|BshQnec7iIz|)RpS$^ zqycx;@N-?8<;%qfF3HOB+4ZwaD|fCgFE1}jV3G)AvvN-5x>{MQfmX0&&#Sp5n2_u3 zWhR1mJ7|KM4ipS}Cgr@zDftOwC2$}h=(I`B3-jloZBlDGJ*Ng6v{pgGpm|k2Z$PzntaX@yES7BUk9G*ooyEmC2Nr^ zTt2!}^H@8+Ul%afp;_;Fv^g}b?y3LldqESgI+W~2&u>#=reLjuQl_@4j*iLl z*L@frW#jC@g{d1#Bm{l!q3NZDkM>vlFinWVTympNqFU~b(;9Yi1;tf2+qo?)!TZ)B zPzck5-?#`1fU~VR0eq^y+}r8+J!)>=tW*wAwO*;;$w|w^5GcG7p7?mmHhVpU<3a!1 z&aPXkyHz+_eE}RfSlMXM%_shmofte%osU!1z`RZa{>AUWtK3hY1PyDtHArv2>5>CS z2>>d#uyTqzhOPz7qe6xyh;UIBR)>FwilNV}g>) zWljqk$dF)R?UdR{1Mb@2XET4(_d|H_MC-(9&~}|p2^^usMtm5%v`k^BXi)kCcEVl- z9UzPeejcU}9CT{Up5O8Q$Dl*7Hn4t4yf%7m4>Cqu(zhMt;>`N%Vs{-l2Z59yyn9Qw z?%OQGVzd!0b^&zZ@OZv>+D}2!RHMM93ONv)_FZXTS?f7Lll}+YBtwsC(p{TzCN9`% z!Mm|nDAgPYS{Mf3vDT~bmrG(My^ zXTSq3uI;uCa%-9pi7+Bi)|S$)veR!@p~MUqQx6vK{p?!dh*V*;&qd=>Gj-7m!1*LW zC3?^#Xr#g?W>&X;Oe%$A)_oC_{CV;|hVVvt9}wlm-U^$KR+O6GAjb zk?dT7(Smk60?TTkU#Hd7s1YjB1sXM)rg2~3g?+WRKx4^5uy_hLwEFr|kdQeXVXN_Y z9l=1I@mlo&#DlCtuaViKsU-W=2_0RgIp$bQaWMXXneQCn4W*(PYJ}N=J5YbA9-Y>j zXpt%j7+JcTAv7av(|hPcaHkym7?h6v=)?yHr4^9}NL{LS78PjJKJkg8f1E#kzrsK( zF+_8RuKZ`ub$4YSv7_9!>j!Q3wv?mg`0?nw_>Jht=-c>-=uv$80sp@$A5QfA30@UB zKNfzQBVYs&98SxmznlPl@U*hcQHpf^An-T}OC5z-4f+UmyXbg#;31DZPJ}*ExMF6E z*mH^goyVzE%u@cBa*2Hgc)hCkAqGkB+bG@_KN3B3g!G4+Hc-elk+l)XjiFb^*P?5) zg073K4}q?Xv?u;FdNO)wR@gHlXNs^KHx1MJP4oxC&I){1dv?c#m$)#%17s*KU{m?~ow zHw5`fbYoV~QB__c<`C%D1f3OjM3vVRbqMx*gPj$4Se3z2JqrAfe@uaAfxcz!9Ri># zLt2**K1@vB4Bb4%my=NCHL9{X*-fxfekmA&+|iI~10T!r^=JnD)Fx=7X~71%1t&vL zS2mXUkDwpV;<^f;;H7~=*qa1ZGOKk3rPBX6ttAyeq4@+|S*8X7sj)5_hHD)7GJ0Yb zy%#tfNWB8hS6J;54E7FXK*EDBXX_aVJB5bKaLb?scHTQ-P%#5W@zxWdPgS$yB0r0y zU0uh9UdKieNjFv`;3l8vPu~^?DE%YoOCww)0|OUHdCMzU&ZEVI3bSqLF_zOoN2CSw9MLHbM;(UF?~oF=ryxK+iD*9n+nZ zo8;(hTs71Uk#jdkoq^nwQKdUDC{J=XIwV6-$y?nHSyDIHa1Ml{N=f(gDB+Vk&_ETOb8eA zjl(aW;@gSP$IgTq>$O#zUQPOR^m(gGn(RyS7f1svU#Za!>J7{lOgJ+7G-+sgK6J1; z*A{d!t>;+g8W^|-~D;Z|C+>aNc^V6Z%O>N#P3M_uEg(2{Jz8= zNc^G1A4&W-iLXoivBaPJzsm=q59PBlDxUQT;%QJkjdPRDGw-kdx$W~)i9eJ0bBVu@ z_)CeulKAfue?5a7M!m$z*bM?m%4L02E@vjmWrK35{=DV#jl|zd{GG(#--0Ys*_@0_ z5P?%BV_n(VNiwNXCiS1UOn#8~$2mxYQaK5Uz;~swHYQRzVXZUrV==b0JjLE)jE%nBh!=o?`Mqt0c0FK{Cmk6Fez(U{w!VVvr(nqD{+-Sh+J+ zqmjcw9@*TqPE9k64@jRrm(MQXr25_n7tcx`-WX^_yukN`Q%7BPG&_W!@~Tg7SvK>B#~UD=G0%d^+Oif@xkje-Afiokf?KmAR(%jG-2h z4pTUP`oKT-p$%83&wd(p3B#|y`8S&U84l?iHg7iOXm1WNe1`#|2L*-v*GQ3nihqi4 z#K*#)KNsQUE}?W!3n_C)GTf5szs;Y%VJ*xlObgj_Uxv!Pyl$Va5#cE7Z4vop$ABf3 z**byM3Y=%iqUc_|B6s`yEMn9GI@AH8K8{QQ03JcHC(8PRfQuJc6__4_(x~!Voj2^l z7Qr~IyF?}rm4PVWt@pY5rRlaSa+j0?2Wnx3Z8=_2OV=&E>n!fnYG^FE;rbM zr!0xZvJ$OEw;>6cpHu*6BZ`V_LSKu$74c&>rcc^`|Af~eT{3=|S7W)=1r6=Pi&C7}w%AkMvl)hZa~X2<;f^E&(BEi5_2 z=s(Q{WWZcl(Ton3y zP{@Lwl-JS2C-Kew|w*@OgpHH%;US%za@Z!Qqj?kM61Kj zt=KcA>rTLZ#-s2$kRAa1Gci! zBo#<{;Ww~wAqB<<^Ba#E@U;BRSU8stBo&Okl#X>W+tEHfohrER@;&(|=E(QXUqv4r zk4aKvhkvkmLYJ-`VQV?bgJk`64#6>O0>fMmP|`7-8f+fXkO^Z)VSc3$TaMi}^$+vs zALOehZi_HAEI_YSJgA09BTl2)d`wkFO+1CPu%L)aNQMP7g3`oO&@3#-a0zN*!Hlpp z@eH;Q78o*N)39JhV48RaJRBAPo4|QkFavZ^IJf?JPy(43lV#SrDFpJYw&ldJz~`@` z!zMn`F{Z{jm3gy4g#>;v-{Kx5n_grtPAp@StQM|Up~EgUc(@QqU_Lm z>FcOEojpr!pz72%&#OZqs4kx|O0jz8n3SqZ+dQuk3qduiFwhATqggW#`(Tu+JKMaF z^0ZpuDg5p+DaDjAc)Fc7A7-4m#_OoFJuWDwjR~4I--0S@{tOG-GH#rNimuB2j@N^1 z{z*;90{_!$Lf~a4b+aVUT@*h|3Wha>5>Sz}8WfN;&Z3lZ`uM6(*g2+eQ^v(Rj|l_2LdXUGMb4S8ys z&A{kihG$lAB6rStsxmkG>Fq&S%wXuITiBn6S$EyIYKTt=aSD@PNCzYO8Ja`% zTio{#gGBB@q`7U5i_)CU1Iff;hB+ve(;=1aGX|%x1b{|NAk@p1?9mI@70TPePBh3@ z{o5T<@A;TZ#~dNh3kPz;j!s~rO_)*F{og8bAF**KvhA4D!3S){W-}O=T12KuD^!>@ zN}@L>Ow+jt!H`DkVNW?Glpt0VwuBzIlf`X4FTmzXb+-%B2!WcHedeS%X@MkB+krM9 zVV)d1mx{koNiZnou4CNS1)HdFPr7_UAn5BpD43W?L~%$($^7*NdnW28BD#stUH4y1 z3Mp4meT2k$t~+R%?pdZ1pv*YyYdN_kB#N^3;=?at$FSdRPUu>R40Chf*OK2@()TQE zUBcuYw9iHKsGA$vCPZVA?TNJaVzOOL(*N2%ZP4Jw)RxgaYr<9%zeBaK0V4O(8ab3c zW(=2teWxqjeaiA93urq^jHrjm9EAHWa*X!Ad$!by?92Hr?7~zy=HFhO+Zj_Ys_2kw z(7k>rVpt?p)w4l}ra&;cko6x^E<4a)V=K0q1OCvO^5dAD#GV z{_?m#+qV-f1yS5G5-L+076*Bdm|)QXk9mdp(}(`ehEOozEe@fy+l5vzh(^B~&0a*L zou=i5)GcJ7HiRPRafVP61$OKo2PNCWVaO`R_&q)R$w)?{-0jWHWvQU6JiWcS#mqDX z^L6y3o}be5(|UeJ&(G@lIXyqG=NI(+qMl#U^UHdEMbEG5`87Siu4fhvejQ!Y^Ba18 zQ_pYd`E5PFqvv<^{GOiQ*YgK@{!q^!>G^MZzOLtw_54ZsWVoe~dq)-d@@ZbTWq~nI z2RvJ;R92P?_3KAVcK-Aw?_=WfDy=HH-oRC@EV=SK%tQaq>y7xc=n4I9SAMtW?aJ@= zyj}U-p113b`19m>yWTju-S*eK^?2IeW>=!w+w98k_BOlTh`&hQW>r{6PO8)@NdG!te literal 0 HcmV?d00001 diff --git a/third_party/utf8_range/utf8_range.h b/third_party/utf8_range/utf8_range.h new file mode 100644 index 0000000000000..24d5c77d2fdc2 --- /dev/null +++ b/third_party/utf8_range/utf8_range.h @@ -0,0 +1,21 @@ +#ifndef THIRD_PARTY_UTF8_RANGE_UTF8_RANGE_H_ +#define THIRD_PARTY_UTF8_RANGE_UTF8_RANGE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if (defined(__ARM_NEON) && defined(__aarch64__)) || defined(__SSE4_1__) +int utf8_range2(const unsigned char* data, int len); +#else +int utf8_naive(const unsigned char* data, int len); +static inline int utf8_range2(const unsigned char* data, int len) { + return utf8_naive(data, len); +} +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // THIRD_PARTY_UTF8_RANGE_UTF8_RANGE_H_ diff --git a/third_party/utf8_range/utf8_to_utf16/Makefile b/third_party/utf8_range/utf8_to_utf16/Makefile new file mode 100644 index 0000000000000..853ffa4162462 --- /dev/null +++ b/third_party/utf8_range/utf8_to_utf16/Makefile @@ -0,0 +1,11 @@ +CC = gcc +CPPFLAGS = -g -O3 -Wall -march=native + +OBJS = main.o iconv.o naive.o + +utf8to16: ${OBJS} + gcc $^ -o $@ + +.PHONY: clean +clean: + rm -f utf8to16 *.o diff --git a/third_party/utf8_range/utf8_to_utf16/iconv.c b/third_party/utf8_range/utf8_to_utf16/iconv.c new file mode 100644 index 0000000000000..35aebb6b784fd --- /dev/null +++ b/third_party/utf8_range/utf8_to_utf16/iconv.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +static iconv_t s_cd; + +/* Call iconv_open only once so the benchmark will be faster? */ +static void __attribute__ ((constructor)) init_iconv(void) +{ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + s_cd = iconv_open("UTF-16LE", "UTF-8"); +#else + s_cd = iconv_open("UTF-16BE", "UTF-8"); +#endif + if (s_cd == (iconv_t)-1) { + perror("iconv_open"); + exit(1); + } +} + +/* + * Parameters: + * - buf8, len8: input utf-8 string + * - buf16: buffer to store decoded utf-16 string + * - *len16: on entry - utf-16 buffer length in bytes + * on exit - length in bytes of valid decoded utf-16 string + * Returns: + * - 0: success + * - >0: error position of input utf-8 string + * - -1: utf-16 buffer overflow + * LE/BE depends on host + */ +int utf8_to16_iconv(const unsigned char *buf8, size_t len8, + unsigned short *buf16, size_t *len16) +{ + size_t ret, len16_save = *len16; + const unsigned char *buf8_0 = buf8; + + ret = iconv(s_cd, (char **)&buf8, &len8, (char **)&buf16, len16); + + *len16 = len16_save - *len16; + + if (ret != (size_t)-1) + return 0; + + if (errno == E2BIG) + return -1; /* Output buffer full */ + + return buf8 - buf8_0 + 1; /* EILSEQ, EINVAL, error position */ +} diff --git a/third_party/utf8_range/utf8_to_utf16/main.c b/third_party/utf8_range/utf8_to_utf16/main.c new file mode 100644 index 0000000000000..cb8976406d169 --- /dev/null +++ b/third_party/utf8_range/utf8_to_utf16/main.c @@ -0,0 +1,424 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int utf8_to16_iconv(const unsigned char *buf8, size_t len8, + unsigned short *buf16, size_t *len16); +int utf8_to16_naive(const unsigned char *buf8, size_t len8, + unsigned short *buf16, size_t *len16); + +static struct ftab { + const char *name; + int (*func)(const unsigned char *buf8, size_t len8, + unsigned short *buf16, size_t *len16); +} ftab[] = { + { + .name = "iconv", + .func = utf8_to16_iconv, + }, { + .name = "naive", + .func = utf8_to16_naive, + }, +}; + +static unsigned char *load_test_buf(int len) +{ + const char utf8[] = "\xF0\x90\xBF\x80"; + const int utf8_len = sizeof(utf8)/sizeof(utf8[0]) - 1; + + unsigned char *data = malloc(len); + unsigned char *p = data; + + while (len >= utf8_len) { + memcpy(p, utf8, utf8_len); + p += utf8_len; + len -= utf8_len; + } + + while (len--) + *p++ = 0x7F; + + return data; +} + +static unsigned char *load_test_file(int *len) +{ + unsigned char *data; + int fd; + struct stat stat; + + fd = open("../UTF-8-demo.txt", O_RDONLY); + if (fd == -1) { + printf("Failed to open ../UTF-8-demo.txt!\n"); + exit(1); + } + if (fstat(fd, &stat) == -1) { + printf("Failed to get file size!\n"); + exit(1); + } + + *len = stat.st_size; + data = malloc(*len); + if (read(fd, data, *len) != *len) { + printf("Failed to read file!\n"); + exit(1); + } + + close(fd); + + return data; +} + +static void print_test(const unsigned char *data, int len) +{ + printf(" [len=%d] \"", len); + while (len--) + printf("\\x%02X", *data++); + + printf("\"\n"); +} + +struct test { + const unsigned char *data; + int len; +}; + +static void prepare_test_buf(unsigned char *buf, const struct test *pos, + int pos_len, int pos_idx) +{ + /* Round concatenate correct tokens to 1024 bytes */ + int buf_idx = 0; + while (buf_idx < 1024) { + int buf_len = 1024 - buf_idx; + + if (buf_len >= pos[pos_idx].len) { + memcpy(buf+buf_idx, pos[pos_idx].data, pos[pos_idx].len); + buf_idx += pos[pos_idx].len; + } else { + memset(buf+buf_idx, 0, buf_len); + buf_idx += buf_len; + } + + if (++pos_idx == pos_len) + pos_idx = 0; + } +} + +/* Return 0 on success, -1 on error */ +static int test_manual(const struct ftab *ftab, unsigned short *buf16, + unsigned short *_buf16) +{ +#define LEN16 4096 + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpointer-sign" + /* positive tests */ + static const struct test pos[] = { + {"", 0}, + {"\x00", 1}, + {"\x66", 1}, + {"\x7F", 1}, + {"\x00\x7F", 2}, + {"\x7F\x00", 2}, + {"\xC2\x80", 2}, + {"\xDF\xBF", 2}, + {"\xE0\xA0\x80", 3}, + {"\xE0\xA0\xBF", 3}, + {"\xED\x9F\x80", 3}, + {"\xEF\x80\xBF", 3}, + {"\xF0\x90\xBF\x80", 4}, + {"\xF2\x81\xBE\x99", 4}, + {"\xF4\x8F\x88\xAA", 4}, + }; + + /* negative tests */ + static const struct test neg[] = { + {"\x80", 1}, + {"\xBF", 1}, + {"\xC0\x80", 2}, + {"\xC1\x00", 2}, + {"\xC2\x7F", 2}, + {"\xDF\xC0", 2}, + {"\xE0\x9F\x80", 3}, + {"\xE0\xC2\x80", 3}, + {"\xED\xA0\x80", 3}, + {"\xED\x7F\x80", 3}, + {"\xEF\x80\x00", 3}, + {"\xF0\x8F\x80\x80", 4}, + {"\xF0\xEE\x80\x80", 4}, + {"\xF2\x90\x91\x7F", 4}, + {"\xF4\x90\x88\xAA", 4}, + {"\xF4\x00\xBF\xBF", 4}, + {"\x00\x00\x00\x00\x00\xC2\x80\x00\x00\x00\xE1\x80\x80\x00\x00\xC2" \ + "\xC2\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + 32}, + {"\x00\x00\x00\x00\x00\xC2\xC2\x80\x00\x00\xE1\x80\x80\x00\x00\x00", + 16}, + {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xF1\x80", + 32}, + {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xF1", + 32}, + {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xF1\x80" \ + "\x80", 33}, + {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xF1\x80" \ + "\xC2\x80", 34}, + {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xF0" \ + "\x80\x80\x80", 35}, + }; +#pragma GCC diagnostic push + + size_t len16 = LEN16, _len16 = LEN16; + int ret, _ret; + + /* Test single token */ + for (int i = 0; i < sizeof(pos)/sizeof(pos[0]); ++i) { + ret = ftab->func(pos[i].data, pos[i].len, buf16, &len16); + _ret = utf8_to16_iconv(pos[i].data, pos[i].len, _buf16, &_len16); + if (ret != _ret || len16 != _len16 || memcmp(buf16, _buf16, len16)) { + printf("FAILED positive test(%d:%d, %lu:%lu): ", + ret, _ret, len16, _len16); + print_test(pos[i].data, pos[i].len); + return -1; + } + len16 = _len16 = LEN16; + } + for (int i = 0; i < sizeof(neg)/sizeof(neg[0]); ++i) { + ret = ftab->func(neg[i].data, neg[i].len, buf16, &len16); + _ret = utf8_to16_iconv(neg[i].data, neg[i].len, _buf16, &_len16); + if (ret != _ret || len16 != _len16 || memcmp(buf16, _buf16, len16)) { + printf("FAILED negitive test(%d:%d, %lu:%lu): ", + ret, _ret, len16, _len16); + print_test(neg[i].data, neg[i].len); + return -1; + } + len16 = _len16 = LEN16; + } + + /* Test shifted buffer to cover 1k length */ + /* buffer size must be greater than 1024 + 16 + max(test string length) */ + const int max_size = 1024*2; + uint64_t buf64[max_size/8 + 2]; + /* Offset 8 bytes by 1 byte */ + unsigned char *buf = ((unsigned char *)buf64) + 1; + int buf_len; + + for (int i = 0; i < sizeof(pos)/sizeof(pos[0]); ++i) { + /* Positive test: shift 16 bytes, validate each shift */ + prepare_test_buf(buf, pos, sizeof(pos)/sizeof(pos[0]), i); + buf_len = 1024; + for (int j = 0; j < 16; ++j) { + ret = ftab->func(buf, buf_len, buf16, &len16); + _ret = utf8_to16_iconv(buf, buf_len, _buf16, &_len16); + if (ret != _ret || len16 != _len16 || \ + memcmp(buf16, _buf16, len16)) { + printf("FAILED positive test(%d:%d, %lu:%lu): ", + ret, _ret, len16, _len16); + print_test(buf, buf_len); + return -1; + } + len16 = _len16 = LEN16; + for (int k = buf_len; k >= 1; --k) + buf[k] = buf[k-1]; + buf[0] = '\x55'; + ++buf_len; + } + + /* Negative test: trunk last non ascii */ + while (buf_len >= 1 && buf[buf_len-1] <= 0x7F) + --buf_len; + if (buf_len) { + ret = ftab->func(buf, buf_len-1, buf16, &len16); + _ret = utf8_to16_iconv(buf, buf_len-1, _buf16, &_len16); + if (ret != _ret || len16 != _len16 || \ + memcmp(buf16, _buf16, len16)) { + printf("FAILED negative test(%d:%d, %lu:%lu): ", + ret, _ret, len16, _len16); + print_test(buf, buf_len-1); + return -1; + } + len16 = _len16 = LEN16; + } + } + + /* Negative test */ + for (int i = 0; i < sizeof(neg)/sizeof(neg[0]); ++i) { + /* Append one error token, shift 16 bytes, validate each shift */ + int pos_idx = i % (sizeof(pos)/sizeof(pos[0])); + prepare_test_buf(buf, pos, sizeof(pos)/sizeof(pos[0]), pos_idx); + memcpy(buf+1024, neg[i].data, neg[i].len); + buf_len = 1024 + neg[i].len; + for (int j = 0; j < 16; ++j) { + ret = ftab->func(buf, buf_len, buf16, &len16); + _ret = utf8_to16_iconv(buf, buf_len, _buf16, &_len16); + if (ret != _ret || len16 != _len16 || \ + memcmp(buf16, _buf16, len16)) { + printf("FAILED negative test(%d:%d, %lu:%lu): ", + ret, _ret, len16, _len16); + print_test(buf, buf_len); + return -1; + } + len16 = _len16 = LEN16; + for (int k = buf_len; k >= 1; --k) + buf[k] = buf[k-1]; + buf[0] = '\x66'; + ++buf_len; + } + } + + return 0; +} + +static void test(const unsigned char *buf8, size_t len8, + unsigned short *buf16, size_t len16, const struct ftab *ftab) +{ + /* Use iconv as the reference answer */ + if (strcmp(ftab->name, "iconv") == 0) + return; + + printf("%s\n", ftab->name); + + /* Test file or buffer */ + size_t _len16 = len16; + unsigned short *_buf16 = (unsigned short *)malloc(_len16); + if (utf8_to16_iconv(buf8, len8, _buf16, &_len16)) { + printf("Invalid test file or buffer!\n"); + exit(1); + } + printf("standard test: "); + if (ftab->func(buf8, len8, buf16, &len16) || len16 != _len16 || \ + memcmp(buf16, _buf16, len16) != 0) + printf("FAIL\n"); + else + printf("pass\n"); + free(_buf16); + + /* Manual cases */ + unsigned short *mbuf8 = (unsigned short *)malloc(LEN16); + unsigned short *mbuf16 = (unsigned short *)malloc(LEN16); + printf("manual test: %s\n", + test_manual(ftab, mbuf8, mbuf16) ? "FAIL" : "pass"); + free(mbuf8); + free(mbuf16); + printf("\n"); +} + +static void bench(const unsigned char *buf8, size_t len8, + unsigned short *buf16, size_t len16, const struct ftab *ftab) +{ + const int loops = 1024*1024*1024/len8; + int ret = 0; + double time, size; + struct timeval tv1, tv2; + + fprintf(stderr, "bench %s... ", ftab->name); + gettimeofday(&tv1, 0); + for (int i = 0; i < loops; ++i) + ret |= ftab->func(buf8, len8, buf16, &len16); + gettimeofday(&tv2, 0); + printf("%s\n", ret?"FAIL":"pass"); + + time = tv2.tv_usec - tv1.tv_usec; + time = time / 1000000 + tv2.tv_sec - tv1.tv_sec; + size = ((double)len8 * loops) / (1024*1024); + printf("time: %.4f s\n", time); + printf("data: %.0f MB\n", size); + printf("BW: %.2f MB/s\n", size / time); + printf("\n"); +} + +static void usage(const char *bin) +{ + printf("Usage:\n"); + printf("%s test [alg] ==> test all or one algorithm\n", bin); + printf("%s bench [alg] ==> benchmark all or one algorithm\n", bin); + printf("%s bench size NUM ==> benchmark with specific buffer size\n", bin); + printf("alg = "); + for (int i = 0; i < sizeof(ftab)/sizeof(ftab[0]); ++i) + printf("%s ", ftab[i].name); + printf("\nNUM = buffer size in bytes, 1 ~ 67108864(64M)\n"); +} + +int main(int argc, char *argv[]) +{ + int len8 = 0, len16; + unsigned char *buf8; + unsigned short *buf16; + const char *alg = NULL; + void (*tb)(const unsigned char *buf8, size_t len8, + unsigned short *buf16, size_t len16, const struct ftab *ftab); + + tb = NULL; + if (argc >= 2) { + if (strcmp(argv[1], "test") == 0) + tb = test; + else if (strcmp(argv[1], "bench") == 0) + tb = bench; + if (argc >= 3) { + alg = argv[2]; + if (strcmp(alg, "size") == 0) { + if (argc < 4) { + tb = NULL; + } else { + alg = NULL; + len8 = atoi(argv[3]); + if (len8 <= 0 || len8 > 67108864) { + printf("Buffer size error!\n\n"); + tb = NULL; + } + } + } + } + } + + if (tb == NULL) { + usage(argv[0]); + return 1; + } + + /* Load UTF8 test buffer */ + if (len8) + buf8 = load_test_buf(len8); + else + buf8 = load_test_file(&len8); + + /* Prepare UTF16 buffer large enough */ + len16 = len8 * 2; + buf16 = (unsigned short *)malloc(len16); + + if (tb == bench) + printf("============== Bench UTF8 (%d bytes) ==============\n", len8); + for (int i = 0; i < sizeof(ftab)/sizeof(ftab[0]); ++i) { + if (alg && strcmp(alg, ftab[i].name) != 0) + continue; + tb((const unsigned char *)buf8, len8, buf16, len16, &ftab[i]); + } + +#if 0 + if (tb == bench) { + printf("==================== Bench ASCII ====================\n"); + /* Change test buffer to ascii */ + for (int i = 0; i < len; i++) + data[i] &= 0x7F; + + for (int i = 0; i < sizeof(ftab)/sizeof(ftab[0]); ++i) { + if (alg && strcmp(alg, ftab[i].name) != 0) + continue; + tb((const unsigned char *)data, len, &ftab[i]); + printf("\n"); + } + } +#endif + + return 0; +} diff --git a/third_party/utf8_range/utf8_to_utf16/naive.c b/third_party/utf8_range/utf8_to_utf16/naive.c new file mode 100644 index 0000000000000..05ab07b76e6d1 --- /dev/null +++ b/third_party/utf8_range/utf8_to_utf16/naive.c @@ -0,0 +1,133 @@ +#include + +/* + * UTF-8 to UTF-16 + * Table from https://woboq.com/blog/utf-8-processing-using-simd.html + * + * +-------------------------------------+-------------------+ + * | UTF-8 | UTF-16LE (HI LO) | + * +-------------------------------------+-------------------+ + * | 0aaaaaaa | 00000000 0aaaaaaa | + * +-------------------------------------+-------------------+ + * | 110bbbbb 10aaaaaa | 00000bbb bbaaaaaa | + * +-------------------------------------+-------------------+ + * | 1110cccc 10bbbbbb 10aaaaaa | ccccbbbb bbaaaaaa | + * +-------------------------------------+-------------------+ + * | 11110ddd 10ddcccc 10bbbbbb 10aaaaaa | 110110uu uuccccbb | + * + uuuu = ddddd - 1 | 110111bb bbaaaaaa | + * +-------------------------------------+-------------------+ + */ + +/* + * Parameters: + * - buf8, len8: input utf-8 string + * - buf16: buffer to store decoded utf-16 string + * - *len16: on entry - utf-16 buffer length in bytes + * on exit - length in bytes of valid decoded utf-16 string + * Returns: + * - 0: success + * - >0: error position of input utf-8 string + * - -1: utf-16 buffer overflow + * LE/BE depends on host + */ +int utf8_to16_naive(const unsigned char *buf8, size_t len8, + unsigned short *buf16, size_t *len16) +{ + int err_pos = 1; + size_t len16_left = *len16; + + *len16 = 0; + + while (len8) { + unsigned char b0, b1, b2, b3; + unsigned int u; + + /* Output buffer full */ + if (len16_left < 2) + return -1; + + /* 1st byte */ + b0 = buf8[0]; + + if ((b0 & 0x80) == 0) { + /* 0aaaaaaa -> 00000000 0aaaaaaa */ + *buf16++ = b0; + ++buf8; + --len8; + ++err_pos; + *len16 += 2; + len16_left -= 2; + continue; + } + + /* Character length */ + size_t clen = b0 & 0xF0; + clen >>= 4; /* 10xx, 110x, 1110, 1111 */ + clen -= 12; /* -4~-1, 0/1, 2, 3 */ + clen += !clen; /* -4~-1, 1, 2, 3 */ + + /* String too short or invalid 1st byte (10xxxxxx) */ + if (len8 <= clen) + return err_pos; + + /* Trailing bytes must be within 0x80 ~ 0xBF */ + b1 = buf8[1]; + if ((signed char)b1 >= (signed char)0xC0) + return err_pos; + b1 &= 0x3F; + + ++clen; + if (clen == 2) { + u = b0 & 0x1F; + u <<= 6; + u |= b1; + if (u <= 0x7F) + return err_pos; + *buf16++ = u; + } else { + b2 = buf8[2]; + if ((signed char)b2 >= (signed char)0xC0) + return err_pos; + b2 &= 0x3F; + if (clen == 3) { + u = b0 & 0x0F; + u <<= 6; + u |= b1; + u <<= 6; + u |= b2; + if (u <= 0x7FF || (u >= 0xD800 && u <= 0xDFFF)) + return err_pos; + *buf16++ = u; + } else { + /* clen == 4 */ + if (len16_left < 4) + return -1; /* Output buffer full */ + b3 = buf8[3]; + if ((signed char)b3 >= (signed char)0xC0) + return err_pos; + u = b0 & 0x07; + u <<= 6; + u |= b1; + u <<= 6; + u |= b2; + u <<= 6; + u |= (b3 & 0x3F); + if (u <= 0xFFFF || u > 0x10FFFF) + return err_pos; + u -= 0x10000; + *buf16++ = (((u >> 10) & 0x3FF) | 0xD800); + *buf16++ = ((u & 0x3FF) | 0xDC00); + *len16 += 2; + len16_left -= 2; + } + } + + buf8 += clen; + len8 -= clen; + err_pos += clen; + *len16 += 2; + len16_left -= 2; + } + + return 0; +} diff --git a/third_party/utf8_range/utf8_validity.cc b/third_party/utf8_range/utf8_validity.cc new file mode 100644 index 0000000000000..db811993f81ea --- /dev/null +++ b/third_party/utf8_range/utf8_validity.cc @@ -0,0 +1,458 @@ +// Copyright 2022 Google LLC +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +/* This is a wrapper for the Google range-sse.cc algorithm which checks whether a + * sequence of bytes is a valid UTF-8 sequence and finds the longest valid prefix of + * the UTF-8 sequence. + * + * The key difference is that it checks for as much ASCII symbols as possible + * and then falls back to the range-sse.cc algorithm. The changes to the + * algorithm are cosmetic, mostly to trick the clang compiler to produce optimal + * code. + * + * For API see the utf8_validity.h header. + */ +#include "utf8_validity.h" + +#include +#include + +#include "absl/strings/ascii.h" +#include "absl/strings/string_view.h" + +#ifdef __SSE4_1__ +#include +#include +#include +#endif + +namespace utf8_range { +namespace { + +inline uint64_t UNALIGNED_LOAD64(const void* p) { + uint64_t t; + memcpy(&t, p, sizeof t); + return t; +} + +inline bool TrailByteOk(const char c) { + return static_cast(c) <= static_cast(0xBF); +} + +/* If ReturnPosition is false then it returns 1 if |data| is a valid utf8 + * sequence, otherwise returns 0. + * If ReturnPosition is set to true, returns the length in bytes of the prefix + of |data| that is all structurally valid UTF-8. + */ +template +size_t ValidUTF8Span(const char* data, const char* end) { + /* We return err_pos in the loop which is always 0 if !ReturnPosition */ + size_t err_pos = 0; + size_t codepoint_bytes = 0; + /* The early check is done because of early continue's on codepoints of all + * sizes, i.e. we first check for ascii and if it is, we call continue, then + * for 2 byte codepoints, etc. This is done in order to reduce indentation and + * improve readability of the codepoint validity check. + */ + while (data + codepoint_bytes < end) { + if (ReturnPosition) { + err_pos += codepoint_bytes; + } + data += codepoint_bytes; + const size_t len = end - data; + const unsigned char byte1 = data[0]; + + /* We do not skip many ascii bytes at the same time as this function is + used for tail checking (< 16 bytes) and for non x86 platforms. We also + don't think that cases where non-ASCII codepoints are followed by ascii + happen often. For small strings it also introduces some penalty. For + purely ascii UTF8 strings (which is the overwhelming case) we call + SkipAscii function which is multiplatform and extremely fast. + */ + /* [00..7F] ASCII -> 1 byte */ + if (absl::ascii_isascii(byte1)) { + codepoint_bytes = 1; + continue; + } + /* [C2..DF], [80..BF] -> 2 bytes */ + if (len >= 2 && byte1 >= 0xC2 && byte1 <= 0xDF && TrailByteOk(data[1])) { + codepoint_bytes = 2; + continue; + } + if (len >= 3) { + const unsigned char byte2 = data[1]; + const unsigned char byte3 = data[2]; + + /* Is byte2, byte3 between [0x80, 0xBF] + * Check for 0x80 was done above. + */ + if (!TrailByteOk(byte2) || !TrailByteOk(byte3)) { + return err_pos; + } + + if (/* E0, A0..BF, 80..BF */ + ((byte1 == 0xE0 && byte2 >= 0xA0) || + /* E1..EC, 80..BF, 80..BF */ + (byte1 >= 0xE1 && byte1 <= 0xEC) || + /* ED, 80..9F, 80..BF */ + (byte1 == 0xED && byte2 <= 0x9F) || + /* EE..EF, 80..BF, 80..BF */ + (byte1 >= 0xEE && byte1 <= 0xEF))) { + codepoint_bytes = 3; + continue; + } + if (len >= 4) { + const unsigned char byte4 = data[3]; + /* Is byte4 between 0x80 ~ 0xBF */ + if (!TrailByteOk(byte4)) { + return err_pos; + } + + if (/* F0, 90..BF, 80..BF, 80..BF */ + ((byte1 == 0xF0 && byte2 >= 0x90) || + /* F1..F3, 80..BF, 80..BF, 80..BF */ + (byte1 >= 0xF1 && byte1 <= 0xF3) || + /* F4, 80..8F, 80..BF, 80..BF */ + (byte1 == 0xF4 && byte2 <= 0x8F))) { + codepoint_bytes = 4; + continue; + } + } + } + return err_pos; + } + if (ReturnPosition) { + err_pos += codepoint_bytes; + } + /* if ReturnPosition is false, this returns 1. + * if ReturnPosition is true, this returns err_pos. + */ + return err_pos + (1 - ReturnPosition); +} + +/* Returns the number of bytes needed to skip backwards to get to the first + byte of codepoint. + */ +inline int CodepointSkipBackwards(int32_t codepoint_word) { + const int8_t* const codepoint = + reinterpret_cast(&codepoint_word); + if (!TrailByteOk(codepoint[3])) { + return 1; + } else if (!TrailByteOk(codepoint[2])) { + return 2; + } else if (!TrailByteOk(codepoint[1])) { + return 3; + } + return 0; +} + +/* Skipping over ASCII as much as possible, per 8 bytes. It is intentional + as most strings to check for validity consist only of 1 byte codepoints. + */ +inline const char* SkipAscii(const char* data, const char* end) { + while (8 <= end - data && + (UNALIGNED_LOAD64(data) & 0x8080808080808080) == 0) { + data += 8; + } + while (data < end && absl::ascii_isascii(*data)) { + ++data; + } + return data; +} + +template +size_t ValidUTF8(const char* data, size_t len) { + if (len == 0) return 1 - ReturnPosition; + const char* const end = data + len; + data = SkipAscii(data, end); + /* SIMD algorithm always outperforms the naive version for any data of + length >=16. + */ + if (end - data < 16) { + return (ReturnPosition ? (data - (end - len)) : 0) + + ValidUTF8Span(data, end); + } +#ifndef __SSE4_1__ + return (ReturnPosition ? (data - (end - len)) : 0) + + ValidUTF8Span(data, end); +#else + /* This code checks that utf-8 ranges are structurally valid 16 bytes at once + * using superscalar instructions. + * The mapping between ranges of codepoint and their corresponding utf-8 + * sequences is below. + */ + + /* + * U+0000...U+007F 00...7F + * U+0080...U+07FF C2...DF 80...BF + * U+0800...U+0FFF E0 A0...BF 80...BF + * U+1000...U+CFFF E1...EC 80...BF 80...BF + * U+D000...U+D7FF ED 80...9F 80...BF + * U+E000...U+FFFF EE...EF 80...BF 80...BF + * U+10000...U+3FFFF F0 90...BF 80...BF 80...BF + * U+40000...U+FFFFF F1...F3 80...BF 80...BF 80...BF + * U+100000...U+10FFFF F4 80...8F 80...BF 80...BF + */ + + /* First we compute the type for each byte, as given by the table below. + * This type will be used as an index later on. + */ + + /* + * Index Min Max Byte Type + * 0 00 7F Single byte sequence + * 1,2,3 80 BF Second, third and fourth byte for many of the sequences. + * 4 A0 BF Second byte after E0 + * 5 80 9F Second byte after ED + * 6 90 BF Second byte after F0 + * 7 80 8F Second byte after F4 + * 8 C2 F4 First non ASCII byte + * 9..15 7F 80 Invalid byte + */ + + /* After the first step we compute the index for all bytes, then we permute + the bytes according to their indices to check the ranges from the range + table. + * The range for a given type can be found in the range_min_table and + range_max_table, the range for type/index X is in range_min_table[X] ... + range_max_table[X]. + */ + + /* Algorithm: + * Put index zero to all bytes. + * Find all non ASCII characters, give them index 8. + * For each tail byte in a codepoint sequence, give it an index corresponding + to the 1 based index from the end. + * If the first byte of the codepoint is in the [C0...DF] range, we write + index 1 in the following byte. + * If the first byte of the codepoint is in the range [E0...EF], we write + indices 2 and 1 in the next two bytes. + * If the first byte of the codepoint is in the range [F0...FF] we write + indices 3,2,1 into the next three bytes. + * For finding the number of bytes we need to look at high nibbles (4 bits) + and do the lookup from the table, it can be done with shift by 4 + shuffle + instructions. We call it `first_len`. + * Then we shift first_len by 8 bits to get the indices of the 2nd bytes. + * Saturating sub 1 and shift by 8 bits to get the indices of the 3rd bytes. + * Again to get the indices of the 4th bytes. + * Take OR of all that 4 values and check within range. + */ + /* For example: + * input C3 80 68 E2 80 20 A6 F0 A0 80 AC 20 F0 93 80 80 + * first_len 1 0 0 2 0 0 0 3 0 0 0 0 3 0 0 0 + * 1st byte 8 0 0 8 0 0 0 8 0 0 0 0 8 0 0 0 + * 2nd byte 0 1 0 0 2 0 0 0 3 0 0 0 0 3 0 0 // Shift + sub + * 3rd byte 0 0 0 0 0 1 0 0 0 2 0 0 0 0 2 0 // Shift + sub + * 4th byte 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 // Shift + sub + * Index 8 1 0 8 2 1 0 8 3 2 1 0 8 3 2 1 // OR of results + */ + + /* Checking for errors: + * Error checking is done by looking up the high nibble (4 bits) of each byte + against an error checking table. + * Because the lookup value for the second byte depends of the value of the + first byte in codepoint, we use saturated operations to adjust the index. + * Specifically we need to add 2 for E0, 3 for ED, 3 for F0 and 4 for F4 to + match the correct index. + * If we subtract from all bytes EF then EO -> 241, ED -> 254, F0 -> 1, + F4 -> 5 + * Do saturating sub 240, then E0 -> 1, ED -> 14 and we can do lookup to + match the adjustment + * Add saturating 112, then F0 -> 113, F4 -> 117, all that were > 16 will + be more 128 and lookup in ef_fe_table will return 0 but for F0 + and F4 it will be 4 and 5 accordingly + */ + /* + * Then just check the appropriate ranges with greater/smaller equal + instructions. Check tail with a naive algorithm. + * To save from previous 16 byte checks we just align previous_first_len to + get correct continuations of the codepoints. + */ + + /* + * Map high nibble of "First Byte" to legal character length minus 1 + * 0x00 ~ 0xBF --> 0 + * 0xC0 ~ 0xDF --> 1 + * 0xE0 ~ 0xEF --> 2 + * 0xF0 ~ 0xFF --> 3 + */ + const __m128i first_len_table = + _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3); + + /* Map "First Byte" to 8-th item of range table (0xC2 ~ 0xF4) */ + const __m128i first_range_table = + _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8); + + /* + * Range table, map range index to min and max values + */ + const __m128i range_min_table = + _mm_setr_epi8(0x00, 0x80, 0x80, 0x80, 0xA0, 0x80, 0x90, 0x80, 0xC2, 0x7F, + 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F); + + const __m128i range_max_table = + _mm_setr_epi8(0x7F, 0xBF, 0xBF, 0xBF, 0xBF, 0x9F, 0xBF, 0x8F, 0xF4, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80); + + /* + * Tables for fast handling of four special First Bytes(E0,ED,F0,F4), after + * which the Second Byte are not 80~BF. It contains "range index adjustment". + * +------------+---------------+------------------+----------------+ + * | First Byte | original range| range adjustment | adjusted range | + * +------------+---------------+------------------+----------------+ + * | E0 | 2 | 2 | 4 | + * +------------+---------------+------------------+----------------+ + * | ED | 2 | 3 | 5 | + * +------------+---------------+------------------+----------------+ + * | F0 | 3 | 3 | 6 | + * +------------+---------------+------------------+----------------+ + * | F4 | 4 | 4 | 8 | + * +------------+---------------+------------------+----------------+ + */ + + /* df_ee_table[1] -> E0, df_ee_table[14] -> ED as ED - E0 = 13 */ + // The values represent the adjustment in the Range Index table for a correct + // index. + const __m128i df_ee_table = + _mm_setr_epi8(0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0); + + /* ef_fe_table[1] -> F0, ef_fe_table[5] -> F4, F4 - F0 = 4 */ + // The values represent the adjustment in the Range Index table for a correct + // index. + const __m128i ef_fe_table = + _mm_setr_epi8(0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + __m128i prev_input = _mm_set1_epi8(0); + __m128i prev_first_len = _mm_set1_epi8(0); + __m128i error = _mm_set1_epi8(0); + while (end - data >= 16) { + const __m128i input = + _mm_loadu_si128(reinterpret_cast(data)); + + /* high_nibbles = input >> 4 */ + const __m128i high_nibbles = + _mm_and_si128(_mm_srli_epi16(input, 4), _mm_set1_epi8(0x0F)); + + /* first_len = legal character length minus 1 */ + /* 0 for 00~7F, 1 for C0~DF, 2 for E0~EF, 3 for F0~FF */ + /* first_len = first_len_table[high_nibbles] */ + __m128i first_len = _mm_shuffle_epi8(first_len_table, high_nibbles); + + /* First Byte: set range index to 8 for bytes within 0xC0 ~ 0xFF */ + /* range = first_range_table[high_nibbles] */ + __m128i range = _mm_shuffle_epi8(first_range_table, high_nibbles); + + /* Second Byte: set range index to first_len */ + /* 0 for 00~7F, 1 for C0~DF, 2 for E0~EF, 3 for F0~FF */ + /* range |= (first_len, prev_first_len) << 1 byte */ + range = _mm_or_si128(range, _mm_alignr_epi8(first_len, prev_first_len, 15)); + + /* Third Byte: set range index to saturate_sub(first_len, 1) */ + /* 0 for 00~7F, 0 for C0~DF, 1 for E0~EF, 2 for F0~FF */ + __m128i tmp1; + __m128i tmp2; + /* tmp1 = saturate_sub(first_len, 1) */ + tmp1 = _mm_subs_epu8(first_len, _mm_set1_epi8(1)); + /* tmp2 = saturate_sub(prev_first_len, 1) */ + tmp2 = _mm_subs_epu8(prev_first_len, _mm_set1_epi8(1)); + /* range |= (tmp1, tmp2) << 2 bytes */ + range = _mm_or_si128(range, _mm_alignr_epi8(tmp1, tmp2, 14)); + + /* Fourth Byte: set range index to saturate_sub(first_len, 2) */ + /* 0 for 00~7F, 0 for C0~DF, 0 for E0~EF, 1 for F0~FF */ + /* tmp1 = saturate_sub(first_len, 2) */ + tmp1 = _mm_subs_epu8(first_len, _mm_set1_epi8(2)); + /* tmp2 = saturate_sub(prev_first_len, 2) */ + tmp2 = _mm_subs_epu8(prev_first_len, _mm_set1_epi8(2)); + /* range |= (tmp1, tmp2) << 3 bytes */ + range = _mm_or_si128(range, _mm_alignr_epi8(tmp1, tmp2, 13)); + + /* + * Now we have below range indices calculated + * Correct cases: + * - 8 for C0~FF + * - 3 for 1st byte after F0~FF + * - 2 for 1st byte after E0~EF or 2nd byte after F0~FF + * - 1 for 1st byte after C0~DF or 2nd byte after E0~EF or + * 3rd byte after F0~FF + * - 0 for others + * Error cases: + * >9 for non ascii First Byte overlapping + * E.g., F1 80 C2 90 --> 8 3 10 2, where 10 indicates error + */ + + /* Adjust Second Byte range for special First Bytes(E0,ED,F0,F4) */ + /* Overlaps lead to index 9~15, which are illegal in range table */ + __m128i shift1; + __m128i pos; + __m128i range2; + /* shift1 = (input, prev_input) << 1 byte */ + shift1 = _mm_alignr_epi8(input, prev_input, 15); + pos = _mm_sub_epi8(shift1, _mm_set1_epi8(0xEF)); + /* + * shift1: | EF F0 ... FE | FF 00 ... ... DE | DF E0 ... EE | + * pos: | 0 1 15 | 16 17 239| 240 241 255| + * pos-240: | 0 0 0 | 0 0 0 | 0 1 15 | + * pos+112: | 112 113 127| >= 128 | >= 128 | + */ + tmp1 = _mm_subs_epu8(pos, _mm_set1_epi8(-16)); + range2 = _mm_shuffle_epi8(df_ee_table, tmp1); + tmp2 = _mm_adds_epu8(pos, _mm_set1_epi8(112)); + range2 = _mm_add_epi8(range2, _mm_shuffle_epi8(ef_fe_table, tmp2)); + + range = _mm_add_epi8(range, range2); + + /* Load min and max values per calculated range index */ + __m128i min_range = _mm_shuffle_epi8(range_min_table, range); + __m128i max_range = _mm_shuffle_epi8(range_max_table, range); + + /* Check value range */ + if (ReturnPosition) { + error = _mm_cmplt_epi8(input, min_range); + error = _mm_or_si128(error, _mm_cmpgt_epi8(input, max_range)); + /* 5% performance drop from this conditional branch */ + if (!_mm_testz_si128(error, error)) { + break; + } + } else { + error = _mm_or_si128(error, _mm_cmplt_epi8(input, min_range)); + error = _mm_or_si128(error, _mm_cmpgt_epi8(input, max_range)); + } + + prev_input = input; + prev_first_len = first_len; + + data += 16; + } + /* If we got to the end, we don't need to skip any bytes backwards */ + if (ReturnPosition && (data - (end - len)) == 0) { + return ValidUTF8Span(data, end); + } + /* Find previous codepoint (not 80~BF) */ + data -= CodepointSkipBackwards(_mm_extract_epi32(prev_input, 3)); + if (ReturnPosition) { + return (data - (end - len)) + ValidUTF8Span(data, end); + } + /* Test if there was any error */ + if (!_mm_testz_si128(error, error)) { + return 0; + } + /* Check the tail */ + return ValidUTF8Span(data, end); +#endif +} + +} // namespace + +bool IsStructurallyValid(absl::string_view str) { + return ValidUTF8(str.data(), str.size()); +} + +size_t SpanStructurallyValid(absl::string_view str) { + return ValidUTF8(str.data(), str.size()); +} + +} // namespace utf8_range diff --git a/third_party/utf8_range/utf8_validity.h b/third_party/utf8_range/utf8_validity.h new file mode 100644 index 0000000000000..4a8d75b3b46d5 --- /dev/null +++ b/third_party/utf8_range/utf8_validity.h @@ -0,0 +1,23 @@ +// Copyright 2022 Google LLC +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +#ifndef THIRD_PARTY_UTF8_RANGE_UTF8_VALIDITY_H_ +#define THIRD_PARTY_UTF8_RANGE_UTF8_VALIDITY_H_ + +#include "absl/strings/string_view.h" + +namespace utf8_range { + +// Returns true if the sequence of characters is a valid UTF-8 sequence. +bool IsStructurallyValid(absl::string_view str); + +// Returns the length in bytes of the prefix of str that is all +// structurally valid UTF-8. +size_t SpanStructurallyValid(absl::string_view str); + +} // namespace utf8_range + +#endif // THIRD_PARTY_UTF8_RANGE_UTF8_VALIDITY_H_ diff --git a/third_party/utf8_range/utf8_validity_test.cc b/third_party/utf8_range/utf8_validity_test.cc new file mode 100644 index 0000000000000..2648df674b257 --- /dev/null +++ b/third_party/utf8_range/utf8_validity_test.cc @@ -0,0 +1,76 @@ +#include "utf8_validity.h" + +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" + +namespace utf8_range { + +TEST(Utf8Validity, SpanStructurallyValid) { + // Test simple good strings + EXPECT_EQ(4, SpanStructurallyValid("abcd")); + EXPECT_EQ(4, SpanStructurallyValid(absl::string_view("a\0cd", 4))); // NULL + EXPECT_EQ(4, SpanStructurallyValid("ab\xc2\x81")); // 2-byte + EXPECT_EQ(4, SpanStructurallyValid("a\xe2\x81\x81")); // 3-byte + EXPECT_EQ(4, SpanStructurallyValid("\xf2\x81\x81\x81")); // 4 + + // Test simple bad strings + EXPECT_EQ(3, SpanStructurallyValid("abc\x80")); // bad char + EXPECT_EQ(3, SpanStructurallyValid("abc\xc2")); // trunc 2 + EXPECT_EQ(2, SpanStructurallyValid("ab\xe2\x81")); // trunc 3 + EXPECT_EQ(1, SpanStructurallyValid("a\xf2\x81\x81")); // trunc 4 + EXPECT_EQ(2, SpanStructurallyValid("ab\xc0\x81")); // not 1 + EXPECT_EQ(1, SpanStructurallyValid("a\xe0\x81\x81")); // not 2 + EXPECT_EQ(0, SpanStructurallyValid("\xf0\x81\x81\x81")); // not 3 + EXPECT_EQ(0, SpanStructurallyValid("\xf4\xbf\xbf\xbf")); // big + // surrogate min, max + EXPECT_EQ(0, SpanStructurallyValid("\xED\xA0\x80")); // U+D800 + EXPECT_EQ(0, SpanStructurallyValid("\xED\xBF\xBF")); // U+DFFF + + // non-shortest forms should all return false + EXPECT_EQ(0, SpanStructurallyValid("\xc0\x80")); + EXPECT_EQ(0, SpanStructurallyValid("\xc1\xbf")); + EXPECT_EQ(0, SpanStructurallyValid("\xe0\x80\x80")); + EXPECT_EQ(0, SpanStructurallyValid("\xe0\x9f\xbf")); + EXPECT_EQ(0, SpanStructurallyValid("\xf0\x80\x80\x80")); + EXPECT_EQ(0, SpanStructurallyValid("\xf0\x83\xbf\xbf")); + + // This string unchecked caused GWS to crash 7/2006: + // invalid sequence 0xc7 0xc8 0xcd 0xcb + EXPECT_EQ(0, SpanStructurallyValid("\xc7\xc8\xcd\xcb")); +} + +TEST(Utf8Validity, IsStructurallyValid) { + // Test simple good strings + EXPECT_TRUE(IsStructurallyValid("abcd")); + EXPECT_TRUE(IsStructurallyValid(absl::string_view("a\0cd", 4))); // NULL + EXPECT_TRUE(IsStructurallyValid("ab\xc2\x81")); // 2-byte + EXPECT_TRUE(IsStructurallyValid("a\xe2\x81\x81")); // 3-byte + EXPECT_TRUE(IsStructurallyValid("\xf2\x81\x81\x81")); // 4 + + // Test simple bad strings + EXPECT_FALSE(IsStructurallyValid("abc\x80")); // bad char + EXPECT_FALSE(IsStructurallyValid("abc\xc2")); // trunc 2 + EXPECT_FALSE(IsStructurallyValid("ab\xe2\x81")); // trunc 3 + EXPECT_FALSE(IsStructurallyValid("a\xf2\x81\x81")); // trunc 4 + EXPECT_FALSE(IsStructurallyValid("ab\xc0\x81")); // not 1 + EXPECT_FALSE(IsStructurallyValid("a\xe0\x81\x81")); // not 2 + EXPECT_FALSE(IsStructurallyValid("\xf0\x81\x81\x81")); // not 3 + EXPECT_FALSE(IsStructurallyValid("\xf4\xbf\xbf\xbf")); // big + // surrogate min, max + EXPECT_FALSE(IsStructurallyValid("\xED\xA0\x80")); // U+D800 + EXPECT_FALSE(IsStructurallyValid("\xED\xBF\xBF")); // U+DFFF + + // non-shortest forms should all return false + EXPECT_FALSE(IsStructurallyValid("\xc0\x80")); + EXPECT_FALSE(IsStructurallyValid("\xc1\xbf")); + EXPECT_FALSE(IsStructurallyValid("\xe0\x80\x80")); + EXPECT_FALSE(IsStructurallyValid("\xe0\x9f\xbf")); + EXPECT_FALSE(IsStructurallyValid("\xf0\x80\x80\x80")); + EXPECT_FALSE(IsStructurallyValid("\xf0\x83\xbf\xbf")); + + // This string unchecked caused GWS to crash 7/2006: + // invalid sequence 0xc7 0xc8 0xcd 0xcb + EXPECT_FALSE(IsStructurallyValid("\xc7\xc8\xcd\xcb")); +} + +} // namespace utf8_range diff --git a/third_party/utf8_range/workspace_deps.bzl b/third_party/utf8_range/workspace_deps.bzl new file mode 100644 index 0000000000000..d296f9ff5179d --- /dev/null +++ b/third_party/utf8_range/workspace_deps.bzl @@ -0,0 +1,11 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") + +def utf8_range_deps(): + maybe( + http_archive, + name = "com_google_absl", + url = "https://github.com/abseil/abseil-cpp/archive/8c0b94e793a66495e0b1f34a5eb26bd7dc672db0.zip", + strip_prefix = "abseil-cpp-8c0b94e793a66495e0b1f34a5eb26bd7dc672db0", + sha256 = "b9f490fae1c0d89a19073a081c3c588452461e5586e4ae31bc50a8f36339135e", + ) From bb4acc99505268fbe4fd1f00fab81fdb2e4e3ba1 Mon Sep 17 00:00:00 2001 From: hedane Date: Wed, 5 Apr 2023 07:27:53 +0800 Subject: [PATCH 06/39] [PHP] fix a little bug in BaseStub.getDefaultChannel() #32705 (#32792) @alto-php This is my first PR on GitHub, thank you.. #32705 --- src/php/lib/Grpc/BaseStub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php index 620122adae91c..c369e953f349d 100644 --- a/src/php/lib/Grpc/BaseStub.php +++ b/src/php/lib/Grpc/BaseStub.php @@ -120,7 +120,7 @@ private static function updateOpts($opts) { public static function getDefaultChannel($hostname, array $opts) { $channel_opts = self::updateOpts($opts); - return new Channel($hostname, $opts); + return new Channel($hostname, $channel_opts); } /** From d42de648316af462142d21a6fa157692930879dc Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Tue, 4 Apr 2023 17:55:22 -0700 Subject: [PATCH 07/39] [HTTP Proxy] Add user guide on how Core performs HTTP proxy determination (#32789) Forked from https://github.com/grpc/proposal/pull/349 --- doc/core/default_http_proxy_mapper.md | 67 +++++++++++++++++++++++++++ tools/doxygen/Doxyfile.core | 1 + tools/doxygen/Doxyfile.core.internal | 1 + 3 files changed, 69 insertions(+) create mode 100644 doc/core/default_http_proxy_mapper.md diff --git a/doc/core/default_http_proxy_mapper.md b/doc/core/default_http_proxy_mapper.md new file mode 100644 index 0000000000000..4b0777b5ca33d --- /dev/null +++ b/doc/core/default_http_proxy_mapper.md @@ -0,0 +1,67 @@ +# Default HTTP Proxy Mapper User Guide for gRPC Core (and dependents) + +[A1-http-connect-proxy-support.md](https://github.com/grpc/proposal/blob/master/A1-http-connect-proxy-support.md) +proposed how gRPC supports TCP-level proxies via the HTTP CONNECT request, +defined in [RFC-2817](https://www.rfc-editor.org/rfc/rfc2817). + +**Case 1** in the proposal documents a use-case where all outbound traffic from +an environment must go through a proxy. Configurations for such environments are +usually performed using environment variables such as `http_proxy`. gRPC +supports this by providing a default proxy mapper implementation that allows for +overriding the server name (provided in the channel creation hostname) to +resolve based on such configurations. + +This guide documents gRPC C-Core's default proxy mapper implementation. + +## Working + +### Enabling HTTP Proxy + +C-Core checks the following places to determine the HTTP proxy to use, stopping +at the first one that is set: + +1. `GRPC_ARG_HTTP_PROXY` channel arg +2. `grpc_proxy` environment variable +3. `https_proxy` environment variable +4. `http_proxy` environment variable + +If none of the above are set, then no HTTP proxy will be used. + +The allowed format is an [RFC3986](https://www.rfc-editor.org/rfc/rfc3986) URI +string where the scheme is expected to be "http" and the authority portion is +used to determine the proxy to be used. For example, for an HTTP proxy setting +of `http://username:password@proxy.google.com:443`, `username:password` would be +used as user credentials for proxy authentication as per +[RFC7617](https://www.rfc-editor.org/rfc/rfc7617) and `proxy.google.com:443` +would be the host:port HTTP proxy target. If the port part of the authority is +omitted, a default port of 443 is used. Note that user credential can also be +omitted if the proxy does not need authentication. + +### Disabling HTTP Proxy + +If an HTTP proxy is set, C-Core then checks the following places to exclude +traffic destined to listed hosts from going through the proxy determined above, +again stopping at the first one that is set: + +1. `no_grpc_proxy` environment variable +2. `no_proxy`environment variable + +If none of the above are set, then the previously found HTTP proxy is used. + +The format takes a comma-separated list of names, and if any of these names +matches as a suffix of the server host (provided in the channel target), then +the proxy will not be used for that target. For example, with a `grpc_proxy` +setting of `proxy.google.com` and a `no_grpc_proxy` setting of `example.com, +google.com`, channel targets such as `dns:///foo.google.com:50051` and +`bar.example.com:1234` will not use the proxy, but `baz.googleapis.com:443` +would still use the configured proxy `proxy.google.com`. + +As of [PR#31119](https://github.com/grpc/grpc/pull/31119), CIDR blocks are also +supported in the list of names. For example, a `no_proxy` setting of +`10.10.0.0/24` would not use the proxy for channel targets that mention IP +addresses as the host between the range `10.10.0.0` to `10.10.0.255`. + +### Disabling HTTP Proxy Channel-wide + +The lookup and subsequent usage of an HTTP proxy for a specific channel can also +be disabled by setting the channel arg `GRPC_ARG_ENABLE_HTTP_PROXY` to 0. diff --git a/tools/doxygen/Doxyfile.core b/tools/doxygen/Doxyfile.core index fb8c0ccca2ced..97638f2fd979c 100644 --- a/tools/doxygen/Doxyfile.core +++ b/tools/doxygen/Doxyfile.core @@ -772,6 +772,7 @@ doc/connection-backoff-interop-test-description.md \ doc/connection-backoff.md \ doc/connectivity-semantics-and-api.md \ doc/core/combiner-explainer.md \ +doc/core/default_http_proxy_mapper.md \ doc/core/epoll-polling-engine.md \ doc/core/grpc-client-server-polling-engine-usage.md \ doc/core/grpc-cq.md \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 606d73d1a9ce7..d1de4b40f2491 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -772,6 +772,7 @@ doc/connection-backoff-interop-test-description.md \ doc/connection-backoff.md \ doc/connectivity-semantics-and-api.md \ doc/core/combiner-explainer.md \ +doc/core/default_http_proxy_mapper.md \ doc/core/epoll-polling-engine.md \ doc/core/grpc-client-server-polling-engine-usage.md \ doc/core/grpc-cq.md \ From b9f0c019d0d8548116167604e3f4a22e15a56bbb Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Wed, 5 Apr 2023 10:20:20 +0200 Subject: [PATCH 08/39] [cmake] Improved stdalign.h workaround for windows build (#32777) Improved the workaround from https://github.com/grpc/grpc/pull/32764 as suggested in https://github.com/grpc/grpc/pull/32764#issuecomment-1491794750. Once merged, I'll backport to 1.54.x and 1.53.x together with https://github.com/grpc/grpc/pull/32764 (these have switched to VS2019) --- tools/run_tests/helper_scripts/build_cxx.bat | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tools/run_tests/helper_scripts/build_cxx.bat b/tools/run_tests/helper_scripts/build_cxx.bat index 589b544560d75..63c1b6417797e 100644 --- a/tools/run_tests/helper_scripts/build_cxx.bat +++ b/tools/run_tests/helper_scripts/build_cxx.bat @@ -30,13 +30,17 @@ If "%GRPC_BUILD_ACTIVATE_VS_TOOLS%" == "2019" ( ) If "%GRPC_CMAKE_GENERATOR%" == "Visual Studio 16 2019" ( - @rem Force using a new-enough Windows 10 SDK that supports C++11's stdalign.h - @rem For some reason, cmake together with visual studio generator by default - @rem pick an old version of Win SDK even when a newer version is installed on the system. + @rem Always use the newest Windows 10 SDK available. + @rem A new-enough Windows 10 SDK that supports C++11's stdalign.h is required + @rem for a successful build. + @rem By default cmake together with Visual Studio generator + @rem pick a version of Win SDK that matches the Windows version, + @rem even when a newer version of the SDK available. + @rem Setting CMAKE_SYSTEM_VERSION=10.0 changes this behavior + @rem to pick the newest Windows SDK available. @rem When using Ninja generator, this problem doesn't happen. - @rem The argument corresponds to Microsoft.VisualStudio.Component.Windows10SDK.20348 - @rem See b/275694647 - set "CMAKE_SYSTEM_VERSION_ARG=-DCMAKE_SYSTEM_VERSION=10.0.20348.0" + @rem See b/275694647 and https://gitlab.kitware.com/cmake/cmake/-/issues/16202#note_140259 + set "CMAKE_SYSTEM_VERSION_ARG=-DCMAKE_SYSTEM_VERSION=10.0" ) else ( @rem Setting the env variable to a single space translates to passing no argument @rem when evaluated on the command line. From a6c723d64ef6857d59ec204380118deb2814bfd6 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Wed, 5 Apr 2023 10:33:53 +0200 Subject: [PATCH 09/39] [test-infra] Re-enable skipped windows tests (#32658) Followup for https://github.com/grpc/grpc/pull/32649 (which disabled the tests mentioned below). Also sets correct path for tests build by ninja on windows, so that they don't get skipped. Once merged, I'll backport to 1.54.x and 1.53.x The original issue with tests being skipped. ``` + python3 workspace_c_windows_dbg_native/tools/run_tests/run_tests.py -t -j 8 -x run_tests/c_windows_dbg_native/sponge_log.xml --report_suite_name c_windows_dbg_native -l c -c dbg --iomgr_platform native --bq_result_table aggregate_results --measure_cpu_costs 2023-03-20 07:56:53,523 START: tools\run_tests\helper_scripts\build_cxx.bat 2023-03-20 08:04:51,388 PASSED: tools\run_tests\helper_scripts\build_cxx.bat [time=477.9sec, retries=0:0; cpu_cost=0.0; estimated=1.0] 2023-03-20 08:04:52,434 detected port server running version 21 2023-03-20 08:04:52,672 my port server is version 21 2023-03-20 08:04:52,703 SUCCESS: All tests passed WARNING: binary not found, skipping cmake/build/Debug/bad_server_response_test.exe WARNING: binary not found, skipping cmake/build/Debug/connection_refused_test.exe WARNING: binary not found, skipping cmake/build/Debug/goaway_server_test.exe WARNING: binary not found, skipping cmake/build/Debug/invalid_call_argument_test.exe WARNING: binary not found, skipping cmake/build/Debug/multiple_server_queues_test.exe WARNING: binary not found, skipping cmake/build/Debug/no_server_test.exe WARNING: binary not found, skipping cmake/build/Debug/pollset_windows_starvation_test.exe WARNING: binary not found, skipping cmake/build/Debug/public_headers_must_be_c89.exe ``` --- tools/run_tests/run_tests.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index d64ac5dac5905..bea30b84dcc10 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -336,17 +336,12 @@ def test_specs(self): if self.args.iomgr_platform in target.get('exclude_iomgrs', []): continue - if self.platform == 'windows' and target['name'] in ( - 'invalid_call_argument_test', - 'bad_server_response_test', 'goaway_server_test'): - # A few tests fail on the win2019 workers, but since they pass on Windows bazel RBE, - # it seems ok to skip them in run_tests.py temporarily. - # TODO(jtattermusch): Reenable the tests. - continue - if self.platform == 'windows': - binary = 'cmake/build/%s/%s.exe' % (_MSBUILD_CONFIG[ - self.config.build_config], target['name']) + if self._cmake_generator_windows == 'Ninja': + binary = 'cmake/build/%s.exe' % target['name'] + else: + binary = 'cmake/build/%s/%s.exe' % (_MSBUILD_CONFIG[ + self.config.build_config], target['name']) else: binary = 'cmake/build/%s' % target['name'] From be99673d069217def9036cc9ae0346da324ba373 Mon Sep 17 00:00:00 2001 From: Eugene Ostroukhov Date: Wed, 5 Apr 2023 11:56:50 -0700 Subject: [PATCH 10/39] [PSM interop test] implement error-code- RPC behavior (#32810) This reverts commit 47ea5062cb0f5167ae7f36438750888aea63a9fe. --- test/cpp/interop/xds_interop_server.cc | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/test/cpp/interop/xds_interop_server.cc b/test/cpp/interop/xds_interop_server.cc index ed3c2909b8103..481d43737ee24 100644 --- a/test/cpp/interop/xds_interop_server.cc +++ b/test/cpp/interop/xds_interop_server.cc @@ -20,6 +20,7 @@ #include "absl/flags/flag.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" #include "absl/synchronization/mutex.h" #include @@ -65,6 +66,46 @@ using grpc::testing::SimpleResponse; using grpc::testing::TestService; using grpc::testing::XdsUpdateHealthService; +namespace { +constexpr absl::string_view kRpcBehaviorMetadataKey = "rpc-behavior"; +constexpr absl::string_view kErrorCodeRpcBehavior = "error-code-"; + +std::set GetRpcBehaviorMetadata(ServerContext* context) { + std::set rpc_behaviors; + auto rpc_behavior_metadata = + context->client_metadata().equal_range(grpc::string_ref( + kRpcBehaviorMetadataKey.data(), kRpcBehaviorMetadataKey.length())); + for (auto metadata = rpc_behavior_metadata.first; + metadata != rpc_behavior_metadata.second; ++metadata) { + auto value = metadata->second; + for (auto behavior : + absl::StrSplit(absl::string_view(value.data(), value.length()), ',')) { + rpc_behaviors.emplace(behavior); + } + } + return rpc_behaviors; +} + +absl::optional GetStatusForRpcBehaviorMetadata( + absl::string_view header_value) { + if (absl::StartsWith(header_value, kErrorCodeRpcBehavior)) { + grpc::StatusCode code; + if (absl::SimpleAtoi(header_value.substr(kErrorCodeRpcBehavior.length()), + &code)) { + std::string message = absl::StrCat( + "Rpc failed as per the rpc-behavior header value: ", header_value); + return Status(code, message); + } else { + std::string message = absl::StrCat( + "Invalid format for rpc-behavior header: ", header_value); + return Status(grpc::StatusCode::INVALID_ARGUMENT, message); + } + } else { + return absl::nullopt; + } +} +} // namespace + class TestServiceImpl : public TestService::Service { public: explicit TestServiceImpl(const std::string& hostname) : hostname_(hostname) {} @@ -72,6 +113,12 @@ class TestServiceImpl : public TestService::Service { Status UnaryCall(ServerContext* context, const SimpleRequest* /*request*/, SimpleResponse* response) override { response->set_server_id(absl::GetFlag(FLAGS_server_id)); + for (const auto& rpc_behavior : GetRpcBehaviorMetadata(context)) { + auto maybe_status = GetStatusForRpcBehaviorMetadata(rpc_behavior); + if (maybe_status.has_value()) { + return *maybe_status; + } + } response->set_hostname(hostname_); context->AddInitialMetadata("hostname", hostname_); return Status::OK; From b58963c66ad45d4a85ceb3e0f4fecaa641c714ed Mon Sep 17 00:00:00 2001 From: ryan-gunderson <73042169+ryan-gunderson@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:05:14 -0400 Subject: [PATCH 11/39] [Aio Type Hints] Update type hints for async iteration (#32655) Fix python type hints for async iteration on `UnaryStreamCall` and `StreamStreamCall`. Those classes _are_ `AsyncIterable`s and `__aiter__` returns an `AsyncIterator`. --- src/python/grpcio/grpc/aio/_base_call.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/python/grpcio/grpc/aio/_base_call.py b/src/python/grpcio/grpc/aio/_base_call.py index 029584e94a53d..ff4071758b555 100644 --- a/src/python/grpcio/grpc/aio/_base_call.py +++ b/src/python/grpcio/grpc/aio/_base_call.py @@ -20,7 +20,7 @@ from abc import ABCMeta from abc import abstractmethod -from typing import AsyncIterable, Awaitable, Generic, Optional, Union +from typing import AsyncIterator, Awaitable, Generic, Optional, Union import grpc @@ -154,13 +154,13 @@ class UnaryStreamCall(Generic[RequestType, ResponseType], metaclass=ABCMeta): @abstractmethod - def __aiter__(self) -> AsyncIterable[ResponseType]: - """Returns the async iterable representation that yields messages. + def __aiter__(self) -> AsyncIterator[ResponseType]: + """Returns the async iterator representation that yields messages. Under the hood, it is calling the "read" method. Returns: - An async iterable object that yields messages. + An async iterator object that yields messages. """ @abstractmethod @@ -210,13 +210,13 @@ class StreamStreamCall(Generic[RequestType, ResponseType], metaclass=ABCMeta): @abstractmethod - def __aiter__(self) -> AsyncIterable[ResponseType]: - """Returns the async iterable representation that yields messages. + def __aiter__(self) -> AsyncIterator[ResponseType]: + """Returns the async iterator representation that yields messages. Under the hood, it is calling the "read" method. Returns: - An async iterable object that yields messages. + An async iterator object that yields messages. """ @abstractmethod From bdd1ac4d1d1dd6f64ab94b46cea3ea9663285f6e Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Wed, 5 Apr 2023 14:45:21 -0700 Subject: [PATCH 12/39] [GcpObservability C++] De-experimentalize API (#32715) This PR aims to de-experimentalize the APIs for GCP Observability. We would have ideally wanted public feedback before declaring the APIs stable, but we need stable APIs for GA. Changes made after API review with @markdroth @veblush, @ctiller and the entire Core/C++ team - * The old experimental APIs `grpc::experimental::GcpObservabilityInit` and `grpc::experimental::GcpObservabilityClose` are now deprecated and will be deleted after v.1.55 release. * The new API gets rid of the Close method and follows the RAII idiom with a single `grpc::GcpObservability::Init()` call that returns an `GcpObservability` object, the lifetime of which controls when observability data is flushed. * The `GcpObservability` class could in the future add more methods. For example, a debug method that shows the current configuration. * Document that GcpObservability initialization and flushing (on `GcpObservability` destruction) are blocking calls. * Document that gRPC is still usable if GcpObservability initialization failed. (Added a test to prove the same). * Since we don't have a good way to flush stats and tracing with OpenCensus, the examples required users to sleep for 25 seconds. This sleep is now part of `GcpObservability` destruction. Additional Implementation details - * `GcpObservability::Init` is now marked with `GRPC_MUST_USE_RESULT` to make sure that the results are used. We ideally want users to store it, but this is better than nothing. * Added a note on GCP Observability lifetime guarantees. --- .../helloworld/greeter_client.cc | 22 ++--- .../helloworld/greeter_server.cc | 23 ++--- include/grpcpp/ext/gcp_observability.h | 96 ++++++++++++++++--- src/cpp/ext/gcp/BUILD | 1 + src/cpp/ext/gcp/environment_autodetect.h | 9 +- src/cpp/ext/gcp/observability.cc | 81 +++++++++++++--- test/cpp/ext/gcp/BUILD | 2 + test/cpp/ext/gcp/observability_test.cc | 47 ++++++++- 8 files changed, 217 insertions(+), 64 deletions(-) diff --git a/examples/cpp/gcp_observability/helloworld/greeter_client.cc b/examples/cpp/gcp_observability/helloworld/greeter_client.cc index cee4bfb367420..7b4d79fab1f09 100644 --- a/examples/cpp/gcp_observability/helloworld/greeter_client.cc +++ b/examples/cpp/gcp_observability/helloworld/greeter_client.cc @@ -88,11 +88,11 @@ int main(int argc, char** argv) { // Turn on GCP Observability for the whole binary. Based on the configuration, // this will emit observability data (stats, tracing and logging) to GCP // backends. Note that this should be done before any other gRPC operation. - auto status = grpc::experimental::GcpObservabilityInit(); - if (!status.ok()) { - std::cerr << "GcpObservabilityInit() failed: " << status.ToString() - << std::endl; - return static_cast(status.code()); + auto observability = grpc::GcpObservability::Init(); + if (!observability.ok()) { + std::cerr << "GcpObservability::Init() failed: " + << observability.status().ToString() << std::endl; + return static_cast(observability.status().code()); } std::cout << "Initialized GCP Observability" << std::endl; // We indicate that the channel isn't authenticated (use of @@ -102,15 +102,7 @@ int main(int argc, char** argv) { std::string user("world"); std::string reply = greeter.SayHello(user); std::cout << "Greeter received: " << reply << std::endl; - // Flush out any pending Observability data - std::cout << "Closing GCP Observability" << std::endl; - grpc::experimental::GcpObservabilityClose(); - std::cout << "Sleeping for 25 seconds to make sure Observability stats and " - "tracing are flushed. Don't shut off server either." - << std::endl; - // Currently, GcpObservabilityClose() only supports flushing logs. Stats and - // tracing get automatically flushed at a regular interval, so sleep for an - // interval to make sure that those are flushed too. - std::this_thread::sleep_for(std::chrono::seconds(25)); + // 'observability' object going out of scope will flush observability data. + std::cout << "Closing and flushing GCP Observability data" << std::endl; return 0; } diff --git a/examples/cpp/gcp_observability/helloworld/greeter_server.cc b/examples/cpp/gcp_observability/helloworld/greeter_server.cc index 4bc624ac45157..5312d68708a99 100644 --- a/examples/cpp/gcp_observability/helloworld/greeter_server.cc +++ b/examples/cpp/gcp_observability/helloworld/greeter_server.cc @@ -99,24 +99,15 @@ int main(int argc, char** argv) { // Turn on GCP Observability for the whole binary. Based on the configuration, // this will emit observability data (stats, tracing and logging) to GCP // backends. Note that this should be done before any other gRPC operation. - auto status = grpc::experimental::GcpObservabilityInit(); - if (!status.ok()) { - std::cerr << "GcpObservabilityInit() failed: " << status.ToString() - << std::endl; - return static_cast(status.code()); + auto observability = grpc::GcpObservability::Init(); + if (!observability.ok()) { + std::cerr << "GcpObservability::Init() failed: " + << observability.status().ToString() << std::endl; + return static_cast(observability.status().code()); } std::cout << "Initialized GCP Observability" << std::endl; RunServer(absl::GetFlag(FLAGS_port)); - // Flush out any pending Observability data - std::cout << "Closing GCP Observability" << std::endl; - grpc::experimental::GcpObservabilityClose(); - std::cout << "Sleeping for 25 seconds to make sure Observability stats and " - "tracing are flushed.(Another Ctrl+C will immediately exit the " - "program.)" - << std::endl; - // Currently, GcpObservabilityClose() only supports flushing logs. Stats and - // tracing get automatically flushed at a regular interval, so sleep for an - // interval to make sure that those are flushed too. - std::this_thread::sleep_for(std::chrono::seconds(25)); + // 'observability' object going out of scope will flush observability data. + std::cout << "Closing and flushing GCP Observability data" << std::endl; return 0; } diff --git a/include/grpcpp/ext/gcp_observability.h b/include/grpcpp/ext/gcp_observability.h index 5efa95c634e33..871b51dce66af 100644 --- a/include/grpcpp/ext/gcp_observability.h +++ b/include/grpcpp/ext/gcp_observability.h @@ -17,25 +17,97 @@ #ifndef GRPCPP_EXT_GCP_OBSERVABILITY_H #define GRPCPP_EXT_GCP_OBSERVABILITY_H +#include + #include "absl/status/status.h" +#include "absl/status/statusor.h" + +#include namespace grpc { -namespace experimental { -// Initialize GCP Observability for gRPC. -// This should be called before any other gRPC operations like creating a -// channel, server, credentials etc. -// The most common usage would call this at the top (or near the top) in main(). -// As an implementation detail, this properly initializes the OpenCensus stats -// and tracing plugin, so applications do not need to perform any additional -// gRPC C++ OpenCensus setup/registration to get GCP Observability for gRPC. -absl::Status GcpObservabilityInit(); +// GcpObservability objects follow the RAII idiom and help manage the lifetime +// of gRPC Observability data exporting to GCP. `GcpObservability::Init()` +// should be invoked instead to return an `GcpObservability` instance. +// Observability data is flushed at regular intervals, and also when this +// instance goes out of scope and its destructor is invoked. +class GcpObservability { + public: + // Initialize GCP Observability for gRPC. + // This should be called before any other gRPC operations like creating a + // channel, server, credentials etc. + // The return value helps determine whether observability was + // successfully enabled or not. On success, an object of class `Observability` + // is returned. When this object goes out of scope, GCP Observability stats, + // tracing and logging data is flushed. On failure, the status message can be + // used to determine the cause of failure. It is up to the applications to + // either crash on failure, or continue without GCP observability being + // enabled. The status codes do not have any special meaning at present, and + // users should not make any assumptions based on the status code, other than + // a non-OK status code meaning that observability initialization failed. + // + // The expected usage is to call this at the top (or near the top) in + // main(), and let it go out of scope after all RPCs and activities that we + // want to observe are done. Please look at + // https://github.com/grpc/grpc/blob/master/examples/cpp/gcp_observability/helloworld/greeter_client.cc + // and + // https://github.com/grpc/grpc/blob/master/examples/cpp/gcp_observability/helloworld/greeter_server.cc + // for sample usage. + // + // It is possible for an initialized GcpObservability object to go out of + // scope while RPCs and other gRPC operations are still ongoing. In this case, + // GCP Observability tries to flush all observability data collected till that + // point. + // + // Note that this is a blocking call which properly sets up gRPC Observability + // to work with GCP and might take a few seconds to return. Similarly, the + // destruction of a non-moved-from `Observability` object is also blocking + // since it flushes the observability data to GCP. + // + // As an implementation detail, this properly initializes the OpenCensus stats + // and tracing plugin, so applications do not need to perform any additional + // gRPC C++ OpenCensus setup/registration to get GCP Observability for gRPC. + static absl::StatusOr Init() GRPC_MUST_USE_RESULT; -// Gracefully shuts down GCP Observability. -// Note that graceful shutdown for stats and tracing is not yet supported. -void GcpObservabilityClose(); + GcpObservability() = default; + // Move constructor and Move-assignment operator. + // The moved-from object will no longer be valid and will not cause GCP + // Observability stats, tracing and logging data to flush. + GcpObservability(GcpObservability&& other) noexcept; + GcpObservability& operator=(GcpObservability&& other) noexcept; + + // Delete copy and copy-assignment operator + GcpObservability(const GcpObservability&) = delete; + GcpObservability& operator=(const GcpObservability&) = delete; + private: + // Helper class that aids in implementing GCP Observability. + // Inheriting from GrpcLibrary makes sure that gRPC is initialized and remains + // initialized for the lifetime of GCP Observability. In the future, when gRPC + // initialization goes away, we might still want to keep gRPC Event Engine + // initialized, just in case, we need to perform some IO operations during + // observability close. + // Note that the lifetime guarantees are only one way, i.e., GcpObservability + // object guarantees that gRPC will not shutdown while the object is still in + // scope, but the other way around does not hold true. Even though that is not + // the expected usage, GCP Observability can shutdown before gRPC shuts down. + // It follows that gRPC should not hold any callbacks from GcpObservability. A + // change in this restriction should go through a design review. + class GcpObservabilityImpl : private internal::GrpcLibrary { + public: + ~GcpObservabilityImpl() override; + }; + std::unique_ptr impl_; +}; + +namespace experimental { +// TODO(yashykt): Delete this after the 1.55 release. +GRPC_DEPRECATED("Use grpc::GcpObservability::Init() instead.") +absl::Status GcpObservabilityInit(); +GRPC_DEPRECATED("Use grpc::GcpObservability::Init() instead.") +void GcpObservabilityClose(); } // namespace experimental + } // namespace grpc #endif // GRPCPP_EXT_GCP_OBSERVABILITY_H diff --git a/src/cpp/ext/gcp/BUILD b/src/cpp/ext/gcp/BUILD index e4ef2bcaf0f49..606d7f2a5178f 100644 --- a/src/cpp/ext/gcp/BUILD +++ b/src/cpp/ext/gcp/BUILD @@ -40,6 +40,7 @@ grpc_cc_library( "absl/status", "absl/status:statusor", "absl/strings", + "absl/time", "absl/types:optional", "google/api:monitored_resource_cc_proto", "googleapis_monitoring_grpc_service", diff --git a/src/cpp/ext/gcp/environment_autodetect.h b/src/cpp/ext/gcp/environment_autodetect.h index d228ea582e04f..dd4738b9a06d5 100644 --- a/src/cpp/ext/gcp/environment_autodetect.h +++ b/src/cpp/ext/gcp/environment_autodetect.h @@ -34,13 +34,10 @@ namespace grpc { -namespace experimental { -// Forward declaration for GcpObservabilityInit -absl::Status GcpObservabilityInit(); -} // namespace experimental - namespace internal { +absl::Status GcpObservabilityInit(); + class EnvironmentAutoDetect { public: struct ResourceType { @@ -64,7 +61,7 @@ class EnvironmentAutoDetect { } private: - friend absl::Status grpc::experimental::GcpObservabilityInit(); + friend absl::Status grpc::internal::GcpObservabilityInit(); // GcpObservabilityInit() is responsible for setting up the singleton with the // project_id. diff --git a/src/cpp/ext/gcp/observability.cc b/src/cpp/ext/gcp/observability.cc index 3a2113e03917d..94a88fd810f0f 100644 --- a/src/cpp/ext/gcp/observability.cc +++ b/src/cpp/ext/gcp/observability.cc @@ -28,6 +28,8 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" #include "absl/types/optional.h" #include "google/api/monitored_resource.pb.h" #include "google/devtools/cloudtrace/v2/tracing.grpc.pb.h" @@ -45,6 +47,7 @@ #include #include "src/core/ext/filters/logging/logging_filter.h" +#include "src/core/lib/gprpp/crash.h" #include "src/core/lib/gprpp/notification.h" #include "src/cpp/client/client_stats_interceptor.h" #include "src/cpp/ext/filters/census/client_filter.h" @@ -55,12 +58,14 @@ #include "src/cpp/ext/gcp/observability_logging_sink.h" namespace grpc { -namespace experimental { +namespace internal { namespace { grpc::internal::ObservabilityLoggingSink* g_logging_sink = nullptr; +bool g_gcp_observability_initialized = false; + // TODO(yashykt): These constants are currently derived from the example at // https://cloud.google.com/traffic-director/docs/observability-proxyless#c++. // We might want these to be configurable. @@ -74,18 +79,20 @@ constexpr char kGoogleStackdriverStatsAddress[] = "monitoring.googleapis.com"; void RegisterOpenCensusViewsForGcpObservability() { // Register client default views for GCP observability - ClientStartedRpcs().RegisterForExport(); - ClientCompletedRpcs().RegisterForExport(); - ClientRoundtripLatency().RegisterForExport(); + experimental::ClientStartedRpcs().RegisterForExport(); + experimental::ClientCompletedRpcs().RegisterForExport(); + experimental::ClientRoundtripLatency().RegisterForExport(); internal::ClientApiLatency().RegisterForExport(); - ClientSentCompressedMessageBytesPerRpc().RegisterForExport(); - ClientReceivedCompressedMessageBytesPerRpc().RegisterForExport(); + experimental::ClientSentCompressedMessageBytesPerRpc().RegisterForExport(); + experimental::ClientReceivedCompressedMessageBytesPerRpc() + .RegisterForExport(); // Register server default views for GCP observability - ServerStartedRpcs().RegisterForExport(); - ServerCompletedRpcs().RegisterForExport(); - ServerSentCompressedMessageBytesPerRpc().RegisterForExport(); - ServerReceivedCompressedMessageBytesPerRpc().RegisterForExport(); - ServerServerLatency().RegisterForExport(); + experimental::ServerStartedRpcs().RegisterForExport(); + experimental::ServerCompletedRpcs().RegisterForExport(); + experimental::ServerSentCompressedMessageBytesPerRpc().RegisterForExport(); + experimental::ServerReceivedCompressedMessageBytesPerRpc() + .RegisterForExport(); + experimental::ServerServerLatency().RegisterForExport(); } } // namespace @@ -100,6 +107,10 @@ absl::Status GcpObservabilityInit() { !config->cloud_logging.has_value()) { return absl::OkStatus(); } + if (g_gcp_observability_initialized) { + grpc_core::Crash("GCP Observability for gRPC was already initialized."); + } + g_gcp_observability_initialized = true; grpc::internal::EnvironmentAutoDetect::Create(config->project_id); if (!config->cloud_trace.has_value()) { // Disable OpenCensus tracing @@ -206,7 +217,55 @@ void GcpObservabilityClose() { if (g_logging_sink != nullptr) { g_logging_sink->FlushAndClose(); } + // Currently, GcpObservabilityClose() only supports flushing logs. Stats and + // tracing get automatically flushed at a regular interval, so sleep for an + // interval to make sure that those are flushed too. + absl::SleepFor(absl::Seconds(25)); } +} // namespace internal + +namespace experimental { + +absl::Status GcpObservabilityInit() { + return grpc::internal::GcpObservabilityInit(); +} + +void GcpObservabilityClose() { return grpc::internal::GcpObservabilityClose(); } + } // namespace experimental + +// +// GcpObservability +// + +absl::StatusOr GcpObservability::Init() { + absl::Status status = grpc::internal::GcpObservabilityInit(); + if (!status.ok()) { + return status; + } + GcpObservability obj; + obj.impl_ = std::make_unique(); + return obj; +} + +GcpObservability::GcpObservability(GcpObservability&& other) noexcept + : impl_(std::move(other.impl_)) {} + +GcpObservability& GcpObservability::operator=( + GcpObservability&& other) noexcept { + if (this != &other) { + impl_ = std::move(other.impl_); + } + return *this; +} + +// +// GcpObservability::GcpObservabilityImpl +// + +GcpObservability::GcpObservabilityImpl::~GcpObservabilityImpl() { + grpc::internal::GcpObservabilityClose(); +} + } // namespace grpc diff --git a/test/cpp/ext/gcp/BUILD b/test/cpp/ext/gcp/BUILD index 4dce17e2fb68c..e7e38f0c943d0 100644 --- a/test/cpp/ext/gcp/BUILD +++ b/test/cpp/ext/gcp/BUILD @@ -30,6 +30,8 @@ grpc_cc_test( uses_polling = False, deps = [ "//:grpcpp_gcp_observability", + "//src/proto/grpc/testing:echo_proto", + "//test/cpp/end2end:test_service_impl", "//test/cpp/util:test_util", ], ) diff --git a/test/cpp/ext/gcp/observability_test.cc b/test/cpp/ext/gcp/observability_test.cc index c8c9c28f3ec87..144c057b73f7a 100644 --- a/test/cpp/ext/gcp/observability_test.cc +++ b/test/cpp/ext/gcp/observability_test.cc @@ -17,25 +17,64 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include #include #include "src/core/lib/config/core_configuration.h" +#include "src/proto/grpc/testing/echo.grpc.pb.h" +#include "src/proto/grpc/testing/echo_messages.pb.h" +#include "test/core/util/port.h" #include "test/core/util/test_config.h" +#include "test/cpp/end2end/test_service_impl.h" +namespace grpc { +namespace testing { namespace { -TEST(GcpObservabilityTest, RegistrationTest) { - auto status = grpc::experimental::GcpObservabilityInit(); - EXPECT_EQ(status, +TEST(GcpObservabilityTest, Basic) { + auto observability = grpc::GcpObservability::Init(); + EXPECT_EQ(observability.status(), absl::FailedPreconditionError( "Environment variables GRPC_GCP_OBSERVABILITY_CONFIG_FILE or " "GRPC_GCP_OBSERVABILITY_CONFIG " "not defined")); - grpc_core::CoreConfiguration::Reset(); } +TEST(GcpObservabilityTest, ContinuesWorkingAfterFailure) { + auto observability = grpc::GcpObservability::Init(); + EXPECT_FALSE(observability.ok()); + + // Set up a synchronous server on a different thread to avoid the asynch + // interface. + grpc::ServerBuilder builder; + TestServiceImpl service; + int port = grpc_pick_unused_port_or_die(); + auto server_address = absl::StrCat("localhost:", port); + // Use IPv4 here because it's less flaky than IPv6 ("[::]:0") on Travis. + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials(), + &port); + builder.RegisterService(&service); + auto server = builder.BuildAndStart(); + ASSERT_NE(nullptr, server); + auto server_thread = std::thread([&]() { server->Wait(); }); + // Send a single RPC to make sure that things work. + auto stub = EchoTestService::NewStub( + grpc::CreateChannel(server_address, grpc::InsecureChannelCredentials())); + EchoRequest request; + request.set_message("foo"); + EchoResponse response; + grpc::ClientContext context; + grpc::Status status = stub->Echo(&context, request, &response); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(response.message(), "foo"); + server->Shutdown(); + server_thread.join(); +} + } // namespace +} // namespace testing +} // namespace grpc int main(int argc, char** argv) { grpc::testing::TestEnvironment env(&argc, argv); From 5452017fa4d6e980d503dc661fbf0d1c255cff0d Mon Sep 17 00:00:00 2001 From: Xuan Wang Date: Wed, 5 Apr 2023 15:23:59 -0700 Subject: [PATCH 13/39] [Async ExecuteBatchError Issue] Log instead of raise ExecuteBatchError in Aysncio server (#32551) ### Description When invoking a RPC, sometimes operations in core will fail which result in an `ExecuteBatchError`, currently we [simply raise this error](https://github.com/grpc/grpc/blob/master/src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi#L705), and left this Exception unhandled in event loop with error message `Task exception was never retrieved`. ### Changes included This PR changes the behavior to log the `ExecuteBatchError` instead of raising it. This shouldn't change existing behavior because even without this change, the exception will simply left on event loop. ### Testing Tested by manually `set_expection` in [this future](https://github.com/grpc/grpc/blob/master/src/python/grpcio/grpc/_cython/_cygrpc/aio/callback_common.pyx.pxi#L84), verified that error was logged correctly and no exception was left in event loop, sample log: ``` ERROR:grpc._cython.cygrpc:ExecuteBatchError raised in core by servicer method [/grpc.testing.TestService/UnaryCall] Traceback (most recent call last): File "src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi", line 682, in _cython.cygrpc._handle_exceptions File "src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi", line 802, in _handle_rpc File "src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi", line 547, in _handle_unary_unary_rpc File "src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi", line 452, in _finish_handler_with_unary_response File "src/python/grpcio/grpc/_cython/_cygrpc/aio/callback_common.pyx.pxi", line 106, in execute_wrong_batch _cython.cygrpc.ExecuteBatchError: Failed "execute_batch_including_SendInitialMetadataOperation": (<_cython.cygrpc.SendInitialMetadataOperation object at 0x7fa86d936ca0>, <_cython.cygrpc.SendMessageOperation object at 0x7fa86cf48cb0>, <_cython.cygrpc.SendStatusFromServerOperation object at 0x7fa86cf4c110>) ``` --- src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi index e85efdd0b9f58..5adaeb4889616 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi @@ -702,7 +702,9 @@ async def _handle_exceptions(RPCState rpc_state, object rpc_coro, object loop): if rpc_state.client_closed: return else: - raise + _LOGGER.exception('ExecuteBatchError raised in core by servicer method [%s]' % ( + _decode(rpc_state.method()))) + return except Exception as e: _LOGGER.exception('Unexpected [%s] raised by servicer method [%s]' % ( type(e).__name__, From 962a21302eac9847054ce313d5edb9555e666403 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Wed, 5 Apr 2023 16:04:22 -0700 Subject: [PATCH 14/39] [client channel] replace CallDispatchController with a simple callback (#32812) I added the `CallDispatchController` interface back in #26200 in preparation for supporting the retry circuit breaker in xDS. However, we subsequently decided not to support that feature, so we no longer need this interface. And switching back to a simple on-commit callback should make it easier to implement stateful session affinity for WeightedClusters. --- BUILD | 1 + src/core/BUILD | 1 + .../filters/client_channel/client_channel.cc | 30 ++++--- .../filters/client_channel/client_channel.h | 36 +++++---- .../client_channel/client_channel_internal.h | 48 ++---------- .../filters/client_channel/config_selector.h | 22 ++---- .../resolver/xds/xds_resolver.cc | 41 ++-------- .../filters/client_channel/retry_filter.cc | 78 ++++++------------- 8 files changed, 78 insertions(+), 179 deletions(-) diff --git a/BUILD b/BUILD index a8d588ffa63f0..7c23227f0700e 100644 --- a/BUILD +++ b/BUILD @@ -2851,6 +2851,7 @@ grpc_cc_library( "absl/cleanup", "absl/container:flat_hash_set", "absl/container:inlined_vector", + "absl/functional:any_invocable", "absl/status", "absl/status:statusor", "absl/strings", diff --git a/src/core/BUILD b/src/core/BUILD index 5688c43ed85bc..308c6dad11cdc 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -4990,6 +4990,7 @@ grpc_cc_library( "ext/filters/client_channel/resolver/xds/xds_resolver.cc", ], external_deps = [ + "absl/functional:any_invocable", "absl/meta:type_traits", "absl/random", "absl/status", diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc index 9d1f2574f2d18..3fce6f5934ad9 100644 --- a/src/core/ext/filters/client_channel/client_channel.cc +++ b/src/core/ext/filters/client_channel/client_channel.cc @@ -400,7 +400,7 @@ class DynamicTerminationFilter::CallData { calld->call_context_[GRPC_CONTEXT_SERVICE_CONFIG_CALL_DATA].value); calld->lb_call_ = client_channel->CreateLoadBalancedCall( args, pollent, nullptr, - service_config_call_data->call_dispatch_controller(), + [service_config_call_data]() { service_config_call_data->Commit(); }, /*is_transparent_retry=*/false); if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_call_trace)) { gpr_log(GPR_INFO, @@ -1121,12 +1121,11 @@ OrphanablePtr ClientChannel::CreateLoadBalancedCall( const grpc_call_element_args& args, grpc_polling_entity* pollent, grpc_closure* on_call_destruction_complete, - ConfigSelector::CallDispatchController* call_dispatch_controller, - bool is_transparent_retry) { + absl::AnyInvocable on_commit, bool is_transparent_retry) { return OrphanablePtr( args.arena->New( this, args, pollent, on_call_destruction_complete, - call_dispatch_controller, is_transparent_retry)); + std::move(on_commit), is_transparent_retry)); } ChannelArgs ClientChannel::MakeSubchannelArgs( @@ -1849,7 +1848,7 @@ grpc_error_handle ClientChannel::CallData::ApplyServiceConfigToCallLocked( arena()->New( std::move(call_config->service_config), call_config->method_configs, std::move(call_config->call_attributes), - call_config->call_dispatch_controller, call_context()); + std::move(call_config->on_commit), call_context()); // Apply our own method params to the call. auto* method_params = static_cast( service_config_call_data->GetMethodParsedConfig( @@ -2010,9 +2009,8 @@ void ClientChannel::FilterBasedCallData::StartTransportStreamOpBatch( grpc_deadline_state_client_start_transport_stream_op_batch( &calld->deadline_state_, batch); } - // Intercept recv_trailing_metadata to call CallDispatchController::Commit(), - // in case we wind up failing the call before we get down to the retry - // or LB call layer. + // Intercept recv_trailing_metadata to commit the call, in case we wind up + // failing the call before we get down to the retry or LB call layer. if (batch->recv_trailing_metadata) { calld->original_recv_trailing_metadata_ready_ = batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready; @@ -2337,7 +2335,7 @@ void ClientChannel::FilterBasedCallData:: service_config_call_data); } if (service_config_call_data != nullptr) { - service_config_call_data->call_dispatch_controller()->Commit(); + service_config_call_data->Commit(); } // Chain to original callback. Closure::Run(DEBUG_LOCATION, calld->original_recv_trailing_metadata_ready_, @@ -2518,14 +2516,13 @@ ClientCallTracer::CallAttemptTracer* CreateCallAttemptTracer( ClientChannel::LoadBalancedCall::LoadBalancedCall( ClientChannel* chand, grpc_call_context_element* call_context, - ConfigSelector::CallDispatchController* call_dispatch_controller, - bool is_transparent_retry) + absl::AnyInvocable on_commit, bool is_transparent_retry) : InternallyRefCounted( GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_lb_call_trace) ? "LoadBalancedCall" : nullptr), chand_(chand), - call_dispatch_controller_(call_dispatch_controller) { + on_commit_(std::move(on_commit)) { CreateCallAttemptTracer(call_context, is_transparent_retry); if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_lb_call_trace)) { gpr_log(GPR_INFO, "chand=%p lb_call=%p: created", chand_, this); @@ -2661,7 +2658,7 @@ absl::optional ClientChannel::LoadBalancedCall::PickSubchannel( return error; } // Pick succeeded. - call_dispatch_controller_->Commit(); + Commit(); return absl::OkStatus(); } } @@ -2762,9 +2759,8 @@ bool ClientChannel::LoadBalancedCall::PickSubchannelImpl( ClientChannel::FilterBasedLoadBalancedCall::FilterBasedLoadBalancedCall( ClientChannel* chand, const grpc_call_element_args& args, grpc_polling_entity* pollent, grpc_closure* on_call_destruction_complete, - ConfigSelector::CallDispatchController* call_dispatch_controller, - bool is_transparent_retry) - : LoadBalancedCall(chand, args.context, call_dispatch_controller, + absl::AnyInvocable on_commit, bool is_transparent_retry) + : LoadBalancedCall(chand, args.context, std::move(on_commit), is_transparent_retry), deadline_(args.deadline), arena_(args.arena), @@ -3131,7 +3127,7 @@ class ClientChannel::FilterBasedLoadBalancedCall::LbQueuedCallCanceller { lb_call->lb_call_canceller_); } if (lb_call->lb_call_canceller_ == self && !error.ok()) { - lb_call->call_dispatch_controller()->Commit(); + lb_call->Commit(); // Remove pick from list of queued picks. lb_call->RemoveCallFromLbQueuedCallsLocked(); // Remove from queued picks list. diff --git a/src/core/ext/filters/client_channel/client_channel.h b/src/core/ext/filters/client_channel/client_channel.h index 957a4748c3b87..1152b86eebca3 100644 --- a/src/core/ext/filters/client_channel/client_channel.h +++ b/src/core/ext/filters/client_channel/client_channel.h @@ -25,9 +25,11 @@ #include #include #include +#include #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_set.h" +#include "absl/functional/any_invocable.h" #include "absl/status/status.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" @@ -159,8 +161,7 @@ class ClientChannel { OrphanablePtr CreateLoadBalancedCall( const grpc_call_element_args& args, grpc_polling_entity* pollent, grpc_closure* on_call_destruction_complete, - ConfigSelector::CallDispatchController* call_dispatch_controller, - bool is_transparent_retry); + absl::AnyInvocable on_commit, bool is_transparent_retry); // Exposed for testing only. static ChannelArgs MakeSubchannelArgs( @@ -365,10 +366,10 @@ class ClientChannel { class ClientChannel::LoadBalancedCall : public InternallyRefCounted { public: - LoadBalancedCall( - ClientChannel* chand, grpc_call_context_element* call_context, - ConfigSelector::CallDispatchController* call_dispatch_controller, - bool is_transparent_retry); + LoadBalancedCall(ClientChannel* chand, + grpc_call_context_element* call_context, + absl::AnyInvocable on_commit, + bool is_transparent_retry); ~LoadBalancedCall() override; void Orphan() override; @@ -384,9 +385,6 @@ class ClientChannel::LoadBalancedCall protected: ClientChannel* chand() const { return chand_; } - ConfigSelector::CallDispatchController* call_dispatch_controller() const { - return call_dispatch_controller_; - } ClientCallTracer::CallAttemptTracer* call_attempt_tracer() const { return static_cast( call_context()[GRPC_CONTEXT_CALL_TRACER].value); @@ -400,6 +398,11 @@ class ClientChannel::LoadBalancedCall return lb_subchannel_call_tracker_.get(); } + void Commit() { + auto on_commit = std::move(on_commit_); + on_commit(); + } + // Attempts an LB pick. The following outcomes are possible: // - No pick result is available yet. The call will be queued and // nullopt will be returned. The channel will later call @@ -441,7 +444,7 @@ class ClientChannel::LoadBalancedCall ClientChannel* chand_; - ConfigSelector::CallDispatchController* call_dispatch_controller_; + absl::AnyInvocable on_commit_; gpr_cycle_counter lb_call_start_time_ = gpr_get_cycle_counter(); @@ -460,11 +463,12 @@ class ClientChannel::FilterBasedLoadBalancedCall // the LB call has a subchannel call and ensuring that the // on_call_destruction_complete closure passed down from the surface // is not invoked until after the subchannel call stack is destroyed. - FilterBasedLoadBalancedCall( - ClientChannel* chand, const grpc_call_element_args& args, - grpc_polling_entity* pollent, grpc_closure* on_call_destruction_complete, - ConfigSelector::CallDispatchController* call_dispatch_controller, - bool is_transparent_retry); + FilterBasedLoadBalancedCall(ClientChannel* chand, + const grpc_call_element_args& args, + grpc_polling_entity* pollent, + grpc_closure* on_call_destruction_complete, + absl::AnyInvocable on_commit, + bool is_transparent_retry); ~FilterBasedLoadBalancedCall() override; void Orphan() override; @@ -480,8 +484,8 @@ class ClientChannel::FilterBasedLoadBalancedCall // Work-around for Windows compilers that don't allow nested classes // to access protected members of the enclosing class's parent class. - using LoadBalancedCall::call_dispatch_controller; using LoadBalancedCall::chand; + using LoadBalancedCall::Commit; Arena* arena() const override { return arena_; } grpc_call_context_element* call_context() const override { diff --git a/src/core/ext/filters/client_channel/client_channel_internal.h b/src/core/ext/filters/client_channel/client_channel_internal.h index a2eaf1bf9687f..402fb3ac1b50f 100644 --- a/src/core/ext/filters/client_channel/client_channel_internal.h +++ b/src/core/ext/filters/client_channel/client_channel_internal.h @@ -21,9 +21,9 @@ #include +#include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "src/core/ext/filters/client_channel/config_selector.h" #include "src/core/lib/channel/context.h" #include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/gprpp/unique_type_name.h" @@ -47,66 +47,34 @@ class ClientChannelLbCallState : public LoadBalancingPolicy::CallState { virtual absl::string_view GetCallAttribute(UniqueTypeName type) = 0; }; -// Internal type for ServiceConfigCallData. Provides access to the -// CallDispatchController. +// Internal type for ServiceConfigCallData. Handles call commits. class ClientChannelServiceConfigCallData : public ServiceConfigCallData { public: ClientChannelServiceConfigCallData( RefCountedPtr service_config, const ServiceConfigParser::ParsedConfigVector* method_configs, ServiceConfigCallData::CallAttributes call_attributes, - ConfigSelector::CallDispatchController* call_dispatch_controller, + absl::AnyInvocable on_commit, grpc_call_context_element* call_context) : ServiceConfigCallData(std::move(service_config), method_configs, std::move(call_attributes)), - call_dispatch_controller_(call_dispatch_controller) { + on_commit_(std::move(on_commit)) { call_context[GRPC_CONTEXT_SERVICE_CONFIG_CALL_DATA].value = this; call_context[GRPC_CONTEXT_SERVICE_CONFIG_CALL_DATA].destroy = Destroy; } - ConfigSelector::CallDispatchController* call_dispatch_controller() { - return &call_dispatch_controller_; + void Commit() { + auto on_commit = std::move(on_commit_); + if (on_commit != nullptr) on_commit(); } private: - // A wrapper for the CallDispatchController returned by the ConfigSelector. - // Handles the case where the ConfigSelector doees not return any - // CallDispatchController. - // Also ensures that we call Commit() at most once, which allows the - // client channel code to call Commit() when the call is complete in case - // it wasn't called earlier, without needing to know whether or not it was. - class CallDispatchControllerWrapper - : public ConfigSelector::CallDispatchController { - public: - explicit CallDispatchControllerWrapper( - ConfigSelector::CallDispatchController* call_dispatch_controller) - : call_dispatch_controller_(call_dispatch_controller) {} - - bool ShouldRetry() override { - if (call_dispatch_controller_ != nullptr) { - return call_dispatch_controller_->ShouldRetry(); - } - return true; - } - - void Commit() override { - if (call_dispatch_controller_ != nullptr && !commit_called_) { - call_dispatch_controller_->Commit(); - commit_called_ = true; - } - } - - private: - ConfigSelector::CallDispatchController* call_dispatch_controller_; - bool commit_called_ = false; - }; - static void Destroy(void* ptr) { auto* self = static_cast(ptr); self->~ClientChannelServiceConfigCallData(); } - CallDispatchControllerWrapper call_dispatch_controller_; + absl::AnyInvocable on_commit_; }; } // namespace grpc_core diff --git a/src/core/ext/filters/client_channel/config_selector.h b/src/core/ext/filters/client_channel/config_selector.h index e434bb9f7d984..a75652fceb59a 100644 --- a/src/core/ext/filters/client_channel/config_selector.h +++ b/src/core/ext/filters/client_channel/config_selector.h @@ -24,6 +24,7 @@ #include #include +#include "absl/functional/any_invocable.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" @@ -50,20 +51,6 @@ namespace grpc_core { // MethodConfig and provide input to LB policies on a per-call basis. class ConfigSelector : public RefCounted { public: - // An interface to be used by the channel when dispatching calls. - class CallDispatchController { - public: - virtual ~CallDispatchController() = default; - - // Called by the channel to decide if it should retry the call upon a - // failure. - virtual bool ShouldRetry() = 0; - - // Called by the channel when no more LB picks will be performed for - // the call. - virtual void Commit() = 0; - }; - struct GetCallConfigArgs { grpc_metadata_batch* initial_metadata; Arena* arena; @@ -78,8 +65,9 @@ class ConfigSelector : public RefCounted { RefCountedPtr service_config; // Call attributes that will be accessible to LB policy implementations. ServiceConfigCallData::CallAttributes call_attributes; - // Call dispatch controller. - CallDispatchController* call_dispatch_controller = nullptr; + // To be called exactly once, when the call has been committed to a + // particular subchannel (i.e., after all LB picks are complete). + absl::AnyInvocable on_commit; }; ~ConfigSelector() override = default; @@ -136,7 +124,7 @@ class DefaultConfigSelector : public ConfigSelector { call_config.method_configs = service_config_->GetMethodParsedConfigVector(path->c_slice()); call_config.service_config = service_config_; - return call_config; + return std::move(call_config); } // Only comparing the ConfigSelector itself, not the underlying diff --git a/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc b/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc index 4e7cb136d6564..752820d7e8719 100644 --- a/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc +++ b/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc @@ -29,6 +29,7 @@ #include #include +#include "absl/functional/any_invocable.h" #include "absl/meta/type_traits.h" #include "absl/random/random.h" #include "absl/status/status.h" @@ -248,11 +249,10 @@ class XdsResolver : public Resolver { .first) {} void Orphan() override { - auto* resolver = resolver_.release(); + auto* resolver = resolver_.get(); resolver->work_serializer_->Run( - [resolver]() { + [resolver = std::move(resolver_)]() { resolver->MaybeRemoveUnusedClusters(); - resolver->Unref(); }, DEBUG_LOCATION); } @@ -264,34 +264,6 @@ class XdsResolver : public Resolver { ClusterStateMap::iterator it_; }; - // Call dispatch controller, created for each call handled by the - // ConfigSelector. Holds a ref to the ClusterState object until the - // call is committed. - class XdsCallDispatchController - : public ConfigSelector::CallDispatchController { - public: - explicit XdsCallDispatchController( - RefCountedPtr cluster_state) - : cluster_state_(std::move(cluster_state)) {} - - bool ShouldRetry() override { - // TODO(donnadionne): Implement the retry circuit breaker here. - return true; - } - - void Commit() override { - // TODO(donnadionne): If ShouldRetry() was called previously, - // decrement the retry circuit breaker counter. - cluster_state_.reset(); - } - - private: - // Note: The XdsCallDispatchController object is never actually destroyed, - // so do not add any data members that require destruction unless you have - // some other way to clean them up. - RefCountedPtr cluster_state_; - }; - class XdsConfigSelector : public ConfigSelector { public: XdsConfigSelector(RefCountedPtr resolver, @@ -766,9 +738,10 @@ XdsResolver::XdsConfigSelector::GetCallConfig(GetCallConfigArgs args) { memcpy(hash_value, hash_string.c_str(), hash_string.size()); hash_value[hash_string.size()] = '\0'; call_config.call_attributes[RequestHashAttributeName()] = hash_value; - call_config.call_dispatch_controller = - args.arena->New(it->second->Ref()); - return call_config; + call_config.on_commit = [cluster_state = it->second->Ref()]() mutable { + cluster_state.reset(); + }; + return std::move(call_config); } // diff --git a/src/core/ext/filters/client_channel/retry_filter.cc b/src/core/ext/filters/client_channel/retry_filter.cc index a92a1837bbfd6..936cbb26a3b8b 100644 --- a/src/core/ext/filters/client_channel/retry_filter.cc +++ b/src/core/ext/filters/client_channel/retry_filter.cc @@ -28,6 +28,7 @@ #include #include "absl/container/inlined_vector.h" +#include "absl/functional/any_invocable.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" @@ -43,7 +44,6 @@ #include "src/core/ext/filters/client_channel/client_channel.h" #include "src/core/ext/filters/client_channel/client_channel_internal.h" -#include "src/core/ext/filters/client_channel/config_selector.h" #include "src/core/ext/filters/client_channel/retry_service_config.h" #include "src/core/ext/filters/client_channel/retry_throttle.h" #include "src/core/lib/backoff/backoff.h" @@ -364,31 +364,6 @@ class RetryFilter::CallData { grpc_closure on_complete_; }; - class AttemptDispatchController - : public ConfigSelector::CallDispatchController { - public: - explicit AttemptDispatchController(CallAttempt* call_attempt) - : call_attempt_(call_attempt) {} - - // Will never be called. - bool ShouldRetry() override { return false; } - - void Commit() override { - call_attempt_->lb_call_committed_ = true; - auto* calld = call_attempt_->calld_; - if (calld->retry_committed_) { - auto* service_config_call_data = - static_cast( - calld->call_context_[GRPC_CONTEXT_SERVICE_CONFIG_CALL_DATA] - .value); - service_config_call_data->call_dispatch_controller()->Commit(); - } - } - - private: - CallAttempt* call_attempt_; - }; - // Creates a BatchData object on the call's arena with the // specified refcount. If set_on_complete is true, the batch's // on_complete callback will be set to point to on_complete(); @@ -450,7 +425,6 @@ class RetryFilter::CallData { void MaybeCancelPerAttemptRecvTimer(); CallData* calld_; - AttemptDispatchController attempt_dispatch_controller_; OrphanablePtr lb_call_; bool lb_call_committed_ = false; @@ -558,9 +532,8 @@ class RetryFilter::CallData { static void StartTransparentRetry(void* arg, grpc_error_handle error); OrphanablePtr - CreateLoadBalancedCall( - ConfigSelector::CallDispatchController* call_dispatch_controller, - bool is_transparent_retry); + CreateLoadBalancedCall(absl::AnyInvocable on_commit, + bool is_transparent_retry); void CreateCallAttempt(bool is_transparent_retry); @@ -693,7 +666,6 @@ RetryFilter::CallData::CallAttempt::CallAttempt(CallData* calld, : RefCounted(GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace) ? "CallAttempt" : nullptr), calld_(calld), - attempt_dispatch_controller_(this), batch_payload_(calld->call_context_), started_send_initial_metadata_(false), completed_send_initial_metadata_(false), @@ -706,8 +678,18 @@ RetryFilter::CallData::CallAttempt::CallAttempt(CallData* calld, sent_cancel_stream_(false), seen_recv_trailing_metadata_from_surface_(false), abandoned_(false) { - lb_call_ = calld->CreateLoadBalancedCall(&attempt_dispatch_controller_, - is_transparent_retry); + lb_call_ = calld->CreateLoadBalancedCall( + [this]() { + lb_call_committed_ = true; + if (calld_->retry_committed_) { + auto* service_config_call_data = + static_cast( + calld_->call_context_[GRPC_CONTEXT_SERVICE_CONFIG_CALL_DATA] + .value); + service_config_call_data->Commit(); + } + }, + is_transparent_retry); if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) { gpr_log(GPR_INFO, "chand=%p calld=%p attempt=%p: created attempt, lb_call=%p", @@ -1199,19 +1181,6 @@ bool RetryFilter::CallData::CallAttempt::ShouldRetry( } } } - // Check with call dispatch controller. - auto* service_config_call_data = - static_cast( - calld_->call_context_[GRPC_CONTEXT_SERVICE_CONFIG_CALL_DATA].value); - if (!service_config_call_data->call_dispatch_controller()->ShouldRetry()) { - if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) { - gpr_log( - GPR_INFO, - "chand=%p calld=%p attempt=%p: call dispatch controller denied retry", - calld_->chand_, calld_, this); - } - return false; - } // We should retry. return true; } @@ -2272,7 +2241,7 @@ void RetryFilter::CallData::StartTransportStreamOpBatch( static_cast( call_context_[GRPC_CONTEXT_SERVICE_CONFIG_CALL_DATA].value); committed_call_ = CreateLoadBalancedCall( - service_config_call_data->call_dispatch_controller(), + [service_config_call_data]() { service_config_call_data->Commit(); }, /*is_transparent_retry=*/false); committed_call_->StartTransportStreamOpBatch(batch); return; @@ -2298,8 +2267,7 @@ void RetryFilter::CallData::StartTransportStreamOpBatch( OrphanablePtr RetryFilter::CallData::CreateLoadBalancedCall( - ConfigSelector::CallDispatchController* call_dispatch_controller, - bool is_transparent_retry) { + absl::AnyInvocable on_commit, bool is_transparent_retry) { grpc_call_element_args args = {owning_call_, nullptr, call_context_, path_, /*start_time=*/0, deadline_, arena_, call_combiner_}; @@ -2308,7 +2276,7 @@ RetryFilter::CallData::CreateLoadBalancedCall( // This callback holds a ref to the CallStackDestructionBarrier // object until the LB call is destroyed. call_stack_destruction_barrier_->MakeLbCallDestructionClosure(this), - call_dispatch_controller, is_transparent_retry); + std::move(on_commit), is_transparent_retry); } void RetryFilter::CallData::CreateCallAttempt(bool is_transparent_retry) { @@ -2548,17 +2516,17 @@ void RetryFilter::CallData::RetryCommit(CallAttempt* call_attempt) { gpr_log(GPR_INFO, "chand=%p calld=%p: committing retries", chand_, this); } if (call_attempt != nullptr) { - // If the call attempt's LB call has been committed, inform the call - // dispatch controller that the call has been committed. + // If the call attempt's LB call has been committed, invoke the + // call's on_commit callback. // Note: If call_attempt is null, this is happening before the first // retry attempt is started, in which case we'll just pass the real - // call dispatch controller down into the LB call, and it won't be - // our problem anymore. + // on_commit callback down into the LB call, and it won't be our + // problem anymore. if (call_attempt->lb_call_committed()) { auto* service_config_call_data = static_cast( call_context_[GRPC_CONTEXT_SERVICE_CONFIG_CALL_DATA].value); - service_config_call_data->call_dispatch_controller()->Commit(); + service_config_call_data->Commit(); } // Free cached send ops. call_attempt->FreeCachedSendOpDataAfterCommit(); From b0636e7a23ef08fcc06f210b83e2c61224664961 Mon Sep 17 00:00:00 2001 From: apolcyn Date: Wed, 5 Apr 2023 17:34:23 -0700 Subject: [PATCH 15/39] Revert "XDS: enable XDS federation by default (#32711)" (#32814) This reverts commit 4b46dbc19e22d8f57d68a1291aca4cd0e2e5f8bc. Reason: this seems to be breaking load reports in certain cases, b/276944116 Let's revert so this doesn't accidentally get released. --- build_autogenerated.yaml | 1 - src/core/ext/xds/xds_bootstrap.cc | 6 ++--- test/core/xds/BUILD | 1 - test/core/xds/xds_bootstrap_test.cc | 8 +++++++ test/core/xds/xds_client_test.cc | 22 +++++++++++++++---- test/cpp/end2end/xds/xds_core_end2end_test.cc | 15 +++++++++---- 6 files changed, 40 insertions(+), 13 deletions(-) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index a36f98314ff30..feb1a1a899e6f 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -12845,7 +12845,6 @@ targets: build: test language: c++ headers: - - test/core/util/scoped_env_var.h - test/core/xds/xds_transport_fake.h src: - src/proto/grpc/testing/xds/v3/base.proto diff --git a/src/core/ext/xds/xds_bootstrap.cc b/src/core/ext/xds/xds_bootstrap.cc index 72263a720af7c..62e4264e07859 100644 --- a/src/core/ext/xds/xds_bootstrap.cc +++ b/src/core/ext/xds/xds_bootstrap.cc @@ -25,11 +25,11 @@ namespace grpc_core { -// TODO(roth,apolcyn): remove this federation env var after the 1.55 -// release. +// TODO(donnadionne): check to see if federation is enabled, this will be +// removed once federation is fully integrated and enabled by default. bool XdsFederationEnabled() { auto value = GetEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION"); - if (!value.has_value()) return true; + if (!value.has_value()) return false; bool parsed_value; bool parse_succeeded = gpr_parse_bool_value(value->c_str(), &parsed_value); return parse_succeeded && parsed_value; diff --git a/test/core/xds/BUILD b/test/core/xds/BUILD index 792987be1defb..3b16932dd600e 100644 --- a/test/core/xds/BUILD +++ b/test/core/xds/BUILD @@ -158,7 +158,6 @@ grpc_cc_test( "//:xds_client", "//src/proto/grpc/testing/xds/v3:discovery_proto", "//test/core/util:grpc_test_util", - "//test/core/util:scoped_env_var", ], ) diff --git a/test/core/xds/xds_bootstrap_test.cc b/test/core/xds/xds_bootstrap_test.cc index 588656f2505f7..7c09359980dc4 100644 --- a/test/core/xds/xds_bootstrap_test.cc +++ b/test/core/xds/xds_bootstrap_test.cc @@ -58,6 +58,7 @@ namespace testing { namespace { TEST(XdsBootstrapTest, Basic) { + SetEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION", "true"); const char* json_str = "{" " \"xds_servers\": [" @@ -193,6 +194,7 @@ TEST(XdsBootstrapTest, Basic) { ::testing::Property(&Json::string, "1"))))); EXPECT_EQ(bootstrap->server_listener_resource_name_template(), "example/resource"); + UnsetEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION"); } TEST(XdsBootstrapTest, ValidWithoutNode) { @@ -496,6 +498,7 @@ TEST(XdsBootstrapTest, CertificateProvidersUnrecognizedPluginName) { } TEST(XdsBootstrapTest, AuthorityXdsServerInvalidResourceTemplate) { + SetEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION", "true"); const char* json_str = "{" " \"xds_servers\": [" @@ -530,9 +533,11 @@ TEST(XdsBootstrapTest, AuthorityXdsServerInvalidResourceTemplate) { ".client_listener_resource_name_template error:" "field must begin with \"xdstp://xds.example.com/\"]") << bootstrap.status(); + UnsetEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION"); } TEST(XdsBootstrapTest, AuthorityXdsServerMissingServerUri) { + SetEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION", "true"); const char* json_str = "{" " \"xds_servers\": [" @@ -559,6 +564,7 @@ TEST(XdsBootstrapTest, AuthorityXdsServerMissingServerUri) { "field:authorities[\"xds.example.com\"].xds_servers[0].server_uri " "error:field not present]") << bootstrap.status(); + UnsetEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION"); } class FakeCertificateProviderFactory : public CertificateProviderFactory { @@ -699,6 +705,7 @@ TEST(XdsBootstrapTest, CertificateProvidersFakePluginEmptyConfig) { } TEST(XdsBootstrapTest, XdsServerToJsonAndParse) { + SetEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION", "true"); const char* json_str = " {" " \"server_uri\": \"fake:///lb\"," @@ -719,6 +726,7 @@ TEST(XdsBootstrapTest, XdsServerToJsonAndParse) { LoadFromJson(output); ASSERT_TRUE(output_xds_server.ok()) << output_xds_server.status(); EXPECT_EQ(*xds_server, *output_xds_server); + UnsetEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION"); } } // namespace diff --git a/test/core/xds/xds_client_test.cc b/test/core/xds/xds_client_test.cc index 24d4c73c50a2b..91ea508e16d07 100644 --- a/test/core/xds/xds_client_test.cc +++ b/test/core/xds/xds_client_test.cc @@ -47,6 +47,7 @@ #include "src/core/ext/xds/xds_resource_type_impl.h" #include "src/core/lib/event_engine/default_event_engine.h" #include "src/core/lib/gprpp/debug_location.h" +#include "src/core/lib/gprpp/env.h" #include "src/core/lib/gprpp/sync.h" #include "src/core/lib/json/json.h" #include "src/core/lib/json/json_args.h" @@ -55,7 +56,6 @@ #include "src/core/lib/json/json_writer.h" #include "src/proto/grpc/testing/xds/v3/base.pb.h" #include "src/proto/grpc/testing/xds/v3/discovery.pb.h" -#include "test/core/util/scoped_env_var.h" #include "test/core/util/test_config.h" #include "test/core/xds/xds_transport_fake.h" @@ -555,6 +555,18 @@ class XdsClientTest : public ::testing::Test { DiscoveryResponse response_; }; + class ScopedExperimentalEnvVar { + public: + explicit ScopedExperimentalEnvVar(const char* env_var) : env_var_(env_var) { + SetEnv(env_var_, "true"); + } + + ~ScopedExperimentalEnvVar() { UnsetEnv(env_var_); } + + private: + const char* env_var_; + }; + // Sets transport_factory_ and initializes xds_client_ with the // specified bootstrap config. void InitXdsClient( @@ -2314,6 +2326,7 @@ TEST_F(XdsClientTest, MultipleResourceTypes) { } TEST_F(XdsClientTest, Federation) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); constexpr char kAuthority[] = "xds.example.com"; const std::string kXdstpResourceName = absl::StrCat( "xdstp://", kAuthority, "/", XdsFooResource::TypeUrl(), "/foo2"); @@ -2402,6 +2415,7 @@ TEST_F(XdsClientTest, Federation) { } TEST_F(XdsClientTest, FederationAuthorityDefaultsToTopLevelXdsServer) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); constexpr char kAuthority[] = "xds.example.com"; const std::string kXdstpResourceName = absl::StrCat( "xdstp://", kAuthority, "/", XdsFooResource::TypeUrl(), "/foo2"); @@ -2490,6 +2504,7 @@ TEST_F(XdsClientTest, FederationAuthorityDefaultsToTopLevelXdsServer) { } TEST_F(XdsClientTest, FederationWithUnknownAuthority) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); constexpr char kAuthority[] = "xds.example.com"; const std::string kXdstpResourceName = absl::StrCat( "xdstp://", kAuthority, "/", XdsFooResource::TypeUrl(), "/foo2"); @@ -2507,6 +2522,7 @@ TEST_F(XdsClientTest, FederationWithUnknownAuthority) { } TEST_F(XdsClientTest, FederationWithUnparseableXdstpResourceName) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); // Note: Not adding authority to bootstrap config. InitXdsClient(); // Start a watch for the xdstp resource name. @@ -2519,10 +2535,7 @@ TEST_F(XdsClientTest, FederationWithUnparseableXdstpResourceName) { << *error; } -// TODO(roth,apolcyn): remove this test when the -// GRPC_EXPERIMENTAL_XDS_FEDERATION env var is removed. TEST_F(XdsClientTest, FederationDisabledWithNewStyleName) { - testing::ScopedEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION", "false"); // We will use this xdstp name, whose authority is not present in // the bootstrap config. But since federation is not enabled, we // will treat this as an opaque old-style name, so we'll send it to @@ -2570,6 +2583,7 @@ TEST_F(XdsClientTest, FederationDisabledWithNewStyleName) { } TEST_F(XdsClientTest, FederationChannelFailureReportedToWatchers) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); constexpr char kAuthority[] = "xds.example.com"; const std::string kXdstpResourceName = absl::StrCat( "xdstp://", kAuthority, "/", XdsFooResource::TypeUrl(), "/foo2"); diff --git a/test/cpp/end2end/xds/xds_core_end2end_test.cc b/test/cpp/end2end/xds/xds_core_end2end_test.cc index 7257156d5ed48..0ccbca39f12ff 100644 --- a/test/cpp/end2end/xds/xds_core_end2end_test.cc +++ b/test/cpp/end2end/xds/xds_core_end2end_test.cc @@ -33,6 +33,8 @@ namespace { using ClientStats = LrsServiceImpl::ClientStats; +using ::grpc_core::testing::ScopedExperimentalEnvVar; + // // XdsClientTest - basic tests of XdsClient functionality // @@ -687,6 +689,7 @@ INSTANTIATE_TEST_SUITE_P( // Bootstrap config default client listener template uses new-style name with // authority "xds.example.com". TEST_P(XdsFederationTest, FederationTargetNoAuthorityWithResourceTemplate) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); const char* kAuthority = "xds.example.com"; const char* kNewListenerTemplate = "xdstp://xds.example.com/envoy.config.listener.v3.Listener/" @@ -743,6 +746,7 @@ TEST_P(XdsFederationTest, FederationTargetNoAuthorityWithResourceTemplate) { // In bootstrap config, authority has no client listener template, so we use the // default. TEST_P(XdsFederationTest, FederationTargetAuthorityDefaultResourceTemplate) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); const char* kAuthority = "xds.example.com"; const char* kNewServerName = "whee%/server.example.com"; const char* kNewListenerName = @@ -809,6 +813,7 @@ TEST_P(XdsFederationTest, FederationTargetAuthorityDefaultResourceTemplate) { // Channel is created with URI "xds://xds.example.com/server.example.com". // Bootstrap entry for that authority specifies a client listener name template. TEST_P(XdsFederationTest, FederationTargetAuthorityWithResourceTemplate) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); const char* kAuthority = "xds.example.com"; const char* kNewServerName = "whee%/server.example.com"; const char* kNewListenerTemplate = @@ -877,6 +882,7 @@ TEST_P(XdsFederationTest, FederationTargetAuthorityWithResourceTemplate) { } TEST_P(XdsFederationTest, TargetUriAuthorityUnknown) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); const char* kAuthority = "xds.example.com"; const char* kNewServerName = "whee%/server.example.com"; const char* kNewListenerTemplate = @@ -902,6 +908,7 @@ TEST_P(XdsFederationTest, TargetUriAuthorityUnknown) { } TEST_P(XdsFederationTest, RdsResourceNameAuthorityUnknown) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); const char* kAuthority = "xds.example.com"; const char* kNewServerName = "whee%/server.example.com"; const char* kNewListenerTemplate = @@ -945,6 +952,7 @@ TEST_P(XdsFederationTest, RdsResourceNameAuthorityUnknown) { } TEST_P(XdsFederationTest, CdsResourceNameAuthorityUnknown) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); const char* kAuthority = "xds.example.com"; const char* kNewServerName = "whee%/server.example.com"; const char* kNewListenerTemplate = @@ -995,6 +1003,7 @@ TEST_P(XdsFederationTest, CdsResourceNameAuthorityUnknown) { } TEST_P(XdsFederationTest, EdsResourceNameAuthorityUnknown) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); const char* kAuthority = "xds.example.com"; const char* kNewServerName = "whee%/server.example.com"; const char* kNewListenerTemplate = @@ -1057,6 +1066,7 @@ TEST_P(XdsFederationTest, EdsResourceNameAuthorityUnknown) { // Setting server_listener_resource_name_template to start with "xdstp:" and // look up xds server under an authority map. TEST_P(XdsFederationTest, FederationServer) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); const char* kAuthority = "xds.example.com"; const char* kNewListenerTemplate = "xdstp://xds.example.com/envoy.config.listener.v3.Listener/" @@ -1144,11 +1154,7 @@ INSTANTIATE_TEST_SUITE_P( ::testing::Values(XdsTestType().set_enable_rds_testing()), &XdsTestType::Name); -// TODO(roth,apolcyn): remove this test when the -// GRPC_EXPERIMENTAL_XDS_FEDERATION env var is removed. TEST_P(XdsFederationDisabledTest, FederationDisabledWithNewStyleNames) { - grpc_core::testing::ScopedEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION", - "false"); const char* kNewRouteConfigName = "xdstp://xds.example.com/envoy.config.route.v3.RouteConfiguration/" "new_route_config_name"; @@ -1207,6 +1213,7 @@ INSTANTIATE_TEST_SUITE_P( // Sending traffic to both default balancer and authority balancer and checking // load reporting with each one. TEST_P(XdsFederationLoadReportingTest, FederationMultipleLoadReportingTest) { + ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_FEDERATION"); const char* kAuthority = "xds.example.com"; const char* kNewServerName = "whee%/server.example.com"; const char* kNewListenerTemplate = From 2845a248d6e1f617bbe0ffcd851cb1818725cda3 Mon Sep 17 00:00:00 2001 From: Stanley Cheung Date: Wed, 5 Apr 2023 22:25:22 -0700 Subject: [PATCH 16/39] [GcpObservability] Remove sleep from observability test client (#32817) .. as it is now part of the `GcpObservabilityClose()` routine [itself](https://github.com/grpc/grpc/pull/32715/files#diff-e1ce0ccb4650e20b62a6d6b31655f446ad9ef72efca5849143e65f4a5a5e0310R223). Background: #32715 may have broken the CI for observability interop testing. The client seems to be taking too long to finish. More info at b/277145074 We need to backport this to the `v1.54.x` branch if this is the right fix. --- test/cpp/interop/observability_client.cc | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/cpp/interop/observability_client.cc b/test/cpp/interop/observability_client.cc index 38d23955d4056..a37e4ab1fd5f2 100644 --- a/test/cpp/interop/observability_client.cc +++ b/test/cpp/interop/observability_client.cc @@ -357,16 +357,6 @@ int main(int argc, char** argv) { if (absl::GetFlag(FLAGS_enable_observability)) { grpc::experimental::GcpObservabilityClose(); - // TODO(stanleycheung): remove this once the observability exporter plugin - // is able to gracefully flush observability data to - // cloud at shutdown - const int observability_exporter_sleep_seconds = 65; - gpr_log(GPR_DEBUG, "Sleeping %ds before shutdown.", - observability_exporter_sleep_seconds); - gpr_sleep_until( - gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), - gpr_time_from_seconds(observability_exporter_sleep_seconds, - GPR_TIMESPAN))); } return ret; From 15ef046440ef0f4e2e4a9ca4db7cf17b76377162 Mon Sep 17 00:00:00 2001 From: Luwei Ge Date: Thu, 6 Apr 2023 06:47:50 -0700 Subject: [PATCH 17/39] [third_party] update envoy-api dependency (#32807) update to main commit 68d4315167352ffac71f149a43b8088397d3f33d This is to include the latest RBAC related change (https://github.com/envoyproxy/envoy/pull/26415) for audit logging. I intentionally did not run steps in https://github.com/grpc/grpc/tree/master/third_party#updating-third_partyenvoy-api because I saw some earlier changes also didn't do that and this envoy-api change isn't going to be needed in Python any time soon. --- CMakeLists.txt | 12 +- bazel/grpc_deps.bzl | 8 +- build_autogenerated.yaml | 8 +- .../envoy/config/core/v3/config_source.upb.c | 11 +- .../envoy/config/core/v3/config_source.upb.h | 40 + .../envoy/config/rbac/v3/rbac.upb.c | 22 +- .../envoy/config/rbac/v3/rbac.upb.h | 91 ++- .../v3/http_connection_manager.upb.c | 5 +- .../v3/http_connection_manager.upb.h | 9 + .../transport_sockets/tls/v3/common.upb.c | 5 +- .../transport_sockets/tls/v3/common.upb.h | 15 + .../config/core/v3/config_source.upbdefs.c | 23 +- .../config/core/v3/config_source.upbdefs.h | 5 + .../envoy/config/rbac/v3/rbac.upbdefs.c | 260 +++--- .../envoy/config/rbac/v3/rbac.upbdefs.h | 5 + .../v3/http_connection_manager.upbdefs.c | 753 +++++++++--------- .../transport_sockets/tls/v3/common.upbdefs.c | 272 +++---- third_party/README.md | 4 +- third_party/envoy-api | 2 +- tools/run_tests/sanity/check_submodules.sh | 2 +- 20 files changed, 868 insertions(+), 684 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fcbec1106cf93..6707312c407b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -340,18 +340,18 @@ if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/envoy-api) # Download the archive via HTTP, validate the checksum, and extract to third_party/envoy-api. download_archive( ${CMAKE_CURRENT_SOURCE_DIR}/third_party/envoy-api - https://storage.googleapis.com/grpc-bazel-mirror/github.com/envoyproxy/data-plane-api/archive/084aa4a0186786570496c3a4001b72c3ef592f7f.tar.gz - 1eef47f983161a522a6fc7f3c5a2574676a268dfb4bbf8a66f4b8b687a33067a - data-plane-api-084aa4a0186786570496c3a4001b72c3ef592f7f + https://storage.googleapis.com/grpc-bazel-mirror/github.com/envoyproxy/data-plane-api/archive/68d4315167352ffac71f149a43b8088397d3f33d.tar.gz + 3c7372b5cb33e5e5cc3afd82573fc6275f9a2cac8b1530e1af14f52f34047328 + data-plane-api-68d4315167352ffac71f149a43b8088397d3f33d ) endif() if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/envoy-api) # Download the archive via HTTP, validate the checksum, and extract to third_party/envoy-api. download_archive( ${CMAKE_CURRENT_SOURCE_DIR}/third_party/envoy-api - https://github.com/envoyproxy/data-plane-api/archive/084aa4a0186786570496c3a4001b72c3ef592f7f.tar.gz - 1eef47f983161a522a6fc7f3c5a2574676a268dfb4bbf8a66f4b8b687a33067a - data-plane-api-084aa4a0186786570496c3a4001b72c3ef592f7f + https://github.com/envoyproxy/data-plane-api/archive/68d4315167352ffac71f149a43b8088397d3f33d.tar.gz + 3c7372b5cb33e5e5cc3afd82573fc6275f9a2cac8b1530e1af14f52f34047328 + data-plane-api-68d4315167352ffac71f149a43b8088397d3f33d ) endif() # Setup external proto library at third_party/googleapis with 2 download URLs diff --git a/bazel/grpc_deps.bzl b/bazel/grpc_deps.bzl index f403fcee7a99b..0d1cffe16a430 100644 --- a/bazel/grpc_deps.bzl +++ b/bazel/grpc_deps.bzl @@ -395,11 +395,11 @@ def grpc_deps(): if "envoy_api" not in native.existing_rules(): http_archive( name = "envoy_api", - sha256 = "1eef47f983161a522a6fc7f3c5a2574676a268dfb4bbf8a66f4b8b687a33067a", - strip_prefix = "data-plane-api-084aa4a0186786570496c3a4001b72c3ef592f7f", + sha256 = "3c7372b5cb33e5e5cc3afd82573fc6275f9a2cac8b1530e1af14f52f34047328", + strip_prefix = "data-plane-api-68d4315167352ffac71f149a43b8088397d3f33d", urls = [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/envoyproxy/data-plane-api/archive/084aa4a0186786570496c3a4001b72c3ef592f7f.tar.gz", - "https://github.com/envoyproxy/data-plane-api/archive/084aa4a0186786570496c3a4001b72c3ef592f7f.tar.gz", + "https://storage.googleapis.com/grpc-bazel-mirror/github.com/envoyproxy/data-plane-api/archive/68d4315167352ffac71f149a43b8088397d3f33d.tar.gz", + "https://github.com/envoyproxy/data-plane-api/archive/68d4315167352ffac71f149a43b8088397d3f33d.tar.gz", ], ) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index feb1a1a899e6f..b5df015c68132 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -13901,12 +13901,12 @@ targets: - mac external_proto_libraries: - destination: third_party/envoy-api - hash: 1eef47f983161a522a6fc7f3c5a2574676a268dfb4bbf8a66f4b8b687a33067a + hash: 3c7372b5cb33e5e5cc3afd82573fc6275f9a2cac8b1530e1af14f52f34047328 proto_prefix: third_party/envoy-api/ - strip_prefix: data-plane-api-084aa4a0186786570496c3a4001b72c3ef592f7f + strip_prefix: data-plane-api-68d4315167352ffac71f149a43b8088397d3f33d urls: - - https://storage.googleapis.com/grpc-bazel-mirror/github.com/envoyproxy/data-plane-api/archive/084aa4a0186786570496c3a4001b72c3ef592f7f.tar.gz - - https://github.com/envoyproxy/data-plane-api/archive/084aa4a0186786570496c3a4001b72c3ef592f7f.tar.gz + - https://storage.googleapis.com/grpc-bazel-mirror/github.com/envoyproxy/data-plane-api/archive/68d4315167352ffac71f149a43b8088397d3f33d.tar.gz + - https://github.com/envoyproxy/data-plane-api/archive/68d4315167352ffac71f149a43b8088397d3f33d.tar.gz - destination: third_party/googleapis hash: 5bb6b0253ccf64b53d6c7249625a7e3f6c3bc6402abd52d3778bfa48258703a0 proto_prefix: third_party/googleapis/ diff --git a/src/core/ext/upb-generated/envoy/config/core/v3/config_source.upb.c b/src/core/ext/upb-generated/envoy/config/core/v3/config_source.upb.c index a8a559e8b8cd3..e109fa6ef8db1 100644 --- a/src/core/ext/upb-generated/envoy/config/core/v3/config_source.upb.c +++ b/src/core/ext/upb-generated/envoy/config/core/v3/config_source.upb.c @@ -140,7 +140,13 @@ const upb_MiniTable envoy_config_core_v3_ExtensionConfigSource_msginit = { UPB_SIZE(16, 32), 4, kUpb_ExtMode_NonExtendable, 4, 255, 0, }; -static const upb_MiniTable *messages_layout[7] = { +const upb_MiniTable envoy_config_core_v3_RestSubscription_msginit = { + NULL, + NULL, + UPB_SIZE(0, 0), 0, kUpb_ExtMode_NonExtendable, 0, 255, 0, +}; + +static const upb_MiniTable *messages_layout[8] = { &envoy_config_core_v3_ApiConfigSource_msginit, &envoy_config_core_v3_AggregatedConfigSource_msginit, &envoy_config_core_v3_SelfConfigSource_msginit, @@ -148,13 +154,14 @@ static const upb_MiniTable *messages_layout[7] = { &envoy_config_core_v3_PathConfigSource_msginit, &envoy_config_core_v3_ConfigSource_msginit, &envoy_config_core_v3_ExtensionConfigSource_msginit, + &envoy_config_core_v3_RestSubscription_msginit, }; const upb_MiniTable_File envoy_config_core_v3_config_source_proto_upb_file_layout = { messages_layout, NULL, NULL, - 7, + 8, 0, 0, }; diff --git a/src/core/ext/upb-generated/envoy/config/core/v3/config_source.upb.h b/src/core/ext/upb-generated/envoy/config/core/v3/config_source.upb.h index 25790c56a5290..3114017ab3240 100644 --- a/src/core/ext/upb-generated/envoy/config/core/v3/config_source.upb.h +++ b/src/core/ext/upb-generated/envoy/config/core/v3/config_source.upb.h @@ -27,6 +27,7 @@ struct envoy_config_core_v3_RateLimitSettings; struct envoy_config_core_v3_PathConfigSource; struct envoy_config_core_v3_ConfigSource; struct envoy_config_core_v3_ExtensionConfigSource; +struct envoy_config_core_v3_RestSubscription; typedef struct envoy_config_core_v3_ApiConfigSource envoy_config_core_v3_ApiConfigSource; typedef struct envoy_config_core_v3_AggregatedConfigSource envoy_config_core_v3_AggregatedConfigSource; typedef struct envoy_config_core_v3_SelfConfigSource envoy_config_core_v3_SelfConfigSource; @@ -34,6 +35,7 @@ typedef struct envoy_config_core_v3_RateLimitSettings envoy_config_core_v3_RateL typedef struct envoy_config_core_v3_PathConfigSource envoy_config_core_v3_PathConfigSource; typedef struct envoy_config_core_v3_ConfigSource envoy_config_core_v3_ConfigSource; typedef struct envoy_config_core_v3_ExtensionConfigSource envoy_config_core_v3_ExtensionConfigSource; +typedef struct envoy_config_core_v3_RestSubscription envoy_config_core_v3_RestSubscription; extern const upb_MiniTable envoy_config_core_v3_ApiConfigSource_msginit; extern const upb_MiniTable envoy_config_core_v3_AggregatedConfigSource_msginit; extern const upb_MiniTable envoy_config_core_v3_SelfConfigSource_msginit; @@ -41,6 +43,7 @@ extern const upb_MiniTable envoy_config_core_v3_RateLimitSettings_msginit; extern const upb_MiniTable envoy_config_core_v3_PathConfigSource_msginit; extern const upb_MiniTable envoy_config_core_v3_ConfigSource_msginit; extern const upb_MiniTable envoy_config_core_v3_ExtensionConfigSource_msginit; +extern const upb_MiniTable envoy_config_core_v3_RestSubscription_msginit; struct envoy_config_core_v3_GrpcService; struct envoy_config_core_v3_TypedExtensionConfig; struct envoy_config_core_v3_WatchedDirectory; @@ -795,6 +798,43 @@ UPB_INLINE bool envoy_config_core_v3_ExtensionConfigSource_add_type_urls(envoy_c return _upb_Array_Append_accessor2(msg, UPB_SIZE(12, 24), UPB_SIZE(3, 4), &val, arena); } +/* envoy.config.core.v3.RestSubscription */ + +UPB_INLINE envoy_config_core_v3_RestSubscription* envoy_config_core_v3_RestSubscription_new(upb_Arena* arena) { + return (envoy_config_core_v3_RestSubscription*)_upb_Message_New(&envoy_config_core_v3_RestSubscription_msginit, arena); +} +UPB_INLINE envoy_config_core_v3_RestSubscription* envoy_config_core_v3_RestSubscription_parse(const char* buf, size_t size, upb_Arena* arena) { + envoy_config_core_v3_RestSubscription* ret = envoy_config_core_v3_RestSubscription_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, ret, &envoy_config_core_v3_RestSubscription_msginit, NULL, 0, arena) != kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE envoy_config_core_v3_RestSubscription* envoy_config_core_v3_RestSubscription_parse_ex(const char* buf, size_t size, + const upb_ExtensionRegistry* extreg, + int options, upb_Arena* arena) { + envoy_config_core_v3_RestSubscription* ret = envoy_config_core_v3_RestSubscription_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, ret, &envoy_config_core_v3_RestSubscription_msginit, extreg, options, arena) != + kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE char* envoy_config_core_v3_RestSubscription_serialize(const envoy_config_core_v3_RestSubscription* msg, upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(msg, &envoy_config_core_v3_RestSubscription_msginit, 0, arena, &ptr, len); + return ptr; +} +UPB_INLINE char* envoy_config_core_v3_RestSubscription_serialize_ex(const envoy_config_core_v3_RestSubscription* msg, int options, + upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(msg, &envoy_config_core_v3_RestSubscription_msginit, options, arena, &ptr, len); + return ptr; +} + + extern const upb_MiniTable_File envoy_config_core_v3_config_source_proto_upb_file_layout; #ifdef __cplusplus diff --git a/src/core/ext/upb-generated/envoy/config/rbac/v3/rbac.upb.c b/src/core/ext/upb-generated/envoy/config/rbac/v3/rbac.upb.c index 4166b86da8447..3604fc0a1462e 100644 --- a/src/core/ext/upb-generated/envoy/config/rbac/v3/rbac.upb.c +++ b/src/core/ext/upb-generated/envoy/config/rbac/v3/rbac.upb.c @@ -45,7 +45,7 @@ const upb_MiniTable envoy_config_rbac_v3_RBAC_msginit = { }; static const upb_MiniTable_Sub envoy_config_rbac_v3_RBAC_AuditLoggingOptions_submsgs[1] = { - {.submsg = &envoy_config_core_v3_TypedExtensionConfig_msginit}, + {.submsg = &envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_msginit}, }; static const upb_MiniTable_Field envoy_config_rbac_v3_RBAC_AuditLoggingOptions__fields[2] = { @@ -59,6 +59,21 @@ const upb_MiniTable envoy_config_rbac_v3_RBAC_AuditLoggingOptions_msginit = { UPB_SIZE(8, 16), 2, kUpb_ExtMode_NonExtendable, 2, 255, 0, }; +static const upb_MiniTable_Sub envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_submsgs[1] = { + {.submsg = &envoy_config_core_v3_TypedExtensionConfig_msginit}, +}; + +static const upb_MiniTable_Field envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig__fields[2] = { + {1, UPB_SIZE(4, 8), UPB_SIZE(1, 1), 0, 11, kUpb_FieldMode_Scalar | (kUpb_FieldRep_Pointer << kUpb_FieldRep_Shift)}, + {2, UPB_SIZE(1, 1), UPB_SIZE(0, 0), kUpb_NoSub, 8, kUpb_FieldMode_Scalar | (kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}, +}; + +const upb_MiniTable envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_msginit = { + &envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_submsgs[0], + &envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig__fields[0], + UPB_SIZE(8, 16), 2, kUpb_ExtMode_NonExtendable, 2, 255, 0, +}; + static const upb_MiniTable_Sub envoy_config_rbac_v3_RBAC_PoliciesEntry_submsgs[1] = { {.submsg = &envoy_config_rbac_v3_Policy_msginit}, }; @@ -216,9 +231,10 @@ const upb_MiniTable envoy_config_rbac_v3_Action_msginit = { UPB_SIZE(16, 24), 2, kUpb_ExtMode_NonExtendable, 2, 255, 0, }; -static const upb_MiniTable *messages_layout[10] = { +static const upb_MiniTable *messages_layout[11] = { &envoy_config_rbac_v3_RBAC_msginit, &envoy_config_rbac_v3_RBAC_AuditLoggingOptions_msginit, + &envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_msginit, &envoy_config_rbac_v3_RBAC_PoliciesEntry_msginit, &envoy_config_rbac_v3_Policy_msginit, &envoy_config_rbac_v3_Permission_msginit, @@ -233,7 +249,7 @@ const upb_MiniTable_File envoy_config_rbac_v3_rbac_proto_upb_file_layout = { messages_layout, NULL, NULL, - 10, + 11, 0, 0, }; diff --git a/src/core/ext/upb-generated/envoy/config/rbac/v3/rbac.upb.h b/src/core/ext/upb-generated/envoy/config/rbac/v3/rbac.upb.h index 9ca05e19ae3ad..09f51e024a693 100644 --- a/src/core/ext/upb-generated/envoy/config/rbac/v3/rbac.upb.h +++ b/src/core/ext/upb-generated/envoy/config/rbac/v3/rbac.upb.h @@ -22,6 +22,7 @@ extern "C" { struct envoy_config_rbac_v3_RBAC; struct envoy_config_rbac_v3_RBAC_AuditLoggingOptions; +struct envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig; struct envoy_config_rbac_v3_RBAC_PoliciesEntry; struct envoy_config_rbac_v3_Policy; struct envoy_config_rbac_v3_Permission; @@ -32,6 +33,7 @@ struct envoy_config_rbac_v3_Principal_Authenticated; struct envoy_config_rbac_v3_Action; typedef struct envoy_config_rbac_v3_RBAC envoy_config_rbac_v3_RBAC; typedef struct envoy_config_rbac_v3_RBAC_AuditLoggingOptions envoy_config_rbac_v3_RBAC_AuditLoggingOptions; +typedef struct envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig; typedef struct envoy_config_rbac_v3_RBAC_PoliciesEntry envoy_config_rbac_v3_RBAC_PoliciesEntry; typedef struct envoy_config_rbac_v3_Policy envoy_config_rbac_v3_Policy; typedef struct envoy_config_rbac_v3_Permission envoy_config_rbac_v3_Permission; @@ -42,6 +44,7 @@ typedef struct envoy_config_rbac_v3_Principal_Authenticated envoy_config_rbac_v3 typedef struct envoy_config_rbac_v3_Action envoy_config_rbac_v3_Action; extern const upb_MiniTable envoy_config_rbac_v3_RBAC_msginit; extern const upb_MiniTable envoy_config_rbac_v3_RBAC_AuditLoggingOptions_msginit; +extern const upb_MiniTable envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_msginit; extern const upb_MiniTable envoy_config_rbac_v3_RBAC_PoliciesEntry_msginit; extern const upb_MiniTable envoy_config_rbac_v3_Policy_msginit; extern const upb_MiniTable envoy_config_rbac_v3_Permission_msginit; @@ -220,32 +223,100 @@ UPB_INLINE void envoy_config_rbac_v3_RBAC_AuditLoggingOptions_clear_audit_condit UPB_INLINE int32_t envoy_config_rbac_v3_RBAC_AuditLoggingOptions_audit_condition(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg) { return *UPB_PTR_AT(msg, UPB_SIZE(0, 0), int32_t); } -UPB_INLINE bool envoy_config_rbac_v3_RBAC_AuditLoggingOptions_has_audit_loggers(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg) { +UPB_INLINE bool envoy_config_rbac_v3_RBAC_AuditLoggingOptions_has_logger_configs(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(4, 8)); } -UPB_INLINE void envoy_config_rbac_v3_RBAC_AuditLoggingOptions_clear_audit_loggers(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg) { +UPB_INLINE void envoy_config_rbac_v3_RBAC_AuditLoggingOptions_clear_logger_configs(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg) { _upb_array_detach(msg, UPB_SIZE(4, 8)); } -UPB_INLINE const struct envoy_config_core_v3_TypedExtensionConfig* const* envoy_config_rbac_v3_RBAC_AuditLoggingOptions_audit_loggers(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg, size_t* len) { - return (const struct envoy_config_core_v3_TypedExtensionConfig* const*)_upb_array_accessor(msg, UPB_SIZE(4, 8), len); +UPB_INLINE const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* const* envoy_config_rbac_v3_RBAC_AuditLoggingOptions_logger_configs(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg, size_t* len) { + return (const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* const*)_upb_array_accessor(msg, UPB_SIZE(4, 8), len); } UPB_INLINE void envoy_config_rbac_v3_RBAC_AuditLoggingOptions_set_audit_condition(envoy_config_rbac_v3_RBAC_AuditLoggingOptions *msg, int32_t value) { *UPB_PTR_AT(msg, UPB_SIZE(0, 0), int32_t) = value; } -UPB_INLINE struct envoy_config_core_v3_TypedExtensionConfig** envoy_config_rbac_v3_RBAC_AuditLoggingOptions_mutable_audit_loggers(envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg, size_t* len) { - return (struct envoy_config_core_v3_TypedExtensionConfig**)_upb_array_mutable_accessor(msg, UPB_SIZE(4, 8), len); +UPB_INLINE envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig** envoy_config_rbac_v3_RBAC_AuditLoggingOptions_mutable_logger_configs(envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg, size_t* len) { + return (envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig**)_upb_array_mutable_accessor(msg, UPB_SIZE(4, 8), len); } -UPB_INLINE struct envoy_config_core_v3_TypedExtensionConfig** envoy_config_rbac_v3_RBAC_AuditLoggingOptions_resize_audit_loggers(envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg, size_t len, upb_Arena* arena) { - return (struct envoy_config_core_v3_TypedExtensionConfig**)_upb_Array_Resize_accessor2(msg, UPB_SIZE(4, 8), len, UPB_SIZE(2, 3), arena); +UPB_INLINE envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig** envoy_config_rbac_v3_RBAC_AuditLoggingOptions_resize_logger_configs(envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg, size_t len, upb_Arena* arena) { + return (envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig**)_upb_Array_Resize_accessor2(msg, UPB_SIZE(4, 8), len, UPB_SIZE(2, 3), arena); } -UPB_INLINE struct envoy_config_core_v3_TypedExtensionConfig* envoy_config_rbac_v3_RBAC_AuditLoggingOptions_add_audit_loggers(envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg, upb_Arena* arena) { - struct envoy_config_core_v3_TypedExtensionConfig* sub = (struct envoy_config_core_v3_TypedExtensionConfig*)_upb_Message_New(&envoy_config_core_v3_TypedExtensionConfig_msginit, arena); +UPB_INLINE struct envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* envoy_config_rbac_v3_RBAC_AuditLoggingOptions_add_logger_configs(envoy_config_rbac_v3_RBAC_AuditLoggingOptions* msg, upb_Arena* arena) { + struct envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* sub = (struct envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig*)_upb_Message_New(&envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_msginit, arena); bool ok = _upb_Array_Append_accessor2(msg, UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena); if (!ok) return NULL; return sub; } +/* envoy.config.rbac.v3.RBAC.AuditLoggingOptions.AuditLoggerConfig */ + +UPB_INLINE envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_new(upb_Arena* arena) { + return (envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig*)_upb_Message_New(&envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_msginit, arena); +} +UPB_INLINE envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_parse(const char* buf, size_t size, upb_Arena* arena) { + envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* ret = envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, ret, &envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_msginit, NULL, 0, arena) != kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_parse_ex(const char* buf, size_t size, + const upb_ExtensionRegistry* extreg, + int options, upb_Arena* arena) { + envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* ret = envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, ret, &envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_msginit, extreg, options, arena) != + kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE char* envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_serialize(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* msg, upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(msg, &envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_msginit, 0, arena, &ptr, len); + return ptr; +} +UPB_INLINE char* envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_serialize_ex(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* msg, int options, + upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(msg, &envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_msginit, options, arena, &ptr, len); + return ptr; +} +UPB_INLINE bool envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_has_audit_logger(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* msg) { + return _upb_hasbit(msg, 1); +} +UPB_INLINE void envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_clear_audit_logger(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* msg) { + *UPB_PTR_AT(msg, UPB_SIZE(4, 8), const upb_Message*) = NULL; +} +UPB_INLINE const struct envoy_config_core_v3_TypedExtensionConfig* envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_audit_logger(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* msg) { + return *UPB_PTR_AT(msg, UPB_SIZE(4, 8), const struct envoy_config_core_v3_TypedExtensionConfig*); +} +UPB_INLINE void envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_clear_is_optional(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* msg) { + *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool) = 0; +} +UPB_INLINE bool envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_is_optional(const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* msg) { + return *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool); +} + +UPB_INLINE void envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_set_audit_logger(envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig *msg, struct envoy_config_core_v3_TypedExtensionConfig* value) { + _upb_sethas(msg, 1); + *UPB_PTR_AT(msg, UPB_SIZE(4, 8), struct envoy_config_core_v3_TypedExtensionConfig*) = value; +} +UPB_INLINE struct envoy_config_core_v3_TypedExtensionConfig* envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_mutable_audit_logger(envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* msg, upb_Arena* arena) { + struct envoy_config_core_v3_TypedExtensionConfig* sub = (struct envoy_config_core_v3_TypedExtensionConfig*)envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_audit_logger(msg); + if (sub == NULL) { + sub = (struct envoy_config_core_v3_TypedExtensionConfig*)_upb_Message_New(&envoy_config_core_v3_TypedExtensionConfig_msginit, arena); + if (!sub) return NULL; + envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_set_audit_logger(msg, sub); + } + return sub; +} +UPB_INLINE void envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_set_is_optional(envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig *msg, bool value) { + *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool) = value; +} + /* envoy.config.rbac.v3.RBAC.PoliciesEntry */ UPB_INLINE upb_StringView envoy_config_rbac_v3_RBAC_PoliciesEntry_key(const envoy_config_rbac_v3_RBAC_PoliciesEntry* msg) { diff --git a/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.c b/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.c index 86f0f5870e979..0054574339ada 100644 --- a/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.c +++ b/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.c @@ -70,7 +70,7 @@ static const upb_MiniTable_Sub envoy_extensions_filters_network_http_connection_ {.submsg = &google_protobuf_Duration_msginit}, }; -static const upb_MiniTable_Field envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager__fields[52] = { +static const upb_MiniTable_Field envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager__fields[53] = { {1, UPB_SIZE(4, 4), UPB_SIZE(0, 0), kUpb_NoSub, 5, kUpb_FieldMode_Scalar | (kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}, {2, UPB_SIZE(52, 56), UPB_SIZE(0, 0), kUpb_NoSub, 9, kUpb_FieldMode_Scalar | (kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, {3, UPB_SIZE(200, 352), UPB_SIZE(-25, -25), 0, 11, kUpb_FieldMode_Scalar | (kUpb_FieldRep_Pointer << kUpb_FieldRep_Shift)}, @@ -123,12 +123,13 @@ static const upb_MiniTable_Field envoy_extensions_filters_network_http_connectio {52, UPB_SIZE(188, 328), UPB_SIZE(0, 0), 31, 11, kUpb_FieldMode_Array | (kUpb_FieldRep_Pointer << kUpb_FieldRep_Shift)}, {53, UPB_SIZE(192, 336), UPB_SIZE(25, 25), 32, 11, kUpb_FieldMode_Scalar | (kUpb_FieldRep_Pointer << kUpb_FieldRep_Shift)}, {54, UPB_SIZE(196, 344), UPB_SIZE(26, 26), 33, 11, kUpb_FieldMode_Scalar | (kUpb_FieldRep_Pointer << kUpb_FieldRep_Shift)}, + {55, UPB_SIZE(50, 50), UPB_SIZE(0, 0), kUpb_NoSub, 8, kUpb_FieldMode_Scalar | (kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}, }; const upb_MiniTable envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_msginit = { &envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_submsgs[0], &envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager__fields[0], - UPB_SIZE(208, 368), 52, kUpb_ExtMode_NonExtendable, 10, 255, 0, + UPB_SIZE(208, 368), 53, kUpb_ExtMode_NonExtendable, 10, 255, 0, }; static const upb_MiniTable_Sub envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_Tracing_submsgs[6] = { diff --git a/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h b/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h index 47a55ea9ae065..54a90f924ee4a 100644 --- a/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h +++ b/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h @@ -633,6 +633,12 @@ UPB_INLINE void envoy_extensions_filters_network_http_connection_manager_v3_Http UPB_INLINE const struct google_protobuf_Duration* envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_access_log_flush_interval(const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager* msg) { return *UPB_PTR_AT(msg, UPB_SIZE(196, 344), const struct google_protobuf_Duration*); } +UPB_INLINE void envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_clear_flush_access_log_on_new_request(const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager* msg) { + *UPB_PTR_AT(msg, UPB_SIZE(50, 50), bool) = 0; +} +UPB_INLINE bool envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_flush_access_log_on_new_request(const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager* msg) { + return *UPB_PTR_AT(msg, UPB_SIZE(50, 50), bool); +} UPB_INLINE void envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_set_codec_type(envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager *msg, int32_t value) { *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t) = value; @@ -1122,6 +1128,9 @@ UPB_INLINE struct google_protobuf_Duration* envoy_extensions_filters_network_htt } return sub; } +UPB_INLINE void envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_set_flush_access_log_on_new_request(envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager *msg, bool value) { + *UPB_PTR_AT(msg, UPB_SIZE(50, 50), bool) = value; +} /* envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.Tracing */ diff --git a/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.c b/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.c index 0c46147d6da1d..e7c61d3215dfc 100644 --- a/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.c +++ b/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.c @@ -23,17 +23,18 @@ #include "upb/port_def.inc" -static const upb_MiniTable_Field envoy_extensions_transport_sockets_tls_v3_TlsParameters__fields[4] = { +static const upb_MiniTable_Field envoy_extensions_transport_sockets_tls_v3_TlsParameters__fields[5] = { {1, UPB_SIZE(0, 0), UPB_SIZE(0, 0), kUpb_NoSub, 5, kUpb_FieldMode_Scalar | (kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}, {2, UPB_SIZE(4, 4), UPB_SIZE(0, 0), kUpb_NoSub, 5, kUpb_FieldMode_Scalar | (kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}, {3, UPB_SIZE(8, 8), UPB_SIZE(0, 0), kUpb_NoSub, 9, kUpb_FieldMode_Array | (kUpb_FieldRep_Pointer << kUpb_FieldRep_Shift)}, {4, UPB_SIZE(12, 16), UPB_SIZE(0, 0), kUpb_NoSub, 9, kUpb_FieldMode_Array | (kUpb_FieldRep_Pointer << kUpb_FieldRep_Shift)}, + {5, UPB_SIZE(16, 24), UPB_SIZE(0, 0), kUpb_NoSub, 9, kUpb_FieldMode_Array | (kUpb_FieldRep_Pointer << kUpb_FieldRep_Shift)}, }; const upb_MiniTable envoy_extensions_transport_sockets_tls_v3_TlsParameters_msginit = { NULL, &envoy_extensions_transport_sockets_tls_v3_TlsParameters__fields[0], - UPB_SIZE(16, 24), 4, kUpb_ExtMode_NonExtendable, 4, 255, 0, + UPB_SIZE(24, 32), 5, kUpb_ExtMode_NonExtendable, 5, 255, 0, }; static const upb_MiniTable_Sub envoy_extensions_transport_sockets_tls_v3_PrivateKeyProvider_submsgs[1] = { diff --git a/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.h b/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.h index a375c1c38ee64..7c3e60a9886cc 100644 --- a/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.h +++ b/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.h @@ -138,6 +138,12 @@ UPB_INLINE void envoy_extensions_transport_sockets_tls_v3_TlsParameters_clear_ec UPB_INLINE upb_StringView const* envoy_extensions_transport_sockets_tls_v3_TlsParameters_ecdh_curves(const envoy_extensions_transport_sockets_tls_v3_TlsParameters* msg, size_t* len) { return (upb_StringView const*)_upb_array_accessor(msg, UPB_SIZE(12, 16), len); } +UPB_INLINE void envoy_extensions_transport_sockets_tls_v3_TlsParameters_clear_signature_algorithms(const envoy_extensions_transport_sockets_tls_v3_TlsParameters* msg) { + _upb_array_detach(msg, UPB_SIZE(16, 24)); +} +UPB_INLINE upb_StringView const* envoy_extensions_transport_sockets_tls_v3_TlsParameters_signature_algorithms(const envoy_extensions_transport_sockets_tls_v3_TlsParameters* msg, size_t* len) { + return (upb_StringView const*)_upb_array_accessor(msg, UPB_SIZE(16, 24), len); +} UPB_INLINE void envoy_extensions_transport_sockets_tls_v3_TlsParameters_set_tls_minimum_protocol_version(envoy_extensions_transport_sockets_tls_v3_TlsParameters *msg, int32_t value) { *UPB_PTR_AT(msg, UPB_SIZE(0, 0), int32_t) = value; @@ -163,6 +169,15 @@ UPB_INLINE upb_StringView* envoy_extensions_transport_sockets_tls_v3_TlsParamete UPB_INLINE bool envoy_extensions_transport_sockets_tls_v3_TlsParameters_add_ecdh_curves(envoy_extensions_transport_sockets_tls_v3_TlsParameters* msg, upb_StringView val, upb_Arena* arena) { return _upb_Array_Append_accessor2(msg, UPB_SIZE(12, 16), UPB_SIZE(3, 4), &val, arena); } +UPB_INLINE upb_StringView* envoy_extensions_transport_sockets_tls_v3_TlsParameters_mutable_signature_algorithms(envoy_extensions_transport_sockets_tls_v3_TlsParameters* msg, size_t* len) { + return (upb_StringView*)_upb_array_mutable_accessor(msg, UPB_SIZE(16, 24), len); +} +UPB_INLINE upb_StringView* envoy_extensions_transport_sockets_tls_v3_TlsParameters_resize_signature_algorithms(envoy_extensions_transport_sockets_tls_v3_TlsParameters* msg, size_t len, upb_Arena* arena) { + return (upb_StringView*)_upb_Array_Resize_accessor2(msg, UPB_SIZE(16, 24), len, UPB_SIZE(3, 4), arena); +} +UPB_INLINE bool envoy_extensions_transport_sockets_tls_v3_TlsParameters_add_signature_algorithms(envoy_extensions_transport_sockets_tls_v3_TlsParameters* msg, upb_StringView val, upb_Arena* arena) { + return _upb_Array_Append_accessor2(msg, UPB_SIZE(16, 24), UPB_SIZE(3, 4), &val, arena); +} /* envoy.extensions.transport_sockets.tls.v3.PrivateKeyProvider */ diff --git a/src/core/ext/upbdefs-generated/envoy/config/core/v3/config_source.upbdefs.c b/src/core/ext/upbdefs-generated/envoy/config/core/v3/config_source.upbdefs.c index cf5de8ff354c7..b0e7360e73cbf 100644 --- a/src/core/ext/upbdefs-generated/envoy/config/core/v3/config_source.upbdefs.c +++ b/src/core/ext/upbdefs-generated/envoy/config/core/v3/config_source.upbdefs.c @@ -21,7 +21,7 @@ extern _upb_DefPool_Init envoy_annotations_deprecation_proto_upbdefinit; extern _upb_DefPool_Init udpa_annotations_status_proto_upbdefinit; extern _upb_DefPool_Init udpa_annotations_versioning_proto_upbdefinit; extern _upb_DefPool_Init validate_validate_proto_upbdefinit; -static const char descriptor[3037] = {'\n', '(', 'e', 'n', 'v', 'o', 'y', '/', 'c', 'o', 'n', 'f', 'i', 'g', '/', 'c', 'o', 'r', 'e', '/', 'v', '3', '/', 'c', 'o', +static const char descriptor[3057] = {'\n', '(', 'e', 'n', 'v', 'o', 'y', '/', 'c', 'o', 'n', 'f', 'i', 'g', '/', 'c', 'o', 'r', 'e', '/', 'v', '3', '/', 'c', 'o', 'n', 'f', 'i', 'g', '_', 's', 'o', 'u', 'r', 'c', 'e', '.', 'p', 'r', 'o', 't', 'o', '\022', '\024', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '\032', '\037', 'e', 'n', 'v', 'o', 'y', '/', 'c', 'o', 'n', 'f', 'i', 'g', '/', 'c', 'o', 'r', 'e', '/', 'v', '3', '/', 'b', 'a', 's', 'e', '.', 'p', 'r', 'o', 't', 'o', '\032', '$', 'e', @@ -134,15 +134,16 @@ static const char descriptor[3037] = {'\n', '(', 'e', 'n', 'v', 'o', 'y', '/', ' ' ', '\001', '(', '\010', 'R', ' ', 'a', 'p', 'p', 'l', 'y', 'D', 'e', 'f', 'a', 'u', 'l', 't', 'C', 'o', 'n', 'f', 'i', 'g', 'W', 'i', 't', 'h', 'o', 'u', 't', 'W', 'a', 'r', 'm', 'i', 'n', 'g', '\022', '%', '\n', '\t', 't', 'y', 'p', 'e', '_', 'u', 'r', 'l', 's', '\030', '\004', ' ', '\003', '(', '\t', 'B', '\010', '\372', 'B', '\005', '\222', '\001', '\002', '\010', '\001', 'R', '\010', 't', 'y', 'p', 'e', 'U', 'r', -'l', 's', '*', '@', '\n', '\n', 'A', 'p', 'i', 'V', 'e', 'r', 's', 'i', 'o', 'n', '\022', '\025', '\n', '\004', 'A', 'U', 'T', 'O', '\020', -'\000', '\032', '\013', '\010', '\001', '\212', '\364', '\233', '\263', '\005', '\003', '3', '.', '0', '\022', '\023', '\n', '\002', 'V', '2', '\020', '\001', '\032', '\013', '\010', -'\001', '\212', '\364', '\233', '\263', '\005', '\003', '3', '.', '0', '\022', '\006', '\n', '\002', 'V', '3', '\020', '\002', 'B', '\205', '\001', '\n', '\"', 'i', 'o', -'.', 'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', 'x', 'y', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', -'c', 'o', 'r', 'e', '.', 'v', '3', 'B', '\021', 'C', 'o', 'n', 'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', 'e', 'P', 'r', 'o', 't', -'o', 'P', '\001', 'Z', 'B', 'g', 'i', 't', 'h', 'u', 'b', '.', 'c', 'o', 'm', '/', 'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', 'x', -'y', '/', 'g', 'o', '-', 'c', 'o', 'n', 't', 'r', 'o', 'l', '-', 'p', 'l', 'a', 'n', 'e', '/', 'e', 'n', 'v', 'o', 'y', '/', -'c', 'o', 'n', 'f', 'i', 'g', '/', 'c', 'o', 'r', 'e', '/', 'v', '3', ';', 'c', 'o', 'r', 'e', 'v', '3', '\272', '\200', '\310', '\321', -'\006', '\002', '\020', '\002', 'b', '\006', 'p', 'r', 'o', 't', 'o', '3', +'l', 's', '\"', '\022', '\n', '\020', 'R', 'e', 's', 't', 'S', 'u', 'b', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '*', '@', '\n', +'\n', 'A', 'p', 'i', 'V', 'e', 'r', 's', 'i', 'o', 'n', '\022', '\025', '\n', '\004', 'A', 'U', 'T', 'O', '\020', '\000', '\032', '\013', '\010', '\001', +'\212', '\364', '\233', '\263', '\005', '\003', '3', '.', '0', '\022', '\023', '\n', '\002', 'V', '2', '\020', '\001', '\032', '\013', '\010', '\001', '\212', '\364', '\233', '\263', +'\005', '\003', '3', '.', '0', '\022', '\006', '\n', '\002', 'V', '3', '\020', '\002', 'B', '\205', '\001', '\n', '\"', 'i', 'o', '.', 'e', 'n', 'v', 'o', +'y', 'p', 'r', 'o', 'x', 'y', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', +'v', '3', 'B', '\021', 'C', 'o', 'n', 'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', 'e', 'P', 'r', 'o', 't', 'o', 'P', '\001', 'Z', 'B', +'g', 'i', 't', 'h', 'u', 'b', '.', 'c', 'o', 'm', '/', 'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', 'x', 'y', '/', 'g', 'o', '-', +'c', 'o', 'n', 't', 'r', 'o', 'l', '-', 'p', 'l', 'a', 'n', 'e', '/', 'e', 'n', 'v', 'o', 'y', '/', 'c', 'o', 'n', 'f', 'i', +'g', '/', 'c', 'o', 'r', 'e', '/', 'v', '3', ';', 'c', 'o', 'r', 'e', 'v', '3', '\272', '\200', '\310', '\321', '\006', '\002', '\020', '\002', 'b', +'\006', 'p', 'r', 'o', 't', 'o', '3', }; static _upb_DefPool_Init *deps[12] = { @@ -164,5 +165,5 @@ _upb_DefPool_Init envoy_config_core_v3_config_source_proto_upbdefinit = { deps, &envoy_config_core_v3_config_source_proto_upb_file_layout, "envoy/config/core/v3/config_source.proto", - UPB_STRINGVIEW_INIT(descriptor, 3037) + UPB_STRINGVIEW_INIT(descriptor, 3057) }; diff --git a/src/core/ext/upbdefs-generated/envoy/config/core/v3/config_source.upbdefs.h b/src/core/ext/upbdefs-generated/envoy/config/core/v3/config_source.upbdefs.h index 146944939e354..c0fdd5a42d59d 100644 --- a/src/core/ext/upbdefs-generated/envoy/config/core/v3/config_source.upbdefs.h +++ b/src/core/ext/upbdefs-generated/envoy/config/core/v3/config_source.upbdefs.h @@ -56,6 +56,11 @@ UPB_INLINE const upb_MessageDef *envoy_config_core_v3_ExtensionConfigSource_getm return upb_DefPool_FindMessageByName(s, "envoy.config.core.v3.ExtensionConfigSource"); } +UPB_INLINE const upb_MessageDef *envoy_config_core_v3_RestSubscription_getmsgdef(upb_DefPool *s) { + _upb_DefPool_LoadDefInit(s, &envoy_config_core_v3_config_source_proto_upbdefinit); + return upb_DefPool_FindMessageByName(s, "envoy.config.core.v3.RestSubscription"); +} + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/core/ext/upbdefs-generated/envoy/config/rbac/v3/rbac.upbdefs.c b/src/core/ext/upbdefs-generated/envoy/config/rbac/v3/rbac.upbdefs.c index 6b00fb7f28f1e..19f30fcb89d50 100644 --- a/src/core/ext/upbdefs-generated/envoy/config/rbac/v3/rbac.upbdefs.c +++ b/src/core/ext/upbdefs-generated/envoy/config/rbac/v3/rbac.upbdefs.c @@ -25,7 +25,7 @@ extern _upb_DefPool_Init udpa_annotations_migrate_proto_upbdefinit; extern _upb_DefPool_Init udpa_annotations_status_proto_upbdefinit; extern _upb_DefPool_Init udpa_annotations_versioning_proto_upbdefinit; extern _upb_DefPool_Init validate_validate_proto_upbdefinit; -static const char descriptor[4073] = {'\n', '\037', 'e', 'n', 'v', 'o', 'y', '/', 'c', 'o', 'n', 'f', 'i', 'g', '/', 'r', 'b', 'a', 'c', '/', 'v', '3', '/', 'r', 'b', +static const char descriptor[4221] = {'\n', '\037', 'e', 'n', 'v', 'o', 'y', '/', 'c', 'o', 'n', 'f', 'i', 'g', '/', 'r', 'b', 'a', 'c', '/', 'v', '3', '/', 'r', 'b', 'a', 'c', '.', 'p', 'r', 'o', 't', 'o', '\022', '\024', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '\032', '\"', 'e', 'n', 'v', 'o', 'y', '/', 'c', 'o', 'n', 'f', 'i', 'g', '/', 'c', 'o', 'r', 'e', '/', 'v', '3', '/', 'a', 'd', 'd', 'r', 'e', 's', 's', '.', 'p', 'r', 'o', 't', 'o', '\032', '$', 'e', 'n', 'v', 'o', 'y', '/', 'c', @@ -48,7 +48,7 @@ static const char descriptor[4073] = {'\n', '\037', 'e', 'n', 'v', 'o', 'y', '/' '\032', '\035', 'u', 'd', 'p', 'a', '/', 'a', 'n', 'n', 'o', 't', 'a', 't', 'i', 'o', 'n', 's', '/', 's', 't', 'a', 't', 'u', 's', '.', 'p', 'r', 'o', 't', 'o', '\032', '!', 'u', 'd', 'p', 'a', '/', 'a', 'n', 'n', 'o', 't', 'a', 't', 'i', 'o', 'n', 's', '/', 'v', 'e', 'r', 's', 'i', 'o', 'n', 'i', 'n', 'g', '.', 'p', 'r', 'o', 't', 'o', '\032', '\027', 'v', 'a', 'l', 'i', 'd', 'a', 't', -'e', '/', 'v', 'a', 'l', 'i', 'd', 'a', 't', 'e', '.', 'p', 'r', 'o', 't', 'o', '\"', '\315', '\005', '\n', '\004', 'R', 'B', 'A', 'C', +'e', '/', 'v', 'a', 'l', 'i', 'd', 'a', 't', 'e', '.', 'p', 'r', 'o', 't', 'o', '\"', '\341', '\006', '\n', '\004', 'R', 'B', 'A', 'C', '\022', 'C', '\n', '\006', 'a', 'c', 't', 'i', 'o', 'n', '\030', '\001', ' ', '\001', '(', '\016', '2', '!', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'R', 'B', 'A', 'C', '.', 'A', 'c', 't', 'i', 'o', 'n', 'B', '\010', '\372', 'B', '\005', '\202', '\001', '\002', '\020', '\001', 'R', '\006', 'a', 'c', 't', 'i', 'o', 'n', '\022', 'D', '\n', '\010', 'p', 'o', @@ -58,136 +58,142 @@ static const char descriptor[4073] = {'\n', '\037', 'e', 'n', 'v', 'o', 'y', '/' 'o', 'g', 'g', 'i', 'n', 'g', '_', 'o', 'p', 't', 'i', 'o', 'n', 's', '\030', '\003', ' ', '\001', '(', '\013', '2', '.', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'R', 'B', 'A', 'C', '.', 'A', 'u', 'd', 'i', 't', 'L', 'o', 'g', 'g', 'i', 'n', 'g', 'O', 'p', 't', 'i', 'o', 'n', 's', 'R', '\023', 'a', 'u', 'd', 'i', 't', -'L', 'o', 'g', 'g', 'i', 'n', 'g', 'O', 'p', 't', 'i', 'o', 'n', 's', '\032', '\260', '\002', '\n', '\023', 'A', 'u', 'd', 'i', 't', 'L', +'L', 'o', 'g', 'g', 'i', 'n', 'g', 'O', 'p', 't', 'i', 'o', 'n', 's', '\032', '\304', '\003', '\n', '\023', 'A', 'u', 'd', 'i', 't', 'L', 'o', 'g', 'g', 'i', 'n', 'g', 'O', 'p', 't', 'i', 'o', 'n', 's', '\022', 'p', '\n', '\017', 'a', 'u', 'd', 'i', 't', '_', 'c', 'o', 'n', 'd', 'i', 't', 'i', 'o', 'n', '\030', '\001', ' ', '\001', '(', '\016', '2', '=', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'R', 'B', 'A', 'C', '.', 'A', 'u', 'd', 'i', 't', 'L', 'o', 'g', 'g', 'i', 'n', 'g', 'O', 'p', 't', 'i', 'o', 'n', 's', '.', 'A', 'u', 'd', 'i', 't', 'C', 'o', 'n', 'd', 'i', 't', 'i', 'o', 'n', 'B', '\010', '\372', 'B', '\005', '\202', '\001', '\002', '\020', '\001', 'R', '\016', 'a', 'u', 'd', 'i', 't', 'C', 'o', 'n', 'd', 'i', 't', 'i', -'o', 'n', '\022', 'Y', '\n', '\r', 'a', 'u', 'd', 'i', 't', '_', 'l', 'o', 'g', 'g', 'e', 'r', 's', '\030', '\002', ' ', '\003', '(', '\013', -'2', '*', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'T', -'y', 'p', 'e', 'd', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', 'B', '\010', '\372', 'B', '\005', '\222', -'\001', '\002', '\010', '\001', 'R', '\014', 'a', 'u', 'd', 'i', 't', 'L', 'o', 'g', 'g', 'e', 'r', 's', '\"', 'L', '\n', '\016', 'A', 'u', 'd', -'i', 't', 'C', 'o', 'n', 'd', 'i', 't', 'i', 'o', 'n', '\022', '\010', '\n', '\004', 'N', 'O', 'N', 'E', '\020', '\000', '\022', '\013', '\n', '\007', -'O', 'N', '_', 'D', 'E', 'N', 'Y', '\020', '\001', '\022', '\014', '\n', '\010', 'O', 'N', '_', 'A', 'L', 'L', 'O', 'W', '\020', '\002', '\022', '\025', -'\n', '\021', 'O', 'N', '_', 'D', 'E', 'N', 'Y', '_', 'A', 'N', 'D', '_', 'A', 'L', 'L', 'O', 'W', '\020', '\003', '\032', 'Y', '\n', '\r', -'P', 'o', 'l', 'i', 'c', 'i', 'e', 's', 'E', 'n', 't', 'r', 'y', '\022', '\020', '\n', '\003', 'k', 'e', 'y', '\030', '\001', ' ', '\001', '(', -'\t', 'R', '\003', 'k', 'e', 'y', '\022', '2', '\n', '\005', 'v', 'a', 'l', 'u', 'e', '\030', '\002', ' ', '\001', '(', '\013', '2', '\034', '.', 'e', -'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'o', 'l', 'i', 'c', -'y', 'R', '\005', 'v', 'a', 'l', 'u', 'e', ':', '\002', '8', '\001', '\"', '&', '\n', '\006', 'A', 'c', 't', 'i', 'o', 'n', '\022', '\t', '\n', -'\005', 'A', 'L', 'L', 'O', 'W', '\020', '\000', '\022', '\010', '\n', '\004', 'D', 'E', 'N', 'Y', '\020', '\001', '\022', '\007', '\n', '\003', 'L', 'O', 'G', -'\020', '\002', ':', ' ', '\232', '\305', '\210', '\036', '\033', '\n', '\031', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', -'b', 'a', 'c', '.', 'v', '2', '.', 'R', 'B', 'A', 'C', '\"', '\223', '\003', '\n', '\006', 'P', 'o', 'l', 'i', 'c', 'y', '\022', 'L', '\n', -'\013', 'p', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', 's', '\030', '\001', ' ', '\003', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', -'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'e', 'r', 'm', 'i', 's', 's', 'i', -'o', 'n', 'B', '\010', '\372', 'B', '\005', '\222', '\001', '\002', '\010', '\001', 'R', '\013', 'p', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', 's', -'\022', 'I', '\n', '\n', 'p', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', 's', '\030', '\002', ' ', '\003', '(', '\013', '2', '\037', '.', 'e', 'n', -'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'r', 'i', 'n', 'c', 'i', -'p', 'a', 'l', 'B', '\010', '\372', 'B', '\005', '\222', '\001', '\002', '\010', '\001', 'R', '\n', 'p', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', 's', -'\022', 'Z', '\n', '\t', 'c', 'o', 'n', 'd', 'i', 't', 'i', 'o', 'n', '\030', '\003', ' ', '\001', '(', '\013', '2', '\036', '.', 'g', 'o', 'o', -'g', 'l', 'e', '.', 'a', 'p', 'i', '.', 'e', 'x', 'p', 'r', '.', 'v', '1', 'a', 'l', 'p', 'h', 'a', '1', '.', 'E', 'x', 'p', -'r', 'B', '\034', '\362', '\230', '\376', '\217', '\005', '\026', '\022', '\024', 'e', 'x', 'p', 'r', 'e', 's', 's', 'i', 'o', 'n', '_', 's', 'p', 'e', -'c', 'i', 'f', 'i', 'e', 'r', 'R', '\t', 'c', 'o', 'n', 'd', 'i', 't', 'i', 'o', 'n', '\022', 'p', '\n', '\021', 'c', 'h', 'e', 'c', -'k', 'e', 'd', '_', 'c', 'o', 'n', 'd', 'i', 't', 'i', 'o', 'n', '\030', '\004', ' ', '\001', '(', '\013', '2', '%', '.', 'g', 'o', 'o', -'g', 'l', 'e', '.', 'a', 'p', 'i', '.', 'e', 'x', 'p', 'r', '.', 'v', '1', 'a', 'l', 'p', 'h', 'a', '1', '.', 'C', 'h', 'e', -'c', 'k', 'e', 'd', 'E', 'x', 'p', 'r', 'B', '\034', '\362', '\230', '\376', '\217', '\005', '\026', '\022', '\024', 'e', 'x', 'p', 'r', 'e', 's', 's', -'i', 'o', 'n', '_', 's', 'p', 'e', 'c', 'i', 'f', 'i', 'e', 'r', 'R', '\020', 'c', 'h', 'e', 'c', 'k', 'e', 'd', 'C', 'o', 'n', -'d', 'i', 't', 'i', 'o', 'n', ':', '\"', '\232', '\305', '\210', '\036', '\035', '\n', '\033', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', -'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '2', '.', 'P', 'o', 'l', 'i', 'c', 'y', '\"', '\332', '\007', '\n', '\n', 'P', 'e', 'r', -'m', 'i', 's', 's', 'i', 'o', 'n', '\022', 'C', '\n', '\t', 'a', 'n', 'd', '_', 'r', 'u', 'l', 'e', 's', '\030', '\001', ' ', '\001', '(', -'\013', '2', '$', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', -'P', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', '.', 'S', 'e', 't', 'H', '\000', 'R', '\010', 'a', 'n', 'd', 'R', 'u', 'l', 'e', -'s', '\022', 'A', '\n', '\010', 'o', 'r', '_', 'r', 'u', 'l', 'e', 's', '\030', '\002', ' ', '\001', '(', '\013', '2', '$', '.', 'e', 'n', 'v', -'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'e', 'r', 'm', 'i', 's', 's', -'i', 'o', 'n', '.', 'S', 'e', 't', 'H', '\000', 'R', '\007', 'o', 'r', 'R', 'u', 'l', 'e', 's', '\022', '\033', '\n', '\003', 'a', 'n', 'y', -'\030', '\003', ' ', '\001', '(', '\010', 'B', '\007', '\372', 'B', '\004', 'j', '\002', '\010', '\001', 'H', '\000', 'R', '\003', 'a', 'n', 'y', '\022', '>', '\n', -'\006', 'h', 'e', 'a', 'd', 'e', 'r', '\030', '\004', ' ', '\001', '(', '\013', '2', '$', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', -'f', 'i', 'g', '.', 'r', 'o', 'u', 't', 'e', '.', 'v', '3', '.', 'H', 'e', 'a', 'd', 'e', 'r', 'M', 'a', 't', 'c', 'h', 'e', -'r', 'H', '\000', 'R', '\006', 'h', 'e', 'a', 'd', 'e', 'r', '\022', '?', '\n', '\010', 'u', 'r', 'l', '_', 'p', 'a', 't', 'h', '\030', '\n', -' ', '\001', '(', '\013', '2', '\"', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', 'r', -'.', 'v', '3', '.', 'P', 'a', 't', 'h', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', '\007', 'u', 'r', 'l', 'P', 'a', 't', -'h', '\022', 'H', '\n', '\016', 'd', 'e', 's', 't', 'i', 'n', 'a', 't', 'i', 'o', 'n', '_', 'i', 'p', '\030', '\005', ' ', '\001', '(', '\013', -'2', '\037', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'C', -'i', 'd', 'r', 'R', 'a', 'n', 'g', 'e', 'H', '\000', 'R', '\r', 'd', 'e', 's', 't', 'i', 'n', 'a', 't', 'i', 'o', 'n', 'I', 'p', -'\022', '6', '\n', '\020', 'd', 'e', 's', 't', 'i', 'n', 'a', 't', 'i', 'o', 'n', '_', 'p', 'o', 'r', 't', '\030', '\006', ' ', '\001', '(', -'\r', 'B', '\t', '\372', 'B', '\006', '*', '\004', '\030', '\377', '\377', '\003', 'H', '\000', 'R', '\017', 'd', 'e', 's', 't', 'i', 'n', 'a', 't', 'i', -'o', 'n', 'P', 'o', 'r', 't', '\022', 'Q', '\n', '\026', 'd', 'e', 's', 't', 'i', 'n', 'a', 't', 'i', 'o', 'n', '_', 'p', 'o', 'r', -'t', '_', 'r', 'a', 'n', 'g', 'e', '\030', '\013', ' ', '\001', '(', '\013', '2', '\031', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', -'e', '.', 'v', '3', '.', 'I', 'n', 't', '3', '2', 'R', 'a', 'n', 'g', 'e', 'H', '\000', 'R', '\024', 'd', 'e', 's', 't', 'i', 'n', -'a', 't', 'i', 'o', 'n', 'P', 'o', 'r', 't', 'R', 'a', 'n', 'g', 'e', '\022', 'D', '\n', '\010', 'm', 'e', 't', 'a', 'd', 'a', 't', -'a', '\030', '\007', ' ', '\001', '(', '\013', '2', '&', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', -'h', 'e', 'r', '.', 'v', '3', '.', 'M', 'e', 't', 'a', 'd', 'a', 't', 'a', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', -'\010', 'm', 'e', 't', 'a', 'd', 'a', 't', 'a', '\022', '=', '\n', '\010', 'n', 'o', 't', '_', 'r', 'u', 'l', 'e', '\030', '\010', ' ', '\001', -'(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', -'.', 'P', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', 'H', '\000', 'R', '\007', 'n', 'o', 't', 'R', 'u', 'l', 'e', '\022', 'Z', '\n', -'\025', 'r', 'e', 'q', 'u', 'e', 's', 't', 'e', 'd', '_', 's', 'e', 'r', 'v', 'e', 'r', '_', 'n', 'a', 'm', 'e', '\030', '\t', ' ', -'\001', '(', '\013', '2', '$', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', 'r', '.', -'v', '3', '.', 'S', 't', 'r', 'i', 'n', 'g', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', '\023', 'r', 'e', 'q', 'u', 'e', -'s', 't', 'e', 'd', 'S', 'e', 'r', 'v', 'e', 'r', 'N', 'a', 'm', 'e', '\022', 'F', '\n', '\007', 'm', 'a', 't', 'c', 'h', 'e', 'r', -'\030', '\014', ' ', '\001', '(', '\013', '2', '*', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', -'e', '.', 'v', '3', '.', 'T', 'y', 'p', 'e', 'd', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', -'H', '\000', 'R', '\007', 'm', 'a', 't', 'c', 'h', 'e', 'r', '\032', 's', '\n', '\003', 'S', 'e', 't', '\022', '@', '\n', '\005', 'r', 'u', 'l', -'e', 's', '\030', '\001', ' ', '\003', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', -'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', 'B', '\010', '\372', 'B', '\005', '\222', '\001', '\002', -'\010', '\001', 'R', '\005', 'r', 'u', 'l', 'e', 's', ':', '*', '\232', '\305', '\210', '\036', '%', '\n', '#', 'e', 'n', 'v', 'o', 'y', '.', 'c', -'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '2', '.', 'P', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', '.', -'S', 'e', 't', ':', '&', '\232', '\305', '\210', '\036', '!', '\n', '\037', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', -'r', 'b', 'a', 'c', '.', 'v', '2', '.', 'P', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', 'B', '\013', '\n', '\004', 'r', 'u', 'l', -'e', '\022', '\003', '\370', 'B', '\001', '\"', '\353', '\010', '\n', '\t', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', '\022', '>', '\n', '\007', 'a', -'n', 'd', '_', 'i', 'd', 's', '\030', '\001', ' ', '\001', '(', '\013', '2', '#', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', -'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', '.', 'S', 'e', 't', 'H', -'\000', 'R', '\006', 'a', 'n', 'd', 'I', 'd', 's', '\022', '<', '\n', '\006', 'o', 'r', '_', 'i', 'd', 's', '\030', '\002', ' ', '\001', '(', '\013', -'2', '#', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', -'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', '.', 'S', 'e', 't', 'H', '\000', 'R', '\005', 'o', 'r', 'I', 'd', 's', '\022', '\033', '\n', '\003', -'a', 'n', 'y', '\030', '\003', ' ', '\001', '(', '\010', 'B', '\007', '\372', 'B', '\004', 'j', '\002', '\010', '\001', 'H', '\000', 'R', '\003', 'a', 'n', 'y', -'\022', 'U', '\n', '\r', 'a', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'a', 't', 'e', 'd', '\030', '\004', ' ', '\001', '(', '\013', '2', '-', +'o', 'n', '\022', 'g', '\n', '\016', 'l', 'o', 'g', 'g', 'e', 'r', '_', 'c', 'o', 'n', 'f', 'i', 'g', 's', '\030', '\002', ' ', '\003', '(', +'\013', '2', '@', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', +'R', 'B', 'A', 'C', '.', 'A', 'u', 'd', 'i', 't', 'L', 'o', 'g', 'g', 'i', 'n', 'g', 'O', 'p', 't', 'i', 'o', 'n', 's', '.', +'A', 'u', 'd', 'i', 't', 'L', 'o', 'g', 'g', 'e', 'r', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\r', 'l', 'o', 'g', 'g', 'e', 'r', +'C', 'o', 'n', 'f', 'i', 'g', 's', '\032', '\203', '\001', '\n', '\021', 'A', 'u', 'd', 'i', 't', 'L', 'o', 'g', 'g', 'e', 'r', 'C', 'o', +'n', 'f', 'i', 'g', '\022', 'M', '\n', '\014', 'a', 'u', 'd', 'i', 't', '_', 'l', 'o', 'g', 'g', 'e', 'r', '\030', '\001', ' ', '\001', '(', +'\013', '2', '*', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', +'T', 'y', 'p', 'e', 'd', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\013', 'a', 'u', 'd', +'i', 't', 'L', 'o', 'g', 'g', 'e', 'r', '\022', '\037', '\n', '\013', 'i', 's', '_', 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l', '\030', '\002', +' ', '\001', '(', '\010', 'R', '\n', 'i', 's', 'O', 'p', 't', 'i', 'o', 'n', 'a', 'l', '\"', 'L', '\n', '\016', 'A', 'u', 'd', 'i', 't', +'C', 'o', 'n', 'd', 'i', 't', 'i', 'o', 'n', '\022', '\010', '\n', '\004', 'N', 'O', 'N', 'E', '\020', '\000', '\022', '\013', '\n', '\007', 'O', 'N', +'_', 'D', 'E', 'N', 'Y', '\020', '\001', '\022', '\014', '\n', '\010', 'O', 'N', '_', 'A', 'L', 'L', 'O', 'W', '\020', '\002', '\022', '\025', '\n', '\021', +'O', 'N', '_', 'D', 'E', 'N', 'Y', '_', 'A', 'N', 'D', '_', 'A', 'L', 'L', 'O', 'W', '\020', '\003', '\032', 'Y', '\n', '\r', 'P', 'o', +'l', 'i', 'c', 'i', 'e', 's', 'E', 'n', 't', 'r', 'y', '\022', '\020', '\n', '\003', 'k', 'e', 'y', '\030', '\001', ' ', '\001', '(', '\t', 'R', +'\003', 'k', 'e', 'y', '\022', '2', '\n', '\005', 'v', 'a', 'l', 'u', 'e', '\030', '\002', ' ', '\001', '(', '\013', '2', '\034', '.', 'e', 'n', 'v', +'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'o', 'l', 'i', 'c', 'y', 'R', +'\005', 'v', 'a', 'l', 'u', 'e', ':', '\002', '8', '\001', '\"', '&', '\n', '\006', 'A', 'c', 't', 'i', 'o', 'n', '\022', '\t', '\n', '\005', 'A', +'L', 'L', 'O', 'W', '\020', '\000', '\022', '\010', '\n', '\004', 'D', 'E', 'N', 'Y', '\020', '\001', '\022', '\007', '\n', '\003', 'L', 'O', 'G', '\020', '\002', +':', ' ', '\232', '\305', '\210', '\036', '\033', '\n', '\031', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', +'c', '.', 'v', '2', '.', 'R', 'B', 'A', 'C', '\"', '\223', '\003', '\n', '\006', 'P', 'o', 'l', 'i', 'c', 'y', '\022', 'L', '\n', '\013', 'p', +'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', 's', '\030', '\001', ' ', '\003', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', +'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', +'B', '\010', '\372', 'B', '\005', '\222', '\001', '\002', '\010', '\001', 'R', '\013', 'p', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', 's', '\022', 'I', +'\n', '\n', 'p', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', 's', '\030', '\002', ' ', '\003', '(', '\013', '2', '\037', '.', 'e', 'n', 'v', 'o', +'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', +'l', 'B', '\010', '\372', 'B', '\005', '\222', '\001', '\002', '\010', '\001', 'R', '\n', 'p', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', 's', '\022', 'Z', +'\n', '\t', 'c', 'o', 'n', 'd', 'i', 't', 'i', 'o', 'n', '\030', '\003', ' ', '\001', '(', '\013', '2', '\036', '.', 'g', 'o', 'o', 'g', 'l', +'e', '.', 'a', 'p', 'i', '.', 'e', 'x', 'p', 'r', '.', 'v', '1', 'a', 'l', 'p', 'h', 'a', '1', '.', 'E', 'x', 'p', 'r', 'B', +'\034', '\362', '\230', '\376', '\217', '\005', '\026', '\022', '\024', 'e', 'x', 'p', 'r', 'e', 's', 's', 'i', 'o', 'n', '_', 's', 'p', 'e', 'c', 'i', +'f', 'i', 'e', 'r', 'R', '\t', 'c', 'o', 'n', 'd', 'i', 't', 'i', 'o', 'n', '\022', 'p', '\n', '\021', 'c', 'h', 'e', 'c', 'k', 'e', +'d', '_', 'c', 'o', 'n', 'd', 'i', 't', 'i', 'o', 'n', '\030', '\004', ' ', '\001', '(', '\013', '2', '%', '.', 'g', 'o', 'o', 'g', 'l', +'e', '.', 'a', 'p', 'i', '.', 'e', 'x', 'p', 'r', '.', 'v', '1', 'a', 'l', 'p', 'h', 'a', '1', '.', 'C', 'h', 'e', 'c', 'k', +'e', 'd', 'E', 'x', 'p', 'r', 'B', '\034', '\362', '\230', '\376', '\217', '\005', '\026', '\022', '\024', 'e', 'x', 'p', 'r', 'e', 's', 's', 'i', 'o', +'n', '_', 's', 'p', 'e', 'c', 'i', 'f', 'i', 'e', 'r', 'R', '\020', 'c', 'h', 'e', 'c', 'k', 'e', 'd', 'C', 'o', 'n', 'd', 'i', +'t', 'i', 'o', 'n', ':', '\"', '\232', '\305', '\210', '\036', '\035', '\n', '\033', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', +'.', 'r', 'b', 'a', 'c', '.', 'v', '2', '.', 'P', 'o', 'l', 'i', 'c', 'y', '\"', '\332', '\007', '\n', '\n', 'P', 'e', 'r', 'm', 'i', +'s', 's', 'i', 'o', 'n', '\022', 'C', '\n', '\t', 'a', 'n', 'd', '_', 'r', 'u', 'l', 'e', 's', '\030', '\001', ' ', '\001', '(', '\013', '2', +'$', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'e', +'r', 'm', 'i', 's', 's', 'i', 'o', 'n', '.', 'S', 'e', 't', 'H', '\000', 'R', '\010', 'a', 'n', 'd', 'R', 'u', 'l', 'e', 's', '\022', +'A', '\n', '\010', 'o', 'r', '_', 'r', 'u', 'l', 'e', 's', '\030', '\002', ' ', '\001', '(', '\013', '2', '$', '.', 'e', 'n', 'v', 'o', 'y', +'.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', +'n', '.', 'S', 'e', 't', 'H', '\000', 'R', '\007', 'o', 'r', 'R', 'u', 'l', 'e', 's', '\022', '\033', '\n', '\003', 'a', 'n', 'y', '\030', '\003', +' ', '\001', '(', '\010', 'B', '\007', '\372', 'B', '\004', 'j', '\002', '\010', '\001', 'H', '\000', 'R', '\003', 'a', 'n', 'y', '\022', '>', '\n', '\006', 'h', +'e', 'a', 'd', 'e', 'r', '\030', '\004', ' ', '\001', '(', '\013', '2', '$', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', +'g', '.', 'r', 'o', 'u', 't', 'e', '.', 'v', '3', '.', 'H', 'e', 'a', 'd', 'e', 'r', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', +'\000', 'R', '\006', 'h', 'e', 'a', 'd', 'e', 'r', '\022', '?', '\n', '\010', 'u', 'r', 'l', '_', 'p', 'a', 't', 'h', '\030', '\n', ' ', '\001', +'(', '\013', '2', '\"', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', 'r', '.', 'v', +'3', '.', 'P', 'a', 't', 'h', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', '\007', 'u', 'r', 'l', 'P', 'a', 't', 'h', '\022', +'H', '\n', '\016', 'd', 'e', 's', 't', 'i', 'n', 'a', 't', 'i', 'o', 'n', '_', 'i', 'p', '\030', '\005', ' ', '\001', '(', '\013', '2', '\037', +'.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'C', 'i', 'd', +'r', 'R', 'a', 'n', 'g', 'e', 'H', '\000', 'R', '\r', 'd', 'e', 's', 't', 'i', 'n', 'a', 't', 'i', 'o', 'n', 'I', 'p', '\022', '6', +'\n', '\020', 'd', 'e', 's', 't', 'i', 'n', 'a', 't', 'i', 'o', 'n', '_', 'p', 'o', 'r', 't', '\030', '\006', ' ', '\001', '(', '\r', 'B', +'\t', '\372', 'B', '\006', '*', '\004', '\030', '\377', '\377', '\003', 'H', '\000', 'R', '\017', 'd', 'e', 's', 't', 'i', 'n', 'a', 't', 'i', 'o', 'n', +'P', 'o', 'r', 't', '\022', 'Q', '\n', '\026', 'd', 'e', 's', 't', 'i', 'n', 'a', 't', 'i', 'o', 'n', '_', 'p', 'o', 'r', 't', '_', +'r', 'a', 'n', 'g', 'e', '\030', '\013', ' ', '\001', '(', '\013', '2', '\031', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', +'v', '3', '.', 'I', 'n', 't', '3', '2', 'R', 'a', 'n', 'g', 'e', 'H', '\000', 'R', '\024', 'd', 'e', 's', 't', 'i', 'n', 'a', 't', +'i', 'o', 'n', 'P', 'o', 'r', 't', 'R', 'a', 'n', 'g', 'e', '\022', 'D', '\n', '\010', 'm', 'e', 't', 'a', 'd', 'a', 't', 'a', '\030', +'\007', ' ', '\001', '(', '\013', '2', '&', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', +'r', '.', 'v', '3', '.', 'M', 'e', 't', 'a', 'd', 'a', 't', 'a', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', '\010', 'm', +'e', 't', 'a', 'd', 'a', 't', 'a', '\022', '=', '\n', '\010', 'n', 'o', 't', '_', 'r', 'u', 'l', 'e', '\030', '\010', ' ', '\001', '(', '\013', +'2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', +'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', 'H', '\000', 'R', '\007', 'n', 'o', 't', 'R', 'u', 'l', 'e', '\022', 'Z', '\n', '\025', 'r', +'e', 'q', 'u', 'e', 's', 't', 'e', 'd', '_', 's', 'e', 'r', 'v', 'e', 'r', '_', 'n', 'a', 'm', 'e', '\030', '\t', ' ', '\001', '(', +'\013', '2', '$', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', 'r', '.', 'v', '3', +'.', 'S', 't', 'r', 'i', 'n', 'g', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', '\023', 'r', 'e', 'q', 'u', 'e', 's', 't', +'e', 'd', 'S', 'e', 'r', 'v', 'e', 'r', 'N', 'a', 'm', 'e', '\022', 'F', '\n', '\007', 'm', 'a', 't', 'c', 'h', 'e', 'r', '\030', '\014', +' ', '\001', '(', '\013', '2', '*', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', +'v', '3', '.', 'T', 'y', 'p', 'e', 'd', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', 'H', '\000', +'R', '\007', 'm', 'a', 't', 'c', 'h', 'e', 'r', '\032', 's', '\n', '\003', 'S', 'e', 't', '\022', '@', '\n', '\005', 'r', 'u', 'l', 'e', 's', +'\030', '\001', ' ', '\003', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', +'c', '.', 'v', '3', '.', 'P', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', 'B', '\010', '\372', 'B', '\005', '\222', '\001', '\002', '\010', '\001', +'R', '\005', 'r', 'u', 'l', 'e', 's', ':', '*', '\232', '\305', '\210', '\036', '%', '\n', '#', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', +'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '2', '.', 'P', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', '.', 'S', 'e', +'t', ':', '&', '\232', '\305', '\210', '\036', '!', '\n', '\037', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', +'a', 'c', '.', 'v', '2', '.', 'P', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', 'B', '\013', '\n', '\004', 'r', 'u', 'l', 'e', '\022', +'\003', '\370', 'B', '\001', '\"', '\353', '\010', '\n', '\t', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', '\022', '>', '\n', '\007', 'a', 'n', 'd', +'_', 'i', 'd', 's', '\030', '\001', ' ', '\001', '(', '\013', '2', '#', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', +'.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', '.', 'S', 'e', 't', 'H', '\000', 'R', +'\006', 'a', 'n', 'd', 'I', 'd', 's', '\022', '<', '\n', '\006', 'o', 'r', '_', 'i', 'd', 's', '\030', '\002', ' ', '\001', '(', '\013', '2', '#', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'r', 'i', -'n', 'c', 'i', 'p', 'a', 'l', '.', 'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'a', 't', 'e', 'd', 'H', '\000', 'R', '\r', 'a', -'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'a', 't', 'e', 'd', '\022', 'K', '\n', '\t', 's', 'o', 'u', 'r', 'c', 'e', '_', 'i', 'p', -'\030', '\005', ' ', '\001', '(', '\013', '2', '\037', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', -'e', '.', 'v', '3', '.', 'C', 'i', 'd', 'r', 'R', 'a', 'n', 'g', 'e', 'B', '\013', '\030', '\001', '\222', '\307', '\206', '\330', '\004', '\003', '3', -'.', '0', 'H', '\000', 'R', '\010', 's', 'o', 'u', 'r', 'c', 'e', 'I', 'p', '\022', 'K', '\n', '\020', 'd', 'i', 'r', 'e', 'c', 't', '_', -'r', 'e', 'm', 'o', 't', 'e', '_', 'i', 'p', '\030', '\n', ' ', '\001', '(', '\013', '2', '\037', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', -'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'C', 'i', 'd', 'r', 'R', 'a', 'n', 'g', 'e', 'H', '\000', -'R', '\016', 'd', 'i', 'r', 'e', 'c', 't', 'R', 'e', 'm', 'o', 't', 'e', 'I', 'p', '\022', '>', '\n', '\t', 'r', 'e', 'm', 'o', 't', -'e', '_', 'i', 'p', '\030', '\013', ' ', '\001', '(', '\013', '2', '\037', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', -'.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'C', 'i', 'd', 'r', 'R', 'a', 'n', 'g', 'e', 'H', '\000', 'R', '\010', 'r', 'e', 'm', -'o', 't', 'e', 'I', 'p', '\022', '>', '\n', '\006', 'h', 'e', 'a', 'd', 'e', 'r', '\030', '\006', ' ', '\001', '(', '\013', '2', '$', '.', 'e', -'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'o', 'u', 't', 'e', '.', 'v', '3', '.', 'H', 'e', 'a', 'd', -'e', 'r', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', '\006', 'h', 'e', 'a', 'd', 'e', 'r', '\022', '?', '\n', '\010', 'u', 'r', -'l', '_', 'p', 'a', 't', 'h', '\030', '\t', ' ', '\001', '(', '\013', '2', '\"', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', -'.', 'm', 'a', 't', 'c', 'h', 'e', 'r', '.', 'v', '3', '.', 'P', 'a', 't', 'h', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', -'R', '\007', 'u', 'r', 'l', 'P', 'a', 't', 'h', '\022', 'D', '\n', '\010', 'm', 'e', 't', 'a', 'd', 'a', 't', 'a', '\030', '\007', ' ', '\001', -'(', '\013', '2', '&', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', 'r', '.', 'v', -'3', '.', 'M', 'e', 't', 'a', 'd', 'a', 't', 'a', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', '\010', 'm', 'e', 't', 'a', -'d', 'a', 't', 'a', '\022', 'N', '\n', '\014', 'f', 'i', 'l', 't', 'e', 'r', '_', 's', 't', 'a', 't', 'e', '\030', '\014', ' ', '\001', '(', -'\013', '2', ')', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', 'r', '.', 'v', '3', -'.', 'F', 'i', 'l', 't', 'e', 'r', 'S', 't', 'a', 't', 'e', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', '\013', 'f', 'i', -'l', 't', 'e', 'r', 'S', 't', 'a', 't', 'e', '\022', '8', '\n', '\006', 'n', 'o', 't', '_', 'i', 'd', '\030', '\010', ' ', '\001', '(', '\013', -'2', '\037', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', -'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', 'H', '\000', 'R', '\005', 'n', 'o', 't', 'I', 'd', '\032', 'm', '\n', '\003', 'S', 'e', 't', '\022', -';', '\n', '\003', 'i', 'd', 's', '\030', '\001', ' ', '\003', '(', '\013', '2', '\037', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', -'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', 'B', '\010', '\372', 'B', '\005', -'\222', '\001', '\002', '\010', '\001', 'R', '\003', 'i', 'd', 's', ':', ')', '\232', '\305', '\210', '\036', '$', '\n', '\"', 'e', 'n', 'v', 'o', 'y', '.', -'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '2', '.', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', '.', -'S', 'e', 't', '\032', '\227', '\001', '\n', '\r', 'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'a', 't', 'e', 'd', '\022', 'K', '\n', '\016', -'p', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', '_', 'n', 'a', 'm', 'e', '\030', '\002', ' ', '\001', '(', '\013', '2', '$', '.', 'e', 'n', -'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', 'r', '.', 'v', '3', '.', 'S', 't', 'r', 'i', 'n', -'g', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'R', '\r', 'p', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', 'N', 'a', 'm', 'e', ':', '3', -'\232', '\305', '\210', '\036', '.', '\n', ',', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', -'v', '2', '.', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', '.', 'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'a', 't', 'e', -'d', 'J', '\004', '\010', '\001', '\020', '\002', ':', '%', '\232', '\305', '\210', '\036', ' ', '\n', '\036', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', -'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '2', '.', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', 'B', '\021', '\n', '\n', -'i', 'd', 'e', 'n', 't', 'i', 'f', 'i', 'e', 'r', '\022', '\003', '\370', 'B', '\001', '\"', '`', '\n', '\006', 'A', 'c', 't', 'i', 'o', 'n', -'\022', '\033', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'B', '\007', '\372', 'B', '\004', 'r', '\002', '\020', '\001', 'R', '\004', -'n', 'a', 'm', 'e', '\022', '9', '\n', '\006', 'a', 'c', 't', 'i', 'o', 'n', '\030', '\002', ' ', '\001', '(', '\016', '2', '!', '.', 'e', 'n', -'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'R', 'B', 'A', 'C', '.', 'A', -'c', 't', 'i', 'o', 'n', 'R', '\006', 'a', 'c', 't', 'i', 'o', 'n', 'B', '}', '\n', '\"', 'i', 'o', '.', 'e', 'n', 'v', 'o', 'y', -'p', 'r', 'o', 'x', 'y', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', -'3', 'B', '\t', 'R', 'b', 'a', 'c', 'P', 'r', 'o', 't', 'o', 'P', '\001', 'Z', 'B', 'g', 'i', 't', 'h', 'u', 'b', '.', 'c', 'o', -'m', '/', 'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', 'x', 'y', '/', 'g', 'o', '-', 'c', 'o', 'n', 't', 'r', 'o', 'l', '-', 'p', -'l', 'a', 'n', 'e', '/', 'e', 'n', 'v', 'o', 'y', '/', 'c', 'o', 'n', 'f', 'i', 'g', '/', 'r', 'b', 'a', 'c', '/', 'v', '3', -';', 'r', 'b', 'a', 'c', 'v', '3', '\272', '\200', '\310', '\321', '\006', '\002', '\020', '\002', 'b', '\006', 'p', 'r', 'o', 't', 'o', '3', +'n', 'c', 'i', 'p', 'a', 'l', '.', 'S', 'e', 't', 'H', '\000', 'R', '\005', 'o', 'r', 'I', 'd', 's', '\022', '\033', '\n', '\003', 'a', 'n', +'y', '\030', '\003', ' ', '\001', '(', '\010', 'B', '\007', '\372', 'B', '\004', 'j', '\002', '\010', '\001', 'H', '\000', 'R', '\003', 'a', 'n', 'y', '\022', 'U', +'\n', '\r', 'a', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'a', 't', 'e', 'd', '\030', '\004', ' ', '\001', '(', '\013', '2', '-', '.', 'e', +'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'r', 'i', 'n', 'c', +'i', 'p', 'a', 'l', '.', 'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'a', 't', 'e', 'd', 'H', '\000', 'R', '\r', 'a', 'u', 't', +'h', 'e', 'n', 't', 'i', 'c', 'a', 't', 'e', 'd', '\022', 'K', '\n', '\t', 's', 'o', 'u', 'r', 'c', 'e', '_', 'i', 'p', '\030', '\005', +' ', '\001', '(', '\013', '2', '\037', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', +'v', '3', '.', 'C', 'i', 'd', 'r', 'R', 'a', 'n', 'g', 'e', 'B', '\013', '\030', '\001', '\222', '\307', '\206', '\330', '\004', '\003', '3', '.', '0', +'H', '\000', 'R', '\010', 's', 'o', 'u', 'r', 'c', 'e', 'I', 'p', '\022', 'K', '\n', '\020', 'd', 'i', 'r', 'e', 'c', 't', '_', 'r', 'e', +'m', 'o', 't', 'e', '_', 'i', 'p', '\030', '\n', ' ', '\001', '(', '\013', '2', '\037', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', +'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'C', 'i', 'd', 'r', 'R', 'a', 'n', 'g', 'e', 'H', '\000', 'R', '\016', +'d', 'i', 'r', 'e', 'c', 't', 'R', 'e', 'm', 'o', 't', 'e', 'I', 'p', '\022', '>', '\n', '\t', 'r', 'e', 'm', 'o', 't', 'e', '_', +'i', 'p', '\030', '\013', ' ', '\001', '(', '\013', '2', '\037', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', +'o', 'r', 'e', '.', 'v', '3', '.', 'C', 'i', 'd', 'r', 'R', 'a', 'n', 'g', 'e', 'H', '\000', 'R', '\010', 'r', 'e', 'm', 'o', 't', +'e', 'I', 'p', '\022', '>', '\n', '\006', 'h', 'e', 'a', 'd', 'e', 'r', '\030', '\006', ' ', '\001', '(', '\013', '2', '$', '.', 'e', 'n', 'v', +'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'o', 'u', 't', 'e', '.', 'v', '3', '.', 'H', 'e', 'a', 'd', 'e', 'r', +'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', '\006', 'h', 'e', 'a', 'd', 'e', 'r', '\022', '?', '\n', '\010', 'u', 'r', 'l', '_', +'p', 'a', 't', 'h', '\030', '\t', ' ', '\001', '(', '\013', '2', '\"', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', +'a', 't', 'c', 'h', 'e', 'r', '.', 'v', '3', '.', 'P', 'a', 't', 'h', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', '\007', +'u', 'r', 'l', 'P', 'a', 't', 'h', '\022', 'D', '\n', '\010', 'm', 'e', 't', 'a', 'd', 'a', 't', 'a', '\030', '\007', ' ', '\001', '(', '\013', +'2', '&', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', 'r', '.', 'v', '3', '.', +'M', 'e', 't', 'a', 'd', 'a', 't', 'a', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', '\010', 'm', 'e', 't', 'a', 'd', 'a', +'t', 'a', '\022', 'N', '\n', '\014', 'f', 'i', 'l', 't', 'e', 'r', '_', 's', 't', 'a', 't', 'e', '\030', '\014', ' ', '\001', '(', '\013', '2', +')', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', 'r', '.', 'v', '3', '.', 'F', +'i', 'l', 't', 'e', 'r', 'S', 't', 'a', 't', 'e', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'H', '\000', 'R', '\013', 'f', 'i', 'l', 't', +'e', 'r', 'S', 't', 'a', 't', 'e', '\022', '8', '\n', '\006', 'n', 'o', 't', '_', 'i', 'd', '\030', '\010', ' ', '\001', '(', '\013', '2', '\037', +'.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'r', 'i', +'n', 'c', 'i', 'p', 'a', 'l', 'H', '\000', 'R', '\005', 'n', 'o', 't', 'I', 'd', '\032', 'm', '\n', '\003', 'S', 'e', 't', '\022', ';', '\n', +'\003', 'i', 'd', 's', '\030', '\001', ' ', '\003', '(', '\013', '2', '\037', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', +'.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', 'B', '\010', '\372', 'B', '\005', '\222', '\001', +'\002', '\010', '\001', 'R', '\003', 'i', 'd', 's', ':', ')', '\232', '\305', '\210', '\036', '$', '\n', '\"', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', +'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '2', '.', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', '.', 'S', 'e', +'t', '\032', '\227', '\001', '\n', '\r', 'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'a', 't', 'e', 'd', '\022', 'K', '\n', '\016', 'p', 'r', +'i', 'n', 'c', 'i', 'p', 'a', 'l', '_', 'n', 'a', 'm', 'e', '\030', '\002', ' ', '\001', '(', '\013', '2', '$', '.', 'e', 'n', 'v', 'o', +'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', 'r', '.', 'v', '3', '.', 'S', 't', 'r', 'i', 'n', 'g', 'M', +'a', 't', 'c', 'h', 'e', 'r', 'R', '\r', 'p', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', 'N', 'a', 'm', 'e', ':', '3', '\232', '\305', +'\210', '\036', '.', '\n', ',', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '2', +'.', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', '.', 'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'a', 't', 'e', 'd', 'J', +'\004', '\010', '\001', '\020', '\002', ':', '%', '\232', '\305', '\210', '\036', ' ', '\n', '\036', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', +'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '2', '.', 'P', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', 'B', '\021', '\n', '\n', 'i', 'd', +'e', 'n', 't', 'i', 'f', 'i', 'e', 'r', '\022', '\003', '\370', 'B', '\001', '\"', '`', '\n', '\006', 'A', 'c', 't', 'i', 'o', 'n', '\022', '\033', +'\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'B', '\007', '\372', 'B', '\004', 'r', '\002', '\020', '\001', 'R', '\004', 'n', 'a', +'m', 'e', '\022', '9', '\n', '\006', 'a', 'c', 't', 'i', 'o', 'n', '\030', '\002', ' ', '\001', '(', '\016', '2', '!', '.', 'e', 'n', 'v', 'o', +'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', '.', 'R', 'B', 'A', 'C', '.', 'A', 'c', 't', +'i', 'o', 'n', 'R', '\006', 'a', 'c', 't', 'i', 'o', 'n', 'B', '}', '\n', '\"', 'i', 'o', '.', 'e', 'n', 'v', 'o', 'y', 'p', 'r', +'o', 'x', 'y', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'b', 'a', 'c', '.', 'v', '3', 'B', +'\t', 'R', 'b', 'a', 'c', 'P', 'r', 'o', 't', 'o', 'P', '\001', 'Z', 'B', 'g', 'i', 't', 'h', 'u', 'b', '.', 'c', 'o', 'm', '/', +'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', 'x', 'y', '/', 'g', 'o', '-', 'c', 'o', 'n', 't', 'r', 'o', 'l', '-', 'p', 'l', 'a', +'n', 'e', '/', 'e', 'n', 'v', 'o', 'y', '/', 'c', 'o', 'n', 'f', 'i', 'g', '/', 'r', 'b', 'a', 'c', '/', 'v', '3', ';', 'r', +'b', 'a', 'c', 'v', '3', '\272', '\200', '\310', '\321', '\006', '\002', '\020', '\002', 'b', '\006', 'p', 'r', 'o', 't', 'o', '3', }; static _upb_DefPool_Init *deps[16] = { @@ -213,5 +219,5 @@ _upb_DefPool_Init envoy_config_rbac_v3_rbac_proto_upbdefinit = { deps, &envoy_config_rbac_v3_rbac_proto_upb_file_layout, "envoy/config/rbac/v3/rbac.proto", - UPB_STRINGVIEW_INIT(descriptor, 4073) + UPB_STRINGVIEW_INIT(descriptor, 4221) }; diff --git a/src/core/ext/upbdefs-generated/envoy/config/rbac/v3/rbac.upbdefs.h b/src/core/ext/upbdefs-generated/envoy/config/rbac/v3/rbac.upbdefs.h index e49d8175f112d..8f0cca914e23d 100644 --- a/src/core/ext/upbdefs-generated/envoy/config/rbac/v3/rbac.upbdefs.h +++ b/src/core/ext/upbdefs-generated/envoy/config/rbac/v3/rbac.upbdefs.h @@ -31,6 +31,11 @@ UPB_INLINE const upb_MessageDef *envoy_config_rbac_v3_RBAC_AuditLoggingOptions_g return upb_DefPool_FindMessageByName(s, "envoy.config.rbac.v3.RBAC.AuditLoggingOptions"); } +UPB_INLINE const upb_MessageDef *envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_getmsgdef(upb_DefPool *s) { + _upb_DefPool_LoadDefInit(s, &envoy_config_rbac_v3_rbac_proto_upbdefinit); + return upb_DefPool_FindMessageByName(s, "envoy.config.rbac.v3.RBAC.AuditLoggingOptions.AuditLoggerConfig"); +} + UPB_INLINE const upb_MessageDef *envoy_config_rbac_v3_RBAC_PoliciesEntry_getmsgdef(upb_DefPool *s) { _upb_DefPool_LoadDefInit(s, &envoy_config_rbac_v3_rbac_proto_upbdefinit); return upb_DefPool_FindMessageByName(s, "envoy.config.rbac.v3.RBAC.PoliciesEntry"); diff --git a/src/core/ext/upbdefs-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upbdefs.c b/src/core/ext/upbdefs-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upbdefs.c index 01f01caf9e8a3..5164142f1c282 100644 --- a/src/core/ext/upbdefs-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upbdefs.c +++ b/src/core/ext/upbdefs-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upbdefs.c @@ -31,7 +31,7 @@ extern _upb_DefPool_Init udpa_annotations_security_proto_upbdefinit; extern _upb_DefPool_Init udpa_annotations_status_proto_upbdefinit; extern _upb_DefPool_Init udpa_annotations_versioning_proto_upbdefinit; extern _upb_DefPool_Init validate_validate_proto_upbdefinit; -static const char descriptor[12759] = {'\n', 'Y', 'e', 'n', 'v', 'o', 'y', '/', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '/', 'f', 'i', 'l', 't', 'e', 'r', +static const char descriptor[12828] = {'\n', 'Y', 'e', 'n', 'v', 'o', 'y', '/', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '/', 'f', 'i', 'l', 't', 'e', 'r', 's', '/', 'n', 'e', 't', 'w', 'o', 'r', 'k', '/', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '/', 'v', '3', '/', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'p', 'r', 'o', 't', 'o', '\022', ';', 'e', 'n', 'v', 'o', 'y', '.', 'e', @@ -67,7 +67,7 @@ static const char descriptor[12759] = {'\n', 'Y', 'e', 'n', 'v', 'o', 'y', '/', '\032', '\035', 'u', 'd', 'p', 'a', '/', 'a', 'n', 'n', 'o', 't', 'a', 't', 'i', 'o', 'n', 's', '/', 's', 't', 'a', 't', 'u', 's', '.', 'p', 'r', 'o', 't', 'o', '\032', '!', 'u', 'd', 'p', 'a', '/', 'a', 'n', 'n', 'o', 't', 'a', 't', 'i', 'o', 'n', 's', '/', 'v', 'e', 'r', 's', 'i', 'o', 'n', 'i', 'n', 'g', '.', 'p', 'r', 'o', 't', 'o', '\032', '\027', 'v', 'a', 'l', 'i', 'd', 'a', 't', -'e', '/', 'v', 'a', 'l', 'i', 'd', 'a', 't', 'e', '.', 'p', 'r', 'o', 't', 'o', '\"', '\276', '<', '\n', '\025', 'H', 't', 't', 'p', +'e', '/', 'v', 'a', 'l', 'i', 'd', 'a', 't', 'e', '.', 'p', 'r', 'o', 't', 'o', '\"', '\203', '=', '\n', '\025', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '\022', '\205', '\001', '\n', '\n', 'c', 'o', 'd', 'e', 'c', '_', 't', 'y', 'p', 'e', '\030', '\001', ' ', '\001', '(', '\016', '2', '\\', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', @@ -156,392 +156,395 @@ static const char descriptor[12759] = {'\n', 'Y', 'e', 'n', 'v', 'o', 'y', '/', 'n', 't', 'e', 'r', 'v', 'a', 'l', '\030', '6', ' ', '\001', '(', '\013', '2', '\031', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'D', 'u', 'r', 'a', 't', 'i', 'o', 'n', 'B', '\014', '\372', 'B', '\t', '\252', '\001', '\006', '2', '\004', '\020', '\300', '\204', '=', 'R', '\026', 'a', 'c', 'c', 'e', 's', 's', 'L', 'o', 'g', 'F', 'l', 'u', 's', 'h', 'I', 'n', 't', 'e', 'r', -'v', 'a', 'l', '\022', 'Q', '\n', '\022', 'u', 's', 'e', '_', 'r', 'e', 'm', 'o', 't', 'e', '_', 'a', 'd', 'd', 'r', 'e', 's', 's', -'\030', '\016', ' ', '\001', '(', '\013', '2', '\032', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', -'B', 'o', 'o', 'l', 'V', 'a', 'l', 'u', 'e', 'B', '\007', '\212', '\223', '\267', '*', '\002', '\010', '\001', 'R', '\020', 'u', 's', 'e', 'R', 'e', -'m', 'o', 't', 'e', 'A', 'd', 'd', 'r', 'e', 's', 's', '\022', '/', '\n', '\024', 'x', 'f', 'f', '_', 'n', 'u', 'm', '_', 't', 'r', -'u', 's', 't', 'e', 'd', '_', 'h', 'o', 'p', 's', '\030', '\023', ' ', '\001', '(', '\r', 'R', '\021', 'x', 'f', 'f', 'N', 'u', 'm', 'T', -'r', 'u', 's', 't', 'e', 'd', 'H', 'o', 'p', 's', '\022', 's', '\n', ' ', 'o', 'r', 'i', 'g', 'i', 'n', 'a', 'l', '_', 'i', 'p', -'_', 'd', 'e', 't', 'e', 'c', 't', 'i', 'o', 'n', '_', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '\030', '.', ' ', '\003', -'(', '\013', '2', '*', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', -'.', 'T', 'y', 'p', 'e', 'd', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\035', 'o', 'r', -'i', 'g', 'i', 'n', 'a', 'l', 'I', 'p', 'D', 'e', 't', 'e', 'c', 't', 'i', 'o', 'n', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', -'n', 's', '\022', 's', '\n', ' ', 'e', 'a', 'r', 'l', 'y', '_', 'h', 'e', 'a', 'd', 'e', 'r', '_', 'm', 'u', 't', 'a', 't', 'i', -'o', 'n', '_', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '\030', '4', ' ', '\003', '(', '\013', '2', '*', '.', 'e', 'n', 'v', -'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'T', 'y', 'p', 'e', 'd', 'E', 'x', -'t', 'e', 'n', 's', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\035', 'e', 'a', 'r', 'l', 'y', 'H', 'e', 'a', 'd', 'e', -'r', 'M', 'u', 't', 'a', 't', 'i', 'o', 'n', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '\022', '\240', '\001', '\n', '\027', 'i', -'n', 't', 'e', 'r', 'n', 'a', 'l', '_', 'a', 'd', 'd', 'r', 'e', 's', 's', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '\031', ' ', -'\001', '(', '\013', '2', 'h', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', -'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', -'t', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', -'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'I', 'n', 't', 'e', 'r', 'n', 'a', 'l', 'A', 'd', 'd', 'r', -'e', 's', 's', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\025', 'i', 'n', 't', 'e', 'r', 'n', 'a', 'l', 'A', 'd', 'd', 'r', 'e', 's', -'s', 'C', 'o', 'n', 'f', 'i', 'g', '\022', '&', '\n', '\017', 's', 'k', 'i', 'p', '_', 'x', 'f', 'f', '_', 'a', 'p', 'p', 'e', 'n', -'d', '\030', '\025', ' ', '\001', '(', '\010', 'R', '\r', 's', 'k', 'i', 'p', 'X', 'f', 'f', 'A', 'p', 'p', 'e', 'n', 'd', '\022', '\035', '\n', -'\003', 'v', 'i', 'a', '\030', '\026', ' ', '\001', '(', '\t', 'B', '\013', '\372', 'B', '\010', 'r', '\006', '\300', '\001', '\002', '\310', '\001', '\000', 'R', '\003', -'v', 'i', 'a', '\022', 'J', '\n', '\023', 'g', 'e', 'n', 'e', 'r', 'a', 't', 'e', '_', 'r', 'e', 'q', 'u', 'e', 's', 't', '_', 'i', -'d', '\030', '\017', ' ', '\001', '(', '\013', '2', '\032', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', -'.', 'B', 'o', 'o', 'l', 'V', 'a', 'l', 'u', 'e', 'R', '\021', 'g', 'e', 'n', 'e', 'r', 'a', 't', 'e', 'R', 'e', 'q', 'u', 'e', -'s', 't', 'I', 'd', '\022', '?', '\n', '\034', 'p', 'r', 'e', 's', 'e', 'r', 'v', 'e', '_', 'e', 'x', 't', 'e', 'r', 'n', 'a', 'l', -'_', 'r', 'e', 'q', 'u', 'e', 's', 't', '_', 'i', 'd', '\030', ' ', ' ', '\001', '(', '\010', 'R', '\031', 'p', 'r', 'e', 's', 'e', 'r', -'v', 'e', 'E', 'x', 't', 'e', 'r', 'n', 'a', 'l', 'R', 'e', 'q', 'u', 'e', 's', 't', 'I', 'd', '\022', 'G', '\n', '!', 'a', 'l', -'w', 'a', 'y', 's', '_', 's', 'e', 't', '_', 'r', 'e', 'q', 'u', 'e', 's', 't', '_', 'i', 'd', '_', 'i', 'n', '_', 'r', 'e', -'s', 'p', 'o', 'n', 's', 'e', '\030', '%', ' ', '\001', '(', '\010', 'R', '\034', 'a', 'l', 'w', 'a', 'y', 's', 'S', 'e', 't', 'R', 'e', -'q', 'u', 'e', 's', 't', 'I', 'd', 'I', 'n', 'R', 'e', 's', 'p', 'o', 'n', 's', 'e', '\022', '\264', '\001', '\n', '\033', 'f', 'o', 'r', -'w', 'a', 'r', 'd', '_', 'c', 'l', 'i', 'e', 'n', 't', '_', 'c', 'e', 'r', 't', '_', 'd', 'e', 't', 'a', 'i', 'l', 's', '\030', -'\020', ' ', '\001', '(', '\016', '2', 'k', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', -'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', -'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', -'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'F', 'o', 'r', 'w', 'a', 'r', 'd', 'C', 'l', 'i', -'e', 'n', 't', 'C', 'e', 'r', 't', 'D', 'e', 't', 'a', 'i', 'l', 's', 'B', '\010', '\372', 'B', '\005', '\202', '\001', '\002', '\020', '\001', 'R', -'\030', 'f', 'o', 'r', 'w', 'a', 'r', 'd', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', 't', 'D', 'e', 't', 'a', 'i', 'l', 's', -'\022', '\264', '\001', '\n', '\037', 's', 'e', 't', '_', 'c', 'u', 'r', 'r', 'e', 'n', 't', '_', 'c', 'l', 'i', 'e', 'n', 't', '_', 'c', -'e', 'r', 't', '_', 'd', 'e', 't', 'a', 'i', 'l', 's', '\030', '\021', ' ', '\001', '(', '\013', '2', 'n', '.', 'e', 'n', 'v', 'o', 'y', -'.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', -'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', -'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', -'r', '.', 'S', 'e', 't', 'C', 'u', 'r', 'r', 'e', 'n', 't', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', 't', 'D', 'e', 't', -'a', 'i', 'l', 's', 'R', '\033', 's', 'e', 't', 'C', 'u', 'r', 'r', 'e', 'n', 't', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', -'t', 'D', 'e', 't', 'a', 'i', 'l', 's', '\022', ',', '\n', '\022', 'p', 'r', 'o', 'x', 'y', '_', '1', '0', '0', '_', 'c', 'o', 'n', -'t', 'i', 'n', 'u', 'e', '\030', '\022', ' ', '\001', '(', '\010', 'R', '\020', 'p', 'r', 'o', 'x', 'y', '1', '0', '0', 'C', 'o', 'n', 't', -'i', 'n', 'u', 'e', '\022', 'e', '\n', '1', 'r', 'e', 'p', 'r', 'e', 's', 'e', 'n', 't', '_', 'i', 'p', 'v', '4', '_', 'r', 'e', -'m', 'o', 't', 'e', '_', 'a', 'd', 'd', 'r', 'e', 's', 's', '_', 'a', 's', '_', 'i', 'p', 'v', '4', '_', 'm', 'a', 'p', 'p', -'e', 'd', '_', 'i', 'p', 'v', '6', '\030', '\024', ' ', '\001', '(', '\010', 'R', '*', 'r', 'e', 'p', 'r', 'e', 's', 'e', 'n', 't', 'I', -'p', 'v', '4', 'R', 'e', 'm', 'o', 't', 'e', 'A', 'd', 'd', 'r', 'e', 's', 's', 'A', 's', 'I', 'p', 'v', '4', 'M', 'a', 'p', -'p', 'e', 'd', 'I', 'p', 'v', '6', '\022', '\211', '\001', '\n', '\017', 'u', 'p', 'g', 'r', 'a', 'd', 'e', '_', 'c', 'o', 'n', 'f', 'i', -'g', 's', '\030', '\027', ' ', '\003', '(', '\013', '2', '`', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', -'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', -'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', -'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'U', 'p', 'g', 'r', 'a', 'd', 'e', -'C', 'o', 'n', 'f', 'i', 'g', 'R', '\016', 'u', 'p', 'g', 'r', 'a', 'd', 'e', 'C', 'o', 'n', 'f', 'i', 'g', 's', '\022', 'A', '\n', -'\016', 'n', 'o', 'r', 'm', 'a', 'l', 'i', 'z', 'e', '_', 'p', 'a', 't', 'h', '\030', '\036', ' ', '\001', '(', '\013', '2', '\032', '.', 'g', -'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', 'V', 'a', 'l', 'u', 'e', 'R', -'\r', 'n', 'o', 'r', 'm', 'a', 'l', 'i', 'z', 'e', 'P', 'a', 't', 'h', '\022', '#', '\n', '\r', 'm', 'e', 'r', 'g', 'e', '_', 's', -'l', 'a', 's', 'h', 'e', 's', '\030', '!', ' ', '\001', '(', '\010', 'R', '\014', 'm', 'e', 'r', 'g', 'e', 'S', 'l', 'a', 's', 'h', 'e', -'s', '\022', '\267', '\001', '\n', ' ', 'p', 'a', 't', 'h', '_', 'w', 'i', 't', 'h', '_', 'e', 's', 'c', 'a', 'p', 'e', 'd', '_', 's', -'l', 'a', 's', 'h', 'e', 's', '_', 'a', 'c', 't', 'i', 'o', 'n', '\030', '-', ' ', '\001', '(', '\016', '2', 'o', '.', 'e', 'n', 'v', -'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', -'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', -'g', 'e', 'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', -'g', 'e', 'r', '.', 'P', 'a', 't', 'h', 'W', 'i', 't', 'h', 'E', 's', 'c', 'a', 'p', 'e', 'd', 'S', 'l', 'a', 's', 'h', 'e', -'s', 'A', 'c', 't', 'i', 'o', 'n', 'R', '\034', 'p', 'a', 't', 'h', 'W', 'i', 't', 'h', 'E', 's', 'c', 'a', 'p', 'e', 'd', 'S', -'l', 'a', 's', 'h', 'e', 's', 'A', 'c', 't', 'i', 'o', 'n', '\022', '\201', '\001', '\n', '\024', 'r', 'e', 'q', 'u', 'e', 's', 't', '_', -'i', 'd', '_', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '\030', '$', ' ', '\001', '(', '\013', '2', 'O', '.', 'e', 'n', 'v', 'o', -'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', -'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', -'e', 'r', '.', 'v', '3', '.', 'R', 'e', 'q', 'u', 'e', 's', 't', 'I', 'D', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'R', -'\022', 'r', 'e', 'q', 'u', 'e', 's', 't', 'I', 'd', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '\022', '{', '\n', '\022', 'l', 'o', -'c', 'a', 'l', '_', 'r', 'e', 'p', 'l', 'y', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '&', ' ', '\001', '(', '\013', '2', 'M', '.', +'v', 'a', 'l', '\022', 'C', '\n', '\037', 'f', 'l', 'u', 's', 'h', '_', 'a', 'c', 'c', 'e', 's', 's', '_', 'l', 'o', 'g', '_', 'o', +'n', '_', 'n', 'e', 'w', '_', 'r', 'e', 'q', 'u', 'e', 's', 't', '\030', '7', ' ', '\001', '(', '\010', 'R', '\032', 'f', 'l', 'u', 's', +'h', 'A', 'c', 'c', 'e', 's', 's', 'L', 'o', 'g', 'O', 'n', 'N', 'e', 'w', 'R', 'e', 'q', 'u', 'e', 's', 't', '\022', 'Q', '\n', +'\022', 'u', 's', 'e', '_', 'r', 'e', 'm', 'o', 't', 'e', '_', 'a', 'd', 'd', 'r', 'e', 's', 's', '\030', '\016', ' ', '\001', '(', '\013', +'2', '\032', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', 'V', 'a', +'l', 'u', 'e', 'B', '\007', '\212', '\223', '\267', '*', '\002', '\010', '\001', 'R', '\020', 'u', 's', 'e', 'R', 'e', 'm', 'o', 't', 'e', 'A', 'd', +'d', 'r', 'e', 's', 's', '\022', '/', '\n', '\024', 'x', 'f', 'f', '_', 'n', 'u', 'm', '_', 't', 'r', 'u', 's', 't', 'e', 'd', '_', +'h', 'o', 'p', 's', '\030', '\023', ' ', '\001', '(', '\r', 'R', '\021', 'x', 'f', 'f', 'N', 'u', 'm', 'T', 'r', 'u', 's', 't', 'e', 'd', +'H', 'o', 'p', 's', '\022', 's', '\n', ' ', 'o', 'r', 'i', 'g', 'i', 'n', 'a', 'l', '_', 'i', 'p', '_', 'd', 'e', 't', 'e', 'c', +'t', 'i', 'o', 'n', '_', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '\030', '.', ' ', '\003', '(', '\013', '2', '*', '.', 'e', +'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'T', 'y', 'p', 'e', 'd', +'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\035', 'o', 'r', 'i', 'g', 'i', 'n', 'a', 'l', +'I', 'p', 'D', 'e', 't', 'e', 'c', 't', 'i', 'o', 'n', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '\022', 's', '\n', ' ', +'e', 'a', 'r', 'l', 'y', '_', 'h', 'e', 'a', 'd', 'e', 'r', '_', 'm', 'u', 't', 'a', 't', 'i', 'o', 'n', '_', 'e', 'x', 't', +'e', 'n', 's', 'i', 'o', 'n', 's', '\030', '4', ' ', '\003', '(', '\013', '2', '*', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', +'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'T', 'y', 'p', 'e', 'd', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', +'n', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\035', 'e', 'a', 'r', 'l', 'y', 'H', 'e', 'a', 'd', 'e', 'r', 'M', 'u', 't', 'a', 't', +'i', 'o', 'n', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '\022', '\240', '\001', '\n', '\027', 'i', 'n', 't', 'e', 'r', 'n', 'a', +'l', '_', 'a', 'd', 'd', 'r', 'e', 's', 's', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '\031', ' ', '\001', '(', '\013', '2', 'h', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', -'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'L', 'o', 'c', 'a', 'l', 'R', 'e', 'p', 'l', 'y', 'C', 'o', 'n', 'f', 'i', -'g', 'R', '\020', 'l', 'o', 'c', 'a', 'l', 'R', 'e', 'p', 'l', 'y', 'C', 'o', 'n', 'f', 'i', 'g', '\022', 'P', '\n', '\030', 's', 't', -'r', 'i', 'p', '_', 'm', 'a', 't', 'c', 'h', 'i', 'n', 'g', '_', 'h', 'o', 's', 't', '_', 'p', 'o', 'r', 't', '\030', '\'', ' ', -'\001', '(', '\010', 'B', '\027', '\362', '\230', '\376', '\217', '\005', '\021', '\022', '\017', 's', 't', 'r', 'i', 'p', '_', 'p', 'o', 'r', 't', '_', 'm', -'o', 'd', 'e', 'R', '\025', 's', 't', 'r', 'i', 'p', 'M', 'a', 't', 'c', 'h', 'i', 'n', 'g', 'H', 'o', 's', 't', 'P', 'o', 'r', -'t', '\022', '/', '\n', '\023', 's', 't', 'r', 'i', 'p', '_', 'a', 'n', 'y', '_', 'h', 'o', 's', 't', '_', 'p', 'o', 'r', 't', '\030', -'*', ' ', '\001', '(', '\010', 'H', '\001', 'R', '\020', 's', 't', 'r', 'i', 'p', 'A', 'n', 'y', 'H', 'o', 's', 't', 'P', 'o', 'r', 't', -'\022', 'i', '\n', '$', 's', 't', 'r', 'e', 'a', 'm', '_', 'e', 'r', 'r', 'o', 'r', '_', 'o', 'n', '_', 'i', 'n', 'v', 'a', 'l', -'i', 'd', '_', 'h', 't', 't', 'p', '_', 'm', 'e', 's', 's', 'a', 'g', 'e', '\030', '(', ' ', '\001', '(', '\013', '2', '\032', '.', 'g', -'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', 'V', 'a', 'l', 'u', 'e', 'R', -'\037', 's', 't', 'r', 'e', 'a', 'm', 'E', 'r', 'r', 'o', 'r', 'O', 'n', 'I', 'n', 'v', 'a', 'l', 'i', 'd', 'H', 't', 't', 'p', -'M', 'e', 's', 's', 'a', 'g', 'e', '\022', '\251', '\001', '\n', '\032', 'p', 'a', 't', 'h', '_', 'n', 'o', 'r', 'm', 'a', 'l', 'i', 'z', -'a', 't', 'i', 'o', 'n', '_', 'o', 'p', 't', 'i', 'o', 'n', 's', '\030', '+', ' ', '\001', '(', '\013', '2', 'k', '.', 'e', 'n', 'v', -'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', -'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', -'g', 'e', 'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', -'g', 'e', 'r', '.', 'P', 'a', 't', 'h', 'N', 'o', 'r', 'm', 'a', 'l', 'i', 'z', 'a', 't', 'i', 'o', 'n', 'O', 'p', 't', 'i', -'o', 'n', 's', 'R', '\030', 'p', 'a', 't', 'h', 'N', 'o', 'r', 'm', 'a', 'l', 'i', 'z', 'a', 't', 'i', 'o', 'n', 'O', 'p', 't', -'i', 'o', 'n', 's', '\022', '5', '\n', '\027', 's', 't', 'r', 'i', 'p', '_', 't', 'r', 'a', 'i', 'l', 'i', 'n', 'g', '_', 'h', 'o', -'s', 't', '_', 'd', 'o', 't', '\030', '/', ' ', '\001', '(', '\010', 'R', '\024', 's', 't', 'r', 'i', 'p', 'T', 'r', 'a', 'i', 'l', 'i', -'n', 'g', 'H', 'o', 's', 't', 'D', 'o', 't', '\022', '\224', '\001', '\n', '\023', 'p', 'r', 'o', 'x', 'y', '_', 's', 't', 'a', 't', 'u', -'s', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '1', ' ', '\001', '(', '\013', '2', 'd', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', -'t', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', -'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', -'3', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'P', -'r', 'o', 'x', 'y', 'S', 't', 'a', 't', 'u', 's', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\021', 'p', 'r', 'o', 'x', 'y', 'S', 't', -'a', 't', 'u', 's', 'C', 'o', 'n', 'f', 'i', 'g', '\022', 'o', '\n', '\036', 't', 'y', 'p', 'e', 'd', '_', 'h', 'e', 'a', 'd', 'e', -'r', '_', 'v', 'a', 'l', 'i', 'd', 'a', 't', 'i', 'o', 'n', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '2', ' ', '\001', '(', '\013', -'2', '*', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'T', -'y', 'p', 'e', 'd', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\033', 't', 'y', 'p', 'e', -'d', 'H', 'e', 'a', 'd', 'e', 'r', 'V', 'a', 'l', 'i', 'd', 'a', 't', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', '\022', '5', -'\n', '\027', 'a', 'p', 'p', 'e', 'n', 'd', '_', 'x', '_', 'f', 'o', 'r', 'w', 'a', 'r', 'd', 'e', 'd', '_', 'p', 'o', 'r', 't', -'\030', '3', ' ', '\001', '(', '\010', 'R', '\024', 'a', 'p', 'p', 'e', 'n', 'd', 'X', 'F', 'o', 'r', 'w', 'a', 'r', 'd', 'e', 'd', 'P', -'o', 'r', 't', '\022', 'h', '\n', '#', 'a', 'd', 'd', '_', 'p', 'r', 'o', 'x', 'y', '_', 'p', 'r', 'o', 't', 'o', 'c', 'o', 'l', -'_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 's', 't', 'a', 't', 'e', '\030', '5', ' ', '\001', '(', '\013', '2', '\032', -'.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', 'V', 'a', 'l', 'u', -'e', 'R', '\037', 'a', 'd', 'd', 'P', 'r', 'o', 'x', 'y', 'P', 'r', 'o', 't', 'o', 'c', 'o', 'l', 'C', 'o', 'n', 'n', 'e', 'c', -'t', 'i', 'o', 'n', 'S', 't', 'a', 't', 'e', '\032', '\366', '\004', '\n', '\007', 'T', 'r', 'a', 'c', 'i', 'n', 'g', '\022', '?', '\n', '\017', -'c', 'l', 'i', 'e', 'n', 't', '_', 's', 'a', 'm', 'p', 'l', 'i', 'n', 'g', '\030', '\003', ' ', '\001', '(', '\013', '2', '\026', '.', 'e', -'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'v', '3', '.', 'P', 'e', 'r', 'c', 'e', 'n', 't', 'R', '\016', 'c', 'l', 'i', -'e', 'n', 't', 'S', 'a', 'm', 'p', 'l', 'i', 'n', 'g', '\022', '?', '\n', '\017', 'r', 'a', 'n', 'd', 'o', 'm', '_', 's', 'a', 'm', -'p', 'l', 'i', 'n', 'g', '\030', '\004', ' ', '\001', '(', '\013', '2', '\026', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', -'v', '3', '.', 'P', 'e', 'r', 'c', 'e', 'n', 't', 'R', '\016', 'r', 'a', 'n', 'd', 'o', 'm', 'S', 'a', 'm', 'p', 'l', 'i', 'n', -'g', '\022', 'A', '\n', '\020', 'o', 'v', 'e', 'r', 'a', 'l', 'l', '_', 's', 'a', 'm', 'p', 'l', 'i', 'n', 'g', '\030', '\005', ' ', '\001', -'(', '\013', '2', '\026', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'v', '3', '.', 'P', 'e', 'r', 'c', 'e', 'n', -'t', 'R', '\017', 'o', 'v', 'e', 'r', 'a', 'l', 'l', 'S', 'a', 'm', 'p', 'l', 'i', 'n', 'g', '\022', '\030', '\n', '\007', 'v', 'e', 'r', -'b', 'o', 's', 'e', '\030', '\006', ' ', '\001', '(', '\010', 'R', '\007', 'v', 'e', 'r', 'b', 'o', 's', 'e', '\022', 'K', '\n', '\023', 'm', 'a', -'x', '_', 'p', 'a', 't', 'h', '_', 't', 'a', 'g', '_', 'l', 'e', 'n', 'g', 't', 'h', '\030', '\007', ' ', '\001', '(', '\013', '2', '\034', -'.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'I', 'n', 't', '3', '2', 'V', 'a', -'l', 'u', 'e', 'R', '\020', 'm', 'a', 'x', 'P', 'a', 't', 'h', 'T', 'a', 'g', 'L', 'e', 'n', 'g', 't', 'h', '\022', 'A', '\n', '\013', -'c', 'u', 's', 't', 'o', 'm', '_', 't', 'a', 'g', 's', '\030', '\010', ' ', '\003', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', -'.', 't', 'y', 'p', 'e', '.', 't', 'r', 'a', 'c', 'i', 'n', 'g', '.', 'v', '3', '.', 'C', 'u', 's', 't', 'o', 'm', 'T', 'a', -'g', 'R', '\n', 'c', 'u', 's', 't', 'o', 'm', 'T', 'a', 'g', 's', '\022', '?', '\n', '\010', 'p', 'r', 'o', 'v', 'i', 'd', 'e', 'r', -'\030', '\t', ' ', '\001', '(', '\013', '2', '#', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 't', 'r', 'a', -'c', 'e', '.', 'v', '3', '.', 'T', 'r', 'a', 'c', 'i', 'n', 'g', '.', 'H', 't', 't', 'p', 'R', '\010', 'p', 'r', 'o', 'v', 'i', -'d', 'e', 'r', '\"', '(', '\n', '\r', 'O', 'p', 'e', 'r', 'a', 't', 'i', 'o', 'n', 'N', 'a', 'm', 'e', '\022', '\013', '\n', '\007', 'I', -'N', 'G', 'R', 'E', 'S', 'S', '\020', '\000', '\022', '\n', '\n', '\006', 'E', 'G', 'R', 'E', 'S', 'S', '\020', '\001', ':', '[', '\232', '\305', '\210', -'\036', 'V', '\n', 'T', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', -'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', -'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', -'n', 'a', 'g', 'e', 'r', '.', 'T', 'r', 'a', 'c', 'i', 'n', 'g', 'J', '\004', '\010', '\001', '\020', '\002', 'J', '\004', '\010', '\002', '\020', '\003', -'R', '\016', 'o', 'p', 'e', 'r', 'a', 't', 'i', 'o', 'n', '_', 'n', 'a', 'm', 'e', 'R', '\030', 'r', 'e', 'q', 'u', 'e', 's', 't', -'_', 'h', 'e', 'a', 'd', 'e', 'r', 's', '_', 'f', 'o', 'r', '_', 't', 'a', 'g', 's', '\032', '\347', '\001', '\n', '\025', 'I', 'n', 't', -'e', 'r', 'n', 'a', 'l', 'A', 'd', 'd', 'r', 'e', 's', 's', 'C', 'o', 'n', 'f', 'i', 'g', '\022', '!', '\n', '\014', 'u', 'n', 'i', -'x', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '\030', '\001', ' ', '\001', '(', '\010', 'R', '\013', 'u', 'n', 'i', 'x', 'S', 'o', 'c', 'k', -'e', 't', 's', '\022', '@', '\n', '\013', 'c', 'i', 'd', 'r', '_', 'r', 'a', 'n', 'g', 'e', 's', '\030', '\002', ' ', '\003', '(', '\013', '2', -'\037', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'C', 'i', -'d', 'r', 'R', 'a', 'n', 'g', 'e', 'R', '\n', 'c', 'i', 'd', 'r', 'R', 'a', 'n', 'g', 'e', 's', ':', 'i', '\232', '\305', '\210', '\036', -'d', '\n', 'b', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', -'t', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', -'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', -'a', 'g', 'e', 'r', '.', 'I', 'n', 't', 'e', 'r', 'n', 'a', 'l', 'A', 'd', 'd', 'r', 'e', 's', 's', 'C', 'o', 'n', 'f', 'i', -'g', '\032', '\230', '\002', '\n', '\033', 'S', 'e', 't', 'C', 'u', 'r', 'r', 'e', 'n', 't', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', -'t', 'D', 'e', 't', 'a', 'i', 'l', 's', '\022', '4', '\n', '\007', 's', 'u', 'b', 'j', 'e', 'c', 't', '\030', '\001', ' ', '\001', '(', '\013', -'2', '\032', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', 'V', 'a', -'l', 'u', 'e', 'R', '\007', 's', 'u', 'b', 'j', 'e', 'c', 't', '\022', '\022', '\n', '\004', 'c', 'e', 'r', 't', '\030', '\003', ' ', '\001', '(', -'\010', 'R', '\004', 'c', 'e', 'r', 't', '\022', '\024', '\n', '\005', 'c', 'h', 'a', 'i', 'n', '\030', '\006', ' ', '\001', '(', '\010', 'R', '\005', 'c', -'h', 'a', 'i', 'n', '\022', '\020', '\n', '\003', 'd', 'n', 's', '\030', '\004', ' ', '\001', '(', '\010', 'R', '\003', 'd', 'n', 's', '\022', '\020', '\n', -'\003', 'u', 'r', 'i', '\030', '\005', ' ', '\001', '(', '\010', 'R', '\003', 'u', 'r', 'i', ':', 'o', '\232', '\305', '\210', '\036', 'j', '\n', 'h', 'e', -'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', -'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', -'.', 'v', '2', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', -'.', 'S', 'e', 't', 'C', 'u', 'r', 'r', 'e', 'n', 't', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', 't', 'D', 'e', 't', 'a', -'i', 'l', 's', 'J', '\004', '\010', '\002', '\020', '\003', '\032', '\256', '\002', '\n', '\r', 'U', 'p', 'g', 'r', 'a', 'd', 'e', 'C', 'o', 'n', 'f', -'i', 'g', '\022', '!', '\n', '\014', 'u', 'p', 'g', 'r', 'a', 'd', 'e', '_', 't', 'y', 'p', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'R', -'\013', 'u', 'p', 'g', 'r', 'a', 'd', 'e', 'T', 'y', 'p', 'e', '\022', 'a', '\n', '\007', 'f', 'i', 'l', 't', 'e', 'r', 's', '\030', '\002', -' ', '\003', '(', '\013', '2', 'G', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', -'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', -'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', 'F', 'i', 'l', 't', -'e', 'r', 'R', '\007', 'f', 'i', 'l', 't', 'e', 'r', 's', '\022', '4', '\n', '\007', 'e', 'n', 'a', 'b', 'l', 'e', 'd', '\030', '\003', ' ', -'\001', '(', '\013', '2', '\032', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', -'l', 'V', 'a', 'l', 'u', 'e', 'R', '\007', 'e', 'n', 'a', 'b', 'l', 'e', 'd', ':', 'a', '\232', '\305', '\210', '\036', '\\', '\n', 'Z', 'e', -'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', -'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', -'.', 'v', '2', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', -'.', 'U', 'p', 'g', 'r', 'a', 'd', 'e', 'C', 'o', 'n', 'f', 'i', 'g', '\032', '\345', '\001', '\n', '\030', 'P', 'a', 't', 'h', 'N', 'o', -'r', 'm', 'a', 'l', 'i', 'z', 'a', 't', 'i', 'o', 'n', 'O', 'p', 't', 'i', 'o', 'n', 's', '\022', 'c', '\n', '\031', 'f', 'o', 'r', -'w', 'a', 'r', 'd', 'i', 'n', 'g', '_', 't', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', '\030', '\001', ' ', -'\001', '(', '\013', '2', '&', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'h', 't', 't', 'p', '.', 'v', '3', '.', -'P', 'a', 't', 'h', 'T', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', 'R', '\030', 'f', 'o', 'r', 'w', 'a', -'r', 'd', 'i', 'n', 'g', 'T', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', '\022', 'd', '\n', '\032', 'h', 't', -'t', 'p', '_', 'f', 'i', 'l', 't', 'e', 'r', '_', 't', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', '\030', -'\002', ' ', '\001', '(', '\013', '2', '&', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'h', 't', 't', 'p', '.', 'v', -'3', '.', 'P', 'a', 't', 'h', 'T', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', 'R', '\030', 'h', 't', 't', -'p', 'F', 'i', 'l', 't', 'e', 'r', 'T', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', '\032', '\344', '\002', '\n', -'\021', 'P', 'r', 'o', 'x', 'y', 'S', 't', 'a', 't', 'u', 's', 'C', 'o', 'n', 'f', 'i', 'g', '\022', '%', '\n', '\016', 'r', 'e', 'm', -'o', 'v', 'e', '_', 'd', 'e', 't', 'a', 'i', 'l', 's', '\030', '\001', ' ', '\001', '(', '\010', 'R', '\r', 'r', 'e', 'm', 'o', 'v', 'e', -'D', 'e', 't', 'a', 'i', 'l', 's', '\022', 'Q', '\n', '%', 'r', 'e', 'm', 'o', 'v', 'e', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', -'i', 'o', 'n', '_', 't', 'e', 'r', 'm', 'i', 'n', 'a', 't', 'i', 'o', 'n', '_', 'd', 'e', 't', 'a', 'i', 'l', 's', '\030', '\002', -' ', '\001', '(', '\010', 'R', '\"', 'r', 'e', 'm', 'o', 'v', 'e', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'T', 'e', 'r', -'m', 'i', 'n', 'a', 't', 'i', 'o', 'n', 'D', 'e', 't', 'a', 'i', 'l', 's', '\022', '2', '\n', '\025', 'r', 'e', 'm', 'o', 'v', 'e', -'_', 'r', 'e', 's', 'p', 'o', 'n', 's', 'e', '_', 'f', 'l', 'a', 'g', 's', '\030', '\003', ' ', '\001', '(', '\010', 'R', '\023', 'r', 'e', -'m', 'o', 'v', 'e', 'R', 'e', 's', 'p', 'o', 'n', 's', 'e', 'F', 'l', 'a', 'g', 's', '\022', 'A', '\n', '\035', 's', 'e', 't', '_', -'r', 'e', 'c', 'o', 'm', 'm', 'e', 'n', 'd', 'e', 'd', '_', 'r', 'e', 's', 'p', 'o', 'n', 's', 'e', '_', 'c', 'o', 'd', 'e', -'\030', '\004', ' ', '\001', '(', '\010', 'R', '\032', 's', 'e', 't', 'R', 'e', 'c', 'o', 'm', 'm', 'e', 'n', 'd', 'e', 'd', 'R', 'e', 's', -'p', 'o', 'n', 's', 'e', 'C', 'o', 'd', 'e', '\022', ' ', '\n', '\013', 'u', 's', 'e', '_', 'n', 'o', 'd', 'e', '_', 'i', 'd', '\030', -'\005', ' ', '\001', '(', '\010', 'H', '\000', 'R', '\t', 'u', 's', 'e', 'N', 'o', 'd', 'e', 'I', 'd', '\022', '.', '\n', '\022', 'l', 'i', 't', -'e', 'r', 'a', 'l', '_', 'p', 'r', 'o', 'x', 'y', '_', 'n', 'a', 'm', 'e', '\030', '\006', ' ', '\001', '(', '\t', 'H', '\000', 'R', '\020', -'l', 'i', 't', 'e', 'r', 'a', 'l', 'P', 'r', 'o', 'x', 'y', 'N', 'a', 'm', 'e', 'B', '\014', '\n', '\n', 'p', 'r', 'o', 'x', 'y', -'_', 'n', 'a', 'm', 'e', '\"', '6', '\n', '\t', 'C', 'o', 'd', 'e', 'c', 'T', 'y', 'p', 'e', '\022', '\010', '\n', '\004', 'A', 'U', 'T', -'O', '\020', '\000', '\022', '\t', '\n', '\005', 'H', 'T', 'T', 'P', '1', '\020', '\001', '\022', '\t', '\n', '\005', 'H', 'T', 'T', 'P', '2', '\020', '\002', -'\022', '\t', '\n', '\005', 'H', 'T', 'T', 'P', '3', '\020', '\003', '\"', 'S', '\n', '\032', 'S', 'e', 'r', 'v', 'e', 'r', 'H', 'e', 'a', 'd', -'e', 'r', 'T', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', '\022', '\r', '\n', '\t', 'O', 'V', 'E', 'R', 'W', -'R', 'I', 'T', 'E', '\020', '\000', '\022', '\024', '\n', '\020', 'A', 'P', 'P', 'E', 'N', 'D', '_', 'I', 'F', '_', 'A', 'B', 'S', 'E', 'N', -'T', '\020', '\001', '\022', '\020', '\n', '\014', 'P', 'A', 'S', 'S', '_', 'T', 'H', 'R', 'O', 'U', 'G', 'H', '\020', '\002', '\"', 'y', '\n', '\030', -'F', 'o', 'r', 'w', 'a', 'r', 'd', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', 't', 'D', 'e', 't', 'a', 'i', 'l', 's', '\022', -'\014', '\n', '\010', 'S', 'A', 'N', 'I', 'T', 'I', 'Z', 'E', '\020', '\000', '\022', '\020', '\n', '\014', 'F', 'O', 'R', 'W', 'A', 'R', 'D', '_', -'O', 'N', 'L', 'Y', '\020', '\001', '\022', '\022', '\n', '\016', 'A', 'P', 'P', 'E', 'N', 'D', '_', 'F', 'O', 'R', 'W', 'A', 'R', 'D', '\020', -'\002', '\022', '\020', '\n', '\014', 'S', 'A', 'N', 'I', 'T', 'I', 'Z', 'E', '_', 'S', 'E', 'T', '\020', '\003', '\022', '\027', '\n', '\023', 'A', 'L', -'W', 'A', 'Y', 'S', '_', 'F', 'O', 'R', 'W', 'A', 'R', 'D', '_', 'O', 'N', 'L', 'Y', '\020', '\004', '\"', '\240', '\001', '\n', '\034', 'P', -'a', 't', 'h', 'W', 'i', 't', 'h', 'E', 's', 'c', 'a', 'p', 'e', 'd', 'S', 'l', 'a', 's', 'h', 'e', 's', 'A', 'c', 't', 'i', -'o', 'n', '\022', '#', '\n', '\037', 'I', 'M', 'P', 'L', 'E', 'M', 'E', 'N', 'T', 'A', 'T', 'I', 'O', 'N', '_', 'S', 'P', 'E', 'C', -'I', 'F', 'I', 'C', '_', 'D', 'E', 'F', 'A', 'U', 'L', 'T', '\020', '\000', '\022', '\022', '\n', '\016', 'K', 'E', 'E', 'P', '_', 'U', 'N', -'C', 'H', 'A', 'N', 'G', 'E', 'D', '\020', '\001', '\022', '\022', '\n', '\016', 'R', 'E', 'J', 'E', 'C', 'T', '_', 'R', 'E', 'Q', 'U', 'E', -'S', 'T', '\020', '\002', '\022', '\031', '\n', '\025', 'U', 'N', 'E', 'S', 'C', 'A', 'P', 'E', '_', 'A', 'N', 'D', '_', 'R', 'E', 'D', 'I', -'R', 'E', 'C', 'T', '\020', '\003', '\022', '\030', '\n', '\024', 'U', 'N', 'E', 'S', 'C', 'A', 'P', 'E', '_', 'A', 'N', 'D', '_', 'F', 'O', -'R', 'W', 'A', 'R', 'D', '\020', '\004', ':', 'S', '\232', '\305', '\210', '\036', 'N', '\n', 'L', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', -'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', -'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'H', 't', 't', 'p', -'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', 'B', '\026', '\n', '\017', 'r', 'o', 'u', 't', -'e', '_', 's', 'p', 'e', 'c', 'i', 'f', 'i', 'e', 'r', '\022', '\003', '\370', 'B', '\001', 'B', '\021', '\n', '\017', 's', 't', 'r', 'i', 'p', -'_', 'p', 'o', 'r', 't', '_', 'm', 'o', 'd', 'e', 'J', '\004', '\010', '\033', '\020', '\034', 'J', '\004', '\010', '\013', '\020', '\014', 'R', '\014', 'i', -'d', 'l', 'e', '_', 't', 'i', 'm', 'e', 'o', 'u', 't', '\"', '\312', '\001', '\n', '\020', 'L', 'o', 'c', 'a', 'l', 'R', 'e', 'p', 'l', -'y', 'C', 'o', 'n', 'f', 'i', 'g', '\022', 'e', '\n', '\007', 'm', 'a', 'p', 'p', 'e', 'r', 's', '\030', '\001', ' ', '\003', '(', '\013', '2', -'K', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', +'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', +'a', 'n', 'a', 'g', 'e', 'r', '.', 'I', 'n', 't', 'e', 'r', 'n', 'a', 'l', 'A', 'd', 'd', 'r', 'e', 's', 's', 'C', 'o', 'n', +'f', 'i', 'g', 'R', '\025', 'i', 'n', 't', 'e', 'r', 'n', 'a', 'l', 'A', 'd', 'd', 'r', 'e', 's', 's', 'C', 'o', 'n', 'f', 'i', +'g', '\022', '&', '\n', '\017', 's', 'k', 'i', 'p', '_', 'x', 'f', 'f', '_', 'a', 'p', 'p', 'e', 'n', 'd', '\030', '\025', ' ', '\001', '(', +'\010', 'R', '\r', 's', 'k', 'i', 'p', 'X', 'f', 'f', 'A', 'p', 'p', 'e', 'n', 'd', '\022', '\035', '\n', '\003', 'v', 'i', 'a', '\030', '\026', +' ', '\001', '(', '\t', 'B', '\013', '\372', 'B', '\010', 'r', '\006', '\300', '\001', '\002', '\310', '\001', '\000', 'R', '\003', 'v', 'i', 'a', '\022', 'J', '\n', +'\023', 'g', 'e', 'n', 'e', 'r', 'a', 't', 'e', '_', 'r', 'e', 'q', 'u', 'e', 's', 't', '_', 'i', 'd', '\030', '\017', ' ', '\001', '(', +'\013', '2', '\032', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', 'V', +'a', 'l', 'u', 'e', 'R', '\021', 'g', 'e', 'n', 'e', 'r', 'a', 't', 'e', 'R', 'e', 'q', 'u', 'e', 's', 't', 'I', 'd', '\022', '?', +'\n', '\034', 'p', 'r', 'e', 's', 'e', 'r', 'v', 'e', '_', 'e', 'x', 't', 'e', 'r', 'n', 'a', 'l', '_', 'r', 'e', 'q', 'u', 'e', +'s', 't', '_', 'i', 'd', '\030', ' ', ' ', '\001', '(', '\010', 'R', '\031', 'p', 'r', 'e', 's', 'e', 'r', 'v', 'e', 'E', 'x', 't', 'e', +'r', 'n', 'a', 'l', 'R', 'e', 'q', 'u', 'e', 's', 't', 'I', 'd', '\022', 'G', '\n', '!', 'a', 'l', 'w', 'a', 'y', 's', '_', 's', +'e', 't', '_', 'r', 'e', 'q', 'u', 'e', 's', 't', '_', 'i', 'd', '_', 'i', 'n', '_', 'r', 'e', 's', 'p', 'o', 'n', 's', 'e', +'\030', '%', ' ', '\001', '(', '\010', 'R', '\034', 'a', 'l', 'w', 'a', 'y', 's', 'S', 'e', 't', 'R', 'e', 'q', 'u', 'e', 's', 't', 'I', +'d', 'I', 'n', 'R', 'e', 's', 'p', 'o', 'n', 's', 'e', '\022', '\264', '\001', '\n', '\033', 'f', 'o', 'r', 'w', 'a', 'r', 'd', '_', 'c', +'l', 'i', 'e', 'n', 't', '_', 'c', 'e', 'r', 't', '_', 'd', 'e', 't', 'a', 'i', 'l', 's', '\030', '\020', ' ', '\001', '(', '\016', '2', +'k', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', -'_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'R', 'e', 's', 'p', 'o', 'n', 's', 'e', 'M', 'a', 'p', 'p', 'e', -'r', 'R', '\007', 'm', 'a', 'p', 'p', 'e', 'r', 's', '\022', 'O', '\n', '\013', 'b', 'o', 'd', 'y', '_', 'f', 'o', 'r', 'm', 'a', 't', -'\030', '\002', ' ', '\001', '(', '\013', '2', '.', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', -'e', '.', 'v', '3', '.', 'S', 'u', 'b', 's', 't', 'i', 't', 'u', 't', 'i', 'o', 'n', 'F', 'o', 'r', 'm', 'a', 't', 'S', 't', -'r', 'i', 'n', 'g', 'R', '\n', 'b', 'o', 'd', 'y', 'F', 'o', 'r', 'm', 'a', 't', '\"', '\234', '\003', '\n', '\016', 'R', 'e', 's', 'p', -'o', 'n', 's', 'e', 'M', 'a', 'p', 'p', 'e', 'r', '\022', 'L', '\n', '\006', 'f', 'i', 'l', 't', 'e', 'r', '\030', '\001', ' ', '\001', '(', -'\013', '2', '*', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'a', 'c', 'c', 'e', 's', 's', 'l', 'o', -'g', '.', 'v', '3', '.', 'A', 'c', 'c', 'e', 's', 's', 'L', 'o', 'g', 'F', 'i', 'l', 't', 'e', 'r', 'B', '\010', '\372', 'B', '\005', -'\212', '\001', '\002', '\020', '\001', 'R', '\006', 'f', 'i', 'l', 't', 'e', 'r', '\022', 'J', '\n', '\013', 's', 't', 'a', 't', 'u', 's', '_', 'c', -'o', 'd', 'e', '\030', '\002', ' ', '\001', '(', '\013', '2', '\034', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', -'u', 'f', '.', 'U', 'I', 'n', 't', '3', '2', 'V', 'a', 'l', 'u', 'e', 'B', '\013', '\372', 'B', '\010', '*', '\006', '\020', '\330', '\004', '(', -'\310', '\001', 'R', '\n', 's', 't', 'a', 't', 'u', 's', 'C', 'o', 'd', 'e', '\022', '4', '\n', '\004', 'b', 'o', 'd', 'y', '\030', '\003', ' ', -'\001', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', -'3', '.', 'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'R', '\004', 'b', 'o', 'd', 'y', '\022', '`', '\n', '\024', 'b', 'o', 'd', -'y', '_', 'f', 'o', 'r', 'm', 'a', 't', '_', 'o', 'v', 'e', 'r', 'r', 'i', 'd', 'e', '\030', '\004', ' ', '\001', '(', '\013', '2', '.', -'.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'S', 'u', 'b', -'s', 't', 'i', 't', 'u', 't', 'i', 'o', 'n', 'F', 'o', 'r', 'm', 'a', 't', 'S', 't', 'r', 'i', 'n', 'g', 'R', '\022', 'b', 'o', -'d', 'y', 'F', 'o', 'r', 'm', 'a', 't', 'O', 'v', 'e', 'r', 'r', 'i', 'd', 'e', '\022', 'X', '\n', '\016', 'h', 'e', 'a', 'd', 'e', -'r', 's', '_', 't', 'o', '_', 'a', 'd', 'd', '\030', '\005', ' ', '\003', '(', '\013', '2', '\'', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', -'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'H', 'e', 'a', 'd', 'e', 'r', 'V', 'a', 'l', 'u', 'e', -'O', 'p', 't', 'i', 'o', 'n', 'B', '\t', '\372', 'B', '\006', '\222', '\001', '\003', '\020', '\350', '\007', 'R', '\014', 'h', 'e', 'a', 'd', 'e', 'r', -'s', 'T', 'o', 'A', 'd', 'd', '\"', '\307', '\001', '\n', '\003', 'R', 'd', 's', '\022', 'Q', '\n', '\r', 'c', 'o', 'n', 'f', 'i', 'g', '_', -'s', 'o', 'u', 'r', 'c', 'e', '\030', '\001', ' ', '\001', '(', '\013', '2', '\"', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', -'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'C', 'o', 'n', 'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', 'e', 'B', '\010', -'\372', 'B', '\005', '\212', '\001', '\002', '\020', '\001', 'R', '\014', 'c', 'o', 'n', 'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', 'e', '\022', '*', '\n', -'\021', 'r', 'o', 'u', 't', 'e', '_', 'c', 'o', 'n', 'f', 'i', 'g', '_', 'n', 'a', 'm', 'e', '\030', '\002', ' ', '\001', '(', '\t', 'R', -'\017', 'r', 'o', 'u', 't', 'e', 'C', 'o', 'n', 'f', 'i', 'g', 'N', 'a', 'm', 'e', ':', 'A', '\232', '\305', '\210', '\036', '<', '\n', ':', -'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', -'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', -'r', '.', 'v', '2', '.', 'R', 'd', 's', '\"', '\367', '\001', '\n', '\035', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 'C', -'o', 'n', 'f', 'i', 'g', 'u', 'r', 'a', 't', 'i', 'o', 'n', 's', 'L', 'i', 's', 't', '\022', 'y', '\n', '\033', 's', 'c', 'o', 'p', -'e', 'd', '_', 'r', 'o', 'u', 't', 'e', '_', 'c', 'o', 'n', 'f', 'i', 'g', 'u', 'r', 'a', 't', 'i', 'o', 'n', 's', '\030', '\001', -' ', '\003', '(', '\013', '2', '/', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'o', 'u', 't', 'e', -'.', 'v', '3', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 'C', 'o', 'n', 'f', 'i', 'g', 'u', 'r', 'a', 't', -'i', 'o', 'n', 'B', '\010', '\372', 'B', '\005', '\222', '\001', '\002', '\010', '\001', 'R', '\031', 's', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', -'e', 'C', 'o', 'n', 'f', 'i', 'g', 'u', 'r', 'a', 't', 'i', 'o', 'n', 's', ':', '[', '\232', '\305', '\210', '\036', 'V', '\n', 'T', 'e', -'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', +'_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', +'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'F', 'o', 'r', 'w', 'a', 'r', 'd', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', +'t', 'D', 'e', 't', 'a', 'i', 'l', 's', 'B', '\010', '\372', 'B', '\005', '\202', '\001', '\002', '\020', '\001', 'R', '\030', 'f', 'o', 'r', 'w', 'a', +'r', 'd', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', 't', 'D', 'e', 't', 'a', 'i', 'l', 's', '\022', '\264', '\001', '\n', '\037', 's', +'e', 't', '_', 'c', 'u', 'r', 'r', 'e', 'n', 't', '_', 'c', 'l', 'i', 'e', 'n', 't', '_', 'c', 'e', 'r', 't', '_', 'd', 'e', +'t', 'a', 'i', 'l', 's', '\030', '\021', ' ', '\001', '(', '\013', '2', 'n', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', +'s', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', +'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'H', +'t', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'S', 'e', 't', 'C', +'u', 'r', 'r', 'e', 'n', 't', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', 't', 'D', 'e', 't', 'a', 'i', 'l', 's', 'R', '\033', +'s', 'e', 't', 'C', 'u', 'r', 'r', 'e', 'n', 't', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', 't', 'D', 'e', 't', 'a', 'i', +'l', 's', '\022', ',', '\n', '\022', 'p', 'r', 'o', 'x', 'y', '_', '1', '0', '0', '_', 'c', 'o', 'n', 't', 'i', 'n', 'u', 'e', '\030', +'\022', ' ', '\001', '(', '\010', 'R', '\020', 'p', 'r', 'o', 'x', 'y', '1', '0', '0', 'C', 'o', 'n', 't', 'i', 'n', 'u', 'e', '\022', 'e', +'\n', '1', 'r', 'e', 'p', 'r', 'e', 's', 'e', 'n', 't', '_', 'i', 'p', 'v', '4', '_', 'r', 'e', 'm', 'o', 't', 'e', '_', 'a', +'d', 'd', 'r', 'e', 's', 's', '_', 'a', 's', '_', 'i', 'p', 'v', '4', '_', 'm', 'a', 'p', 'p', 'e', 'd', '_', 'i', 'p', 'v', +'6', '\030', '\024', ' ', '\001', '(', '\010', 'R', '*', 'r', 'e', 'p', 'r', 'e', 's', 'e', 'n', 't', 'I', 'p', 'v', '4', 'R', 'e', 'm', +'o', 't', 'e', 'A', 'd', 'd', 'r', 'e', 's', 's', 'A', 's', 'I', 'p', 'v', '4', 'M', 'a', 'p', 'p', 'e', 'd', 'I', 'p', 'v', +'6', '\022', '\211', '\001', '\n', '\017', 'u', 'p', 'g', 'r', 'a', 'd', 'e', '_', 'c', 'o', 'n', 'f', 'i', 'g', 's', '\030', '\027', ' ', '\003', +'(', '\013', '2', '`', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', +'t', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', +'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', +'t', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'U', 'p', 'g', 'r', 'a', 'd', 'e', 'C', 'o', 'n', 'f', 'i', 'g', +'R', '\016', 'u', 'p', 'g', 'r', 'a', 'd', 'e', 'C', 'o', 'n', 'f', 'i', 'g', 's', '\022', 'A', '\n', '\016', 'n', 'o', 'r', 'm', 'a', +'l', 'i', 'z', 'e', '_', 'p', 'a', 't', 'h', '\030', '\036', ' ', '\001', '(', '\013', '2', '\032', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', +'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', 'V', 'a', 'l', 'u', 'e', 'R', '\r', 'n', 'o', 'r', 'm', 'a', +'l', 'i', 'z', 'e', 'P', 'a', 't', 'h', '\022', '#', '\n', '\r', 'm', 'e', 'r', 'g', 'e', '_', 's', 'l', 'a', 's', 'h', 'e', 's', +'\030', '!', ' ', '\001', '(', '\010', 'R', '\014', 'm', 'e', 'r', 'g', 'e', 'S', 'l', 'a', 's', 'h', 'e', 's', '\022', '\267', '\001', '\n', ' ', +'p', 'a', 't', 'h', '_', 'w', 'i', 't', 'h', '_', 'e', 's', 'c', 'a', 'p', 'e', 'd', '_', 's', 'l', 'a', 's', 'h', 'e', 's', +'_', 'a', 'c', 't', 'i', 'o', 'n', '\030', '-', ' ', '\001', '(', '\016', '2', 'o', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', +'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', +'t', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', +'.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'P', 'a', +'t', 'h', 'W', 'i', 't', 'h', 'E', 's', 'c', 'a', 'p', 'e', 'd', 'S', 'l', 'a', 's', 'h', 'e', 's', 'A', 'c', 't', 'i', 'o', +'n', 'R', '\034', 'p', 'a', 't', 'h', 'W', 'i', 't', 'h', 'E', 's', 'c', 'a', 'p', 'e', 'd', 'S', 'l', 'a', 's', 'h', 'e', 's', +'A', 'c', 't', 'i', 'o', 'n', '\022', '\201', '\001', '\n', '\024', 'r', 'e', 'q', 'u', 'e', 's', 't', '_', 'i', 'd', '_', 'e', 'x', 't', +'e', 'n', 's', 'i', 'o', 'n', '\030', '$', ' ', '\001', '(', '\013', '2', 'O', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', +'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', +'t', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', +'R', 'e', 'q', 'u', 'e', 's', 't', 'I', 'D', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'R', '\022', 'r', 'e', 'q', 'u', 'e', +'s', 't', 'I', 'd', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '\022', '{', '\n', '\022', 'l', 'o', 'c', 'a', 'l', '_', 'r', 'e', +'p', 'l', 'y', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '&', ' ', '\001', '(', '\013', '2', 'M', '.', 'e', 'n', 'v', 'o', 'y', '.', +'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', -'.', 'v', '2', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 'C', 'o', 'n', 'f', 'i', 'g', 'u', 'r', 'a', 't', -'i', 'o', 'n', 's', 'L', 'i', 's', 't', '\"', '\337', '\016', '\n', '\014', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', -'\022', '\033', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'B', '\007', '\372', 'B', '\004', 'r', '\002', '\020', '\001', 'R', '\004', -'n', 'a', 'm', 'e', '\022', '\217', '\001', '\n', '\021', 's', 'c', 'o', 'p', 'e', '_', 'k', 'e', 'y', '_', 'b', 'u', 'i', 'l', 'd', 'e', -'r', '\030', '\002', ' ', '\001', '(', '\013', '2', 'Y', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', -'s', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', -'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'S', 'c', 'o', 'p', 'e', -'d', 'R', 'o', 'u', 't', 'e', 's', '.', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', 'B', '\010', -'\372', 'B', '\005', '\212', '\001', '\002', '\020', '\001', 'R', '\017', 's', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', -'\022', 'N', '\n', '\021', 'r', 'd', 's', '_', 'c', 'o', 'n', 'f', 'i', 'g', '_', 's', 'o', 'u', 'r', 'c', 'e', '\030', '\003', ' ', '\001', -'(', '\013', '2', '\"', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', -'.', 'C', 'o', 'n', 'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', 'e', 'R', '\017', 'r', 'd', 's', 'C', 'o', 'n', 'f', 'i', 'g', 'S', -'o', 'u', 'r', 'c', 'e', '\022', '\245', '\001', '\n', ' ', 's', 'c', 'o', 'p', 'e', 'd', '_', 'r', 'o', 'u', 't', 'e', '_', 'c', 'o', -'n', 'f', 'i', 'g', 'u', 'r', 'a', 't', 'i', 'o', 'n', 's', '_', 'l', 'i', 's', 't', '\030', '\004', ' ', '\001', '(', '\013', '2', 'Z', +'.', 'v', '3', '.', 'L', 'o', 'c', 'a', 'l', 'R', 'e', 'p', 'l', 'y', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\020', 'l', 'o', 'c', +'a', 'l', 'R', 'e', 'p', 'l', 'y', 'C', 'o', 'n', 'f', 'i', 'g', '\022', 'P', '\n', '\030', 's', 't', 'r', 'i', 'p', '_', 'm', 'a', +'t', 'c', 'h', 'i', 'n', 'g', '_', 'h', 'o', 's', 't', '_', 'p', 'o', 'r', 't', '\030', '\'', ' ', '\001', '(', '\010', 'B', '\027', '\362', +'\230', '\376', '\217', '\005', '\021', '\022', '\017', 's', 't', 'r', 'i', 'p', '_', 'p', 'o', 'r', 't', '_', 'm', 'o', 'd', 'e', 'R', '\025', 's', +'t', 'r', 'i', 'p', 'M', 'a', 't', 'c', 'h', 'i', 'n', 'g', 'H', 'o', 's', 't', 'P', 'o', 'r', 't', '\022', '/', '\n', '\023', 's', +'t', 'r', 'i', 'p', '_', 'a', 'n', 'y', '_', 'h', 'o', 's', 't', '_', 'p', 'o', 'r', 't', '\030', '*', ' ', '\001', '(', '\010', 'H', +'\001', 'R', '\020', 's', 't', 'r', 'i', 'p', 'A', 'n', 'y', 'H', 'o', 's', 't', 'P', 'o', 'r', 't', '\022', 'i', '\n', '$', 's', 't', +'r', 'e', 'a', 'm', '_', 'e', 'r', 'r', 'o', 'r', '_', 'o', 'n', '_', 'i', 'n', 'v', 'a', 'l', 'i', 'd', '_', 'h', 't', 't', +'p', '_', 'm', 'e', 's', 's', 'a', 'g', 'e', '\030', '(', ' ', '\001', '(', '\013', '2', '\032', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', +'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', 'V', 'a', 'l', 'u', 'e', 'R', '\037', 's', 't', 'r', 'e', 'a', +'m', 'E', 'r', 'r', 'o', 'r', 'O', 'n', 'I', 'n', 'v', 'a', 'l', 'i', 'd', 'H', 't', 't', 'p', 'M', 'e', 's', 's', 'a', 'g', +'e', '\022', '\251', '\001', '\n', '\032', 'p', 'a', 't', 'h', '_', 'n', 'o', 'r', 'm', 'a', 'l', 'i', 'z', 'a', 't', 'i', 'o', 'n', '_', +'o', 'p', 't', 'i', 'o', 'n', 's', '\030', '+', ' ', '\001', '(', '\013', '2', 'k', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', +'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', +'t', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', +'.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'P', 'a', +'t', 'h', 'N', 'o', 'r', 'm', 'a', 'l', 'i', 'z', 'a', 't', 'i', 'o', 'n', 'O', 'p', 't', 'i', 'o', 'n', 's', 'R', '\030', 'p', +'a', 't', 'h', 'N', 'o', 'r', 'm', 'a', 'l', 'i', 'z', 'a', 't', 'i', 'o', 'n', 'O', 'p', 't', 'i', 'o', 'n', 's', '\022', '5', +'\n', '\027', 's', 't', 'r', 'i', 'p', '_', 't', 'r', 'a', 'i', 'l', 'i', 'n', 'g', '_', 'h', 'o', 's', 't', '_', 'd', 'o', 't', +'\030', '/', ' ', '\001', '(', '\010', 'R', '\024', 's', 't', 'r', 'i', 'p', 'T', 'r', 'a', 'i', 'l', 'i', 'n', 'g', 'H', 'o', 's', 't', +'D', 'o', 't', '\022', '\224', '\001', '\n', '\023', 'p', 'r', 'o', 'x', 'y', '_', 's', 't', 'a', 't', 'u', 's', '_', 'c', 'o', 'n', 'f', +'i', 'g', '\030', '1', ' ', '\001', '(', '\013', '2', 'd', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', +'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', +'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', +'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'P', 'r', 'o', 'x', 'y', 'S', 't', +'a', 't', 'u', 's', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\021', 'p', 'r', 'o', 'x', 'y', 'S', 't', 'a', 't', 'u', 's', 'C', 'o', +'n', 'f', 'i', 'g', '\022', 'o', '\n', '\036', 't', 'y', 'p', 'e', 'd', '_', 'h', 'e', 'a', 'd', 'e', 'r', '_', 'v', 'a', 'l', 'i', +'d', 'a', 't', 'i', 'o', 'n', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '2', ' ', '\001', '(', '\013', '2', '*', '.', 'e', 'n', 'v', +'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'T', 'y', 'p', 'e', 'd', 'E', 'x', +'t', 'e', 'n', 's', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\033', 't', 'y', 'p', 'e', 'd', 'H', 'e', 'a', 'd', 'e', +'r', 'V', 'a', 'l', 'i', 'd', 'a', 't', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', '\022', '5', '\n', '\027', 'a', 'p', 'p', 'e', +'n', 'd', '_', 'x', '_', 'f', 'o', 'r', 'w', 'a', 'r', 'd', 'e', 'd', '_', 'p', 'o', 'r', 't', '\030', '3', ' ', '\001', '(', '\010', +'R', '\024', 'a', 'p', 'p', 'e', 'n', 'd', 'X', 'F', 'o', 'r', 'w', 'a', 'r', 'd', 'e', 'd', 'P', 'o', 'r', 't', '\022', 'h', '\n', +'#', 'a', 'd', 'd', '_', 'p', 'r', 'o', 'x', 'y', '_', 'p', 'r', 'o', 't', 'o', 'c', 'o', 'l', '_', 'c', 'o', 'n', 'n', 'e', +'c', 't', 'i', 'o', 'n', '_', 's', 't', 'a', 't', 'e', '\030', '5', ' ', '\001', '(', '\013', '2', '\032', '.', 'g', 'o', 'o', 'g', 'l', +'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', 'V', 'a', 'l', 'u', 'e', 'R', '\037', 'a', 'd', 'd', +'P', 'r', 'o', 'x', 'y', 'P', 'r', 'o', 't', 'o', 'c', 'o', 'l', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'S', 't', +'a', 't', 'e', '\032', '\366', '\004', '\n', '\007', 'T', 'r', 'a', 'c', 'i', 'n', 'g', '\022', '?', '\n', '\017', 'c', 'l', 'i', 'e', 'n', 't', +'_', 's', 'a', 'm', 'p', 'l', 'i', 'n', 'g', '\030', '\003', ' ', '\001', '(', '\013', '2', '\026', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', +'y', 'p', 'e', '.', 'v', '3', '.', 'P', 'e', 'r', 'c', 'e', 'n', 't', 'R', '\016', 'c', 'l', 'i', 'e', 'n', 't', 'S', 'a', 'm', +'p', 'l', 'i', 'n', 'g', '\022', '?', '\n', '\017', 'r', 'a', 'n', 'd', 'o', 'm', '_', 's', 'a', 'm', 'p', 'l', 'i', 'n', 'g', '\030', +'\004', ' ', '\001', '(', '\013', '2', '\026', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'v', '3', '.', 'P', 'e', 'r', +'c', 'e', 'n', 't', 'R', '\016', 'r', 'a', 'n', 'd', 'o', 'm', 'S', 'a', 'm', 'p', 'l', 'i', 'n', 'g', '\022', 'A', '\n', '\020', 'o', +'v', 'e', 'r', 'a', 'l', 'l', '_', 's', 'a', 'm', 'p', 'l', 'i', 'n', 'g', '\030', '\005', ' ', '\001', '(', '\013', '2', '\026', '.', 'e', +'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'v', '3', '.', 'P', 'e', 'r', 'c', 'e', 'n', 't', 'R', '\017', 'o', 'v', 'e', +'r', 'a', 'l', 'l', 'S', 'a', 'm', 'p', 'l', 'i', 'n', 'g', '\022', '\030', '\n', '\007', 'v', 'e', 'r', 'b', 'o', 's', 'e', '\030', '\006', +' ', '\001', '(', '\010', 'R', '\007', 'v', 'e', 'r', 'b', 'o', 's', 'e', '\022', 'K', '\n', '\023', 'm', 'a', 'x', '_', 'p', 'a', 't', 'h', +'_', 't', 'a', 'g', '_', 'l', 'e', 'n', 'g', 't', 'h', '\030', '\007', ' ', '\001', '(', '\013', '2', '\034', '.', 'g', 'o', 'o', 'g', 'l', +'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'I', 'n', 't', '3', '2', 'V', 'a', 'l', 'u', 'e', 'R', '\020', 'm', +'a', 'x', 'P', 'a', 't', 'h', 'T', 'a', 'g', 'L', 'e', 'n', 'g', 't', 'h', '\022', 'A', '\n', '\013', 'c', 'u', 's', 't', 'o', 'm', +'_', 't', 'a', 'g', 's', '\030', '\010', ' ', '\003', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', +'t', 'r', 'a', 'c', 'i', 'n', 'g', '.', 'v', '3', '.', 'C', 'u', 's', 't', 'o', 'm', 'T', 'a', 'g', 'R', '\n', 'c', 'u', 's', +'t', 'o', 'm', 'T', 'a', 'g', 's', '\022', '?', '\n', '\010', 'p', 'r', 'o', 'v', 'i', 'd', 'e', 'r', '\030', '\t', ' ', '\001', '(', '\013', +'2', '#', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 't', 'r', 'a', 'c', 'e', '.', 'v', '3', '.', +'T', 'r', 'a', 'c', 'i', 'n', 'g', '.', 'H', 't', 't', 'p', 'R', '\010', 'p', 'r', 'o', 'v', 'i', 'd', 'e', 'r', '\"', '(', '\n', +'\r', 'O', 'p', 'e', 'r', 'a', 't', 'i', 'o', 'n', 'N', 'a', 'm', 'e', '\022', '\013', '\n', '\007', 'I', 'N', 'G', 'R', 'E', 'S', 'S', +'\020', '\000', '\022', '\n', '\n', '\006', 'E', 'G', 'R', 'E', 'S', 'S', '\020', '\001', ':', '[', '\232', '\305', '\210', '\036', 'V', '\n', 'T', 'e', 'n', +'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', +'.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', +'v', '2', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', +'T', 'r', 'a', 'c', 'i', 'n', 'g', 'J', '\004', '\010', '\001', '\020', '\002', 'J', '\004', '\010', '\002', '\020', '\003', 'R', '\016', 'o', 'p', 'e', 'r', +'a', 't', 'i', 'o', 'n', '_', 'n', 'a', 'm', 'e', 'R', '\030', 'r', 'e', 'q', 'u', 'e', 's', 't', '_', 'h', 'e', 'a', 'd', 'e', +'r', 's', '_', 'f', 'o', 'r', '_', 't', 'a', 'g', 's', '\032', '\347', '\001', '\n', '\025', 'I', 'n', 't', 'e', 'r', 'n', 'a', 'l', 'A', +'d', 'd', 'r', 'e', 's', 's', 'C', 'o', 'n', 'f', 'i', 'g', '\022', '!', '\n', '\014', 'u', 'n', 'i', 'x', '_', 's', 'o', 'c', 'k', +'e', 't', 's', '\030', '\001', ' ', '\001', '(', '\010', 'R', '\013', 'u', 'n', 'i', 'x', 'S', 'o', 'c', 'k', 'e', 't', 's', '\022', '@', '\n', +'\013', 'c', 'i', 'd', 'r', '_', 'r', 'a', 'n', 'g', 'e', 's', '\030', '\002', ' ', '\003', '(', '\013', '2', '\037', '.', 'e', 'n', 'v', 'o', +'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'C', 'i', 'd', 'r', 'R', 'a', 'n', 'g', +'e', 'R', '\n', 'c', 'i', 'd', 'r', 'R', 'a', 'n', 'g', 'e', 's', ':', 'i', '\232', '\305', '\210', '\036', 'd', '\n', 'b', 'e', 'n', 'v', +'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', +'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', +'2', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'I', +'n', 't', 'e', 'r', 'n', 'a', 'l', 'A', 'd', 'd', 'r', 'e', 's', 's', 'C', 'o', 'n', 'f', 'i', 'g', '\032', '\230', '\002', '\n', '\033', +'S', 'e', 't', 'C', 'u', 'r', 'r', 'e', 'n', 't', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', 't', 'D', 'e', 't', 'a', 'i', +'l', 's', '\022', '4', '\n', '\007', 's', 'u', 'b', 'j', 'e', 'c', 't', '\030', '\001', ' ', '\001', '(', '\013', '2', '\032', '.', 'g', 'o', 'o', +'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', 'V', 'a', 'l', 'u', 'e', 'R', '\007', 's', +'u', 'b', 'j', 'e', 'c', 't', '\022', '\022', '\n', '\004', 'c', 'e', 'r', 't', '\030', '\003', ' ', '\001', '(', '\010', 'R', '\004', 'c', 'e', 'r', +'t', '\022', '\024', '\n', '\005', 'c', 'h', 'a', 'i', 'n', '\030', '\006', ' ', '\001', '(', '\010', 'R', '\005', 'c', 'h', 'a', 'i', 'n', '\022', '\020', +'\n', '\003', 'd', 'n', 's', '\030', '\004', ' ', '\001', '(', '\010', 'R', '\003', 'd', 'n', 's', '\022', '\020', '\n', '\003', 'u', 'r', 'i', '\030', '\005', +' ', '\001', '(', '\010', 'R', '\003', 'u', 'r', 'i', ':', 'o', '\232', '\305', '\210', '\036', 'j', '\n', 'h', 'e', 'n', 'v', 'o', 'y', '.', 'c', +'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', +'_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'H', 't', +'t', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'S', 'e', 't', 'C', 'u', +'r', 'r', 'e', 'n', 't', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', 't', 'D', 'e', 't', 'a', 'i', 'l', 's', 'J', '\004', '\010', +'\002', '\020', '\003', '\032', '\256', '\002', '\n', '\r', 'U', 'p', 'g', 'r', 'a', 'd', 'e', 'C', 'o', 'n', 'f', 'i', 'g', '\022', '!', '\n', '\014', +'u', 'p', 'g', 'r', 'a', 'd', 'e', '_', 't', 'y', 'p', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'R', '\013', 'u', 'p', 'g', 'r', 'a', +'d', 'e', 'T', 'y', 'p', 'e', '\022', 'a', '\n', '\007', 'f', 'i', 'l', 't', 'e', 'r', 's', '\030', '\002', ' ', '\003', '(', '\013', '2', 'G', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', -'m', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 'C', 'o', 'n', -'f', 'i', 'g', 'u', 'r', 'a', 't', 'i', 'o', 'n', 's', 'L', 'i', 's', 't', 'H', '\000', 'R', '\035', 's', 'c', 'o', 'p', 'e', 'd', -'R', 'o', 'u', 't', 'e', 'C', 'o', 'n', 'f', 'i', 'g', 'u', 'r', 'a', 't', 'i', 'o', 'n', 's', 'L', 'i', 's', 't', '\022', 'g', -'\n', '\n', 's', 'c', 'o', 'p', 'e', 'd', '_', 'r', 'd', 's', '\030', '\005', ' ', '\001', '(', '\013', '2', 'F', '.', 'e', 'n', 'v', 'o', -'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', -'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', -'e', 'r', '.', 'v', '3', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'd', 's', 'H', '\000', 'R', '\t', 's', 'c', 'o', 'p', 'e', 'd', -'R', 'd', 's', '\032', '\331', '\t', '\n', '\017', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '\022', '\221', -'\001', '\n', '\t', 'f', 'r', 'a', 'g', 'm', 'e', 'n', 't', 's', '\030', '\001', ' ', '\003', '(', '\013', '2', 'i', '.', 'e', 'n', 'v', 'o', +'m', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', 'F', 'i', 'l', 't', 'e', 'r', 'R', '\007', 'f', 'i', +'l', 't', 'e', 'r', 's', '\022', '4', '\n', '\007', 'e', 'n', 'a', 'b', 'l', 'e', 'd', '\030', '\003', ' ', '\001', '(', '\013', '2', '\032', '.', +'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', 'V', 'a', 'l', 'u', 'e', +'R', '\007', 'e', 'n', 'a', 'b', 'l', 'e', 'd', ':', 'a', '\232', '\305', '\210', '\036', '\\', '\n', 'Z', 'e', 'n', 'v', 'o', 'y', '.', 'c', +'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', +'_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'H', 't', +'t', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'U', 'p', 'g', 'r', 'a', +'d', 'e', 'C', 'o', 'n', 'f', 'i', 'g', '\032', '\345', '\001', '\n', '\030', 'P', 'a', 't', 'h', 'N', 'o', 'r', 'm', 'a', 'l', 'i', 'z', +'a', 't', 'i', 'o', 'n', 'O', 'p', 't', 'i', 'o', 'n', 's', '\022', 'c', '\n', '\031', 'f', 'o', 'r', 'w', 'a', 'r', 'd', 'i', 'n', +'g', '_', 't', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', '\030', '\001', ' ', '\001', '(', '\013', '2', '&', '.', +'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'h', 't', 't', 'p', '.', 'v', '3', '.', 'P', 'a', 't', 'h', 'T', 'r', +'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', 'R', '\030', 'f', 'o', 'r', 'w', 'a', 'r', 'd', 'i', 'n', 'g', 'T', +'r', 'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', '\022', 'd', '\n', '\032', 'h', 't', 't', 'p', '_', 'f', 'i', 'l', +'t', 'e', 'r', '_', 't', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', '\030', '\002', ' ', '\001', '(', '\013', '2', +'&', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'h', 't', 't', 'p', '.', 'v', '3', '.', 'P', 'a', 't', 'h', +'T', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', 'R', '\030', 'h', 't', 't', 'p', 'F', 'i', 'l', 't', 'e', +'r', 'T', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', '\032', '\344', '\002', '\n', '\021', 'P', 'r', 'o', 'x', 'y', +'S', 't', 'a', 't', 'u', 's', 'C', 'o', 'n', 'f', 'i', 'g', '\022', '%', '\n', '\016', 'r', 'e', 'm', 'o', 'v', 'e', '_', 'd', 'e', +'t', 'a', 'i', 'l', 's', '\030', '\001', ' ', '\001', '(', '\010', 'R', '\r', 'r', 'e', 'm', 'o', 'v', 'e', 'D', 'e', 't', 'a', 'i', 'l', +'s', '\022', 'Q', '\n', '%', 'r', 'e', 'm', 'o', 'v', 'e', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 't', 'e', +'r', 'm', 'i', 'n', 'a', 't', 'i', 'o', 'n', '_', 'd', 'e', 't', 'a', 'i', 'l', 's', '\030', '\002', ' ', '\001', '(', '\010', 'R', '\"', +'r', 'e', 'm', 'o', 'v', 'e', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'T', 'e', 'r', 'm', 'i', 'n', 'a', 't', 'i', +'o', 'n', 'D', 'e', 't', 'a', 'i', 'l', 's', '\022', '2', '\n', '\025', 'r', 'e', 'm', 'o', 'v', 'e', '_', 'r', 'e', 's', 'p', 'o', +'n', 's', 'e', '_', 'f', 'l', 'a', 'g', 's', '\030', '\003', ' ', '\001', '(', '\010', 'R', '\023', 'r', 'e', 'm', 'o', 'v', 'e', 'R', 'e', +'s', 'p', 'o', 'n', 's', 'e', 'F', 'l', 'a', 'g', 's', '\022', 'A', '\n', '\035', 's', 'e', 't', '_', 'r', 'e', 'c', 'o', 'm', 'm', +'e', 'n', 'd', 'e', 'd', '_', 'r', 'e', 's', 'p', 'o', 'n', 's', 'e', '_', 'c', 'o', 'd', 'e', '\030', '\004', ' ', '\001', '(', '\010', +'R', '\032', 's', 'e', 't', 'R', 'e', 'c', 'o', 'm', 'm', 'e', 'n', 'd', 'e', 'd', 'R', 'e', 's', 'p', 'o', 'n', 's', 'e', 'C', +'o', 'd', 'e', '\022', ' ', '\n', '\013', 'u', 's', 'e', '_', 'n', 'o', 'd', 'e', '_', 'i', 'd', '\030', '\005', ' ', '\001', '(', '\010', 'H', +'\000', 'R', '\t', 'u', 's', 'e', 'N', 'o', 'd', 'e', 'I', 'd', '\022', '.', '\n', '\022', 'l', 'i', 't', 'e', 'r', 'a', 'l', '_', 'p', +'r', 'o', 'x', 'y', '_', 'n', 'a', 'm', 'e', '\030', '\006', ' ', '\001', '(', '\t', 'H', '\000', 'R', '\020', 'l', 'i', 't', 'e', 'r', 'a', +'l', 'P', 'r', 'o', 'x', 'y', 'N', 'a', 'm', 'e', 'B', '\014', '\n', '\n', 'p', 'r', 'o', 'x', 'y', '_', 'n', 'a', 'm', 'e', '\"', +'6', '\n', '\t', 'C', 'o', 'd', 'e', 'c', 'T', 'y', 'p', 'e', '\022', '\010', '\n', '\004', 'A', 'U', 'T', 'O', '\020', '\000', '\022', '\t', '\n', +'\005', 'H', 'T', 'T', 'P', '1', '\020', '\001', '\022', '\t', '\n', '\005', 'H', 'T', 'T', 'P', '2', '\020', '\002', '\022', '\t', '\n', '\005', 'H', 'T', +'T', 'P', '3', '\020', '\003', '\"', 'S', '\n', '\032', 'S', 'e', 'r', 'v', 'e', 'r', 'H', 'e', 'a', 'd', 'e', 'r', 'T', 'r', 'a', 'n', +'s', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', '\022', '\r', '\n', '\t', 'O', 'V', 'E', 'R', 'W', 'R', 'I', 'T', 'E', '\020', '\000', +'\022', '\024', '\n', '\020', 'A', 'P', 'P', 'E', 'N', 'D', '_', 'I', 'F', '_', 'A', 'B', 'S', 'E', 'N', 'T', '\020', '\001', '\022', '\020', '\n', +'\014', 'P', 'A', 'S', 'S', '_', 'T', 'H', 'R', 'O', 'U', 'G', 'H', '\020', '\002', '\"', 'y', '\n', '\030', 'F', 'o', 'r', 'w', 'a', 'r', +'d', 'C', 'l', 'i', 'e', 'n', 't', 'C', 'e', 'r', 't', 'D', 'e', 't', 'a', 'i', 'l', 's', '\022', '\014', '\n', '\010', 'S', 'A', 'N', +'I', 'T', 'I', 'Z', 'E', '\020', '\000', '\022', '\020', '\n', '\014', 'F', 'O', 'R', 'W', 'A', 'R', 'D', '_', 'O', 'N', 'L', 'Y', '\020', '\001', +'\022', '\022', '\n', '\016', 'A', 'P', 'P', 'E', 'N', 'D', '_', 'F', 'O', 'R', 'W', 'A', 'R', 'D', '\020', '\002', '\022', '\020', '\n', '\014', 'S', +'A', 'N', 'I', 'T', 'I', 'Z', 'E', '_', 'S', 'E', 'T', '\020', '\003', '\022', '\027', '\n', '\023', 'A', 'L', 'W', 'A', 'Y', 'S', '_', 'F', +'O', 'R', 'W', 'A', 'R', 'D', '_', 'O', 'N', 'L', 'Y', '\020', '\004', '\"', '\240', '\001', '\n', '\034', 'P', 'a', 't', 'h', 'W', 'i', 't', +'h', 'E', 's', 'c', 'a', 'p', 'e', 'd', 'S', 'l', 'a', 's', 'h', 'e', 's', 'A', 'c', 't', 'i', 'o', 'n', '\022', '#', '\n', '\037', +'I', 'M', 'P', 'L', 'E', 'M', 'E', 'N', 'T', 'A', 'T', 'I', 'O', 'N', '_', 'S', 'P', 'E', 'C', 'I', 'F', 'I', 'C', '_', 'D', +'E', 'F', 'A', 'U', 'L', 'T', '\020', '\000', '\022', '\022', '\n', '\016', 'K', 'E', 'E', 'P', '_', 'U', 'N', 'C', 'H', 'A', 'N', 'G', 'E', +'D', '\020', '\001', '\022', '\022', '\n', '\016', 'R', 'E', 'J', 'E', 'C', 'T', '_', 'R', 'E', 'Q', 'U', 'E', 'S', 'T', '\020', '\002', '\022', '\031', +'\n', '\025', 'U', 'N', 'E', 'S', 'C', 'A', 'P', 'E', '_', 'A', 'N', 'D', '_', 'R', 'E', 'D', 'I', 'R', 'E', 'C', 'T', '\020', '\003', +'\022', '\030', '\n', '\024', 'U', 'N', 'E', 'S', 'C', 'A', 'P', 'E', '_', 'A', 'N', 'D', '_', 'F', 'O', 'R', 'W', 'A', 'R', 'D', '\020', +'\004', ':', 'S', '\232', '\305', '\210', '\036', 'N', '\n', 'L', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', +'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', +'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', +'t', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', 'B', '\026', '\n', '\017', 'r', 'o', 'u', 't', 'e', '_', 's', 'p', 'e', 'c', +'i', 'f', 'i', 'e', 'r', '\022', '\003', '\370', 'B', '\001', 'B', '\021', '\n', '\017', 's', 't', 'r', 'i', 'p', '_', 'p', 'o', 'r', 't', '_', +'m', 'o', 'd', 'e', 'J', '\004', '\010', '\033', '\020', '\034', 'J', '\004', '\010', '\013', '\020', '\014', 'R', '\014', 'i', 'd', 'l', 'e', '_', 't', 'i', +'m', 'e', 'o', 'u', 't', '\"', '\312', '\001', '\n', '\020', 'L', 'o', 'c', 'a', 'l', 'R', 'e', 'p', 'l', 'y', 'C', 'o', 'n', 'f', 'i', +'g', '\022', 'e', '\n', '\007', 'm', 'a', 'p', 'p', 'e', 'r', 's', '\030', '\001', ' ', '\003', '(', '\013', '2', 'K', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', -'e', 'r', '.', 'v', '3', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', '.', 'S', 'c', 'o', 'p', 'e', 'K', -'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '.', 'F', 'r', 'a', 'g', 'm', 'e', 'n', 't', 'B', 'u', 'i', 'l', 'd', 'e', 'r', -'B', '\010', '\372', 'B', '\005', '\222', '\001', '\002', '\010', '\001', 'R', '\t', 'f', 'r', 'a', 'g', 'm', 'e', 'n', 't', 's', '\032', '\325', '\007', '\n', -'\017', 'F', 'r', 'a', 'g', 'm', 'e', 'n', 't', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '\022', '\266', '\001', '\n', '\026', 'h', 'e', 'a', 'd', -'e', 'r', '_', 'v', 'a', 'l', 'u', 'e', '_', 'e', 'x', 't', 'r', 'a', 'c', 't', 'o', 'r', '\030', '\001', ' ', '\001', '(', '\013', '2', -'~', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', -'s', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', -'_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', '.', -'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '.', 'F', 'r', 'a', 'g', 'm', 'e', 'n', 't', 'B', -'u', 'i', 'l', 'd', 'e', 'r', '.', 'H', 'e', 'a', 'd', 'e', 'r', 'V', 'a', 'l', 'u', 'e', 'E', 'x', 't', 'r', 'a', 'c', 't', -'o', 'r', 'H', '\000', 'R', '\024', 'h', 'e', 'a', 'd', 'e', 'r', 'V', 'a', 'l', 'u', 'e', 'E', 'x', 't', 'r', 'a', 'c', 't', 'o', -'r', '\032', '\217', '\005', '\n', '\024', 'H', 'e', 'a', 'd', 'e', 'r', 'V', 'a', 'l', 'u', 'e', 'E', 'x', 't', 'r', 'a', 'c', 't', 'o', -'r', '\022', '\033', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'B', '\007', '\372', 'B', '\004', 'r', '\002', '\020', '\001', 'R', -'\004', 'n', 'a', 'm', 'e', '\022', '+', '\n', '\021', 'e', 'l', 'e', 'm', 'e', 'n', 't', '_', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', -'r', '\030', '\002', ' ', '\001', '(', '\t', 'R', '\020', 'e', 'l', 'e', 'm', 'e', 'n', 't', 'S', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', -'\022', '\026', '\n', '\005', 'i', 'n', 'd', 'e', 'x', '\030', '\003', ' ', '\001', '(', '\r', 'H', '\000', 'R', '\005', 'i', 'n', 'd', 'e', 'x', '\022', -'\245', '\001', '\n', '\007', 'e', 'l', 'e', 'm', 'e', 'n', 't', '\030', '\004', ' ', '\001', '(', '\013', '2', '\210', '\001', '.', 'e', 'n', 'v', 'o', +'e', 'r', '.', 'v', '3', '.', 'R', 'e', 's', 'p', 'o', 'n', 's', 'e', 'M', 'a', 'p', 'p', 'e', 'r', 'R', '\007', 'm', 'a', 'p', +'p', 'e', 'r', 's', '\022', 'O', '\n', '\013', 'b', 'o', 'd', 'y', '_', 'f', 'o', 'r', 'm', 'a', 't', '\030', '\002', ' ', '\001', '(', '\013', +'2', '.', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'S', +'u', 'b', 's', 't', 'i', 't', 'u', 't', 'i', 'o', 'n', 'F', 'o', 'r', 'm', 'a', 't', 'S', 't', 'r', 'i', 'n', 'g', 'R', '\n', +'b', 'o', 'd', 'y', 'F', 'o', 'r', 'm', 'a', 't', '\"', '\234', '\003', '\n', '\016', 'R', 'e', 's', 'p', 'o', 'n', 's', 'e', 'M', 'a', +'p', 'p', 'e', 'r', '\022', 'L', '\n', '\006', 'f', 'i', 'l', 't', 'e', 'r', '\030', '\001', ' ', '\001', '(', '\013', '2', '*', '.', 'e', 'n', +'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'a', 'c', 'c', 'e', 's', 's', 'l', 'o', 'g', '.', 'v', '3', '.', 'A', +'c', 'c', 'e', 's', 's', 'L', 'o', 'g', 'F', 'i', 'l', 't', 'e', 'r', 'B', '\010', '\372', 'B', '\005', '\212', '\001', '\002', '\020', '\001', 'R', +'\006', 'f', 'i', 'l', 't', 'e', 'r', '\022', 'J', '\n', '\013', 's', 't', 'a', 't', 'u', 's', '_', 'c', 'o', 'd', 'e', '\030', '\002', ' ', +'\001', '(', '\013', '2', '\034', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'I', 'n', +'t', '3', '2', 'V', 'a', 'l', 'u', 'e', 'B', '\013', '\372', 'B', '\010', '*', '\006', '\020', '\330', '\004', '(', '\310', '\001', 'R', '\n', 's', 't', +'a', 't', 'u', 's', 'C', 'o', 'd', 'e', '\022', '4', '\n', '\004', 'b', 'o', 'd', 'y', '\030', '\003', ' ', '\001', '(', '\013', '2', ' ', '.', +'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', 'a', 't', 'a', +'S', 'o', 'u', 'r', 'c', 'e', 'R', '\004', 'b', 'o', 'd', 'y', '\022', '`', '\n', '\024', 'b', 'o', 'd', 'y', '_', 'f', 'o', 'r', 'm', +'a', 't', '_', 'o', 'v', 'e', 'r', 'r', 'i', 'd', 'e', '\030', '\004', ' ', '\001', '(', '\013', '2', '.', '.', 'e', 'n', 'v', 'o', 'y', +'.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'S', 'u', 'b', 's', 't', 'i', 't', 'u', 't', +'i', 'o', 'n', 'F', 'o', 'r', 'm', 'a', 't', 'S', 't', 'r', 'i', 'n', 'g', 'R', '\022', 'b', 'o', 'd', 'y', 'F', 'o', 'r', 'm', +'a', 't', 'O', 'v', 'e', 'r', 'r', 'i', 'd', 'e', '\022', 'X', '\n', '\016', 'h', 'e', 'a', 'd', 'e', 'r', 's', '_', 't', 'o', '_', +'a', 'd', 'd', '\030', '\005', ' ', '\003', '(', '\013', '2', '\'', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', +'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'H', 'e', 'a', 'd', 'e', 'r', 'V', 'a', 'l', 'u', 'e', 'O', 'p', 't', 'i', 'o', 'n', +'B', '\t', '\372', 'B', '\006', '\222', '\001', '\003', '\020', '\350', '\007', 'R', '\014', 'h', 'e', 'a', 'd', 'e', 'r', 's', 'T', 'o', 'A', 'd', 'd', +'\"', '\307', '\001', '\n', '\003', 'R', 'd', 's', '\022', 'Q', '\n', '\r', 'c', 'o', 'n', 'f', 'i', 'g', '_', 's', 'o', 'u', 'r', 'c', 'e', +'\030', '\001', ' ', '\001', '(', '\013', '2', '\"', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', +'e', '.', 'v', '3', '.', 'C', 'o', 'n', 'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', 'e', 'B', '\010', '\372', 'B', '\005', '\212', '\001', '\002', +'\020', '\001', 'R', '\014', 'c', 'o', 'n', 'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', 'e', '\022', '*', '\n', '\021', 'r', 'o', 'u', 't', 'e', +'_', 'c', 'o', 'n', 'f', 'i', 'g', '_', 'n', 'a', 'm', 'e', '\030', '\002', ' ', '\001', '(', '\t', 'R', '\017', 'r', 'o', 'u', 't', 'e', +'C', 'o', 'n', 'f', 'i', 'g', 'N', 'a', 'm', 'e', ':', 'A', '\232', '\305', '\210', '\036', '<', '\n', ':', 'e', 'n', 'v', 'o', 'y', '.', +'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', +'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'R', +'d', 's', '\"', '\367', '\001', '\n', '\035', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 'C', 'o', 'n', 'f', 'i', 'g', 'u', +'r', 'a', 't', 'i', 'o', 'n', 's', 'L', 'i', 's', 't', '\022', 'y', '\n', '\033', 's', 'c', 'o', 'p', 'e', 'd', '_', 'r', 'o', 'u', +'t', 'e', '_', 'c', 'o', 'n', 'f', 'i', 'g', 'u', 'r', 'a', 't', 'i', 'o', 'n', 's', '\030', '\001', ' ', '\003', '(', '\013', '2', '/', +'.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'r', 'o', 'u', 't', 'e', '.', 'v', '3', '.', 'S', 'c', +'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 'C', 'o', 'n', 'f', 'i', 'g', 'u', 'r', 'a', 't', 'i', 'o', 'n', 'B', '\010', '\372', +'B', '\005', '\222', '\001', '\002', '\010', '\001', 'R', '\031', 's', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 'C', 'o', 'n', 'f', 'i', +'g', 'u', 'r', 'a', 't', 'i', 'o', 'n', 's', ':', '[', '\232', '\305', '\210', '\036', 'V', '\n', 'T', 'e', 'n', 'v', 'o', 'y', '.', 'c', +'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', +'_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'S', 'c', +'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 'C', 'o', 'n', 'f', 'i', 'g', 'u', 'r', 'a', 't', 'i', 'o', 'n', 's', 'L', 'i', +'s', 't', '\"', '\337', '\016', '\n', '\014', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', '\022', '\033', '\n', '\004', 'n', 'a', +'m', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'B', '\007', '\372', 'B', '\004', 'r', '\002', '\020', '\001', 'R', '\004', 'n', 'a', 'm', 'e', '\022', '\217', +'\001', '\n', '\021', 's', 'c', 'o', 'p', 'e', '_', 'k', 'e', 'y', '_', 'b', 'u', 'i', 'l', 'd', 'e', 'r', '\030', '\002', ' ', '\001', '(', +'\013', '2', 'Y', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', +'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', +'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', +'s', '.', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', 'B', '\010', '\372', 'B', '\005', '\212', '\001', '\002', +'\020', '\001', 'R', '\017', 's', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '\022', 'N', '\n', '\021', 'r', 'd', +'s', '_', 'c', 'o', 'n', 'f', 'i', 'g', '_', 's', 'o', 'u', 'r', 'c', 'e', '\030', '\003', ' ', '\001', '(', '\013', '2', '\"', '.', 'e', +'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'C', 'o', 'n', 'f', 'i', +'g', 'S', 'o', 'u', 'r', 'c', 'e', 'R', '\017', 'r', 'd', 's', 'C', 'o', 'n', 'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', 'e', '\022', +'\245', '\001', '\n', ' ', 's', 'c', 'o', 'p', 'e', 'd', '_', 'r', 'o', 'u', 't', 'e', '_', 'c', 'o', 'n', 'f', 'i', 'g', 'u', 'r', +'a', 't', 'i', 'o', 'n', 's', '_', 'l', 'i', 's', 't', '\030', '\004', ' ', '\001', '(', '\013', '2', 'Z', '.', 'e', 'n', 'v', 'o', 'y', +'.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', +'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', +'r', '.', 'v', '3', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 'C', 'o', 'n', 'f', 'i', 'g', 'u', 'r', 'a', +'t', 'i', 'o', 'n', 's', 'L', 'i', 's', 't', 'H', '\000', 'R', '\035', 's', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 'C', +'o', 'n', 'f', 'i', 'g', 'u', 'r', 'a', 't', 'i', 'o', 'n', 's', 'L', 'i', 's', 't', '\022', 'g', '\n', '\n', 's', 'c', 'o', 'p', +'e', 'd', '_', 'r', 'd', 's', '\030', '\005', ' ', '\001', '(', '\013', '2', 'F', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', +'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', +'t', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', +'S', 'c', 'o', 'p', 'e', 'd', 'R', 'd', 's', 'H', '\000', 'R', '\t', 's', 'c', 'o', 'p', 'e', 'd', 'R', 'd', 's', '\032', '\331', '\t', +'\n', '\017', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '\022', '\221', '\001', '\n', '\t', 'f', 'r', 'a', +'g', 'm', 'e', 'n', 't', 's', '\030', '\001', ' ', '\003', '(', '\013', '2', 'i', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', +'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', +'t', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', +'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', '.', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', +'d', 'e', 'r', '.', 'F', 'r', 'a', 'g', 'm', 'e', 'n', 't', 'B', 'u', 'i', 'l', 'd', 'e', 'r', 'B', '\010', '\372', 'B', '\005', '\222', +'\001', '\002', '\010', '\001', 'R', '\t', 'f', 'r', 'a', 'g', 'm', 'e', 'n', 't', 's', '\032', '\325', '\007', '\n', '\017', 'F', 'r', 'a', 'g', 'm', +'e', 'n', 't', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '\022', '\266', '\001', '\n', '\026', 'h', 'e', 'a', 'd', 'e', 'r', '_', 'v', 'a', 'l', +'u', 'e', '_', 'e', 'x', 't', 'r', 'a', 'c', 't', 'o', 'r', '\030', '\001', ' ', '\001', '(', '\013', '2', '~', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', '.', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '.', 'F', 'r', 'a', 'g', 'm', 'e', 'n', 't', 'B', 'u', 'i', 'l', 'd', 'e', 'r', -'.', 'H', 'e', 'a', 'd', 'e', 'r', 'V', 'a', 'l', 'u', 'e', 'E', 'x', 't', 'r', 'a', 'c', 't', 'o', 'r', '.', 'K', 'v', 'E', -'l', 'e', 'm', 'e', 'n', 't', 'H', '\000', 'R', '\007', 'e', 'l', 'e', 'm', 'e', 'n', 't', '\032', '\333', '\001', '\n', '\t', 'K', 'v', 'E', -'l', 'e', 'm', 'e', 'n', 't', '\022', '%', '\n', '\t', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', '\030', '\001', ' ', '\001', '(', '\t', -'B', '\007', '\372', 'B', '\004', 'r', '\002', '\020', '\001', 'R', '\t', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', '\022', '\031', '\n', '\003', 'k', -'e', 'y', '\030', '\002', ' ', '\001', '(', '\t', 'B', '\007', '\372', 'B', '\004', 'r', '\002', '\020', '\001', 'R', '\003', 'k', 'e', 'y', ':', '\213', '\001', -'\232', '\305', '\210', '\036', '\205', '\001', '\n', '\202', '\001', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', -'t', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', -'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', -'s', '.', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '.', 'F', 'r', 'a', 'g', 'm', 'e', 'n', -'t', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '.', 'H', 'e', 'a', 'd', 'e', 'r', 'V', 'a', 'l', 'u', 'e', 'E', 'x', 't', 'r', 'a', -'c', 't', 'o', 'r', '.', 'K', 'v', 'E', 'l', 'e', 'm', 'e', 'n', 't', ':', '\177', '\232', '\305', '\210', '\036', 'z', '\n', 'x', 'e', 'n', -'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', -'.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', -'v', '2', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', '.', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', -'u', 'i', 'l', 'd', 'e', 'r', '.', 'F', 'r', 'a', 'g', 'm', 'e', 'n', 't', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '.', 'H', 'e', -'a', 'd', 'e', 'r', 'V', 'a', 'l', 'u', 'e', 'E', 'x', 't', 'r', 'a', 'c', 't', 'o', 'r', 'B', '\016', '\n', '\014', 'e', 'x', 't', -'r', 'a', 'c', 't', '_', 't', 'y', 'p', 'e', ':', 'j', '\232', '\305', '\210', '\036', 'e', '\n', 'c', 'e', 'n', 'v', 'o', 'y', '.', 'c', -'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', -'_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'S', 'c', -'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', '.', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', -'r', '.', 'F', 'r', 'a', 'g', 'm', 'e', 'n', 't', 'B', 'u', 'i', 'l', 'd', 'e', 'r', 'B', '\013', '\n', '\004', 't', 'y', 'p', 'e', -'\022', '\003', '\370', 'B', '\001', ':', 'Z', '\232', '\305', '\210', '\036', 'U', '\n', 'S', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', -'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', -'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'S', 'c', 'o', 'p', 'e', 'd', -'R', 'o', 'u', 't', 'e', 's', '.', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', ':', 'J', '\232', -'\305', '\210', '\036', 'E', '\n', 'C', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', -'.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', -'m', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', 'B', '\027', -'\n', '\020', 'c', 'o', 'n', 'f', 'i', 'g', '_', 's', 'p', 'e', 'c', 'i', 'f', 'i', 'e', 'r', '\022', '\003', '\370', 'B', '\001', '\"', '\361', -'\001', '\n', '\t', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'd', 's', '\022', 'e', '\n', '\030', 's', 'c', 'o', 'p', 'e', 'd', '_', 'r', 'd', -'s', '_', 'c', 'o', 'n', 'f', 'i', 'g', '_', 's', 'o', 'u', 'r', 'c', 'e', '\030', '\001', ' ', '\001', '(', '\013', '2', '\"', '.', 'e', -'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'C', 'o', 'n', 'f', 'i', -'g', 'S', 'o', 'u', 'r', 'c', 'e', 'B', '\010', '\372', 'B', '\005', '\212', '\001', '\002', '\020', '\001', 'R', '\025', 's', 'c', 'o', 'p', 'e', 'd', -'R', 'd', 's', 'C', 'o', 'n', 'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', 'e', '\022', '4', '\n', '\026', 's', 'r', 'd', 's', '_', 'r', -'e', 's', 'o', 'u', 'r', 'c', 'e', 's', '_', 'l', 'o', 'c', 'a', 't', 'o', 'r', '\030', '\002', ' ', '\001', '(', '\t', 'R', '\024', 's', -'r', 'd', 's', 'R', 'e', 's', 'o', 'u', 'r', 'c', 'e', 's', 'L', 'o', 'c', 'a', 't', 'o', 'r', ':', 'G', '\232', '\305', '\210', '\036', -'B', '\n', '@', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', +'.', 'H', 'e', 'a', 'd', 'e', 'r', 'V', 'a', 'l', 'u', 'e', 'E', 'x', 't', 'r', 'a', 'c', 't', 'o', 'r', 'H', '\000', 'R', '\024', +'h', 'e', 'a', 'd', 'e', 'r', 'V', 'a', 'l', 'u', 'e', 'E', 'x', 't', 'r', 'a', 'c', 't', 'o', 'r', '\032', '\217', '\005', '\n', '\024', +'H', 'e', 'a', 'd', 'e', 'r', 'V', 'a', 'l', 'u', 'e', 'E', 'x', 't', 'r', 'a', 'c', 't', 'o', 'r', '\022', '\033', '\n', '\004', 'n', +'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'B', '\007', '\372', 'B', '\004', 'r', '\002', '\020', '\001', 'R', '\004', 'n', 'a', 'm', 'e', '\022', +'+', '\n', '\021', 'e', 'l', 'e', 'm', 'e', 'n', 't', '_', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', '\030', '\002', ' ', '\001', '(', +'\t', 'R', '\020', 'e', 'l', 'e', 'm', 'e', 'n', 't', 'S', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', '\022', '\026', '\n', '\005', 'i', 'n', +'d', 'e', 'x', '\030', '\003', ' ', '\001', '(', '\r', 'H', '\000', 'R', '\005', 'i', 'n', 'd', 'e', 'x', '\022', '\245', '\001', '\n', '\007', 'e', 'l', +'e', 'm', 'e', 'n', 't', '\030', '\004', ' ', '\001', '(', '\013', '2', '\210', '\001', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', +'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', +'t', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', +'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', '.', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', +'d', 'e', 'r', '.', 'F', 'r', 'a', 'g', 'm', 'e', 'n', 't', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '.', 'H', 'e', 'a', 'd', 'e', +'r', 'V', 'a', 'l', 'u', 'e', 'E', 'x', 't', 'r', 'a', 'c', 't', 'o', 'r', '.', 'K', 'v', 'E', 'l', 'e', 'm', 'e', 'n', 't', +'H', '\000', 'R', '\007', 'e', 'l', 'e', 'm', 'e', 'n', 't', '\032', '\333', '\001', '\n', '\t', 'K', 'v', 'E', 'l', 'e', 'm', 'e', 'n', 't', +'\022', '%', '\n', '\t', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', '\030', '\001', ' ', '\001', '(', '\t', 'B', '\007', '\372', 'B', '\004', 'r', +'\002', '\020', '\001', 'R', '\t', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', '\022', '\031', '\n', '\003', 'k', 'e', 'y', '\030', '\002', ' ', '\001', +'(', '\t', 'B', '\007', '\372', 'B', '\004', 'r', '\002', '\020', '\001', 'R', '\003', 'k', 'e', 'y', ':', '\213', '\001', '\232', '\305', '\210', '\036', '\205', '\001', +'\n', '\202', '\001', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', -'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'd', 's', '\"', '\314', '\002', '\n', '\n', 'H', 't', 't', -'p', 'F', 'i', 'l', 't', 'e', 'r', '\022', '\033', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'B', '\007', '\372', 'B', -'\004', 'r', '\002', '\020', '\001', 'R', '\004', 'n', 'a', 'm', 'e', '\022', '9', '\n', '\014', 't', 'y', 'p', 'e', 'd', '_', 'c', 'o', 'n', 'f', -'i', 'g', '\030', '\004', ' ', '\001', '(', '\013', '2', '\024', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', -'f', '.', 'A', 'n', 'y', 'H', '\000', 'R', '\013', 't', 'y', 'p', 'e', 'd', 'C', 'o', 'n', 'f', 'i', 'g', '\022', 'X', '\n', '\020', 'c', -'o', 'n', 'f', 'i', 'g', '_', 'd', 'i', 's', 'c', 'o', 'v', 'e', 'r', 'y', '\030', '\005', ' ', '\001', '(', '\013', '2', '+', '.', 'e', -'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'E', 'x', 't', 'e', 'n', -'s', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', 'e', 'H', '\000', 'R', '\017', 'c', 'o', 'n', 'f', 'i', -'g', 'D', 'i', 's', 'c', 'o', 'v', 'e', 'r', 'y', '\022', '\037', '\n', '\013', 'i', 's', '_', 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l', -'\030', '\006', ' ', '\001', '(', '\010', 'R', '\n', 'i', 's', 'O', 'p', 't', 'i', 'o', 'n', 'a', 'l', ':', 'H', '\232', '\305', '\210', '\036', 'C', -'\n', 'A', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', -'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', -'g', 'e', 'r', '.', 'v', '2', '.', 'H', 't', 't', 'p', 'F', 'i', 'l', 't', 'e', 'r', 'B', '\r', '\n', '\013', 'c', 'o', 'n', 'f', -'i', 'g', '_', 't', 'y', 'p', 'e', 'J', '\004', '\010', '\003', '\020', '\004', 'J', '\004', '\010', '\002', '\020', '\003', 'R', '\006', 'c', 'o', 'n', 'f', -'i', 'g', '\"', '\237', '\001', '\n', '\022', 'R', 'e', 'q', 'u', 'e', 's', 't', 'I', 'D', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', -'\022', '7', '\n', '\014', 't', 'y', 'p', 'e', 'd', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '\001', ' ', '\001', '(', '\013', '2', '\024', '.', -'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'A', 'n', 'y', 'R', '\013', 't', 'y', 'p', 'e', -'d', 'C', 'o', 'n', 'f', 'i', 'g', ':', 'P', '\232', '\305', '\210', '\036', 'K', '\n', 'I', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', -'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', -'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'R', 'e', 'q', 'u', -'e', 's', 't', 'I', 'D', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '\"', '\216', '\001', '\n', ' ', 'E', 'n', 'v', 'o', 'y', 'M', -'o', 'b', 'i', 'l', 'e', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', -'r', '\022', 'j', '\n', '\006', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '\001', ' ', '\001', '(', '\013', '2', 'R', '.', 'e', 'n', 'v', 'o', 'y', -'.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', +'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', '.', 'S', 'c', 'o', 'p', +'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '.', 'F', 'r', 'a', 'g', 'm', 'e', 'n', 't', 'B', 'u', 'i', 'l', 'd', +'e', 'r', '.', 'H', 'e', 'a', 'd', 'e', 'r', 'V', 'a', 'l', 'u', 'e', 'E', 'x', 't', 'r', 'a', 'c', 't', 'o', 'r', '.', 'K', +'v', 'E', 'l', 'e', 'm', 'e', 'n', 't', ':', '\177', '\232', '\305', '\210', '\036', 'z', '\n', 'x', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', +'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', +'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'S', 'c', 'o', +'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', '.', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', +'.', 'F', 'r', 'a', 'g', 'm', 'e', 'n', 't', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '.', 'H', 'e', 'a', 'd', 'e', 'r', 'V', 'a', +'l', 'u', 'e', 'E', 'x', 't', 'r', 'a', 'c', 't', 'o', 'r', 'B', '\016', '\n', '\014', 'e', 'x', 't', 'r', 'a', 'c', 't', '_', 't', +'y', 'p', 'e', ':', 'j', '\232', '\305', '\210', '\036', 'e', '\n', 'c', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', +'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', +'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', +'u', 't', 'e', 's', '.', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', '.', 'F', 'r', 'a', 'g', +'m', 'e', 'n', 't', 'B', 'u', 'i', 'l', 'd', 'e', 'r', 'B', '\013', '\n', '\004', 't', 'y', 'p', 'e', '\022', '\003', '\370', 'B', '\001', ':', +'Z', '\232', '\305', '\210', '\036', 'U', '\n', 'S', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', +'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', +'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', +'.', 'S', 'c', 'o', 'p', 'e', 'K', 'e', 'y', 'B', 'u', 'i', 'l', 'd', 'e', 'r', ':', 'J', '\232', '\305', '\210', '\036', 'E', '\n', 'C', +'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', -'r', '.', 'v', '3', '.', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', -'r', 'R', '\006', 'c', 'o', 'n', 'f', 'i', 'g', 'B', '\357', '\001', '\n', 'I', 'i', 'o', '.', 'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', -'x', 'y', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', -'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', -'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', 'B', '\032', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', -'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', 'P', 'r', 'o', 't', 'o', 'P', '\001', 'Z', '|', 'g', 'i', 't', 'h', 'u', 'b', -'.', 'c', 'o', 'm', '/', 'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', 'x', 'y', '/', 'g', 'o', '-', 'c', 'o', 'n', 't', 'r', 'o', -'l', '-', 'p', 'l', 'a', 'n', 'e', '/', 'e', 'n', 'v', 'o', 'y', '/', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '/', -'f', 'i', 'l', 't', 'e', 'r', 's', '/', 'n', 'e', 't', 'w', 'o', 'r', 'k', '/', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', -'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '/', 'v', '3', ';', 'h', 't', 't', 'p', '_', 'c', 'o', -'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', 'v', '3', '\272', '\200', '\310', '\321', '\006', '\002', '\020', -'\002', 'b', '\006', 'p', 'r', 'o', 't', 'o', '3', +'r', '.', 'v', '2', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'o', 'u', 't', 'e', 's', 'B', '\027', '\n', '\020', 'c', 'o', 'n', 'f', +'i', 'g', '_', 's', 'p', 'e', 'c', 'i', 'f', 'i', 'e', 'r', '\022', '\003', '\370', 'B', '\001', '\"', '\361', '\001', '\n', '\t', 'S', 'c', 'o', +'p', 'e', 'd', 'R', 'd', 's', '\022', 'e', '\n', '\030', 's', 'c', 'o', 'p', 'e', 'd', '_', 'r', 'd', 's', '_', 'c', 'o', 'n', 'f', +'i', 'g', '_', 's', 'o', 'u', 'r', 'c', 'e', '\030', '\001', ' ', '\001', '(', '\013', '2', '\"', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', +'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'C', 'o', 'n', 'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', +'e', 'B', '\010', '\372', 'B', '\005', '\212', '\001', '\002', '\020', '\001', 'R', '\025', 's', 'c', 'o', 'p', 'e', 'd', 'R', 'd', 's', 'C', 'o', 'n', +'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', 'e', '\022', '4', '\n', '\026', 's', 'r', 'd', 's', '_', 'r', 'e', 's', 'o', 'u', 'r', 'c', +'e', 's', '_', 'l', 'o', 'c', 'a', 't', 'o', 'r', '\030', '\002', ' ', '\001', '(', '\t', 'R', '\024', 's', 'r', 'd', 's', 'R', 'e', 's', +'o', 'u', 'r', 'c', 'e', 's', 'L', 'o', 'c', 'a', 't', 'o', 'r', ':', 'G', '\232', '\305', '\210', '\036', 'B', '\n', '@', 'e', 'n', 'v', +'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', +'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', +'2', '.', 'S', 'c', 'o', 'p', 'e', 'd', 'R', 'd', 's', '\"', '\314', '\002', '\n', '\n', 'H', 't', 't', 'p', 'F', 'i', 'l', 't', 'e', +'r', '\022', '\033', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'B', '\007', '\372', 'B', '\004', 'r', '\002', '\020', '\001', 'R', +'\004', 'n', 'a', 'm', 'e', '\022', '9', '\n', '\014', 't', 'y', 'p', 'e', 'd', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '\004', ' ', '\001', +'(', '\013', '2', '\024', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'A', 'n', 'y', 'H', +'\000', 'R', '\013', 't', 'y', 'p', 'e', 'd', 'C', 'o', 'n', 'f', 'i', 'g', '\022', 'X', '\n', '\020', 'c', 'o', 'n', 'f', 'i', 'g', '_', +'d', 'i', 's', 'c', 'o', 'v', 'e', 'r', 'y', '\030', '\005', ' ', '\001', '(', '\013', '2', '+', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', +'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'C', 'o', +'n', 'f', 'i', 'g', 'S', 'o', 'u', 'r', 'c', 'e', 'H', '\000', 'R', '\017', 'c', 'o', 'n', 'f', 'i', 'g', 'D', 'i', 's', 'c', 'o', +'v', 'e', 'r', 'y', '\022', '\037', '\n', '\013', 'i', 's', '_', 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l', '\030', '\006', ' ', '\001', '(', '\010', +'R', '\n', 'i', 's', 'O', 'p', 't', 'i', 'o', 'n', 'a', 'l', ':', 'H', '\232', '\305', '\210', '\036', 'C', '\n', 'A', 'e', 'n', 'v', 'o', +'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', 'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', +'t', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', +'.', 'H', 't', 't', 'p', 'F', 'i', 'l', 't', 'e', 'r', 'B', '\r', '\n', '\013', 'c', 'o', 'n', 'f', 'i', 'g', '_', 't', 'y', 'p', +'e', 'J', '\004', '\010', '\003', '\020', '\004', 'J', '\004', '\010', '\002', '\020', '\003', 'R', '\006', 'c', 'o', 'n', 'f', 'i', 'g', '\"', '\237', '\001', '\n', +'\022', 'R', 'e', 'q', 'u', 'e', 's', 't', 'I', 'D', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '\022', '7', '\n', '\014', 't', 'y', +'p', 'e', 'd', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '\001', ' ', '\001', '(', '\013', '2', '\024', '.', 'g', 'o', 'o', 'g', 'l', 'e', +'.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'A', 'n', 'y', 'R', '\013', 't', 'y', 'p', 'e', 'd', 'C', 'o', 'n', 'f', 'i', +'g', ':', 'P', '\232', '\305', '\210', '\036', 'K', '\n', 'I', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'f', 'i', +'l', 't', 'e', 'r', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', +'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '2', '.', 'R', 'e', 'q', 'u', 'e', 's', 't', 'I', 'D', 'E', +'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '\"', '\216', '\001', '\n', ' ', 'E', 'n', 'v', 'o', 'y', 'M', 'o', 'b', 'i', 'l', 'e', 'H', +'t', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', '\022', 'j', '\n', '\006', 'c', +'o', 'n', 'f', 'i', 'g', '\030', '\001', ' ', '\001', '(', '\013', '2', 'R', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', +'s', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', 'w', 'o', 'r', 'k', '.', 'h', 't', 't', +'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '.', 'v', '3', '.', 'H', +'t', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', 'a', 'g', 'e', 'r', 'R', '\006', 'c', 'o', 'n', +'f', 'i', 'g', 'B', '\357', '\001', '\n', 'I', 'i', 'o', '.', 'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', 'x', 'y', '.', 'e', 'n', 'v', +'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 'f', 'i', 'l', 't', 'e', 'r', 's', '.', 'n', 'e', 't', +'w', 'o', 'r', 'k', '.', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', '_', 'm', 'a', 'n', 'a', +'g', 'e', 'r', '.', 'v', '3', 'B', '\032', 'H', 't', 't', 'p', 'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', 'M', 'a', 'n', +'a', 'g', 'e', 'r', 'P', 'r', 'o', 't', 'o', 'P', '\001', 'Z', '|', 'g', 'i', 't', 'h', 'u', 'b', '.', 'c', 'o', 'm', '/', 'e', +'n', 'v', 'o', 'y', 'p', 'r', 'o', 'x', 'y', '/', 'g', 'o', '-', 'c', 'o', 'n', 't', 'r', 'o', 'l', '-', 'p', 'l', 'a', 'n', +'e', '/', 'e', 'n', 'v', 'o', 'y', '/', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '/', 'f', 'i', 'l', 't', 'e', 'r', +'s', '/', 'n', 'e', 't', 'w', 'o', 'r', 'k', '/', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', +'_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', '/', 'v', '3', ';', 'h', 't', 't', 'p', '_', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', +'o', 'n', '_', 'm', 'a', 'n', 'a', 'g', 'e', 'r', 'v', '3', '\272', '\200', '\310', '\321', '\006', '\002', '\020', '\002', 'b', '\006', 'p', 'r', 'o', +'t', 'o', '3', }; static _upb_DefPool_Init *deps[22] = { @@ -573,5 +576,5 @@ _upb_DefPool_Init envoy_extensions_filters_network_http_connection_manager_v3_ht deps, &envoy_extensions_filters_network_http_connection_manager_v3_http_connection_manager_proto_upb_file_layout, "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto", - UPB_STRINGVIEW_INIT(descriptor, 12759) + UPB_STRINGVIEW_INIT(descriptor, 12828) }; diff --git a/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/common.upbdefs.c b/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/common.upbdefs.c index a1da4acf2e872..b88aba6bde437 100644 --- a/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/common.upbdefs.c +++ b/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/common.upbdefs.c @@ -21,7 +21,7 @@ extern _upb_DefPool_Init udpa_annotations_sensitive_proto_upbdefinit; extern _upb_DefPool_Init udpa_annotations_status_proto_upbdefinit; extern _upb_DefPool_Init udpa_annotations_versioning_proto_upbdefinit; extern _upb_DefPool_Init validate_validate_proto_upbdefinit; -static const char descriptor[4173] = {'\n', '6', 'e', 'n', 'v', 'o', 'y', '/', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '/', 't', 'r', 'a', 'n', 's', 'p', +static const char descriptor[4224] = {'\n', '6', 'e', 'n', 'v', 'o', 'y', '/', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '/', 't', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '/', 't', 'l', 's', '/', 'v', '3', '/', 'c', 'o', 'm', 'm', 'o', 'n', '.', 'p', 'r', 'o', 't', 'o', '\022', ')', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 't', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '.', 't', 'l', 's', '.', 'v', '3', '\032', @@ -39,7 +39,7 @@ static const char descriptor[4173] = {'\n', '6', 'e', 'n', 'v', 'o', 'y', '/', ' 'a', 'n', 'n', 'o', 't', 'a', 't', 'i', 'o', 'n', 's', '/', 's', 't', 'a', 't', 'u', 's', '.', 'p', 'r', 'o', 't', 'o', '\032', '!', 'u', 'd', 'p', 'a', '/', 'a', 'n', 'n', 'o', 't', 'a', 't', 'i', 'o', 'n', 's', '/', 'v', 'e', 'r', 's', 'i', 'o', 'n', 'i', 'n', 'g', '.', 'p', 'r', 'o', 't', 'o', '\032', '\027', 'v', 'a', 'l', 'i', 'd', 'a', 't', 'e', '/', 'v', 'a', 'l', 'i', 'd', -'a', 't', 'e', '.', 'p', 'r', 'o', 't', 'o', '\"', '\362', '\003', '\n', '\r', 'T', 'l', 's', 'P', 'a', 'r', 'a', 'm', 'e', 't', 'e', +'a', 't', 'e', '.', 'p', 'r', 'o', 't', 'o', '\"', '\245', '\004', '\n', '\r', 'T', 'l', 's', 'P', 'a', 'r', 'a', 'm', 'e', 't', 'e', 'r', 's', '\022', '\217', '\001', '\n', '\034', 't', 'l', 's', '_', 'm', 'i', 'n', 'i', 'm', 'u', 'm', '_', 'p', 'r', 'o', 't', 'o', 'c', 'o', 'l', '_', 'v', 'e', 'r', 's', 'i', 'o', 'n', '\030', '\001', ' ', '\001', '(', '\016', '2', 'D', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 't', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', @@ -54,140 +54,142 @@ static const char descriptor[4173] = {'\n', '6', 'e', 'n', 'v', 'o', 'y', '/', ' 'i', 'm', 'u', 'm', 'P', 'r', 'o', 't', 'o', 'c', 'o', 'l', 'V', 'e', 'r', 's', 'i', 'o', 'n', '\022', '#', '\n', '\r', 'c', 'i', 'p', 'h', 'e', 'r', '_', 's', 'u', 'i', 't', 'e', 's', '\030', '\003', ' ', '\003', '(', '\t', 'R', '\014', 'c', 'i', 'p', 'h', 'e', 'r', 'S', 'u', 'i', 't', 'e', 's', '\022', '\037', '\n', '\013', 'e', 'c', 'd', 'h', '_', 'c', 'u', 'r', 'v', 'e', 's', '\030', '\004', ' ', '\003', -'(', '\t', 'R', '\n', 'e', 'c', 'd', 'h', 'C', 'u', 'r', 'v', 'e', 's', '\"', 'O', '\n', '\013', 'T', 'l', 's', 'P', 'r', 'o', 't', -'o', 'c', 'o', 'l', '\022', '\014', '\n', '\010', 'T', 'L', 'S', '_', 'A', 'U', 'T', 'O', '\020', '\000', '\022', '\013', '\n', '\007', 'T', 'L', 'S', -'v', '1', '_', '0', '\020', '\001', '\022', '\013', '\n', '\007', 'T', 'L', 'S', 'v', '1', '_', '1', '\020', '\002', '\022', '\013', '\n', '\007', 'T', 'L', -'S', 'v', '1', '_', '2', '\020', '\003', '\022', '\013', '\n', '\007', 'T', 'L', 'S', 'v', '1', '_', '3', '\020', '\004', ':', '&', '\232', '\305', '\210', -'\036', '!', '\n', '\037', 'e', 'n', 'v', 'o', 'y', '.', 'a', 'p', 'i', '.', 'v', '2', '.', 'a', 'u', 't', 'h', '.', 'T', 'l', 's', -'P', 'a', 'r', 'a', 'm', 'e', 't', 'e', 'r', 's', '\"', '\317', '\001', '\n', '\022', 'P', 'r', 'i', 'v', 'a', 't', 'e', 'K', 'e', 'y', -'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r', '\022', ',', '\n', '\r', 'p', 'r', 'o', 'v', 'i', 'd', 'e', 'r', '_', 'n', 'a', 'm', 'e', -'\030', '\001', ' ', '\001', '(', '\t', 'B', '\007', '\372', 'B', '\004', 'r', '\002', '\020', '\001', 'R', '\014', 'p', 'r', 'o', 'v', 'i', 'd', 'e', 'r', -'N', 'a', 'm', 'e', '\022', 'A', '\n', '\014', 't', 'y', 'p', 'e', 'd', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '\003', ' ', '\001', '(', -'\013', '2', '\024', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'A', 'n', 'y', 'B', '\006', -'\270', '\267', '\213', '\244', '\002', '\001', 'H', '\000', 'R', '\013', 't', 'y', 'p', 'e', 'd', 'C', 'o', 'n', 'f', 'i', 'g', ':', '+', '\232', '\305', -'\210', '\036', '&', '\n', '$', 'e', 'n', 'v', 'o', 'y', '.', 'a', 'p', 'i', '.', 'v', '2', '.', 'a', 'u', 't', 'h', '.', 'P', 'r', -'i', 'v', 'a', 't', 'e', 'K', 'e', 'y', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r', 'B', '\r', '\n', '\013', 'c', 'o', 'n', 'f', 'i', -'g', '_', 't', 'y', 'p', 'e', 'J', '\004', '\010', '\002', '\020', '\003', 'R', '\006', 'c', 'o', 'n', 'f', 'i', 'g', '\"', '\310', '\005', '\n', '\016', -'T', 'l', 's', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '\022', 'M', '\n', '\021', 'c', 'e', 'r', 't', 'i', 'f', 'i', -'c', 'a', 't', 'e', '_', 'c', 'h', 'a', 'i', 'n', '\030', '\001', ' ', '\001', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', -'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', -'R', '\020', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'C', 'h', 'a', 'i', 'n', '\022', 'I', '\n', '\013', 'p', 'r', 'i', -'v', 'a', 't', 'e', '_', 'k', 'e', 'y', '\030', '\002', ' ', '\001', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', -'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'B', '\006', -'\270', '\267', '\213', '\244', '\002', '\001', 'R', '\n', 'p', 'r', 'i', 'v', 'a', 't', 'e', 'K', 'e', 'y', '\022', '@', '\n', '\006', 'p', 'k', 'c', -'s', '1', '2', '\030', '\010', ' ', '\001', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', -'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'B', '\006', '\270', '\267', '\213', '\244', '\002', -'\001', 'R', '\006', 'p', 'k', 'c', 's', '1', '2', '\022', 'S', '\n', '\021', 'w', 'a', 't', 'c', 'h', 'e', 'd', '_', 'd', 'i', 'r', 'e', -'c', 't', 'o', 'r', 'y', '\030', '\007', ' ', '\001', '(', '\013', '2', '&', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', -'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'W', 'a', 't', 'c', 'h', 'e', 'd', 'D', 'i', 'r', 'e', 'c', 't', 'o', 'r', -'y', 'R', '\020', 'w', 'a', 't', 'c', 'h', 'e', 'd', 'D', 'i', 'r', 'e', 'c', 't', 'o', 'r', 'y', '\022', 'o', '\n', '\024', 'p', 'r', -'i', 'v', 'a', 't', 'e', '_', 'k', 'e', 'y', '_', 'p', 'r', 'o', 'v', 'i', 'd', 'e', 'r', '\030', '\006', ' ', '\001', '(', '\013', '2', -'=', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 't', 'r', 'a', 'n', 's', 'p', -'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '.', 't', 'l', 's', '.', 'v', '3', '.', 'P', 'r', 'i', 'v', 'a', 't', -'e', 'K', 'e', 'y', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r', 'R', '\022', 'p', 'r', 'i', 'v', 'a', 't', 'e', 'K', 'e', 'y', 'P', -'r', 'o', 'v', 'i', 'd', 'e', 'r', '\022', 'D', '\n', '\010', 'p', 'a', 's', 's', 'w', 'o', 'r', 'd', '\030', '\003', ' ', '\001', '(', '\013', -'2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', -'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'B', '\006', '\270', '\267', '\213', '\244', '\002', '\001', 'R', '\010', 'p', 'a', 's', 's', 'w', 'o', -'r', 'd', '\022', 'A', '\n', '\013', 'o', 'c', 's', 'p', '_', 's', 't', 'a', 'p', 'l', 'e', '\030', '\004', ' ', '\001', '(', '\013', '2', ' ', -'.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', 'a', 't', -'a', 'S', 'o', 'u', 'r', 'c', 'e', 'R', '\n', 'o', 'c', 's', 'p', 'S', 't', 'a', 'p', 'l', 'e', '\022', 'b', '\n', '\034', 's', 'i', -'g', 'n', 'e', 'd', '_', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '_', 't', 'i', 'm', 'e', 's', 't', 'a', 'm', -'p', '\030', '\005', ' ', '\003', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', -'r', 'e', '.', 'v', '3', '.', 'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'R', '\032', 's', 'i', 'g', 'n', 'e', 'd', 'C', -'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'T', 'i', 'm', 'e', 's', 't', 'a', 'm', 'p', ':', '\'', '\232', '\305', '\210', '\036', -'\"', '\n', ' ', 'e', 'n', 'v', 'o', 'y', '.', 'a', 'p', 'i', '.', 'v', '2', '.', 'a', 'u', 't', 'h', '.', 'T', 'l', 's', 'C', -'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '\"', '\213', '\001', '\n', '\024', 'T', 'l', 's', 'S', 'e', 's', 's', 'i', 'o', 'n', -'T', 'i', 'c', 'k', 'e', 't', 'K', 'e', 'y', 's', '\022', 'D', '\n', '\004', 'k', 'e', 'y', 's', '\030', '\001', ' ', '\003', '(', '\013', '2', -' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', 'a', -'t', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'B', '\016', '\372', 'B', '\005', '\222', '\001', '\002', '\010', '\001', '\270', '\267', '\213', '\244', '\002', '\001', 'R', -'\004', 'k', 'e', 'y', 's', ':', '-', '\232', '\305', '\210', '\036', '(', '\n', '&', 'e', 'n', 'v', 'o', 'y', '.', 'a', 'p', 'i', '.', 'v', -'2', '.', 'a', 'u', 't', 'h', '.', 'T', 'l', 's', 'S', 'e', 's', 's', 'i', 'o', 'n', 'T', 'i', 'c', 'k', 'e', 't', 'K', 'e', -'y', 's', '\"', 's', '\n', '!', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r', -'P', 'l', 'u', 'g', 'i', 'n', 'I', 'n', 's', 't', 'a', 'n', 'c', 'e', '\022', '#', '\n', '\r', 'i', 'n', 's', 't', 'a', 'n', 'c', -'e', '_', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'R', '\014', 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'N', 'a', 'm', -'e', '\022', ')', '\n', '\020', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '_', 'n', 'a', 'm', 'e', '\030', '\002', ' ', '\001', -'(', '\t', 'R', '\017', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'N', 'a', 'm', 'e', '\"', '\244', '\002', '\n', '\025', 'S', -'u', 'b', 'j', 'e', 'c', 't', 'A', 'l', 't', 'N', 'a', 'm', 'e', 'M', 'a', 't', 'c', 'h', 'e', 'r', '\022', 'o', '\n', '\010', 's', -'a', 'n', '_', 't', 'y', 'p', 'e', '\030', '\001', ' ', '\001', '(', '\016', '2', 'H', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', -'e', 'n', 's', 'i', 'o', 'n', 's', '.', 't', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', -'.', 't', 'l', 's', '.', 'v', '3', '.', 'S', 'u', 'b', 'j', 'e', 'c', 't', 'A', 'l', 't', 'N', 'a', 'm', 'e', 'M', 'a', 't', -'c', 'h', 'e', 'r', '.', 'S', 'a', 'n', 'T', 'y', 'p', 'e', 'B', '\n', '\372', 'B', '\007', '\202', '\001', '\004', '\020', '\001', ' ', '\000', 'R', -'\007', 's', 'a', 'n', 'T', 'y', 'p', 'e', '\022', 'H', '\n', '\007', 'm', 'a', 't', 'c', 'h', 'e', 'r', '\030', '\002', ' ', '\001', '(', '\013', -'2', '$', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', 'r', '.', 'v', '3', '.', -'S', 't', 'r', 'i', 'n', 'g', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'B', '\010', '\372', 'B', '\005', '\212', '\001', '\002', '\020', '\001', 'R', '\007', -'m', 'a', 't', 'c', 'h', 'e', 'r', '\"', 'P', '\n', '\007', 'S', 'a', 'n', 'T', 'y', 'p', 'e', '\022', '\030', '\n', '\024', 'S', 'A', 'N', -'_', 'T', 'Y', 'P', 'E', '_', 'U', 'N', 'S', 'P', 'E', 'C', 'I', 'F', 'I', 'E', 'D', '\020', '\000', '\022', '\t', '\n', '\005', 'E', 'M', -'A', 'I', 'L', '\020', '\001', '\022', '\007', '\n', '\003', 'D', 'N', 'S', '\020', '\002', '\022', '\007', '\n', '\003', 'U', 'R', 'I', '\020', '\003', '\022', '\016', -'\n', '\n', 'I', 'P', '_', 'A', 'D', 'D', 'R', 'E', 'S', 'S', '\020', '\004', '\"', '\220', '\014', '\n', '\034', 'C', 'e', 'r', 't', 'i', 'f', -'i', 'c', 'a', 't', 'e', 'V', 'a', 'l', 'i', 'd', 'a', 't', 'i', 'o', 'n', 'C', 'o', 'n', 't', 'e', 'x', 't', '\022', 'W', '\n', -'\n', 't', 'r', 'u', 's', 't', 'e', 'd', '_', 'c', 'a', '\030', '\001', ' ', '\001', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', +'(', '\t', 'R', '\n', 'e', 'c', 'd', 'h', 'C', 'u', 'r', 'v', 'e', 's', '\022', '1', '\n', '\024', 's', 'i', 'g', 'n', 'a', 't', 'u', +'r', 'e', '_', 'a', 'l', 'g', 'o', 'r', 'i', 't', 'h', 'm', 's', '\030', '\005', ' ', '\003', '(', '\t', 'R', '\023', 's', 'i', 'g', 'n', +'a', 't', 'u', 'r', 'e', 'A', 'l', 'g', 'o', 'r', 'i', 't', 'h', 'm', 's', '\"', 'O', '\n', '\013', 'T', 'l', 's', 'P', 'r', 'o', +'t', 'o', 'c', 'o', 'l', '\022', '\014', '\n', '\010', 'T', 'L', 'S', '_', 'A', 'U', 'T', 'O', '\020', '\000', '\022', '\013', '\n', '\007', 'T', 'L', +'S', 'v', '1', '_', '0', '\020', '\001', '\022', '\013', '\n', '\007', 'T', 'L', 'S', 'v', '1', '_', '1', '\020', '\002', '\022', '\013', '\n', '\007', 'T', +'L', 'S', 'v', '1', '_', '2', '\020', '\003', '\022', '\013', '\n', '\007', 'T', 'L', 'S', 'v', '1', '_', '3', '\020', '\004', ':', '&', '\232', '\305', +'\210', '\036', '!', '\n', '\037', 'e', 'n', 'v', 'o', 'y', '.', 'a', 'p', 'i', '.', 'v', '2', '.', 'a', 'u', 't', 'h', '.', 'T', 'l', +'s', 'P', 'a', 'r', 'a', 'm', 'e', 't', 'e', 'r', 's', '\"', '\317', '\001', '\n', '\022', 'P', 'r', 'i', 'v', 'a', 't', 'e', 'K', 'e', +'y', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r', '\022', ',', '\n', '\r', 'p', 'r', 'o', 'v', 'i', 'd', 'e', 'r', '_', 'n', 'a', 'm', +'e', '\030', '\001', ' ', '\001', '(', '\t', 'B', '\007', '\372', 'B', '\004', 'r', '\002', '\020', '\001', 'R', '\014', 'p', 'r', 'o', 'v', 'i', 'd', 'e', +'r', 'N', 'a', 'm', 'e', '\022', 'A', '\n', '\014', 't', 'y', 'p', 'e', 'd', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '\003', ' ', '\001', +'(', '\013', '2', '\024', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'A', 'n', 'y', 'B', +'\006', '\270', '\267', '\213', '\244', '\002', '\001', 'H', '\000', 'R', '\013', 't', 'y', 'p', 'e', 'd', 'C', 'o', 'n', 'f', 'i', 'g', ':', '+', '\232', +'\305', '\210', '\036', '&', '\n', '$', 'e', 'n', 'v', 'o', 'y', '.', 'a', 'p', 'i', '.', 'v', '2', '.', 'a', 'u', 't', 'h', '.', 'P', +'r', 'i', 'v', 'a', 't', 'e', 'K', 'e', 'y', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r', 'B', '\r', '\n', '\013', 'c', 'o', 'n', 'f', +'i', 'g', '_', 't', 'y', 'p', 'e', 'J', '\004', '\010', '\002', '\020', '\003', 'R', '\006', 'c', 'o', 'n', 'f', 'i', 'g', '\"', '\310', '\005', '\n', +'\016', 'T', 'l', 's', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '\022', 'M', '\n', '\021', 'c', 'e', 'r', 't', 'i', 'f', +'i', 'c', 'a', 't', 'e', '_', 'c', 'h', 'a', 'i', 'n', '\030', '\001', ' ', '\001', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', -'e', 'B', '\026', '\362', '\230', '\376', '\217', '\005', '\020', '\022', '\016', 'c', 'a', '_', 'c', 'e', 'r', 't', '_', 's', 'o', 'u', 'r', 'c', 'e', -'R', '\t', 't', 'r', 'u', 's', 't', 'e', 'd', 'C', 'a', '\022', '\255', '\001', '\n', ' ', 'c', 'a', '_', 'c', 'e', 'r', 't', 'i', 'f', -'i', 'c', 'a', 't', 'e', '_', 'p', 'r', 'o', 'v', 'i', 'd', 'e', 'r', '_', 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', '\030', '\r', -' ', '\001', '(', '\013', '2', 'L', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 't', -'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '.', 't', 'l', 's', '.', 'v', '3', '.', 'C', -'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r', 'P', 'l', 'u', 'g', 'i', 'n', 'I', -'n', 's', 't', 'a', 'n', 'c', 'e', 'B', '\026', '\362', '\230', '\376', '\217', '\005', '\020', '\022', '\016', 'c', 'a', '_', 'c', 'e', 'r', 't', '_', -'s', 'o', 'u', 'r', 'c', 'e', 'R', '\035', 'c', 'a', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'P', 'r', 'o', 'v', -'i', 'd', 'e', 'r', 'I', 'n', 's', 't', 'a', 'n', 'c', 'e', '\022', 'S', '\n', '\021', 'w', 'a', 't', 'c', 'h', 'e', 'd', '_', 'd', -'i', 'r', 'e', 'c', 't', 'o', 'r', 'y', '\030', '\013', ' ', '\001', '(', '\013', '2', '&', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', -'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'W', 'a', 't', 'c', 'h', 'e', 'd', 'D', 'i', 'r', 'e', 'c', -'t', 'o', 'r', 'y', 'R', '\020', 'w', 'a', 't', 'c', 'h', 'e', 'd', 'D', 'i', 'r', 'e', 'c', 't', 'o', 'r', 'y', '\022', 'F', '\n', -'\027', 'v', 'e', 'r', 'i', 'f', 'y', '_', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '_', 's', 'p', 'k', 'i', '\030', -'\003', ' ', '\003', '(', '\t', 'B', '\016', '\372', 'B', '\013', '\222', '\001', '\010', '\"', '\006', 'r', '\004', '\020', ',', '(', ',', 'R', '\025', 'v', 'e', -'r', 'i', 'f', 'y', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'S', 'p', 'k', 'i', '\022', 'F', '\n', '\027', 'v', 'e', -'r', 'i', 'f', 'y', '_', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '_', 'h', 'a', 's', 'h', '\030', '\002', ' ', '\003', -'(', '\t', 'B', '\016', '\372', 'B', '\013', '\222', '\001', '\010', '\"', '\006', 'r', '\004', '\020', '@', '(', '_', 'R', '\025', 'v', 'e', 'r', 'i', 'f', -'y', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'H', 'a', 's', 'h', '\022', '\202', '\001', '\n', '\035', 'm', 'a', 't', 'c', -'h', '_', 't', 'y', 'p', 'e', 'd', '_', 's', 'u', 'b', 'j', 'e', 'c', 't', '_', 'a', 'l', 't', '_', 'n', 'a', 'm', 'e', 's', -'\030', '\017', ' ', '\003', '(', '\013', '2', '@', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', -'.', 't', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '.', 't', 'l', 's', '.', 'v', '3', -'.', 'S', 'u', 'b', 'j', 'e', 'c', 't', 'A', 'l', 't', 'N', 'a', 'm', 'e', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'R', '\031', 'm', -'a', 't', 'c', 'h', 'T', 'y', 'p', 'e', 'd', 'S', 'u', 'b', 'j', 'e', 'c', 't', 'A', 'l', 't', 'N', 'a', 'm', 'e', 's', '\022', -'h', '\n', '\027', 'm', 'a', 't', 'c', 'h', '_', 's', 'u', 'b', 'j', 'e', 'c', 't', '_', 'a', 'l', 't', '_', 'n', 'a', 'm', 'e', -'s', '\030', '\t', ' ', '\003', '(', '\013', '2', '$', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', -'h', 'e', 'r', '.', 'v', '3', '.', 'S', 't', 'r', 'i', 'n', 'g', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'B', '\013', '\030', '\001', '\222', -'\307', '\206', '\330', '\004', '\003', '3', '.', '0', 'R', '\024', 'm', 'a', 't', 'c', 'h', 'S', 'u', 'b', 'j', 'e', 'c', 't', 'A', 'l', 't', -'N', 'a', 'm', 'e', 's', '\022', 'k', '\n', '$', 'r', 'e', 'q', 'u', 'i', 'r', 'e', '_', 's', 'i', 'g', 'n', 'e', 'd', '_', 'c', -'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '_', 't', 'i', 'm', 'e', 's', 't', 'a', 'm', 'p', '\030', '\006', ' ', '\001', '(', -'\013', '2', '\032', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', 'V', -'a', 'l', 'u', 'e', 'R', '!', 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'S', 'i', 'g', 'n', 'e', 'd', 'C', 'e', 'r', 't', 'i', 'f', -'i', 'c', 'a', 't', 'e', 'T', 'i', 'm', 'e', 's', 't', 'a', 'm', 'p', '\022', '2', '\n', '\003', 'c', 'r', 'l', '\030', '\007', ' ', '\001', -'(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', -'.', 'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'R', '\003', 'c', 'r', 'l', '\022', ':', '\n', '\031', 'a', 'l', 'l', 'o', 'w', -'_', 'e', 'x', 'p', 'i', 'r', 'e', 'd', '_', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '\030', '\010', ' ', '\001', '(', -'\010', 'R', '\027', 'a', 'l', 'l', 'o', 'w', 'E', 'x', 'p', 'i', 'r', 'e', 'd', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', -'e', '\022', '\242', '\001', '\n', '\030', 't', 'r', 'u', 's', 't', '_', 'c', 'h', 'a', 'i', 'n', '_', 'v', 'e', 'r', 'i', 'f', 'i', 'c', -'a', 't', 'i', 'o', 'n', '\030', '\n', ' ', '\001', '(', '\016', '2', '^', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', +'e', 'R', '\020', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'C', 'h', 'a', 'i', 'n', '\022', 'I', '\n', '\013', 'p', 'r', +'i', 'v', 'a', 't', 'e', '_', 'k', 'e', 'y', '\030', '\002', ' ', '\001', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', +'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'B', +'\006', '\270', '\267', '\213', '\244', '\002', '\001', 'R', '\n', 'p', 'r', 'i', 'v', 'a', 't', 'e', 'K', 'e', 'y', '\022', '@', '\n', '\006', 'p', 'k', +'c', 's', '1', '2', '\030', '\010', ' ', '\001', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', +'.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'B', '\006', '\270', '\267', '\213', '\244', +'\002', '\001', 'R', '\006', 'p', 'k', 'c', 's', '1', '2', '\022', 'S', '\n', '\021', 'w', 'a', 't', 'c', 'h', 'e', 'd', '_', 'd', 'i', 'r', +'e', 'c', 't', 'o', 'r', 'y', '\030', '\007', ' ', '\001', '(', '\013', '2', '&', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', +'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'W', 'a', 't', 'c', 'h', 'e', 'd', 'D', 'i', 'r', 'e', 'c', 't', 'o', +'r', 'y', 'R', '\020', 'w', 'a', 't', 'c', 'h', 'e', 'd', 'D', 'i', 'r', 'e', 'c', 't', 'o', 'r', 'y', '\022', 'o', '\n', '\024', 'p', +'r', 'i', 'v', 'a', 't', 'e', '_', 'k', 'e', 'y', '_', 'p', 'r', 'o', 'v', 'i', 'd', 'e', 'r', '\030', '\006', ' ', '\001', '(', '\013', +'2', '=', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 't', 'r', 'a', 'n', 's', +'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '.', 't', 'l', 's', '.', 'v', '3', '.', 'P', 'r', 'i', 'v', 'a', +'t', 'e', 'K', 'e', 'y', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r', 'R', '\022', 'p', 'r', 'i', 'v', 'a', 't', 'e', 'K', 'e', 'y', +'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r', '\022', 'D', '\n', '\010', 'p', 'a', 's', 's', 'w', 'o', 'r', 'd', '\030', '\003', ' ', '\001', '(', +'\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', +'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'B', '\006', '\270', '\267', '\213', '\244', '\002', '\001', 'R', '\010', 'p', 'a', 's', 's', 'w', +'o', 'r', 'd', '\022', 'A', '\n', '\013', 'o', 'c', 's', 'p', '_', 's', 't', 'a', 'p', 'l', 'e', '\030', '\004', ' ', '\001', '(', '\013', '2', +' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', 'a', +'t', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'R', '\n', 'o', 'c', 's', 'p', 'S', 't', 'a', 'p', 'l', 'e', '\022', 'b', '\n', '\034', 's', +'i', 'g', 'n', 'e', 'd', '_', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '_', 't', 'i', 'm', 'e', 's', 't', 'a', +'m', 'p', '\030', '\005', ' ', '\003', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', +'o', 'r', 'e', '.', 'v', '3', '.', 'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'R', '\032', 's', 'i', 'g', 'n', 'e', 'd', +'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'T', 'i', 'm', 'e', 's', 't', 'a', 'm', 'p', ':', '\'', '\232', '\305', '\210', +'\036', '\"', '\n', ' ', 'e', 'n', 'v', 'o', 'y', '.', 'a', 'p', 'i', '.', 'v', '2', '.', 'a', 'u', 't', 'h', '.', 'T', 'l', 's', +'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '\"', '\213', '\001', '\n', '\024', 'T', 'l', 's', 'S', 'e', 's', 's', 'i', 'o', +'n', 'T', 'i', 'c', 'k', 'e', 't', 'K', 'e', 'y', 's', '\022', 'D', '\n', '\004', 'k', 'e', 'y', 's', '\030', '\001', ' ', '\003', '(', '\013', +'2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', +'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'B', '\016', '\372', 'B', '\005', '\222', '\001', '\002', '\010', '\001', '\270', '\267', '\213', '\244', '\002', '\001', +'R', '\004', 'k', 'e', 'y', 's', ':', '-', '\232', '\305', '\210', '\036', '(', '\n', '&', 'e', 'n', 'v', 'o', 'y', '.', 'a', 'p', 'i', '.', +'v', '2', '.', 'a', 'u', 't', 'h', '.', 'T', 'l', 's', 'S', 'e', 's', 's', 'i', 'o', 'n', 'T', 'i', 'c', 'k', 'e', 't', 'K', +'e', 'y', 's', '\"', 's', '\n', '!', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', +'r', 'P', 'l', 'u', 'g', 'i', 'n', 'I', 'n', 's', 't', 'a', 'n', 'c', 'e', '\022', '#', '\n', '\r', 'i', 'n', 's', 't', 'a', 'n', +'c', 'e', '_', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'R', '\014', 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'N', 'a', +'m', 'e', '\022', ')', '\n', '\020', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '_', 'n', 'a', 'm', 'e', '\030', '\002', ' ', +'\001', '(', '\t', 'R', '\017', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'N', 'a', 'm', 'e', '\"', '\244', '\002', '\n', '\025', +'S', 'u', 'b', 'j', 'e', 'c', 't', 'A', 'l', 't', 'N', 'a', 'm', 'e', 'M', 'a', 't', 'c', 'h', 'e', 'r', '\022', 'o', '\n', '\010', +'s', 'a', 'n', '_', 't', 'y', 'p', 'e', '\030', '\001', ' ', '\001', '(', '\016', '2', 'H', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', +'t', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 't', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', +'s', '.', 't', 'l', 's', '.', 'v', '3', '.', 'S', 'u', 'b', 'j', 'e', 'c', 't', 'A', 'l', 't', 'N', 'a', 'm', 'e', 'M', 'a', +'t', 'c', 'h', 'e', 'r', '.', 'S', 'a', 'n', 'T', 'y', 'p', 'e', 'B', '\n', '\372', 'B', '\007', '\202', '\001', '\004', '\020', '\001', ' ', '\000', +'R', '\007', 's', 'a', 'n', 'T', 'y', 'p', 'e', '\022', 'H', '\n', '\007', 'm', 'a', 't', 'c', 'h', 'e', 'r', '\030', '\002', ' ', '\001', '(', +'\013', '2', '$', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', 'c', 'h', 'e', 'r', '.', 'v', '3', +'.', 'S', 't', 'r', 'i', 'n', 'g', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'B', '\010', '\372', 'B', '\005', '\212', '\001', '\002', '\020', '\001', 'R', +'\007', 'm', 'a', 't', 'c', 'h', 'e', 'r', '\"', 'P', '\n', '\007', 'S', 'a', 'n', 'T', 'y', 'p', 'e', '\022', '\030', '\n', '\024', 'S', 'A', +'N', '_', 'T', 'Y', 'P', 'E', '_', 'U', 'N', 'S', 'P', 'E', 'C', 'I', 'F', 'I', 'E', 'D', '\020', '\000', '\022', '\t', '\n', '\005', 'E', +'M', 'A', 'I', 'L', '\020', '\001', '\022', '\007', '\n', '\003', 'D', 'N', 'S', '\020', '\002', '\022', '\007', '\n', '\003', 'U', 'R', 'I', '\020', '\003', '\022', +'\016', '\n', '\n', 'I', 'P', '_', 'A', 'D', 'D', 'R', 'E', 'S', 'S', '\020', '\004', '\"', '\220', '\014', '\n', '\034', 'C', 'e', 'r', 't', 'i', +'f', 'i', 'c', 'a', 't', 'e', 'V', 'a', 'l', 'i', 'd', 'a', 't', 'i', 'o', 'n', 'C', 'o', 'n', 't', 'e', 'x', 't', '\022', 'W', +'\n', '\n', 't', 'r', 'u', 's', 't', 'e', 'd', '_', 'c', 'a', '\030', '\001', ' ', '\001', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', +'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', +'c', 'e', 'B', '\026', '\362', '\230', '\376', '\217', '\005', '\020', '\022', '\016', 'c', 'a', '_', 'c', 'e', 'r', 't', '_', 's', 'o', 'u', 'r', 'c', +'e', 'R', '\t', 't', 'r', 'u', 's', 't', 'e', 'd', 'C', 'a', '\022', '\255', '\001', '\n', ' ', 'c', 'a', '_', 'c', 'e', 'r', 't', 'i', +'f', 'i', 'c', 'a', 't', 'e', '_', 'p', 'r', 'o', 'v', 'i', 'd', 'e', 'r', '_', 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', '\030', +'\r', ' ', '\001', '(', '\013', '2', 'L', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', +'t', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '.', 't', 'l', 's', '.', 'v', '3', '.', +'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r', 'P', 'l', 'u', 'g', 'i', 'n', +'I', 'n', 's', 't', 'a', 'n', 'c', 'e', 'B', '\026', '\362', '\230', '\376', '\217', '\005', '\020', '\022', '\016', 'c', 'a', '_', 'c', 'e', 'r', 't', +'_', 's', 'o', 'u', 'r', 'c', 'e', 'R', '\035', 'c', 'a', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'P', 'r', 'o', +'v', 'i', 'd', 'e', 'r', 'I', 'n', 's', 't', 'a', 'n', 'c', 'e', '\022', 'S', '\n', '\021', 'w', 'a', 't', 'c', 'h', 'e', 'd', '_', +'d', 'i', 'r', 'e', 'c', 't', 'o', 'r', 'y', '\030', '\013', ' ', '\001', '(', '\013', '2', '&', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', +'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'W', 'a', 't', 'c', 'h', 'e', 'd', 'D', 'i', 'r', 'e', +'c', 't', 'o', 'r', 'y', 'R', '\020', 'w', 'a', 't', 'c', 'h', 'e', 'd', 'D', 'i', 'r', 'e', 'c', 't', 'o', 'r', 'y', '\022', 'F', +'\n', '\027', 'v', 'e', 'r', 'i', 'f', 'y', '_', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '_', 's', 'p', 'k', 'i', +'\030', '\003', ' ', '\003', '(', '\t', 'B', '\016', '\372', 'B', '\013', '\222', '\001', '\010', '\"', '\006', 'r', '\004', '\020', ',', '(', ',', 'R', '\025', 'v', +'e', 'r', 'i', 'f', 'y', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'S', 'p', 'k', 'i', '\022', 'F', '\n', '\027', 'v', +'e', 'r', 'i', 'f', 'y', '_', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '_', 'h', 'a', 's', 'h', '\030', '\002', ' ', +'\003', '(', '\t', 'B', '\016', '\372', 'B', '\013', '\222', '\001', '\010', '\"', '\006', 'r', '\004', '\020', '@', '(', '_', 'R', '\025', 'v', 'e', 'r', 'i', +'f', 'y', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'H', 'a', 's', 'h', '\022', '\202', '\001', '\n', '\035', 'm', 'a', 't', +'c', 'h', '_', 't', 'y', 'p', 'e', 'd', '_', 's', 'u', 'b', 'j', 'e', 'c', 't', '_', 'a', 'l', 't', '_', 'n', 'a', 'm', 'e', +'s', '\030', '\017', ' ', '\003', '(', '\013', '2', '@', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', +'s', '.', 't', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '.', 't', 'l', 's', '.', 'v', +'3', '.', 'S', 'u', 'b', 'j', 'e', 'c', 't', 'A', 'l', 't', 'N', 'a', 'm', 'e', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'R', '\031', +'m', 'a', 't', 'c', 'h', 'T', 'y', 'p', 'e', 'd', 'S', 'u', 'b', 'j', 'e', 'c', 't', 'A', 'l', 't', 'N', 'a', 'm', 'e', 's', +'\022', 'h', '\n', '\027', 'm', 'a', 't', 'c', 'h', '_', 's', 'u', 'b', 'j', 'e', 'c', 't', '_', 'a', 'l', 't', '_', 'n', 'a', 'm', +'e', 's', '\030', '\t', ' ', '\003', '(', '\013', '2', '$', '.', 'e', 'n', 'v', 'o', 'y', '.', 't', 'y', 'p', 'e', '.', 'm', 'a', 't', +'c', 'h', 'e', 'r', '.', 'v', '3', '.', 'S', 't', 'r', 'i', 'n', 'g', 'M', 'a', 't', 'c', 'h', 'e', 'r', 'B', '\013', '\030', '\001', +'\222', '\307', '\206', '\330', '\004', '\003', '3', '.', '0', 'R', '\024', 'm', 'a', 't', 'c', 'h', 'S', 'u', 'b', 'j', 'e', 'c', 't', 'A', 'l', +'t', 'N', 'a', 'm', 'e', 's', '\022', 'k', '\n', '$', 'r', 'e', 'q', 'u', 'i', 'r', 'e', '_', 's', 'i', 'g', 'n', 'e', 'd', '_', +'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '_', 't', 'i', 'm', 'e', 's', 't', 'a', 'm', 'p', '\030', '\006', ' ', '\001', +'(', '\013', '2', '\032', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'B', 'o', 'o', 'l', +'V', 'a', 'l', 'u', 'e', 'R', '!', 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'S', 'i', 'g', 'n', 'e', 'd', 'C', 'e', 'r', 't', 'i', +'f', 'i', 'c', 'a', 't', 'e', 'T', 'i', 'm', 'e', 's', 't', 'a', 'm', 'p', '\022', '2', '\n', '\003', 'c', 'r', 'l', '\030', '\007', ' ', +'\001', '(', '\013', '2', ' ', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', +'3', '.', 'D', 'a', 't', 'a', 'S', 'o', 'u', 'r', 'c', 'e', 'R', '\003', 'c', 'r', 'l', '\022', ':', '\n', '\031', 'a', 'l', 'l', 'o', +'w', '_', 'e', 'x', 'p', 'i', 'r', 'e', 'd', '_', 'c', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '\030', '\010', ' ', '\001', +'(', '\010', 'R', '\027', 'a', 'l', 'l', 'o', 'w', 'E', 'x', 'p', 'i', 'r', 'e', 'd', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', +'t', 'e', '\022', '\242', '\001', '\n', '\030', 't', 'r', 'u', 's', 't', '_', 'c', 'h', 'a', 'i', 'n', '_', 'v', 'e', 'r', 'i', 'f', 'i', +'c', 'a', 't', 'i', 'o', 'n', '\030', '\n', ' ', '\001', '(', '\016', '2', '^', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', +'n', 's', 'i', 'o', 'n', 's', '.', 't', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '.', +'t', 'l', 's', '.', 'v', '3', '.', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'V', 'a', 'l', 'i', 'd', 'a', 't', +'i', 'o', 'n', 'C', 'o', 'n', 't', 'e', 'x', 't', '.', 'T', 'r', 'u', 's', 't', 'C', 'h', 'a', 'i', 'n', 'V', 'e', 'r', 'i', +'f', 'i', 'c', 'a', 't', 'i', 'o', 'n', 'B', '\010', '\372', 'B', '\005', '\202', '\001', '\002', '\020', '\001', 'R', '\026', 't', 'r', 'u', 's', 't', +'C', 'h', 'a', 'i', 'n', 'V', 'e', 'r', 'i', 'f', 'i', 'c', 'a', 't', 'i', 'o', 'n', '\022', 'b', '\n', '\027', 'c', 'u', 's', 't', +'o', 'm', '_', 'v', 'a', 'l', 'i', 'd', 'a', 't', 'o', 'r', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '\014', ' ', '\001', '(', '\013', +'2', '*', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'T', +'y', 'p', 'e', 'd', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\025', 'c', 'u', 's', 't', +'o', 'm', 'V', 'a', 'l', 'i', 'd', 'a', 't', 'o', 'r', 'C', 'o', 'n', 'f', 'i', 'g', '\022', '8', '\n', '\031', 'o', 'n', 'l', 'y', +'_', 'v', 'e', 'r', 'i', 'f', 'y', '_', 'l', 'e', 'a', 'f', '_', 'c', 'e', 'r', 't', '_', 'c', 'r', 'l', '\030', '\016', ' ', '\001', +'(', '\010', 'R', '\025', 'o', 'n', 'l', 'y', 'V', 'e', 'r', 'i', 'f', 'y', 'L', 'e', 'a', 'f', 'C', 'e', 'r', 't', 'C', 'r', 'l', +'\022', 'O', '\n', '\020', 'm', 'a', 'x', '_', 'v', 'e', 'r', 'i', 'f', 'y', '_', 'd', 'e', 'p', 't', 'h', '\030', '\020', ' ', '\001', '(', +'\013', '2', '\034', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'I', 'n', 't', '3', +'2', 'V', 'a', 'l', 'u', 'e', 'B', '\007', '\372', 'B', '\004', '*', '\002', '\030', 'd', 'R', '\016', 'm', 'a', 'x', 'V', 'e', 'r', 'i', 'f', +'y', 'D', 'e', 'p', 't', 'h', '\"', 'F', '\n', '\026', 'T', 'r', 'u', 's', 't', 'C', 'h', 'a', 'i', 'n', 'V', 'e', 'r', 'i', 'f', +'i', 'c', 'a', 't', 'i', 'o', 'n', '\022', '\026', '\n', '\022', 'V', 'E', 'R', 'I', 'F', 'Y', '_', 'T', 'R', 'U', 'S', 'T', '_', 'C', +'H', 'A', 'I', 'N', '\020', '\000', '\022', '\024', '\n', '\020', 'A', 'C', 'C', 'E', 'P', 'T', '_', 'U', 'N', 'T', 'R', 'U', 'S', 'T', 'E', +'D', '\020', '\001', ':', '5', '\232', '\305', '\210', '\036', '0', '\n', '.', 'e', 'n', 'v', 'o', 'y', '.', 'a', 'p', 'i', '.', 'v', '2', '.', +'a', 'u', 't', 'h', '.', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'V', 'a', 'l', 'i', 'd', 'a', 't', 'i', 'o', +'n', 'C', 'o', 'n', 't', 'e', 'x', 't', 'J', '\004', '\010', '\004', '\020', '\005', 'J', '\004', '\010', '\005', '\020', '\006', 'R', '\027', 'v', 'e', 'r', +'i', 'f', 'y', '_', 's', 'u', 'b', 'j', 'e', 'c', 't', '_', 'a', 'l', 't', '_', 'n', 'a', 'm', 'e', 'B', '\250', '\001', '\n', '7', +'i', 'o', '.', 'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', 'x', 'y', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 's', '.', 't', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '.', 't', -'l', 's', '.', 'v', '3', '.', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'V', 'a', 'l', 'i', 'd', 'a', 't', 'i', -'o', 'n', 'C', 'o', 'n', 't', 'e', 'x', 't', '.', 'T', 'r', 'u', 's', 't', 'C', 'h', 'a', 'i', 'n', 'V', 'e', 'r', 'i', 'f', -'i', 'c', 'a', 't', 'i', 'o', 'n', 'B', '\010', '\372', 'B', '\005', '\202', '\001', '\002', '\020', '\001', 'R', '\026', 't', 'r', 'u', 's', 't', 'C', -'h', 'a', 'i', 'n', 'V', 'e', 'r', 'i', 'f', 'i', 'c', 'a', 't', 'i', 'o', 'n', '\022', 'b', '\n', '\027', 'c', 'u', 's', 't', 'o', -'m', '_', 'v', 'a', 'l', 'i', 'd', 'a', 't', 'o', 'r', '_', 'c', 'o', 'n', 'f', 'i', 'g', '\030', '\014', ' ', '\001', '(', '\013', '2', -'*', '.', 'e', 'n', 'v', 'o', 'y', '.', 'c', 'o', 'n', 'f', 'i', 'g', '.', 'c', 'o', 'r', 'e', '.', 'v', '3', '.', 'T', 'y', -'p', 'e', 'd', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'C', 'o', 'n', 'f', 'i', 'g', 'R', '\025', 'c', 'u', 's', 't', 'o', -'m', 'V', 'a', 'l', 'i', 'd', 'a', 't', 'o', 'r', 'C', 'o', 'n', 'f', 'i', 'g', '\022', '8', '\n', '\031', 'o', 'n', 'l', 'y', '_', -'v', 'e', 'r', 'i', 'f', 'y', '_', 'l', 'e', 'a', 'f', '_', 'c', 'e', 'r', 't', '_', 'c', 'r', 'l', '\030', '\016', ' ', '\001', '(', -'\010', 'R', '\025', 'o', 'n', 'l', 'y', 'V', 'e', 'r', 'i', 'f', 'y', 'L', 'e', 'a', 'f', 'C', 'e', 'r', 't', 'C', 'r', 'l', '\022', -'O', '\n', '\020', 'm', 'a', 'x', '_', 'v', 'e', 'r', 'i', 'f', 'y', '_', 'd', 'e', 'p', 't', 'h', '\030', '\020', ' ', '\001', '(', '\013', -'2', '\034', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'I', 'n', 't', '3', '2', -'V', 'a', 'l', 'u', 'e', 'B', '\007', '\372', 'B', '\004', '*', '\002', '\030', 'd', 'R', '\016', 'm', 'a', 'x', 'V', 'e', 'r', 'i', 'f', 'y', -'D', 'e', 'p', 't', 'h', '\"', 'F', '\n', '\026', 'T', 'r', 'u', 's', 't', 'C', 'h', 'a', 'i', 'n', 'V', 'e', 'r', 'i', 'f', 'i', -'c', 'a', 't', 'i', 'o', 'n', '\022', '\026', '\n', '\022', 'V', 'E', 'R', 'I', 'F', 'Y', '_', 'T', 'R', 'U', 'S', 'T', '_', 'C', 'H', -'A', 'I', 'N', '\020', '\000', '\022', '\024', '\n', '\020', 'A', 'C', 'C', 'E', 'P', 'T', '_', 'U', 'N', 'T', 'R', 'U', 'S', 'T', 'E', 'D', -'\020', '\001', ':', '5', '\232', '\305', '\210', '\036', '0', '\n', '.', 'e', 'n', 'v', 'o', 'y', '.', 'a', 'p', 'i', '.', 'v', '2', '.', 'a', -'u', 't', 'h', '.', 'C', 'e', 'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', 'V', 'a', 'l', 'i', 'd', 'a', 't', 'i', 'o', 'n', -'C', 'o', 'n', 't', 'e', 'x', 't', 'J', '\004', '\010', '\004', '\020', '\005', 'J', '\004', '\010', '\005', '\020', '\006', 'R', '\027', 'v', 'e', 'r', 'i', -'f', 'y', '_', 's', 'u', 'b', 'j', 'e', 'c', 't', '_', 'a', 'l', 't', '_', 'n', 'a', 'm', 'e', 'B', '\250', '\001', '\n', '7', 'i', -'o', '.', 'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', 'x', 'y', '.', 'e', 'n', 'v', 'o', 'y', '.', 'e', 'x', 't', 'e', 'n', 's', -'i', 'o', 'n', 's', '.', 't', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '.', 't', 'l', -'s', '.', 'v', '3', 'B', '\013', 'C', 'o', 'm', 'm', 'o', 'n', 'P', 'r', 'o', 't', 'o', 'P', '\001', 'Z', 'V', 'g', 'i', 't', 'h', -'u', 'b', '.', 'c', 'o', 'm', '/', 'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', 'x', 'y', '/', 'g', 'o', '-', 'c', 'o', 'n', 't', -'r', 'o', 'l', '-', 'p', 'l', 'a', 'n', 'e', '/', 'e', 'n', 'v', 'o', 'y', '/', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', -'s', '/', 't', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '/', 't', 'l', 's', '/', 'v', -'3', ';', 't', 'l', 's', 'v', '3', '\272', '\200', '\310', '\321', '\006', '\002', '\020', '\002', 'b', '\006', 'p', 'r', 'o', 't', 'o', '3', +'l', 's', '.', 'v', '3', 'B', '\013', 'C', 'o', 'm', 'm', 'o', 'n', 'P', 'r', 'o', 't', 'o', 'P', '\001', 'Z', 'V', 'g', 'i', 't', +'h', 'u', 'b', '.', 'c', 'o', 'm', '/', 'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', 'x', 'y', '/', 'g', 'o', '-', 'c', 'o', 'n', +'t', 'r', 'o', 'l', '-', 'p', 'l', 'a', 'n', 'e', '/', 'e', 'n', 'v', 'o', 'y', '/', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', +'n', 's', '/', 't', 'r', 'a', 'n', 's', 'p', 'o', 'r', 't', '_', 's', 'o', 'c', 'k', 'e', 't', 's', '/', 't', 'l', 's', '/', +'v', '3', ';', 't', 'l', 's', 'v', '3', '\272', '\200', '\310', '\321', '\006', '\002', '\020', '\002', 'b', '\006', 'p', 'r', 'o', 't', 'o', '3', }; static _upb_DefPool_Init *deps[12] = { @@ -209,5 +211,5 @@ _upb_DefPool_Init envoy_extensions_transport_sockets_tls_v3_common_proto_upbdefi deps, &envoy_extensions_transport_sockets_tls_v3_common_proto_upb_file_layout, "envoy/extensions/transport_sockets/tls/v3/common.proto", - UPB_STRINGVIEW_INIT(descriptor, 4173) + UPB_STRINGVIEW_INIT(descriptor, 4224) }; diff --git a/third_party/README.md b/third_party/README.md index 7fc8e4356b8bc..40b0b64d362ec 100644 --- a/third_party/README.md +++ b/third_party/README.md @@ -111,7 +111,9 @@ Updating the protobuf dependency is now part of the internal release process (se ### Updating third_party/envoy-api -Apart from the above steps, please perform the following two steps to generate the Python `xds-protos` package: +Apart from the above steps, please run `tools/codegen/core/gen_upb_api.sh` to regenerate upb files. + +In addition, please perform the following two steps to generate the Python `xds-protos` package: 1. Bump the version in the `tools/distrib/python/xds_protos/setup.py`; 2. Run `tools/distrib/python/xds_protos/build_validate_upload.sh` to upload the built wheel. diff --git a/third_party/envoy-api b/third_party/envoy-api index 084aa4a018678..68d4315167352 160000 --- a/third_party/envoy-api +++ b/third_party/envoy-api @@ -1 +1 @@ -Subproject commit 084aa4a0186786570496c3a4001b72c3ef592f7f +Subproject commit 68d4315167352ffac71f149a43b8088397d3f33d diff --git a/tools/run_tests/sanity/check_submodules.sh b/tools/run_tests/sanity/check_submodules.sh index 8f3bdcc9b6067..5dbd51afca819 100755 --- a/tools/run_tests/sanity/check_submodules.sh +++ b/tools/run_tests/sanity/check_submodules.sh @@ -30,7 +30,7 @@ third_party/benchmark 361e8d1cfe0c6c36d30b39f1b61302ece5507320 third_party/bloaty 60209eb1ccc34d5deefb002d1b7f37545204f7f2 third_party/boringssl-with-bazel 8872d958b7b07173bf29b8f3b8bf36a1ca8c94a3 third_party/cares/cares 6654436a307a5a686b008c1d4c93b0085da6e6d8 -third_party/envoy-api 084aa4a0186786570496c3a4001b72c3ef592f7f +third_party/envoy-api 68d4315167352ffac71f149a43b8088397d3f33d third_party/googleapis 2f9af297c84c55c8b871ba4495e01ade42476c92 third_party/googletest 0e402173c97aea7a00749e825b194bfede4f2e45 third_party/libuv 02a9e1be252b623ee032a3137c0b0c94afbe6809 From afddf1a70c891d70f46d6fc5e7829ee12223ab3a Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Thu, 6 Apr 2023 09:38:09 -0700 Subject: [PATCH 18/39] [chttp2] Better error message on metadata size exceeded message (#32809) This error can trigger for either initial or trailing metadata (and we've had outages where the latter was the cause). I don't think we know at this layer if we're parsing initial or trailing - though it'd be a good exercise to plumb that through. For now remove the word initial because it's better to give less information than wrong information. --- src/core/ext/transport/chttp2/transport/hpack_parser.cc | 4 ++-- test/core/end2end/tests/large_metadata.cc | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.cc b/src/core/ext/transport/chttp2/transport/hpack_parser.cc index fd15f300ae8b3..09d154e5c5340 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_parser.cc +++ b/src/core/ext/transport/chttp2/transport/hpack_parser.cc @@ -837,11 +837,11 @@ class HPackParser::Parser { "B)", summary.empty() ? "" : " to ", summary); if (exceeded_hard_limit) { error_message = absl::StrCat( - "received initial metadata size exceeds hard limit (", *frame_length_, + "received metadata size exceeds hard limit (", *frame_length_, " vs. ", metadata_early_detection_->hard_limit(), ")", summary); } else { error_message = absl::StrCat( - "received initial metadata size exceeds soft limit (", *frame_length_, + "received metadata size exceeds soft limit (", *frame_length_, " vs. ", metadata_early_detection_->soft_limit(), "), rejecting requests with some random probability", summary); } diff --git a/test/core/end2end/tests/large_metadata.cc b/test/core/end2end/tests/large_metadata.cc index 6ae8adb512a0e..2344e61961303 100644 --- a/test/core/end2end/tests/large_metadata.cc +++ b/test/core/end2end/tests/large_metadata.cc @@ -47,9 +47,8 @@ class LargeMetadataTest { for (int i = 0; i < count; ++i) { auto status = PerformOneRequest(metadata_size); if (status.status() == GRPC_STATUS_RESOURCE_EXHAUSTED) { - EXPECT_THAT( - status.message(), - ::testing::StartsWith("received initial metadata size exceeds")); + EXPECT_THAT(status.message(), + ::testing::StartsWith("received metadata size exceeds")); } else { num_requests_accepted++; EXPECT_EQ(status.status(), GRPC_STATUS_OK); From 3a2c8b9bc11b13dce2186ee2012ea9943f29a3fa Mon Sep 17 00:00:00 2001 From: Eugene Ostroukhov Date: Thu, 6 Apr 2023 10:23:55 -0700 Subject: [PATCH 19/39] [PSM Interop] Enable custom_lb_test for CXX (#32799) --- tools/internal_ci/linux/grpc_xds_k8s_lb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/internal_ci/linux/grpc_xds_k8s_lb.sh b/tools/internal_ci/linux/grpc_xds_k8s_lb.sh index 859262f76f222..f0c1c43918c4f 100755 --- a/tools/internal_ci/linux/grpc_xds_k8s_lb.sh +++ b/tools/internal_ci/linux/grpc_xds_k8s_lb.sh @@ -169,7 +169,7 @@ main() { cd "${TEST_DRIVER_FULL_DIR}" local failed_tests=0 run_alpha_test subsetting_test || (( ++failed_tests )) - test_suites=("api_listener_test" "change_backend_service_test" "failover_test" "remove_neg_test" "round_robin_test" "affinity_test" "outlier_detection_test") + test_suites=("api_listener_test" "change_backend_service_test" "failover_test" "remove_neg_test" "round_robin_test" "affinity_test" "outlier_detection_test" "custom_lb_test") for test in "${test_suites[@]}"; do run_test $test || (( ++failed_tests )) done From 3e3e92af999a3a0a6116610b67865c28f20268f0 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Thu, 6 Apr 2023 11:19:03 -0700 Subject: [PATCH 20/39] [infra] Consolidate auto-tag and check-title GH actions (#32824) These need to run at about the same times, and each runs quickly. Doesn't seem worthwhile to fire up a VM for each of them individually. --- ....yaml => pr-auto-tag-and-check-title.yaml} | 15 ++++++++++++- .github/workflows/pr-check-title.yaml | 21 ------------------- 2 files changed, 14 insertions(+), 22 deletions(-) rename .github/workflows/{pr-auto-tag.yaml => pr-auto-tag-and-check-title.yaml} (57%) delete mode 100644 .github/workflows/pr-check-title.yaml diff --git a/.github/workflows/pr-auto-tag.yaml b/.github/workflows/pr-auto-tag-and-check-title.yaml similarity index 57% rename from .github/workflows/pr-auto-tag.yaml rename to .github/workflows/pr-auto-tag-and-check-title.yaml index 4341bbb8d680d..b761fe713170b 100644 --- a/.github/workflows/pr-auto-tag.yaml +++ b/.github/workflows/pr-auto-tag-and-check-title.yaml @@ -1,4 +1,4 @@ -name: PR AutoTag +name: PR Title Check & Tag on: pull_request_target: types: [opened, reopened, synchronized, edited] @@ -17,3 +17,16 @@ jobs: with: repo-token: "${{ secrets.GITHUB_TOKEN }}" sync-labels: "" + + title-check: + permissions: + contents: read + pull-requests: write + + runs-on: ubuntu-latest + steps: + - uses: thehanimo/pr-title-checker@v1.3.5 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + pass_on_octokit_error: false + configuration_path: ".github/pr_title_checker_config.json" diff --git a/.github/workflows/pr-check-title.yaml b/.github/workflows/pr-check-title.yaml deleted file mode 100644 index ae5c085e50d66..0000000000000 --- a/.github/workflows/pr-check-title.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: PR CheckTitle -on: - pull_request_target: - types: [opened, reopened, synchronized, edited, labeled, unlabeled] - workflow_dispatch: -permissions: - contents: read - -jobs: - title-check: - permissions: - contents: read - pull-requests: write - - runs-on: ubuntu-latest - steps: - - uses: thehanimo/pr-title-checker@v1.3.5 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - pass_on_octokit_error: false - configuration_path: ".github/pr_title_checker_config.json" From c8d0110a10c2bb80b96b7ac2f538b7ed56e1599e Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Thu, 6 Apr 2023 20:30:50 +0200 Subject: [PATCH 21/39] [python] Python fixes for upcoming protobuf 22.x upgrade (#32803) Various fixes that can be merged in advance, to simplify the 22.x upgrade. - Use NOMINMAX for grpcio_tools build to avoid build failure when absl is added as a dependency. Fixes https://github.com/grpc/grpc/issues/32779 - in spawn_patch command file, put arguments on multiple lines to avoid lines going over the limit for large link commands: Fixes https://github.com/grpc/grpc/issues/32795 - Use `python/generator.h` instead of the deprecated `python/python_generator.h` in grpcio_tools. --- src/python/grpcio/_spawn_patch.py | 5 ++++- tools/distrib/python/grpcio_tools/grpc_tools/main.cc | 2 +- tools/distrib/python/grpcio_tools/setup.py | 6 +++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/python/grpcio/_spawn_patch.py b/src/python/grpcio/_spawn_patch.py index 377cc7a9f37d8..421c7dfee83b1 100644 --- a/src/python/grpcio/_spawn_patch.py +++ b/src/python/grpcio/_spawn_patch.py @@ -44,7 +44,10 @@ def _commandfile_spawn(self, command): escaped_args = [ '"' + arg.replace('\\', '\\\\') + '"' for arg in command[1:] ] - command_file.write(' '.join(escaped_args)) + # add each arg on a separate line to avoid hitting the + # "line in command file contains 131071 or more characters" error + # (can happen for extra long link commands) + command_file.write(' \n'.join(escaped_args)) modified_command = command[:1] + ['@{}'.format(command_filename)] try: _classic_spawn(self, modified_command) diff --git a/tools/distrib/python/grpcio_tools/grpc_tools/main.cc b/tools/distrib/python/grpcio_tools/grpc_tools/main.cc index 12b40e8ab4aaa..d49edd323a9a8 100644 --- a/tools/distrib/python/grpcio_tools/grpc_tools/main.cc +++ b/tools/distrib/python/grpcio_tools/grpc_tools/main.cc @@ -24,8 +24,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/tools/distrib/python/grpcio_tools/setup.py b/tools/distrib/python/grpcio_tools/setup.py index bf09c0ae9221f..0b5dc69591f1d 100644 --- a/tools/distrib/python/grpcio_tools/setup.py +++ b/tools/distrib/python/grpcio_tools/setup.py @@ -203,7 +203,11 @@ def get_ext_filename(self, ext_name): DEFINE_MACROS = () if "win32" in sys.platform: - DEFINE_MACROS += (('WIN32_LEAN_AND_MEAN', 1),) + DEFINE_MACROS += ( + ('WIN32_LEAN_AND_MEAN', 1), + # avoid https://github.com/abseil/abseil-cpp/issues/1425 + ('NOMINMAX', 1), + ) if '64bit' in platform.architecture()[0]: DEFINE_MACROS += (('MS_WIN64', 1),) elif "linux" in sys.platform or "darwin" in sys.platform: From 63c094cf5b7bdf91f245aa81f10e553f4db42c45 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Thu, 6 Apr 2023 12:32:23 -0700 Subject: [PATCH 22/39] [promises] Run C++ end to end tests with server promises (#32537) Expand server promises to run with C++ end2end tests. Across connected_channel/call/batch_builder/pipe/transport: - fix a bug where read errors weren't propagated from transport to call so that we can populate failed_before_recv_message for the c++ bindings - ensure those errors are not, however, used to populate the returned call status Add a new latch call arg to lazily propagate the bound CQ for a server call (and client call, but here it's used degenerately - it's always populated). This allows server calls to be properly bound to pollsets.(1)/(2) In call.cc: - move some profiling code from FilterStackCall to Call, and then use it in PromiseBasedCall (this should be cleaned up with tracing work) - implement GetServerAuthority In server.cc: - use an RAII pattern on `MatchResult` to avoid a bug whereby a tag could be dropped if we cancel a request after it's been matched but before it's published - fix deadline export to ServerContext In resource_quota_server.cc: - fix some long standing flakes (that were finally obvious with the new test code) - it's legal here to have client calls not arrive at the server due to resource starvation, work through that (includes adding expectations during a `Step` call, which required some small tweaks to cq_verifier) In the C++ end2end_test.cc: - strengthen a flaky test so it passes consistently (it's likely we'll revisit this with the fuzzing efforts to strengthen it into an actually robust test) (1) It's time to remove this concept (2) Surprisingly the only test that *reliably* demonstrates this not being done is time_change_test --------- Co-authored-by: ctiller --- bazel/experiments.bzl | 6 + src/core/lib/channel/connected_channel.cc | 109 +++++++++++++----- src/core/lib/channel/promise_based_filter.cc | 10 +- src/core/lib/experiments/experiments.yaml | 2 +- src/core/lib/promise/pipe.h | 7 ++ src/core/lib/surface/call.cc | 91 ++++++++++----- src/core/lib/surface/call.h | 6 +- src/core/lib/surface/completion_queue.cc | 3 +- src/core/lib/surface/server.cc | 66 ++++++++--- src/core/lib/transport/batch_builder.h | 10 +- src/core/lib/transport/transport.h | 3 + test/core/end2end/cq_verifier.cc | 12 +- .../end2end/tests/resource_quota_server.cc | 72 ++++++++---- test/core/filters/filter_fuzzer.cc | 5 +- test/core/filters/filter_test.cc | 2 +- test/core/promise/pipe_test.cc | 30 +++++ test/cpp/end2end/BUILD | 67 +++++++++-- test/cpp/end2end/end2end_test.cc | 66 ++++++----- test/cpp/end2end/test_service_impl.h | 6 + test/cpp/end2end/xds/BUILD | 19 ++- 20 files changed, 437 insertions(+), 155 deletions(-) diff --git a/bazel/experiments.bzl b/bazel/experiments.bzl index 4e880cab025a4..28c5faaf470f9 100644 --- a/bazel/experiments.bzl +++ b/bazel/experiments.bzl @@ -27,6 +27,9 @@ EXPERIMENTS = { "promise_based_client_call", "promise_based_server_call", ], + "cpp_end2end_test": [ + "promise_based_server_call", + ], "endpoint_test": [ "tcp_frame_size_tuning", "tcp_rcv_lowat", @@ -50,6 +53,9 @@ EXPERIMENTS = { "memory_pressure_controller", "unconstrained_max_quota_buffer_size", ], + "xds_end2end_test": [ + "promise_based_server_call", + ], }, "on": { "flow_control_test": [ diff --git a/src/core/lib/channel/connected_channel.cc b/src/core/lib/channel/connected_channel.cc index fa66027557f57..dc6d9e2b0173b 100644 --- a/src/core/lib/channel/connected_channel.cc +++ b/src/core/lib/channel/connected_channel.cc @@ -56,8 +56,8 @@ #include "src/core/lib/promise/activity.h" #include "src/core/lib/promise/arena_promise.h" #include "src/core/lib/promise/context.h" -#include "src/core/lib/promise/detail/basic_join.h" #include "src/core/lib/promise/detail/basic_seq.h" +#include "src/core/lib/promise/detail/status.h" #include "src/core/lib/promise/for_each.h" #include "src/core/lib/promise/if.h" #include "src/core/lib/promise/latch.h" @@ -69,7 +69,6 @@ #include "src/core/lib/promise/promise.h" #include "src/core/lib/promise/race.h" #include "src/core/lib/promise/seq.h" -#include "src/core/lib/promise/try_join.h" #include "src/core/lib/promise/try_seq.h" #include "src/core/lib/resource_quota/arena.h" #include "src/core/lib/slice/slice.h" @@ -321,7 +320,8 @@ class ConnectedChannelStream : public Orphanable { } // Returns a promise that implements the receive message loop. - auto RecvMessages(PipeSender* incoming_messages); + auto RecvMessages(PipeSender* incoming_messages, + bool cancel_on_error); // Returns a promise that implements the send message loop. auto SendMessages(PipeReceiver* outgoing_messages); @@ -374,12 +374,12 @@ class ConnectedChannelStream : public Orphanable { }; auto ConnectedChannelStream::RecvMessages( - PipeSender* incoming_messages) { - return Loop([self = InternalRef(), + PipeSender* incoming_messages, bool cancel_on_error) { + return Loop([self = InternalRef(), cancel_on_error, incoming_messages = std::move(*incoming_messages)]() mutable { return Seq( GetContext()->ReceiveMessage(self->batch_target()), - [&incoming_messages]( + [cancel_on_error, &incoming_messages]( absl::StatusOr> status) mutable { bool has_message = status.ok() && status->has_value(); auto publish_message = [&incoming_messages, &status]() { @@ -405,7 +405,8 @@ auto ConnectedChannelStream::RecvMessages( return Continue{}; }); }; - auto publish_close = [&status]() mutable { + auto publish_close = [cancel_on_error, &incoming_messages, + &status]() mutable { if (grpc_call_trace.enabled()) { gpr_log(GPR_INFO, "%s[connected] RecvMessage: reached end of stream with " @@ -413,6 +414,9 @@ auto ConnectedChannelStream::RecvMessages( Activity::current()->DebugTag().c_str(), status.status().ToString().c_str()); } + if (cancel_on_error && !status.ok()) { + incoming_messages.CloseWithError(); + } return Immediate(LoopCtl(status.status())); }; return If(has_message, std::move(publish_message), @@ -442,9 +446,13 @@ ArenaPromise MakeClientCallPromise( grpc_transport_init_stream(transport, stream->stream(), stream->stream_refcount(), nullptr, GetContext()); - grpc_transport_set_pops(transport, stream->stream(), - GetContext()->polling_entity()); auto* party = static_cast(Activity::current()); + party->Spawn( + "set_polling_entity", call_args.polling_entity->Wait(), + [transport, + stream = stream->InternalRef()](grpc_polling_entity polling_entity) { + grpc_transport_set_pops(transport, stream->stream(), &polling_entity); + }); // Start a loop to send messages from client_to_server_messages to the // transport. When the pipe closes and the loop completes, send a trailing // metadata batch to close the stream. @@ -523,14 +531,44 @@ ArenaPromise MakeClientCallPromise( // complete (or one fails). // Next: receive trailing metadata, and return that up the stack. auto recv_messages = - stream->RecvMessages(call_args.server_to_client_messages); - return Map(TrySeq(TryJoin(std::move(send_initial_metadata), - std::move(recv_messages)), - std::move(recv_trailing_metadata)), - [stream = std::move(stream)](ServerMetadataHandle result) { - stream->set_finished(); - return result; - }); + stream->RecvMessages(call_args.server_to_client_messages, false); + return Map( + [send_initial_metadata = std::move(send_initial_metadata), + recv_messages = std::move(recv_messages), + recv_trailing_metadata = std::move(recv_trailing_metadata), + done_send_initial_metadata = false, done_recv_messages = false, + done_recv_trailing_metadata = + false]() mutable -> Poll { + if (!done_send_initial_metadata) { + auto p = send_initial_metadata(); + if (auto* r = p.value_if_ready()) { + done_send_initial_metadata = true; + if (!r->ok()) return StatusCast(*r); + } + } + if (!done_recv_messages) { + auto p = recv_messages(); + if (auto* r = p.value_if_ready()) { + // NOTE: ignore errors here, they'll be collected in the + // recv_trailing_metadata. + done_recv_messages = true; + } else { + return Pending{}; + } + } + if (!done_recv_trailing_metadata) { + auto p = recv_trailing_metadata(); + if (auto* r = p.value_if_ready()) { + done_recv_trailing_metadata = true; + return std::move(*r); + } + } + return Pending{}; + }, + [stream = std::move(stream)](ServerMetadataHandle result) { + stream->set_finished(); + return result; + }); } #endif @@ -547,9 +585,6 @@ ArenaPromise MakeServerCallPromise( transport, stream->stream(), stream->stream_refcount(), GetContext()->server_call_context()->server_stream_data(), GetContext()); - grpc_transport_set_pops(transport, stream->stream(), - GetContext()->polling_entity()); - auto* party = static_cast(Activity::current()); // Arifacts we need for the lifetime of the call. @@ -558,11 +593,19 @@ ArenaPromise MakeServerCallPromise( Pipe client_to_server; Pipe server_initial_metadata; Latch failure_latch; + Latch polling_entity_latch; bool sent_initial_metadata = false; bool sent_trailing_metadata = false; }; auto* call_data = GetContext()->ManagedNew(); + party->Spawn( + "set_polling_entity", call_data->polling_entity_latch.Wait(), + [transport, + stream = stream->InternalRef()](grpc_polling_entity polling_entity) { + grpc_transport_set_pops(transport, stream->stream(), &polling_entity); + }); + auto server_to_client_empty = call_data->server_to_client.receiver.AwaitEmpty(); @@ -580,6 +623,7 @@ ArenaPromise MakeServerCallPromise( auto call_promise = next_promise_factory(CallArgs{ std::move(client_initial_metadata), ClientInitialMetadataOutstandingToken::Empty(), + &call_data->polling_entity_latch, &call_data->server_initial_metadata.sender, &call_data->client_to_server.receiver, &call_data->server_to_client.sender, @@ -680,7 +724,7 @@ ArenaPromise MakeServerCallPromise( "recv_messages", Race( Map(stream->WaitFinished(), [](Empty) { return absl::OkStatus(); }), - Map(stream->RecvMessages(&call_data->client_to_server.sender), + Map(stream->RecvMessages(&call_data->client_to_server.sender, true), [failure_latch = &call_data->failure_latch](absl::Status status) { if (!status.ok() && !failure_latch->is_set()) { failure_latch->Set(ServerMetadataFromStatus(status)); @@ -769,12 +813,23 @@ ArenaPromise MakeServerCallPromise( // (allowing the call code to decide on what signalling to give the // application). - return Map(Seq(std::move(recv_initial_metadata_then_run_promise), - std::move(send_trailing_metadata)), - [stream = std::move(stream)](ServerMetadataHandle md) { - stream->set_finished(); - return md; - }); + struct CleanupPollingEntityLatch { + void operator()(Latch* latch) { + if (!latch->is_set()) latch->Set(grpc_polling_entity()); + } + }; + auto cleanup_polling_entity_latch = + std::unique_ptr, CleanupPollingEntityLatch>( + &call_data->polling_entity_latch); + + return Map( + Seq(std::move(recv_initial_metadata_then_run_promise), + std::move(send_trailing_metadata)), + [cleanup_polling_entity_latch = std::move(cleanup_polling_entity_latch), + stream = std::move(stream)](ServerMetadataHandle md) { + stream->set_finished(); + return md; + }); } #endif diff --git a/src/core/lib/channel/promise_based_filter.cc b/src/core/lib/channel/promise_based_filter.cc index 9082022770d3a..aafdcd0faee52 100644 --- a/src/core/lib/channel/promise_based_filter.cc +++ b/src/core/lib/channel/promise_based_filter.cc @@ -16,8 +16,6 @@ #include "src/core/lib/channel/promise_based_filter.h" -#include - #include #include #include @@ -245,10 +243,6 @@ void BaseCallData::CapturedBatch::CancelWith(grpc_error_handle error, auto* batch = std::exchange(batch_, nullptr); GPR_ASSERT(batch != nullptr); uintptr_t& refcnt = *RefCountField(batch); - gpr_log(GPR_DEBUG, "%sCancelWith: %p refs=%" PRIdPTR " err=%s [%s]", - releaser->call()->DebugTag().c_str(), batch, refcnt, - error.ToString().c_str(), - grpc_transport_stream_op_batch_string(batch, false).c_str()); if (refcnt == 0) { // refcnt==0 ==> cancelled if (grpc_trace_channel.enabled()) { @@ -1583,7 +1577,7 @@ void ClientCallData::StartPromise(Flusher* flusher) { promise_ = filter->MakeCallPromise( CallArgs{WrapMetadata(send_initial_metadata_batch_->payload ->send_initial_metadata.send_initial_metadata), - std::move(initial_metadata_outstanding_token_), + std::move(initial_metadata_outstanding_token_), nullptr, server_initial_metadata_pipe() == nullptr ? nullptr : &server_initial_metadata_pipe()->sender, @@ -2373,7 +2367,7 @@ void ServerCallData::RecvInitialMetadataReady(grpc_error_handle error) { FakeActivity(this).Run([this, filter] { promise_ = filter->MakeCallPromise( CallArgs{WrapMetadata(recv_initial_metadata_), - ClientInitialMetadataOutstandingToken::Empty(), + ClientInitialMetadataOutstandingToken::Empty(), nullptr, server_initial_metadata_pipe() == nullptr ? nullptr : &server_initial_metadata_pipe()->sender, diff --git a/src/core/lib/experiments/experiments.yaml b/src/core/lib/experiments/experiments.yaml index 2ff867b35fa50..bbb65c4c20e69 100644 --- a/src/core/lib/experiments/experiments.yaml +++ b/src/core/lib/experiments/experiments.yaml @@ -119,7 +119,7 @@ default: false expiry: 2023/06/01 owner: ctiller@google.com - test_tags: ["core_end2end_test"] + test_tags: ["core_end2end_test", "cpp_end2end_test", "xds_end2end_test"] - name: transport_supplies_client_latency description: If set, use the transport represented value for client latency in opencensus diff --git a/src/core/lib/promise/pipe.h b/src/core/lib/promise/pipe.h index e989b5cc9a7e8..599bf0d817df5 100644 --- a/src/core/lib/promise/pipe.h +++ b/src/core/lib/promise/pipe.h @@ -477,6 +477,13 @@ class PipeSender { } } + void CloseWithError() { + if (center_ != nullptr) { + center_->MarkCancelled(); + center_.reset(); + } + } + void Swap(PipeSender* other) { std::swap(center_, other->center_); } // Send a single message along the pipe. diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc index 179d52b08899b..26ab6a61d1cec 100644 --- a/src/core/lib/surface/call.cc +++ b/src/core/lib/surface/call.cc @@ -242,6 +242,8 @@ class Call : public CppImplOf { void HandleCompressionAlgorithmNotAccepted( grpc_compression_algorithm compression_algorithm) GPR_ATTRIBUTE_NOINLINE; + gpr_cycle_counter start_time() const { return start_time_; } + private: RefCountedPtr channel_; Arena* const arena_; @@ -263,6 +265,7 @@ class Call : public CppImplOf { // of the recv_initial_metadata op. The mutex should be mostly uncontended. mutable Mutex peer_mu_; Slice peer_string_; + gpr_cycle_counter start_time_ = gpr_get_cycle_counter(); }; Call::ParentCall* Call::GetOrCreateParentCall() { @@ -706,7 +709,6 @@ class FilterStackCall final : public Call { CallCombiner call_combiner_; grpc_completion_queue* cq_; grpc_polling_entity pollent_; - gpr_cycle_counter start_time_ = gpr_get_cycle_counter(); /// has grpc_call_unref been called bool destroy_called_ = false; @@ -868,7 +870,7 @@ grpc_error_handle FilterStackCall::Create(grpc_call_create_args* args, grpc_call_element_args call_args = { call->call_stack(), args->server_transport_data, call->context_, path, - call->start_time_, call->send_deadline(), + call->start_time(), call->send_deadline(), call->arena(), &call->call_combiner_}; add_init_error(&error, grpc_call_stack_init(channel_stack, 1, DestroyCall, call, &call_args)); @@ -950,7 +952,7 @@ void FilterStackCall::DestroyCall(void* call, grpc_error_handle /*error*/) { &(c->final_info_.error_string)); c->status_error_.set(absl::OkStatus()); c->final_info_.stats.latency = - gpr_cycle_counter_sub(gpr_get_cycle_counter(), c->start_time_); + gpr_cycle_counter_sub(gpr_get_cycle_counter(), c->start_time()); grpc_call_stack_destroy(c->call_stack(), &c->final_info_, GRPC_CLOSURE_INIT(&c->release_call_, ReleaseCall, c, grpc_schedule_on_exec_ctx)); @@ -1995,11 +1997,18 @@ class PromiseBasedCall : public Call, void UpdateDeadline(Timestamp deadline) ABSL_LOCKS_EXCLUDED(deadline_mu_); void ResetDeadline() ABSL_LOCKS_EXCLUDED(deadline_mu_); + Timestamp deadline() { + MutexLock lock(&deadline_mu_); + return deadline_; + } // Implementation of EventEngine::Closure, called when deadline expires void Run() override; virtual ServerCallContext* server_call_context() { return nullptr; } + bool failed_before_recv_message() const final { + return failed_before_recv_message_.load(std::memory_order_relaxed); + } using Call::arena; @@ -2145,6 +2154,8 @@ class PromiseBasedCall : public Call, final_info.stats = final_stats_; final_info.final_status = status; final_info.error_string = status_details; + final_info.stats.latency = + gpr_cycle_counter_sub(gpr_get_cycle_counter(), start_time()); finalization_.Run(&final_info); } @@ -2320,6 +2331,7 @@ class PromiseBasedCall : public Call, // GRPC_OP_SEND_MESSAGE (one count each), and 0 once those payloads have been // pushed onto the outgoing pipe. std::atomic sends_queued_{0}; + std::atomic failed_before_recv_message_{false}; // Waiter for when sends_queued_ becomes 0. IntraActivityWaiter waiting_for_queued_sends_; grpc_byte_buffer** recv_message_ = nullptr; @@ -2346,16 +2358,7 @@ PromiseBasedCall::PromiseBasedCall(Arena* arena, uint32_t initial_external_refs, Party(arena, initial_external_refs), cq_(args.cq) { if (args.cq != nullptr) { - GPR_ASSERT(args.pollset_set_alternative == nullptr && - "Only one of 'cq' and 'pollset_set_alternative' should be " - "non-nullptr."); GRPC_CQ_INTERNAL_REF(args.cq, "bind"); - call_context_.pollent_ = - grpc_polling_entity_create_from_pollset(grpc_cq_pollset(args.cq)); - } - if (args.pollset_set_alternative != nullptr) { - call_context_.pollent_ = grpc_polling_entity_create_from_pollset_set( - args.pollset_set_alternative); } } @@ -2468,8 +2471,6 @@ void PromiseBasedCall::FinishOpOnCompletion(Completion* completion, void PromiseBasedCall::SetCompletionQueue(grpc_completion_queue* cq) { cq_ = cq; GRPC_CQ_INTERNAL_REF(cq, "bind"); - call_context_.pollent_ = - grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq)); } void PromiseBasedCall::UpdateDeadline(Timestamp deadline) { @@ -2577,6 +2578,7 @@ void PromiseBasedCall::StartRecvMessage( "finishes: received end-of-stream with error", DebugTag().c_str()); } + failed_before_recv_message_.store(true); FailCompletion(completion); *recv_message_ = nullptr; } else { @@ -2609,6 +2611,8 @@ void CallContext::UpdateDeadline(Timestamp deadline) { call_->UpdateDeadline(deadline); } +Timestamp CallContext::deadline() const { return call_->deadline(); } + ServerCallContext* CallContext::server_call_context() { return call_->server_call_context(); } @@ -2639,6 +2643,17 @@ class ClientPromiseBasedCall final : public PromiseBasedCall { ClientPromiseBasedCall(Arena* arena, grpc_call_create_args* args) : PromiseBasedCall(arena, 1, *args) { global_stats().IncrementClientCallsCreated(); + if (args->cq != nullptr) { + GPR_ASSERT(args->pollset_set_alternative == nullptr && + "Only one of 'cq' and 'pollset_set_alternative' should be " + "non-nullptr."); + polling_entity_.Set( + grpc_polling_entity_create_from_pollset(grpc_cq_pollset(args->cq))); + } + if (args->pollset_set_alternative != nullptr) { + polling_entity_.Set(grpc_polling_entity_create_from_pollset_set( + args->pollset_set_alternative)); + } ScopedContext context(this); send_initial_metadata_ = GetContext()->MakePooled(GetContext()); @@ -2691,7 +2706,6 @@ class ClientPromiseBasedCall final : public PromiseBasedCall { } absl::string_view GetServerAuthority() const override { abort(); } bool is_trailers_only() const override { return is_trailers_only_; } - bool failed_before_recv_message() const override { return false; } grpc_call_error StartBatch(const grpc_op* ops, size_t nops, void* notify_tag, bool is_notify_tag_closure) override; @@ -2730,6 +2744,7 @@ class ClientPromiseBasedCall final : public PromiseBasedCall { Pipe server_initial_metadata_{arena()}; Latch server_trailing_metadata_; Latch cancel_error_; + Latch polling_entity_; Pipe client_to_server_messages_{arena()}; Pipe server_to_client_messages_{arena()}; bool is_trailers_only_; @@ -2763,11 +2778,11 @@ void ClientPromiseBasedCall::StartPromise( token = std::move(token)]() mutable { return Race( cancel_error_.Wait(), - Map(channel()->channel_stack()->MakeClientCallPromise( - CallArgs{std::move(client_initial_metadata), - std::move(token), &server_initial_metadata_.sender, - &client_to_server_messages_.receiver, - &server_to_client_messages_.sender}), + Map(channel()->channel_stack()->MakeClientCallPromise(CallArgs{ + std::move(client_initial_metadata), std::move(token), + &polling_entity_, &server_initial_metadata_.sender, + &client_to_server_messages_.receiver, + &server_to_client_messages_.sender}), [this](ServerMetadataHandle trailing_metadata) { // If we're cancelled the transport doesn't get to return // stats. @@ -3023,9 +3038,13 @@ class ServerPromiseBasedCall final : public PromiseBasedCall { void CancelWithError(grpc_error_handle) override; grpc_call_error StartBatch(const grpc_op* ops, size_t nops, void* notify_tag, bool is_notify_tag_closure) override; - bool failed_before_recv_message() const override { return false; } bool is_trailers_only() const override { abort(); } - absl::string_view GetServerAuthority() const override { return ""; } + absl::string_view GetServerAuthority() const override { + const Slice* authority_metadata = + client_initial_metadata_->get_pointer(HttpAuthorityMetadata()); + if (authority_metadata == nullptr) return ""; + return authority_metadata->as_string_view(); + } // Polling order for the server promise stack: // @@ -3189,7 +3208,7 @@ ServerPromiseBasedCall::ServerPromiseBasedCall(Arena* arena, Spawn("server_promise", channel()->channel_stack()->MakeServerCallPromise( CallArgs{nullptr, ClientInitialMetadataOutstandingToken::Empty(), - nullptr, nullptr, nullptr}), + nullptr, nullptr, nullptr, nullptr}), [this](ServerMetadataHandle result) { Finish(std::move(result)); }); } @@ -3207,15 +3226,27 @@ void ServerPromiseBasedCall::Finish(ServerMetadataHandle result) { if (server_initial_metadata_ != nullptr) { server_initial_metadata_->Close(); } + const auto status = + result->get(GrpcStatusMetadata()).value_or(GRPC_STATUS_UNKNOWN); channelz::ServerNode* channelz_node = server_->channelz_node(); if (channelz_node != nullptr) { - if (result->get(GrpcStatusMetadata()).value_or(GRPC_STATUS_UNKNOWN) == - GRPC_STATUS_OK) { + if (status == GRPC_STATUS_OK) { channelz_node->RecordCallSucceeded(); } else { channelz_node->RecordCallFailed(); } } + absl::string_view message_string; + if (Slice* message = result->get_pointer(GrpcMessageMetadata())) { + message_string = message->as_string_view(); + } + AcceptTransportStatsFromContext(); + if (message_string.empty()) { + RunFinalization(status, nullptr); + } else { + std::string error_string(message_string); + RunFinalization(status, error_string.c_str()); + } set_completed(); ResetDeadline(); PropagateCancellationToChildren(); @@ -3307,7 +3338,12 @@ void ServerPromiseBasedCall::CommitBatch(const grpc_op* ops, size_t nops, metadata->Set(GrpcStatusMetadata(), op.data.send_status_from_server.status); if (auto* details = op.data.send_status_from_server.status_details) { - metadata->Set(GrpcMessageMetadata(), Slice(CSliceRef(*details))); + // TODO(ctiller): this should not be a copy, but we have callers that + // allocate and pass in a slice created with + // grpc_slice_from_static_string and then delete the string after + // passing it in, which shouldn't be a supported API. + metadata->Set(GrpcMessageMetadata(), + Slice(grpc_slice_copy(*details))); } spawner.Spawn( "call_send_status_from_server", @@ -3403,11 +3439,14 @@ ServerCallContext::MakeTopOfServerCallPromise( grpc_metadata_array* publish_initial_metadata, absl::FunctionRef publish) { call_->SetCompletionQueue(cq); + call_args.polling_entity->Set( + grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq))); call_->server_to_client_messages_ = call_args.server_to_client_messages; call_->client_to_server_messages_ = call_args.client_to_server_messages; call_->server_initial_metadata_ = call_args.server_initial_metadata; call_->client_initial_metadata_ = std::move(call_args.client_initial_metadata); + call_->set_send_deadline(call_->deadline()); call_->ProcessIncomingInitialMetadata(*call_->client_initial_metadata_); PublishMetadataArray(call_->client_initial_metadata_.get(), publish_initial_metadata); diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h index 61176bbe932ca..d3c1e94bf8ed2 100644 --- a/src/core/lib/surface/call.h +++ b/src/core/lib/surface/call.h @@ -43,7 +43,6 @@ #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/iomgr/iomgr_fwd.h" -#include "src/core/lib/iomgr/polling_entity.h" #include "src/core/lib/promise/arena_promise.h" #include "src/core/lib/promise/context.h" #include "src/core/lib/resource_quota/arena.h" @@ -107,6 +106,7 @@ class CallContext { // Update the deadline (if deadline < the current deadline). void UpdateDeadline(Timestamp deadline); + Timestamp deadline() const; // Run some action in the call activity context. This is needed to adapt some // legacy systems to promises, and will likely disappear once that conversion @@ -126,7 +126,6 @@ class CallContext { grpc_call_stats* call_stats() { return &call_stats_; } gpr_atm* peer_string_atm_ptr(); - grpc_polling_entity* polling_entity() { return &pollent_; } ServerCallContext* server_call_context(); @@ -137,9 +136,6 @@ class CallContext { friend class PromiseBasedCall; // Call final info. grpc_call_stats call_stats_; - // Pollset stuff, can't wait to remove. - // TODO(ctiller): bring forth EventEngine. - grpc_polling_entity pollent_; // TODO(ctiller): remove this once transport APIs are promise based and we // don't need refcounting here. PromiseBasedCall* const call_; diff --git a/src/core/lib/surface/completion_queue.cc b/src/core/lib/surface/completion_queue.cc index aaa0caf811b1f..8bb96965a2955 100644 --- a/src/core/lib/surface/completion_queue.cc +++ b/src/core/lib/surface/completion_queue.cc @@ -539,7 +539,8 @@ grpc_completion_queue* grpc_completion_queue_create_internal( cq->poller_vtable = poller_vtable; // One for destroy(), one for pollset_shutdown - new (&cq->owning_refs) grpc_core::RefCount(2); + new (&cq->owning_refs) grpc_core::RefCount( + 2, grpc_trace_cq_refcount.enabled() ? "completion_queue" : nullptr); poller_vtable->init(POLLSET_FROM_CQ(cq), &cq->mu); vtable->init(DATA_FROM_CQ(cq), shutdown_callback); diff --git a/src/core/lib/surface/server.cc b/src/core/lib/surface/server.cc index 2071a232da1fc..9c67a82cfe6ee 100644 --- a/src/core/lib/surface/server.cc +++ b/src/core/lib/surface/server.cc @@ -39,6 +39,7 @@ #include "absl/types/variant.h" #include +#include #include #include #include @@ -195,9 +196,35 @@ class Server::RequestMatcherInterface { virtual void RequestCallWithPossiblePublish(size_t request_queue_index, RequestedCall* call) = 0; - struct MatchResult { - size_t cq_idx; - RequestedCall* requested_call; + class MatchResult { + public: + MatchResult(Server* server, size_t cq_idx, RequestedCall* requested_call) + : server_(server), cq_idx_(cq_idx), requested_call_(requested_call) {} + ~MatchResult() { + if (requested_call_ != nullptr) { + server_->FailCall(cq_idx_, requested_call_, absl::CancelledError()); + } + } + + MatchResult(const MatchResult&) = delete; + MatchResult& operator=(const MatchResult&) = delete; + + MatchResult(MatchResult&& other) noexcept + : server_(other.server_), + cq_idx_(other.cq_idx_), + requested_call_(std::exchange(other.requested_call_, nullptr)) {} + + RequestedCall* TakeCall() { + return std::exchange(requested_call_, nullptr); + } + + grpc_completion_queue* cq() const { return server_->cqs_[cq_idx_]; } + size_t cq_idx() const { return cq_idx_; } + + private: + Server* server_; + size_t cq_idx_; + RequestedCall* requested_call_; }; // This function is invoked on an incoming promise based RPC. @@ -294,18 +321,20 @@ class Server::RealRequestMatcher : public RequestMatcherInterface { while (true) { NextPendingCall next_pending = pop_next_pending(); if (next_pending.rc == nullptr) break; - auto mr = MatchResult{request_queue_index, next_pending.rc}; + auto mr = MatchResult(server(), request_queue_index, next_pending.rc); Match( next_pending.pending, - [mr](CallData* calld) { + [&mr](CallData* calld) { if (!calld->MaybeActivate()) { // Zombied Call calld->KillZombie(); } else { - calld->Publish(mr.cq_idx, mr.requested_call); + calld->Publish(mr.cq_idx(), mr.TakeCall()); } }, - [mr](const std::shared_ptr& w) { w->Finish(mr); }); + [&mr](const std::shared_ptr& w) { + w->Finish(std::move(mr)); + }); } } } @@ -357,7 +386,7 @@ class Server::RealRequestMatcher : public RequestMatcherInterface { RequestedCall* rc = reinterpret_cast(requests_per_cq_[cq_idx].TryPop()); if (rc != nullptr) { - return Immediate(MatchResult{cq_idx, rc}); + return Immediate(MatchResult(server(), cq_idx, rc)); } } // No cq to take the request found; queue it on the slow list. @@ -390,10 +419,10 @@ class Server::RealRequestMatcher : public RequestMatcherInterface { }; } } - return Immediate(MatchResult{cq_idx, rc}); + return Immediate(MatchResult(server(), cq_idx, rc)); } - Server* server() const override { return server_; } + Server* server() const final { return server_; } private: Server* const server_; @@ -444,7 +473,7 @@ class Server::AllocatingRequestMatcherBase : public RequestMatcherInterface { Crash("unreachable"); } - Server* server() const override { return server_; } + Server* server() const final { return server_; } // Supply the completion queue related to this request matcher grpc_completion_queue* cq() const { return cq_; } @@ -501,7 +530,7 @@ class Server::AllocatingRequestMatcherBatch RequestedCall* rc = new RequestedCall( static_cast(call_info.tag), call_info.cq, call_info.call, call_info.initial_metadata, call_info.details); - return Immediate(MatchResult{cq_idx(), rc}); + return Immediate(MatchResult(server(), cq_idx(), rc)); } else { return Immediate(absl::InternalError("Server shutdown")); } @@ -556,7 +585,7 @@ class Server::AllocatingRequestMatcherRegistered new RequestedCall(call_info.tag, call_info.cq, call_info.call, call_info.initial_metadata, registered_method_, call_info.deadline, call_info.optional_payload); - return Immediate(MatchResult{cq_idx(), rc}); + return Immediate(MatchResult(server(), cq_idx(), rc)); } else { return Immediate(absl::InternalError("Server shutdown")); } @@ -1282,8 +1311,7 @@ ArenaPromise Server::ChannelData::MakeCallPromise( absl::InternalError("Missing :authority header")); }; } - // TODO(ctiller): deadline handling - Timestamp deadline = Timestamp::InfFuture(); + Timestamp deadline = GetContext()->deadline(); // Find request matcher. RequestMatcherInterface* matcher; ChannelRegisteredMethod* rm = @@ -1309,19 +1337,19 @@ ArenaPromise Server::ChannelData::MakeCallPromise( return TrySeq( TryJoin(matcher->MatchRequest(chand->cq_idx()), std::move(maybe_read_first_message)), - [path = std::move(*path), host = std::move(*host_ptr), deadline, server, + [path = std::move(*path), host_ptr, deadline, call_args = std::move(call_args)]( std::tuple> match_result_and_payload) mutable { auto& mr = std::get<0>(match_result_and_payload); auto& payload = std::get<1>(match_result_and_payload); - auto* rc = mr.requested_call; - auto* cq_for_new_request = server->cqs_[mr.cq_idx]; + auto* rc = mr.TakeCall(); + auto* cq_for_new_request = mr.cq(); switch (rc->type) { case RequestedCall::Type::BATCH_CALL: GPR_ASSERT(!payload.has_value()); - rc->data.batch.details->host = CSliceRef(host.c_slice()); + rc->data.batch.details->host = CSliceRef(host_ptr->c_slice()); rc->data.batch.details->method = CSliceRef(path.c_slice()); rc->data.batch.details->deadline = deadline.as_timespec(GPR_CLOCK_MONOTONIC); diff --git a/src/core/lib/transport/batch_builder.h b/src/core/lib/transport/batch_builder.h index 5b0056aebe782..6b494a00fb1ff 100644 --- a/src/core/lib/transport/batch_builder.h +++ b/src/core/lib/transport/batch_builder.h @@ -154,6 +154,7 @@ class BatchBuilder { absl::optional payload; uint32_t flags; + bool call_failed_before_recv_message = false; }; // A pending receive metadata. @@ -392,12 +393,19 @@ inline auto BatchBuilder::ReceiveMessage(Target target) { payload_->recv_message.recv_message_ready = &pc->on_done_closure; payload_->recv_message.recv_message = &pc->payload; payload_->recv_message.flags = &pc->flags; + payload_->recv_message.call_failed_before_recv_message = + &pc->call_failed_before_recv_message; return batch->RefUntil( Map(pc->done_latch.Wait(), [pc](absl::Status status) -> absl::StatusOr> { if (!status.ok()) return status; - if (!pc->payload.has_value()) return absl::nullopt; + if (!pc->payload.has_value()) { + if (pc->call_failed_before_recv_message) { + return absl::CancelledError(); + } + return absl::nullopt; + } return pc->IntoMessageHandle(); })); } diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h index d74aa477aca65..f970fb014072e 100644 --- a/src/core/lib/transport/transport.h +++ b/src/core/lib/transport/transport.h @@ -211,6 +211,9 @@ struct CallArgs { // This should be moved around and only destroyed when the transport is // satisfied that the metadata has passed any flow control measures it has. ClientInitialMetadataOutstandingToken client_initial_metadata_outstanding; + // Latch that will ultimately contain the polling entity for the call. + // TODO(ctiller): remove once event engine lands + Latch* polling_entity; // Initial metadata from the server to the client. // Set once when it's available. // During promise setup filters can substitute their own latch for this diff --git a/test/core/end2end/cq_verifier.cc b/test/core/end2end/cq_verifier.cc index 67e71626a043e..11308a46cb3c9 100644 --- a/test/core/end2end/cq_verifier.cc +++ b/test/core/end2end/cq_verifier.cc @@ -213,8 +213,10 @@ std::string CqVerifier::Expectation::ToString() const { }, [](Maybe) { return std::string("maybe"); }, [](AnyStatus) { return std::string("any success value"); }, - [](PerformAction) { return std::string("perform some action"); }, - [](MaybePerformAction) { + [](const PerformAction&) { + return std::string("perform some action"); + }, + [](const MaybePerformAction&) { return std::string("maybe perform action"); })); } @@ -284,8 +286,11 @@ void CqVerifier::Verify(Duration timeout, SourceLocation location) { bool found = false; for (auto it = expectations_.begin(); it != expectations_.end(); ++it) { if (it->tag != ev.tag) continue; + auto expectation = std::move(*it); + expectations_.erase(it); const bool expected = Match( - it->result, [ev](bool success) { return ev.success == success; }, + expectation.result, + [ev](bool success) { return ev.success == success; }, [ev](Maybe m) { if (m.seen != nullptr) *m.seen = true; return ev.success != 0; @@ -305,7 +310,6 @@ void CqVerifier::Verify(Duration timeout, SourceLocation location) { if (!expected) { FailUnexpectedEvent(&ev, location); } - expectations_.erase(it); found = true; break; } diff --git a/test/core/end2end/tests/resource_quota_server.cc b/test/core/end2end/tests/resource_quota_server.cc index fa642ed71cad6..32c4b9ce13fce 100644 --- a/test/core/end2end/tests/resource_quota_server.cc +++ b/test/core/end2end/tests/resource_quota_server.cc @@ -16,6 +16,8 @@ // // +#include + #include #include #include @@ -66,13 +68,20 @@ TEST_P(ResourceQuotaTest, ResourceQuota) { auto requests = MakeVec([](int) { return RandomSlice(128 * 1024); }); auto server_calls = MakeVec([this](int i) { return RequestCall(kServerRecvBaseTag + i); }); - std::vector server_metadata(kNumCalls); - std::vector server_status(kNumCalls); - std::vector client_message(kNumCalls); - std::vector client_close(kNumCalls); + IncomingMetadata server_metadata[kNumCalls]; + IncomingStatusOnClient server_status[kNumCalls]; + IncomingMessage client_message[kNumCalls]; + IncomingCloseOnServer client_close[kNumCalls]; + enum class SeenServerCall { + kNotSeen = 0, + kSeenWithSuccess, + kSeenWithFailure + }; + // Yep, this really initializes all the elements. + SeenServerCall seen_server_call[kNumCalls] = {SeenServerCall::kNotSeen}; auto client_calls = MakeVec([this, &requests, &server_metadata, &server_status](int i) { - auto c = NewClientCall("/foo").Timeout(Duration::Minutes(1)).Create(); + auto c = NewClientCall("/foo").Timeout(Duration::Seconds(5)).Create(); c.NewBatch(kClientBaseTag + i) .SendInitialMetadata({}, GRPC_INITIAL_METADATA_WAIT_FOR_READY) .SendMessage(requests[i].Ref()) @@ -83,24 +92,28 @@ TEST_P(ResourceQuotaTest, ResourceQuota) { }); for (int i = 0; i < kNumCalls; i++) { Expect(kClientBaseTag + i, true); - Expect(kServerRecvBaseTag + i, - PerformAction{[&server_calls, &client_message, i](bool success) { - EXPECT_TRUE(success); - server_calls[i] - .NewBatch(kServerStartBaseTag + i) - .RecvMessage(client_message[i]) - .SendInitialMetadata({}); - }}); - Expect(kServerStartBaseTag + i, - PerformAction{[&server_calls, &client_close, i](bool) { - server_calls[i] - .NewBatch(kServerEndBaseTag + i) - .RecvCloseOnServer(client_close[i]) - .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); - }}); - Expect(kServerEndBaseTag + i, true); + Expect( + kServerRecvBaseTag + i, + MaybePerformAction{[this, &seen_server_call, &server_calls, + &client_message, &client_close, i](bool success) { + seen_server_call[i] = success ? SeenServerCall::kSeenWithSuccess + : SeenServerCall::kSeenWithFailure; + if (!success) return; + server_calls[i] + .NewBatch(kServerStartBaseTag + i) + .RecvMessage(client_message[i]) + .SendInitialMetadata({}); + Expect(kServerStartBaseTag + i, + PerformAction{[&server_calls, &client_close, i](bool) { + server_calls[i] + .NewBatch(kServerEndBaseTag + i) + .RecvCloseOnServer(client_close[i]) + .SendStatusFromServer(GRPC_STATUS_OK, "xyz", {}); + }}); + Expect(kServerEndBaseTag + i, true); + }}); } - Step(Duration::Seconds(30)); + Step(); int cancelled_calls_on_client = 0; int cancelled_calls_on_server = 0; @@ -123,7 +136,8 @@ TEST_P(ResourceQuotaTest, ResourceQuota) { Crash(absl::StrFormat("Unexpected status code: %d", server_status[i].status())); } - if (client_close[i].was_cancelled()) { + if (seen_server_call[i] == SeenServerCall::kSeenWithSuccess && + client_close[i].was_cancelled()) { cancelled_calls_on_server++; } } @@ -132,6 +146,18 @@ TEST_P(ResourceQuotaTest, ResourceQuota) { "client, %d timed out, %d unavailable.", kNumCalls, cancelled_calls_on_server, cancelled_calls_on_client, deadline_exceeded, unavailable); + + ShutdownServerAndNotify(0); + Expect(0, PerformAction{[this](bool success) { + EXPECT_TRUE(success); + DestroyServer(); + }}); + for (size_t i = 0; i < kNumCalls; i++) { + if (seen_server_call[i] == SeenServerCall::kNotSeen) { + Expect(kServerRecvBaseTag + i, false); + } + } + Step(); } } // namespace diff --git a/test/core/filters/filter_fuzzer.cc b/test/core/filters/filter_fuzzer.cc index ebf985351bc10..941f175e668cd 100644 --- a/test/core/filters/filter_fuzzer.cc +++ b/test/core/filters/filter_fuzzer.cc @@ -479,7 +479,10 @@ class MainLoop { CallArgs call_args{std::move(*LoadMetadata(client_initial_metadata, &client_initial_metadata_)), ClientInitialMetadataOutstandingToken::Empty(), - &server_initial_metadata->sender, nullptr, nullptr}; + nullptr, + &server_initial_metadata->sender, + nullptr, + nullptr}; if (is_client) { promise_ = main_loop_->channel_stack_->MakeClientCallPromise( std::move(call_args)); diff --git a/test/core/filters/filter_test.cc b/test/core/filters/filter_test.cc index 00e1af43676a1..b69e12fb08929 100644 --- a/test/core/filters/filter_test.cc +++ b/test/core/filters/filter_test.cc @@ -116,7 +116,7 @@ void FilterTestBase::Call::Impl::Start(ClientMetadataHandle md) { EXPECT_EQ(promise_, absl::nullopt); promise_ = channel_->filter->MakeCallPromise( CallArgs{std::move(md), ClientInitialMetadataOutstandingToken::Empty(), - &pipe_server_initial_metadata_.sender, + nullptr, &pipe_server_initial_metadata_.sender, &pipe_client_to_server_messages_.receiver, &pipe_server_to_client_messages_.sender}, [this](CallArgs args) -> ArenaPromise { diff --git a/test/core/promise/pipe_test.cc b/test/core/promise/pipe_test.cc index 4613c40274da4..be4444f84464d 100644 --- a/test/core/promise/pipe_test.cc +++ b/test/core/promise/pipe_test.cc @@ -275,6 +275,36 @@ TEST_F(PipeTest, CanCloseSend) { MakeScopedArena(1024, &memory_allocator_)); } +TEST_F(PipeTest, CanCloseWithErrorSend) { + StrictMock> on_done; + EXPECT_CALL(on_done, Call(absl::OkStatus())); + MakeActivity( + [] { + auto* pipe = GetContext()->ManagedNew>(); + return Seq( + // Concurrently: + // - wait for a received value (will stall forever since we push + // nothing into the queue) + // - close the sender, which will signal the receiver to return an + // end-of-stream. + Join(pipe->receiver.Next(), + [pipe]() mutable { + pipe->sender.CloseWithError(); + return absl::OkStatus(); + }), + // Verify we received end-of-stream and closed the sender. + [](std::tuple, absl::Status> result) { + EXPECT_FALSE(std::get<0>(result).has_value()); + EXPECT_TRUE(std::get<0>(result).cancelled()); + EXPECT_EQ(std::get<1>(result), absl::OkStatus()); + return absl::OkStatus(); + }); + }, + NoWakeupScheduler(), + [&on_done](absl::Status status) { on_done.Call(std::move(status)); }, + MakeScopedArena(1024, &memory_allocator_)); +} + TEST_F(PipeTest, CanCloseSendWithInterceptor) { StrictMock> on_done; EXPECT_CALL(on_done, Call(absl::OkStatus())); diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD index 074e7923bebe6..903765831d610 100644 --- a/test/cpp/end2end/BUILD +++ b/test/cpp/end2end/BUILD @@ -103,7 +103,11 @@ grpc_cc_test( external_deps = [ "gtest", ], - tags = ["no_test_ios"], + shard_count = 10, + tags = [ + "cpp_end2end_test", + "no_test_ios", + ], deps = [ "//:gpr", "//:grpc", @@ -127,6 +131,7 @@ grpc_cc_test( "gtest", ], tags = [ + "cpp_end2end_test", "no_test_android", # android_cc_test doesn't work with data dependency. "no_test_ios", "no_windows", @@ -197,6 +202,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":interceptors_util", ":test_service_impl", @@ -217,6 +223,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":test_service_impl", "//:gpr", @@ -235,6 +242,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":interceptors_util", ":test_service_impl", @@ -287,7 +295,10 @@ grpc_cc_test( ], # TODO(yulin-liang): The test is not able to load the certificate files on # iOS. Figure out why. - tags = ["no_test_ios"], + tags = [ + "cpp_end2end_test", + "no_test_ios", + ], deps = [ ":test_service_impl", "//:gpr", @@ -309,6 +320,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ "//:gpr", "//:grpc", @@ -324,7 +336,11 @@ grpc_cc_test( name = "end2end_test", size = "large", flaky = True, # TODO(b/151704375) - tags = ["no_test_ios"], + shard_count = 10, + tags = [ + "cpp_end2end_test", + "no_test_ios", + ], deps = [ ":end2end_test_lib", # DO NOT REMOVE THE grpc++ dependence below since the internal build @@ -356,6 +372,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ "//:gpr", "//:grpc", @@ -374,6 +391,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ "//:gpr", "//:grpc", @@ -392,6 +410,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":test_health_check_service_impl", ":test_service_impl", @@ -413,6 +432,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":test_service_impl", "//:gpr", @@ -432,6 +452,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":test_service_impl", "//:gpr", @@ -451,6 +472,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ "//:gpr", "//:grpc", @@ -470,6 +492,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ "//:gpr", "//:grpc", @@ -488,7 +511,10 @@ grpc_cc_test( "gtest", ], flaky = True, # TODO(b/151315347) - tags = ["no_windows"], # TODO(jtattermusch): fix test on windows + tags = [ + "cpp_end2end_test", + "no_windows", + ], # TODO(jtattermusch): fix test on windows deps = [ ":connection_attempt_injector", ":test_service_impl", @@ -518,7 +544,10 @@ grpc_cc_test( "absl/types:optional", ], flaky = True, - tags = ["no_test_ios"], + tags = [ + "cpp_end2end_test", + "no_test_ios", + ], deps = [ ":counted_service", ":rls_server", @@ -544,6 +573,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":test_service_impl", "//:gpr", @@ -565,7 +595,10 @@ grpc_cc_test( "gtest", ], flaky = True, # TODO(b/150567713) - tags = ["no_windows"], # TODO(jtattermusch): fix test on windows + tags = [ + "cpp_end2end_test", + "no_windows", + ], # TODO(jtattermusch): fix test on windows deps = [ ":counted_service", ":test_service_impl", @@ -590,6 +623,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":test_service_impl", "//:gpr", @@ -611,6 +645,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":test_service_impl", "//:gpr", @@ -677,6 +712,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":interceptors_util", ":test_service_impl", @@ -697,6 +733,7 @@ grpc_cc_test( "gtest", ], tags = [ + "cpp_end2end_test", "no_test_ios", "no_windows", ], @@ -739,6 +776,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ "//:gpr", "//:grpc", @@ -757,7 +795,10 @@ grpc_cc_test( external_deps = [ "gtest", ], - tags = ["no_windows"], + tags = [ + "cpp_end2end_test", + "no_windows", + ], deps = [ "//:gpr", "//:grpc", @@ -778,7 +819,10 @@ grpc_cc_test( "gtest", ], shard_count = 5, - tags = ["no_windows"], # TODO(jtattermusch): fix test on windows + tags = [ + "cpp_end2end_test", + "no_windows", + ], # TODO(jtattermusch): fix test on windows deps = [ "//:gpr", "//:grpc", @@ -825,6 +869,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":test_service_impl", "//:gpr", @@ -844,6 +889,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":test_service_impl", "//:gpr", @@ -863,6 +909,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":test_service_impl", "//:gpr", @@ -881,6 +928,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ "//:grpc++", "//:grpc++_reflection", @@ -903,6 +951,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ ":test_service_impl", "//:gpr", @@ -929,6 +978,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ "//:gpr", "//:grpc", @@ -946,6 +996,7 @@ grpc_cc_test( external_deps = [ "gtest", ], + tags = ["cpp_end2end_test"], deps = [ "//:grpc++", "//:grpcpp_backend_metric_recorder", diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc index 6749a5c0a8e9a..7fc2d33132a19 100644 --- a/test/cpp/end2end/end2end_test.cc +++ b/test/cpp/end2end/end2end_test.cc @@ -1195,38 +1195,48 @@ TEST_P(End2endTest, CancelRpcBeforeStart) { } TEST_P(End2endTest, CancelRpcAfterStart) { - ResetStub(); - EchoRequest request; - EchoResponse response; - ClientContext context; - request.set_message("hello"); - request.mutable_param()->set_server_notify_client_when_started(true); - request.mutable_param()->set_skip_cancelled_check(true); - Status s; - std::thread echo_thread([this, &s, &context, &request, &response] { - s = stub_->Echo(&context, request, &response); - EXPECT_EQ(StatusCode::CANCELLED, s.error_code()); - }); - if (!GetParam().callback_server()) { - service_.ClientWaitUntilRpcStarted(); - } else { - callback_service_.ClientWaitUntilRpcStarted(); - } + for (int i = 0; i < 10; i++) { + ResetStub(); + EchoRequest request; + EchoResponse response; + ClientContext context; + request.set_message("hello"); + request.mutable_param()->set_server_notify_client_when_started(true); + request.mutable_param()->set_skip_cancelled_check(true); + Status s; + std::thread echo_thread([this, &s, &context, &request, &response] { + s = stub_->Echo(&context, request, &response); + }); + if (!GetParam().callback_server()) { + service_.ClientWaitUntilRpcStarted(); + } else { + callback_service_.ClientWaitUntilRpcStarted(); + } - context.TryCancel(); + context.TryCancel(); - if (!GetParam().callback_server()) { - service_.SignalServerToContinue(); - } else { - callback_service_.SignalServerToContinue(); - } + if (!GetParam().callback_server()) { + service_.SignalServerToContinue(); + } else { + callback_service_.SignalServerToContinue(); + } - echo_thread.join(); - EXPECT_EQ("", response.message()); - EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code()); - if (GetParam().use_interceptors()) { - EXPECT_EQ(20, PhonyInterceptor::GetNumTimesCancel()); + echo_thread.join(); + // TODO(ctiller): improve test to not be flaky + // + // TryCancel is best effort, and it can happen that the cancellation is not + // acted upon before the server wakes up, sends a response, and the client + // reads that. + // For this reason, we try a few times here to see the cancellation result. + if (s.ok()) continue; + EXPECT_EQ("", response.message()); + EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code()); + if (GetParam().use_interceptors()) { + EXPECT_EQ(20, PhonyInterceptor::GetNumTimesCancel()); + } + return; } + GTEST_FAIL() << "Failed to get cancellation"; } // Client cancels request stream after sending two messages diff --git a/test/cpp/end2end/test_service_impl.h b/test/cpp/end2end/test_service_impl.h index 0ce60738f2180..c6c9edd65411e 100644 --- a/test/cpp/end2end/test_service_impl.h +++ b/test/cpp/end2end/test_service_impl.h @@ -90,19 +90,25 @@ void ServerTryCancel(ServerContext* context); class TestServiceSignaller { public: void ClientWaitUntilRpcStarted() { + gpr_log(GPR_DEBUG, "*** enter ClientWaitUntilRpcStarted ***"); std::unique_lock lock(mu_); cv_rpc_started_.wait(lock, [this] { return rpc_started_; }); + gpr_log(GPR_DEBUG, "*** leave ClientWaitUntilRpcStarted ***"); } void ServerWaitToContinue() { + gpr_log(GPR_DEBUG, "*** enter ServerWaitToContinue ***"); std::unique_lock lock(mu_); cv_server_continue_.wait(lock, [this] { return server_should_continue_; }); + gpr_log(GPR_DEBUG, "*** leave ServerWaitToContinue ***"); } void SignalClientThatRpcStarted() { + gpr_log(GPR_DEBUG, "*** SignalClientThatRpcStarted ***"); std::unique_lock lock(mu_); rpc_started_ = true; cv_rpc_started_.notify_one(); } void SignalServerToContinue() { + gpr_log(GPR_DEBUG, "*** SignalServerToContinue ***"); std::unique_lock lock(mu_); server_should_continue_ = true; cv_server_continue_.notify_one(); diff --git a/test/cpp/end2end/xds/BUILD b/test/cpp/end2end/xds/BUILD index 827d729d81567..a17c4c1104b44 100644 --- a/test/cpp/end2end/xds/BUILD +++ b/test/cpp/end2end/xds/BUILD @@ -101,6 +101,7 @@ grpc_cc_test( tags = [ "no_test_ios", "no_windows", + "xds_end2end_test", ], # TODO(jtattermusch): fix test on windows deps = [ ":xds_end2end_test_lib", @@ -139,10 +140,11 @@ grpc_cc_test( ], flaky = True, # TODO(b/144705388) linkstatic = True, # Fixes dyld error on MacOS - shard_count = 20, + shard_count = 50, tags = [ "no_test_ios", "no_windows", + "xds_end2end_test", ], # TODO(jtattermusch): fix test on windows deps = [ ":xds_end2end_test_lib", @@ -166,6 +168,7 @@ grpc_cc_test( tags = [ "no_test_ios", "no_windows", + "xds_end2end_test", ], # TODO(jtattermusch): fix test on windows deps = [ ":xds_end2end_test_lib", @@ -192,6 +195,7 @@ grpc_cc_test( tags = [ "no_test_ios", "no_windows", + "xds_end2end_test", ], # TODO(jtattermusch): fix test on windows deps = [ ":xds_end2end_test_lib", @@ -214,6 +218,7 @@ grpc_cc_test( tags = [ "no_test_ios", "no_windows", + "xds_end2end_test", ], # TODO(jtattermusch): fix test on windows deps = [ ":xds_end2end_test_lib", @@ -242,6 +247,7 @@ grpc_cc_test( tags = [ "no_test_ios", "no_windows", + "xds_end2end_test", ], # TODO(jtattermusch): fix test on windows deps = [ ":xds_end2end_test_lib", @@ -268,6 +274,7 @@ grpc_cc_test( tags = [ "no_test_ios", "no_windows", + "xds_end2end_test", ], # TODO(jtattermusch): fix test on windows deps = [ ":xds_end2end_test_lib", @@ -291,6 +298,7 @@ grpc_cc_test( tags = [ "no_test_ios", "no_windows", + "xds_end2end_test", ], # TODO(jtattermusch): fix test on windows deps = [ ":xds_end2end_test_lib", @@ -318,6 +326,7 @@ grpc_cc_test( tags = [ "no_test_ios", "no_windows", + "xds_end2end_test", ], # TODO(jtattermusch): fix test on windows deps = [ ":xds_end2end_test_lib", @@ -346,6 +355,7 @@ grpc_cc_test( tags = [ "no_test_ios", "no_windows", + "xds_end2end_test", ], # TODO(jtattermusch): fix test on windows deps = [ ":xds_end2end_test_lib", @@ -373,6 +383,7 @@ grpc_cc_test( tags = [ "no_test_ios", "no_windows", + "xds_end2end_test", ], # TODO(jtattermusch): fix test on windows deps = [ ":xds_end2end_test_lib", @@ -391,7 +402,10 @@ grpc_cc_test( external_deps = [ "gtest", ], - tags = ["no_test_ios"], + tags = [ + "no_test_ios", + "xds_end2end_test", + ], deps = [ "//:gpr", "//:grpc", @@ -415,6 +429,7 @@ grpc_cc_test( tags = [ "no_test_ios", "no_windows", + "xds_end2end_test", ], # TODO(jtattermusch): fix test on windows deps = [ ":xds_end2end_test_lib", From 9393cd887cee2f057c1ee558e1e876cb595742e3 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Thu, 6 Apr 2023 13:37:42 -0700 Subject: [PATCH 23/39] [JSON] remove mutable accessor methods. (#32806) Co-authored-by: markdroth --- src/core/BUILD | 2 + .../client_channel/lb_policy/rls/rls.cc | 60 ++++++------- src/core/ext/xds/xds_http_rbac_filter.cc | 9 +- src/core/lib/json/json.h | 3 - src/core/lib/json/json_reader.cc | 87 +++++++++++-------- .../credentials/jwt/jwt_credentials.cc | 6 +- test/core/channel/channelz_test.cc | 76 ++++++++++------ .../client_channel_service_config_test.cc | 2 +- .../lb_policy/outlier_detection_test.cc | 26 +++--- test/cpp/util/channelz_sampler.cc | 6 +- 10 files changed, 162 insertions(+), 115 deletions(-) diff --git a/src/core/BUILD b/src/core/BUILD index 308c6dad11cdc..8c1b87f4163f7 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -3295,10 +3295,12 @@ grpc_cc_library( "absl/status:statusor", "absl/strings", "absl/strings:str_format", + "absl/types:variant", ], visibility = ["@grpc:json_reader_legacy"], deps = [ "json", + "match", "//:gpr", ], ) diff --git a/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc b/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc index 2de1bc5e5ff8f..7448a494305f6 100644 --- a/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc +++ b/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc @@ -752,58 +752,61 @@ void RlsLb::ChildPolicyWrapper::Orphan() { picker_.reset(); } -bool InsertOrUpdateChildPolicyField(const std::string& field, - const std::string& value, Json* config, - ValidationErrors* errors) { - if (config->type() != Json::Type::kArray) { +absl::optional InsertOrUpdateChildPolicyField(const std::string& field, + const std::string& value, + const Json& config, + ValidationErrors* errors) { + if (config.type() != Json::Type::kArray) { errors->AddError("is not an array"); - return false; + return absl::nullopt; } - bool success = true; - for (size_t i = 0; i < config->array().size(); ++i) { - Json& child_json = (*config->mutable_array())[i]; + const size_t original_num_errors = errors->size(); + Json::Array array; + for (size_t i = 0; i < config.array().size(); ++i) { + const Json& child_json = config.array()[i]; ValidationErrors::ScopedField json_field(errors, absl::StrCat("[", i, "]")); if (child_json.type() != Json::Type::kObject) { errors->AddError("is not an object"); - success = false; } else { - Json::Object& child = *child_json.mutable_object(); + const Json::Object& child = child_json.object(); if (child.size() != 1) { errors->AddError("child policy object contains more than one field"); - success = false; } else { + const std::string& child_name = child.begin()->first; ValidationErrors::ScopedField json_field( - errors, absl::StrCat("[\"", child.begin()->first, "\"]")); - Json& child_config_json = child.begin()->second; + errors, absl::StrCat("[\"", child_name, "\"]")); + const Json& child_config_json = child.begin()->second; if (child_config_json.type() != Json::Type::kObject) { errors->AddError("child policy config is not an object"); - success = false; } else { - Json::Object& child_config = *child_config_json.mutable_object(); + Json::Object child_config = child_config_json.object(); child_config[field] = Json(value); + array.emplace_back( + Json::Object{{child_name, std::move(child_config)}}); } } } } - return success; + if (errors->size() != original_num_errors) return absl::nullopt; + return array; } void RlsLb::ChildPolicyWrapper::StartUpdate() { - Json child_policy_config = lb_policy_->config_->child_policy_config(); ValidationErrors errors; - GPR_ASSERT(InsertOrUpdateChildPolicyField( + auto child_policy_config = InsertOrUpdateChildPolicyField( lb_policy_->config_->child_policy_config_target_field_name(), target_, - &child_policy_config, &errors)); + lb_policy_->config_->child_policy_config(), &errors); + GPR_ASSERT(child_policy_config.has_value()); if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_rls_trace)) { gpr_log( GPR_INFO, "[rlslb %p] ChildPolicyWrapper=%p [%s]: validating update, config: %s", lb_policy_.get(), this, target_.c_str(), - JsonDump(child_policy_config).c_str()); + JsonDump(*child_policy_config).c_str()); } auto config = CoreConfiguration::Get().lb_policy_registry().ParseLoadBalancingConfig( - child_policy_config); + *child_policy_config); // Returned RLS target fails the validation. if (!config.ok()) { if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_rls_trace)) { @@ -2447,13 +2450,13 @@ void RlsLbConfig::JsonPostLoad(const Json& json, const JsonArgs&, errors->AddError("field not present"); } else { // Add target to all child policy configs in the list. - child_policy_config_ = it->second; std::string target = route_lookup_config_.default_target.empty() ? kFakeTargetFieldValue : route_lookup_config_.default_target; - if (InsertOrUpdateChildPolicyField(child_policy_config_target_field_name_, - target, &child_policy_config_, - errors)) { + auto child_policy_config = InsertOrUpdateChildPolicyField( + child_policy_config_target_field_name_, target, it->second, errors); + if (child_policy_config.has_value()) { + child_policy_config_ = std::move(*child_policy_config); // Parse the config. auto parsed_config = CoreConfiguration::Get() @@ -2467,12 +2470,9 @@ void RlsLbConfig::JsonPostLoad(const Json& json, const JsonArgs&, // we leave the target field in place, set to the default value. // This slightly optimizes what we need to do later when we update // a child policy for a given target. - for (Json& config : *(child_policy_config_.mutable_array())) { + for (const Json& config : child_policy_config_.array()) { if (config.object().begin()->first == (*parsed_config)->name()) { - Json save_config = std::move(config); - child_policy_config_.mutable_array()->clear(); - child_policy_config_.mutable_array()->push_back( - std::move(save_config)); + child_policy_config_ = Json::Array{config}; break; } } diff --git a/src/core/ext/xds/xds_http_rbac_filter.cc b/src/core/ext/xds/xds_http_rbac_filter.cc index c5f3d0e009e2f..e2ee291166b6a 100644 --- a/src/core/ext/xds/xds_http_rbac_filter.cc +++ b/src/core/ext/xds/xds_http_rbac_filter.cc @@ -22,7 +22,6 @@ #include #include -#include #include #include @@ -297,9 +296,7 @@ Json ParsePrincipalToJson(const envoy_config_rbac_v3_Principal* principal, principal_json.emplace("any", envoy_config_rbac_v3_Principal_any(principal)); } else if (envoy_config_rbac_v3_Principal_has_authenticated(principal)) { - auto* authenticated_json = - principal_json.emplace("authenticated", Json::Object()) - .first->second.mutable_object(); + Json::Object authenticated_json; const auto* principal_name = envoy_config_rbac_v3_Principal_Authenticated_principal_name( envoy_config_rbac_v3_Principal_authenticated(principal)); @@ -308,9 +305,9 @@ Json ParsePrincipalToJson(const envoy_config_rbac_v3_Principal* principal, ".authenticated.principal_name"); Json principal_name_json = ParseStringMatcherToJson(principal_name, errors); - authenticated_json->emplace("principalName", - std::move(principal_name_json)); + authenticated_json["principalName"] = std::move(principal_name_json); } + principal_json["authenticated"] = std::move(authenticated_json); } else if (envoy_config_rbac_v3_Principal_has_source_ip(principal)) { principal_json.emplace( "sourceIp", ParseCidrRangeToJson( diff --git a/src/core/lib/json/json.h b/src/core/lib/json/json.h index 3ce9ae1c664c1..3592554cfef3a 100644 --- a/src/core/lib/json/json.h +++ b/src/core/lib/json/json.h @@ -154,11 +154,8 @@ class Json { // Accessor methods. Type type() const { return type_; } const std::string& string() const { return string_value_; } - std::string* mutable_string() { return &string_value_; } const Object& object() const { return object_value_; } - Object* mutable_object() { return &object_value_; } const Array& array() const { return array_value_; } - Array* mutable_array() { return &array_value_; } bool operator==(const Json& other) const { if (type_ != other.type_) return false; diff --git a/src/core/lib/json/json_reader.cc b/src/core/lib/json/json_reader.cc index b17a800db4424..d6d8799b6ad77 100644 --- a/src/core/lib/json/json_reader.cc +++ b/src/core/lib/json/json_reader.cc @@ -33,9 +33,11 @@ #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" +#include "absl/types/variant.h" #include +#include "src/core/lib/gprpp/match.h" #include "src/core/lib/json/json.h" #define GRPC_JSON_MAX_DEPTH 255 @@ -93,6 +95,23 @@ class JsonReader { // static constexpr uint32_t GRPC_JSON_READ_CHAR_EOF = 0x7ffffff0; + struct Scope { + std::string parent_object_key; + absl::variant data; + + Json::Type type() const { + return Match( + data, [](const Json::Object&) { return Json::Type::kObject; }, + [](const Json::Array&) { return Json::Type::kArray; }); + } + + Json TakeAsJson() { + return MatchMutable( + &data, [&](Json::Object* object) { return Json(std::move(*object)); }, + [&](Json::Array* array) { return Json(std::move(*array)); }); + } + }; + explicit JsonReader(absl::string_view input) : original_input_(reinterpret_cast(input.data())), input_(original_input_), @@ -132,7 +151,7 @@ class JsonReader { uint8_t utf8_first_byte_ = 0; Json root_value_; - std::vector stack_; + std::vector stack_; std::string key_; std::string string_; @@ -229,28 +248,14 @@ uint32_t JsonReader::ReadChar() { } Json* JsonReader::CreateAndLinkValue() { - Json* value; - if (stack_.empty()) { - value = &root_value_; - } else { - Json* parent = stack_.back(); - if (parent->type() == Json::Type::kObject) { - if (parent->object().find(key_) != parent->object().end()) { - if (errors_.size() == GRPC_JSON_MAX_ERRORS) { - truncated_errors_ = true; - } else { - errors_.push_back(absl::StrFormat( - "duplicate key \"%s\" at index %" PRIuPTR, key_, CurrentIndex())); - } - } - value = &(*parent->mutable_object())[std::move(key_)]; - } else { - GPR_ASSERT(parent->type() == Json::Type::kArray); - parent->mutable_array()->emplace_back(); - value = &parent->mutable_array()->back(); - } - } - return value; + if (stack_.empty()) return &root_value_; + return MatchMutable( + &stack_.back().data, + [&](Json::Object* object) { return &(*object)[std::move(key_)]; }, + [&](Json::Array* array) { + array->emplace_back(); + return &array->back(); + }); } bool JsonReader::StartContainer(Json::Type type) { @@ -264,25 +269,40 @@ bool JsonReader::StartContainer(Json::Type type) { } return false; } - Json* value = CreateAndLinkValue(); + stack_.emplace_back(); + Scope& scope = stack_.back(); + scope.parent_object_key = std::move(key_); if (type == Json::Type::kObject) { - *value = Json::Object(); + scope.data = Json::Object(); } else { GPR_ASSERT(type == Json::Type::kArray); - *value = Json::Array(); + scope.data = Json::Array(); } - stack_.push_back(value); return true; } void JsonReader::EndContainer() { GPR_ASSERT(!stack_.empty()); + Scope scope = std::move(stack_.back()); stack_.pop_back(); + key_ = std::move(scope.parent_object_key); + Json* value = CreateAndLinkValue(); + *value = scope.TakeAsJson(); } void JsonReader::SetKey() { key_ = std::move(string_); string_.clear(); + const Json::Object& object = absl::get(stack_.back().data); + if (object.find(key_) != object.end()) { + if (errors_.size() == GRPC_JSON_MAX_ERRORS) { + truncated_errors_ = true; + } else { + errors_.push_back( + absl::StrFormat("duplicate key \"%s\" at index %" PRIuPTR, key_, + CurrentIndex() - key_.size() - 2)); + } + } } void JsonReader::SetString() { @@ -405,10 +425,9 @@ JsonReader::Status JsonReader::Run() { if (stack_.empty()) { return Status::GRPC_JSON_PARSE_ERROR; } else if (c == '}' && - stack_.back()->type() != Json::Type::kObject) { + stack_.back().type() != Json::Type::kObject) { return Status::GRPC_JSON_PARSE_ERROR; - } else if (c == ']' && - stack_.back()->type() != Json::Type::kArray) { + } else if (c == ']' && stack_.back().type() != Json::Type::kArray) { return Status::GRPC_JSON_PARSE_ERROR; } if (!SetNumber()) return Status::GRPC_JSON_PARSE_ERROR; @@ -423,10 +442,10 @@ JsonReader::Status JsonReader::Run() { return Status::GRPC_JSON_PARSE_ERROR; } if (!stack_.empty() && - stack_.back()->type() == Json::Type::kObject) { + stack_.back().type() == Json::Type::kObject) { state_ = State::GRPC_JSON_STATE_OBJECT_KEY_BEGIN; } else if (!stack_.empty() && - stack_.back()->type() == Json::Type::kArray) { + stack_.back().type() == Json::Type::kArray) { state_ = State::GRPC_JSON_STATE_VALUE_BEGIN; } else { return Status::GRPC_JSON_PARSE_ERROR; @@ -435,7 +454,7 @@ JsonReader::Status JsonReader::Run() { if (stack_.empty()) { return Status::GRPC_JSON_PARSE_ERROR; } - if (c == '}' && stack_.back()->type() != Json::Type::kObject) { + if (c == '}' && stack_.back().type() != Json::Type::kObject) { return Status::GRPC_JSON_PARSE_ERROR; } if (c == '}' && @@ -443,7 +462,7 @@ JsonReader::Status JsonReader::Run() { !container_just_begun_) { return Status::GRPC_JSON_PARSE_ERROR; } - if (c == ']' && stack_.back()->type() != Json::Type::kArray) { + if (c == ']' && stack_.back().type() != Json::Type::kArray) { return Status::GRPC_JSON_PARSE_ERROR; } if (c == ']' && state_ == State::GRPC_JSON_STATE_VALUE_BEGIN && diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.cc b/src/core/lib/security/credentials/jwt/jwt_credentials.cc index c50a1d47552e8..3f668460545af 100644 --- a/src/core/lib/security/credentials/jwt/jwt_credentials.cc +++ b/src/core/lib/security/credentials/jwt/jwt_credentials.cc @@ -144,8 +144,10 @@ static char* redact_private_key(const char* json_key) { if (!json.ok() || json->type() != Json::Type::kObject) { return gpr_strdup(""); } - (*json->mutable_object())["private_key"] = ""; - return gpr_strdup(grpc_core::JsonDump(*json, /*indent=*/2).c_str()); + Json::Object object = json->object(); + object["private_key"] = ""; + return gpr_strdup( + grpc_core::JsonDump(Json(std::move(object)), /*indent=*/2).c_str()); } grpc_call_credentials* grpc_service_account_jwt_access_credentials_create( diff --git a/test/core/channel/channelz_test.cc b/test/core/channel/channelz_test.cc index 0a3af365c256d..386cb8593ed5e 100644 --- a/test/core/channel/channelz_test.cc +++ b/test/core/channel/channelz_test.cc @@ -113,8 +113,10 @@ void ValidateGetTopChannels(size_t expected_channels) { ASSERT_EQ(parsed_json->type(), Json::Type::kObject); // This check will naturally have to change when we support pagination. // tracked: https://github.com/grpc/grpc/issues/16019. - ValidateJsonArraySize((*parsed_json->mutable_object())["channel"], - expected_channels); + Json channel_json; + auto it = parsed_json->object().find("channel"); + if (it != parsed_json->object().end()) channel_json = it->second; + ValidateJsonArraySize(channel_json, expected_channels); ValidateJsonEnd(*parsed_json, true); // Also check that the core API formats this correctly. char* core_api_json_str = grpc_channelz_get_top_channels(0); @@ -132,8 +134,10 @@ void ValidateGetServers(size_t expected_servers) { ASSERT_EQ(parsed_json->type(), Json::Type::kObject); // This check will naturally have to change when we support pagination. // tracked: https://github.com/grpc/grpc/issues/16019. - ValidateJsonArraySize((*parsed_json->mutable_object())["server"], - expected_servers); + Json server_json; + auto it = parsed_json->object().find("server"); + if (it != parsed_json->object().end()) server_json = it->second; + ValidateJsonArraySize(server_json, expected_servers); ValidateJsonEnd(*parsed_json, true); // Also check that the core API formats this correctly. char* core_api_json_str = grpc_channelz_get_servers(0); @@ -212,8 +216,10 @@ void ValidateCounters(const std::string& json_str, auto json = JsonParse(json_str); ASSERT_TRUE(json.ok()) << json.status(); ASSERT_EQ(json->type(), Json::Type::kObject); - Json::Object* object = json->mutable_object(); - Json& data = (*object)["data"]; + const Json::Object& object = json->object(); + auto it = object.find("data"); + ASSERT_NE(it, object.end()); + const Json& data = it->second; ASSERT_EQ(data.type(), Json::Type::kObject); ValidateChildInteger(data.object(), "callsStarted", args.calls_started); ValidateChildInteger(data.object(), "callsFailed", args.calls_failed); @@ -364,7 +370,10 @@ TEST_F(ChannelzRegistryBasedTest, GetTopChannelsPagination) { ASSERT_TRUE(parsed_json.ok()) << parsed_json.status(); ASSERT_EQ(parsed_json->type(), Json::Type::kObject); // 100 is the pagination limit. - ValidateJsonArraySize((*parsed_json->mutable_object())["channel"], 100); + Json channel_json; + auto it = parsed_json->object().find("channel"); + if (it != parsed_json->object().end()) channel_json = it->second; + ValidateJsonArraySize(channel_json, 100); ValidateJsonEnd(*parsed_json, false); // Now we get the rest. json_str = ChannelzRegistry::GetTopChannels(101); @@ -373,7 +382,10 @@ TEST_F(ChannelzRegistryBasedTest, GetTopChannelsPagination) { parsed_json = JsonParse(json_str); ASSERT_TRUE(parsed_json.ok()) << parsed_json.status(); ASSERT_EQ(parsed_json->type(), Json::Type::kObject); - ValidateJsonArraySize((*parsed_json->mutable_object())["channel"], 50); + channel_json = Json(); + it = parsed_json->object().find("channel"); + if (it != parsed_json->object().end()) channel_json = it->second; + ValidateJsonArraySize(channel_json, 50); ValidateJsonEnd(*parsed_json, true); } @@ -386,9 +398,11 @@ TEST_F(ChannelzRegistryBasedTest, GetTopChannelsUuidCheck) { auto parsed_json = JsonParse(json_str); ASSERT_TRUE(parsed_json.ok()) << parsed_json.status(); ASSERT_EQ(parsed_json->type(), Json::Type::kObject); - Json& array = (*parsed_json->mutable_object())["channel"]; - ValidateJsonArraySize(array, kNumChannels); - std::vector uuids = GetUuidListFromArray(array.array()); + Json channel_json; + auto it = parsed_json->object().find("channel"); + if (it != parsed_json->object().end()) channel_json = it->second; + ValidateJsonArraySize(channel_json, kNumChannels); + std::vector uuids = GetUuidListFromArray(channel_json.array()); for (int i = 0; i < kNumChannels; ++i) { EXPECT_EQ(i + 1, uuids[i]); } @@ -405,9 +419,11 @@ TEST_F(ChannelzRegistryBasedTest, GetTopChannelsMiddleUuidCheck) { auto parsed_json = JsonParse(json_str); ASSERT_TRUE(parsed_json.ok()) << parsed_json.status(); ASSERT_EQ(parsed_json->type(), Json::Type::kObject); - Json& array = (*parsed_json->mutable_object())["channel"]; - ValidateJsonArraySize(array, kNumChannels - kMidQuery + 1); - std::vector uuids = GetUuidListFromArray(array.array()); + Json channel_json; + auto it = parsed_json->object().find("channel"); + if (it != parsed_json->object().end()) channel_json = it->second; + ValidateJsonArraySize(channel_json, kNumChannels - kMidQuery + 1); + std::vector uuids = GetUuidListFromArray(channel_json.array()); for (size_t i = 0; i < uuids.size(); ++i) { EXPECT_EQ(static_cast(kMidQuery + i), uuids[i]); } @@ -426,9 +442,11 @@ TEST_F(ChannelzRegistryBasedTest, GetTopChannelsNoHitUuid) { auto parsed_json = JsonParse(json_str); ASSERT_TRUE(parsed_json.ok()) << parsed_json.status(); ASSERT_EQ(parsed_json->type(), Json::Type::kObject); - Json& array = (*parsed_json->mutable_object())["channel"]; - ValidateJsonArraySize(array, 10); - std::vector uuids = GetUuidListFromArray(array.array()); + Json channel_json; + auto it = parsed_json->object().find("channel"); + if (it != parsed_json->object().end()) channel_json = it->second; + ValidateJsonArraySize(channel_json, 10); + std::vector uuids = GetUuidListFromArray(channel_json.array()); for (size_t i = 0; i < uuids.size(); ++i) { EXPECT_EQ(static_cast(51 + i), uuids[i]); } @@ -446,18 +464,22 @@ TEST_F(ChannelzRegistryBasedTest, GetTopChannelsMoreGaps) { auto parsed_json = JsonParse(json_str); ASSERT_TRUE(parsed_json.ok()) << parsed_json.status(); ASSERT_EQ(parsed_json->type(), Json::Type::kObject); - Json array = (*parsed_json->mutable_object())["channel"]; - ValidateJsonArraySize(array, 2); - std::vector uuids = GetUuidListFromArray(array.array()); + Json channel_json; + auto it = parsed_json->object().find("channel"); + if (it != parsed_json->object().end()) channel_json = it->second; + ValidateJsonArraySize(channel_json, 2); + std::vector uuids = GetUuidListFromArray(channel_json.array()); EXPECT_EQ(3, uuids[0]); EXPECT_EQ(5, uuids[1]); json_str = ChannelzRegistry::GetTopChannels(4); parsed_json = JsonParse(json_str); ASSERT_TRUE(parsed_json.ok()) << parsed_json.status(); ASSERT_EQ(parsed_json->type(), Json::Type::kObject); - array = (*parsed_json->mutable_object())["channel"]; - ValidateJsonArraySize(array, 1); - uuids = GetUuidListFromArray(array.array()); + channel_json = Json(); + it = parsed_json->object().find("channel"); + if (it != parsed_json->object().end()) channel_json = it->second; + ValidateJsonArraySize(channel_json, 1); + uuids = GetUuidListFromArray(channel_json.array()); EXPECT_EQ(5, uuids[0]); } @@ -477,9 +499,11 @@ TEST_F(ChannelzRegistryBasedTest, GetTopChannelsUuidAfterCompaction) { auto parsed_json = JsonParse(json_str); ASSERT_TRUE(parsed_json.ok()) << parsed_json.status(); ASSERT_EQ(parsed_json->type(), Json::Type::kObject); - Json& array = (*parsed_json->mutable_object())["channel"]; - ValidateJsonArraySize(array, kLoopIterations); - std::vector uuids = GetUuidListFromArray(array.array()); + Json channel_json; + auto it = parsed_json->object().find("channel"); + if (it != parsed_json->object().end()) channel_json = it->second; + ValidateJsonArraySize(channel_json, kLoopIterations); + std::vector uuids = GetUuidListFromArray(channel_json.array()); for (int i = 0; i < kLoopIterations; ++i) { // only the even uuids will still be present. EXPECT_EQ((i + 1) * 2, uuids[i]); diff --git a/test/core/client_channel/client_channel_service_config_test.cc b/test/core/client_channel/client_channel_service_config_test.cc index 224000195e882..f709c8199a2ce 100644 --- a/test/core/client_channel/client_channel_service_config_test.cc +++ b/test/core/client_channel/client_channel_service_config_test.cc @@ -296,7 +296,7 @@ TEST_F(ClientChannelParserTest, InvalidHealthCheckMultipleEntries) { EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); EXPECT_EQ(service_config.status().message(), "JSON parsing failed: [" - "duplicate key \"healthCheckConfig\" at index 104]") + "duplicate key \"healthCheckConfig\" at index 82]") << service_config.status(); } diff --git a/test/core/client_channel/lb_policy/outlier_detection_test.cc b/test/core/client_channel/lb_policy/outlier_detection_test.cc index 8087082607946..c366dd7483b80 100644 --- a/test/core/client_channel/lb_policy/outlier_detection_test.cc +++ b/test/core/client_channel/lb_policy/outlier_detection_test.cc @@ -17,13 +17,11 @@ #include #include -#include -#include -#include #include #include "absl/status/status.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "gtest/gtest.h" #include @@ -104,24 +102,32 @@ class OutlierDetectionTest : public LoadBalancingPolicyTest { } RefCountedPtr Build() { - Json config = - Json::Array{Json::Object{{"outlier_detection_experimental", json_}}}; + Json::Object fields = json_; + if (success_rate_.has_value()) { + fields["successRateEjection"] = *success_rate_; + } + if (failure_percentage_.has_value()) { + fields["failurePercentageEjection"] = *failure_percentage_; + } + Json config = Json::Array{ + Json::Object{{"outlier_detection_experimental", std::move(fields)}}}; return MakeConfig(config); } private: Json::Object& GetSuccessRate() { - auto it = json_.emplace("successRateEjection", Json::Object()).first; - return *it->second.mutable_object(); + if (!success_rate_.has_value()) success_rate_.emplace(); + return *success_rate_; } Json::Object& GetFailurePercentage() { - auto it = - json_.emplace("failurePercentageEjection", Json::Object()).first; - return *it->second.mutable_object(); + if (!failure_percentage_.has_value()) failure_percentage_.emplace(); + return *failure_percentage_; } Json::Object json_; + absl::optional success_rate_; + absl::optional failure_percentage_; }; OutlierDetectionTest() diff --git a/test/cpp/util/channelz_sampler.cc b/test/cpp/util/channelz_sampler.cc index dbc165b424155..79c0f8a7685b4 100644 --- a/test/cpp/util/channelz_sampler.cc +++ b/test/cpp/util/channelz_sampler.cc @@ -530,11 +530,11 @@ class ChannelzSampler final { {"ID", id}, {"Type", type}, {"Description", description}}; - json_.mutable_array()->push_back(obj); + json_.push_back(obj); } // Dump data in json - std::string DumpJson() { return JsonDump(json_); } + std::string DumpJson() { return JsonDump(grpc_core::Json(json_)); } // Check if one entity has been recorded bool CheckID(int64_t id) { @@ -557,7 +557,7 @@ class ChannelzSampler final { std::vector all_subchannels_; std::vector all_sockets_; std::unordered_set id_set_; - grpc_core::Json json_; + grpc_core::Json::Array json_; int64_t rpc_timeout_seconds_; gpr_timespec now_; }; From aee41600f429dc92d44f40513447896276b3a065 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Thu, 6 Apr 2023 15:22:09 -0700 Subject: [PATCH 24/39] [JSON] use absl::variant for JSON values (#32808) This changes the `JSON` class to use `absl::variant<>` internally for storing the type and value. The only API-visible change here is that now the accessor methods will crash if you use them on the wrong type, courtesy of `absl::get<>`. This does not appear to break any tests, so it appears that we have already cleaned up all of the cases we had in the past where we might have done something like that. --- src/core/BUILD | 1 + src/core/lib/json/json.h | 155 +++++++++++++++------------------------ 2 files changed, 62 insertions(+), 94 deletions(-) diff --git a/src/core/BUILD b/src/core/BUILD index 8c1b87f4163f7..f122adbdc0bdb 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -3278,6 +3278,7 @@ grpc_cc_library( hdrs = [ "lib/json/json.h", ], + external_deps = ["absl/types:variant"], deps = ["//:gpr"], ) diff --git a/src/core/lib/json/json.h b/src/core/lib/json/json.h index 3592554cfef3a..eb74c9c28163c 100644 --- a/src/core/lib/json/json.h +++ b/src/core/lib/json/json.h @@ -24,6 +24,8 @@ #include #include +#include "absl/types/variant.h" + namespace grpc_core { // A JSON value, which can be any one of object, array, string, @@ -43,16 +45,16 @@ class Json { Json() = default; // Copyable. - Json(const Json& other) { CopyFrom(other); } - Json& operator=(const Json& other) { - CopyFrom(other); - return *this; - } + Json(const Json& other) = default; + Json& operator=(const Json& other) = default; // Moveable. - Json(Json&& other) noexcept { MoveFrom(std::move(other)); } + Json(Json&& other) noexcept : value_(std::move(other.value_)) { + other.value_ = absl::monostate(); + } Json& operator=(Json&& other) noexcept { - MoveFrom(std::move(other)); + value_ = std::move(other.value_); + other.value_ = absl::monostate(); return *this; } @@ -60,11 +62,9 @@ class Json { // If is_number is true, the type will be kNumber instead of kString. // NOLINTNEXTLINE(google-explicit-constructor) Json(const std::string& string, bool is_number = false) - : type_(is_number ? Type::kNumber : Type::kString), - string_value_(string) {} + : value_(is_number ? Value(NumberValue{string}) : Value(string)) {} Json& operator=(const std::string& string) { - type_ = Type::kString; - string_value_ = string; + value_ = string; return *this; } @@ -86,140 +86,107 @@ class Json { // Construct by moving a string. // NOLINTNEXTLINE(google-explicit-constructor) - Json(std::string&& string) - : type_(Type::kString), string_value_(std::move(string)) {} + Json(std::string&& string) : value_(Value(std::move(string))) {} Json& operator=(std::string&& string) { - type_ = Type::kString; - string_value_ = std::move(string); + value_ = Value(std::move(string)); return *this; } // Construct from bool. // NOLINTNEXTLINE(google-explicit-constructor) - Json(bool b) : type_(b ? Type::kTrue : Type::kFalse) {} + Json(bool b) : value_(b) {} Json& operator=(bool b) { - type_ = b ? Type::kTrue : Type::kFalse; + value_ = b; return *this; } // Construct from any numeric type. template // NOLINTNEXTLINE(google-explicit-constructor) - Json(NumericType number) - : type_(Type::kNumber), string_value_(std::to_string(number)) {} + Json(NumericType number) : value_(NumberValue{std::to_string(number)}) {} template Json& operator=(NumericType number) { - type_ = Type::kNumber; - string_value_ = std::to_string(number); + value_ = NumberValue{std::to_string(number)}; return *this; } // Construct by copying object. // NOLINTNEXTLINE(google-explicit-constructor) - Json(const Object& object) : type_(Type::kObject), object_value_(object) {} + Json(const Object& object) : value_(object) {} Json& operator=(const Object& object) { - type_ = Type::kObject; - object_value_ = object; + value_ = object; return *this; } // Construct by moving object. // NOLINTNEXTLINE(google-explicit-constructor) - Json(Object&& object) - : type_(Type::kObject), object_value_(std::move(object)) {} + Json(Object&& object) : value_(std::move(object)) {} Json& operator=(Object&& object) { - type_ = Type::kObject; - object_value_ = std::move(object); + value_ = std::move(object); return *this; } // Construct by copying array. // NOLINTNEXTLINE(google-explicit-constructor) - Json(const Array& array) : type_(Type::kArray), array_value_(array) {} + Json(const Array& array) : value_(array) {} Json& operator=(const Array& array) { - type_ = Type::kArray; - array_value_ = array; + value_ = array; return *this; } // Construct by moving array. // NOLINTNEXTLINE(google-explicit-constructor) - Json(Array&& array) : type_(Type::kArray), array_value_(std::move(array)) {} + Json(Array&& array) : value_(std::move(array)) {} Json& operator=(Array&& array) { - type_ = Type::kArray; - array_value_ = std::move(array); + value_ = std::move(array); return *this; } + // Returns the JSON type. + Type type() const { + struct ValueFunctor { + Json::Type operator()(const absl::monostate&) { return Type::kNull; } + Json::Type operator()(bool value) { + return value ? Type::kTrue : Type::kFalse; + } + Json::Type operator()(const NumberValue&) { return Type::kNumber; } + Json::Type operator()(const std::string&) { return Type::kString; } + Json::Type operator()(const Object&) { return Type::kObject; } + Json::Type operator()(const Array&) { return Type::kArray; } + }; + return absl::visit(ValueFunctor(), value_); + } + // Accessor methods. - Type type() const { return type_; } - const std::string& string() const { return string_value_; } - const Object& object() const { return object_value_; } - const Array& array() const { return array_value_; } - - bool operator==(const Json& other) const { - if (type_ != other.type_) return false; - switch (type_) { - case Type::kNumber: - case Type::kString: - if (string_value_ != other.string_value_) return false; - break; - case Type::kObject: - if (object_value_ != other.object_value_) return false; - break; - case Type::kArray: - if (array_value_ != other.array_value_) return false; - break; - default: - break; - } - return true; + const std::string& string() const { + const NumberValue* num = absl::get_if(&value_); + if (num != nullptr) return num->value; + return absl::get(value_); } + const Object& object() const { return absl::get(value_); } + const Array& array() const { return absl::get(value_); } + bool operator==(const Json& other) const { return value_ == other.value_; } bool operator!=(const Json& other) const { return !(*this == other); } private: - void CopyFrom(const Json& other) { - type_ = other.type_; - switch (type_) { - case Type::kNumber: - case Type::kString: - string_value_ = other.string_value_; - break; - case Type::kObject: - object_value_ = other.object_value_; - break; - case Type::kArray: - array_value_ = other.array_value_; - break; - default: - break; - } - } + struct NumberValue { + std::string value; - void MoveFrom(Json&& other) { - type_ = other.type_; - other.type_ = Type::kNull; - switch (type_) { - case Type::kNumber: - case Type::kString: - string_value_ = std::move(other.string_value_); - break; - case Type::kObject: - object_value_ = std::move(other.object_value_); - break; - case Type::kArray: - array_value_ = std::move(other.array_value_); - break; - default: - break; + bool operator==(const NumberValue& other) const { + return value == other.value; } - } + }; + using Value = absl::variant; // kArray + + explicit Json(Value value) : value_(std::move(value)) {} - Type type_ = Type::kNull; - std::string string_value_; - Object object_value_; - Array array_value_; + Value value_; }; } // namespace grpc_core From 9395f033045a0fb9f6b6febc132a78338e790c6d Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Mon, 9 Jan 2023 09:07:06 -0800 Subject: [PATCH 25/39] cf event engine client support --- src/core/BUILD | 15 +- .../lib/event_engine/cf_engine/cf_engine.cc | 38 ++- .../lib/event_engine/cf_engine/cf_engine.h | 4 + .../cf_engine/cfstream_endpoint.cc | 294 ++++++++++++++++++ .../cf_engine/cfstream_endpoint.h | 92 ++++++ .../cf_engine/cftype_unique_ref.h | 76 +++++ test/core/event_engine/test_suite/BUILD | 4 +- .../test_suite/cf_event_engine_test.cc | 11 +- 8 files changed, 521 insertions(+), 13 deletions(-) create mode 100644 src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc create mode 100644 src/core/lib/event_engine/cf_engine/cfstream_endpoint.h create mode 100644 src/core/lib/event_engine/cf_engine/cftype_unique_ref.h diff --git a/src/core/BUILD b/src/core/BUILD index f122adbdc0bdb..325cf522d3c98 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -2069,16 +2069,27 @@ grpc_cc_library( grpc_cc_library( name = "cf_event_engine", - srcs = ["lib/event_engine/cf_engine/cf_engine.cc"], - hdrs = ["lib/event_engine/cf_engine/cf_engine.h"], + srcs = [ + "lib/event_engine/cf_engine/cf_engine.cc", + "lib/event_engine/cf_engine/cfstream_endpoint.cc", + ], + hdrs = [ + "lib/event_engine/cf_engine/cf_engine.h", + "lib/event_engine/cf_engine/cfstream_endpoint.h", + "lib/event_engine/cf_engine/cftype_unique_ref.h", + ], deps = [ "event_engine_common", + "event_engine_tcp_socket_utils", "event_engine_trace", "event_engine_utils", "init_internally", + "posix_event_engine_lockfree_event", "posix_event_engine_timer_manager", + "strerror", "//:event_engine_base_hdrs", "//:gpr", + "//:sockaddr_utils", ], ) diff --git a/src/core/lib/event_engine/cf_engine/cf_engine.cc b/src/core/lib/event_engine/cf_engine/cf_engine.cc index 4a33d84d0057a..4cdc7fd7554be 100644 --- a/src/core/lib/event_engine/cf_engine/cf_engine.cc +++ b/src/core/lib/event_engine/cf_engine/cf_engine.cc @@ -16,8 +16,12 @@ #ifdef GPR_APPLE +#include + #include "src/core/lib/event_engine/cf_engine/cf_engine.h" +#include "src/core/lib/event_engine/cf_engine/cfstream_endpoint.h" #include "src/core/lib/event_engine/posix_engine/timer_manager.h" +#include "src/core/lib/event_engine/tcp_socket_utils.h" #include "src/core/lib/event_engine/trace.h" #include "src/core/lib/event_engine/utils.h" #include "src/core/lib/gprpp/crash.h" @@ -72,14 +76,30 @@ CFEventEngine::CreateListener( } CFEventEngine::ConnectionHandle CFEventEngine::Connect( - OnConnectCallback /* on_connect */, const ResolvedAddress& /* addr */, - const EndpointConfig& /* args */, MemoryAllocator /* memory_allocator */, - Duration /* timeout */) { - grpc_core::Crash("unimplemented"); + OnConnectCallback on_connect, const ResolvedAddress& addr, + const EndpointConfig& /* args */, MemoryAllocator memory_allocator, + Duration timeout) { + auto addr_uri = ResolvedAddressToURI(addr); + gpr_log(GPR_INFO, "CFEventEngine::connect: %s", addr_uri.value().c_str()); + if (!addr_uri.ok()) { + Run([on_connect = std::move(on_connect), + ep = absl::FailedPreconditionError(absl::StrCat( + "connect failed: ", "invalid addr: ", + addr_uri.value()))]() mutable { on_connect(std::move(ep)); }); + return {0, 0}; + } + + auto endpoint_ptr = new CFStreamEndpoint( + std::static_pointer_cast(shared_from_this()), + std::move(memory_allocator)); + + endpoint_ptr->Connect(std::move(on_connect), addr, std::move(timeout)); + + return {reinterpret_cast(endpoint_ptr), 0}; } bool CFEventEngine::CancelConnect(ConnectionHandle /* handle */) { - grpc_core::Crash("unimplemented"); + return false; } bool CFEventEngine::IsWorkerThread() { grpc_core::Crash("unimplemented"); } @@ -89,12 +109,12 @@ std::unique_ptr CFEventEngine::GetDNSResolver( grpc_core::Crash("unimplemented"); } -void CFEventEngine::Run(EventEngine::Closure* /* closure */) { - grpc_core::Crash("unimplemented"); +void CFEventEngine::Run(EventEngine::Closure* closure) { + executor_->Run(closure); } -void CFEventEngine::Run(absl::AnyInvocable /* closure */) { - grpc_core::Crash("unimplemented"); +void CFEventEngine::Run(absl::AnyInvocable closure) { + executor_->Run(std::move(closure)); } EventEngine::TaskHandle CFEventEngine::RunAfter(Duration when, diff --git a/src/core/lib/event_engine/cf_engine/cf_engine.h b/src/core/lib/event_engine/cf_engine/cf_engine.h index c4f58e74789a7..18a021e03f794 100644 --- a/src/core/lib/event_engine/cf_engine/cf_engine.h +++ b/src/core/lib/event_engine/cf_engine/cf_engine.h @@ -20,6 +20,9 @@ #include #include "src/core/lib/event_engine/handle_containers.h" +#include "src/core/lib/event_engine/posix_engine/event_poller.h" +#include "src/core/lib/event_engine/posix_engine/lockfree_event.h" +#include "src/core/lib/event_engine/posix_engine/posix_engine_closure.h" #include "src/core/lib/event_engine/posix_engine/timer_manager.h" #include "src/core/lib/gprpp/sync.h" #include "src/core/lib/surface/init_internally.h" @@ -28,6 +31,7 @@ namespace grpc_event_engine { namespace experimental { class CFEventEngine : public EventEngine, + public Scheduler, public grpc_core::KeepsGrpcInitialized { public: CFEventEngine(); diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc new file mode 100644 index 0000000000000..c720ce58e2f99 --- /dev/null +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc @@ -0,0 +1,294 @@ +// Copyright 2022 The 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 + +#ifdef GPR_APPLE + +#include "src/core/lib/event_engine/cf_engine/cfstream_endpoint.h" +#include "src/core/lib/gprpp/strerror.h" + +namespace grpc_event_engine { +namespace experimental { + +namespace { + +absl::Status CFErrorToStatus(CFTypeUniqueRef cf_error) { + CFErrorDomain cf_domain = CFErrorGetDomain((cf_error)); + CFIndex code = CFErrorGetCode((cf_error)); + CFTypeUniqueRef cf_desc = CFErrorCopyDescription((cf_error)); + char domain_buf[256]; + char desc_buf[256]; + CFStringGetCString(cf_domain, domain_buf, 256, kCFStringEncodingUTF8); + CFStringGetCString(cf_desc, desc_buf, 256, kCFStringEncodingUTF8); + return absl::Status(absl::StatusCode::kInternal, + absl::StrFormat("(domain:%s, code:%ld, description:%s)", + domain_buf, code, desc_buf)); +} + +absl::StatusOr CFReadStreamLocallAddress( + CFReadStreamRef stream) { + CFTypeUniqueRef cf_native_handle = static_cast( + CFReadStreamCopyProperty(stream, kCFStreamPropertySocketNativeHandle)); + CFSocketNativeHandle socket; + CFDataGetBytes(cf_native_handle, CFRangeMake(0, sizeof(CFSocketNativeHandle)), + (UInt8*)&socket); + EventEngine::ResolvedAddress addr; + socklen_t len = EventEngine::ResolvedAddress::MAX_SIZE_BYTES; + if (getsockname(socket, const_cast(addr.address()), &len) < 0) { + return absl::InternalError( + absl::StrCat("getsockname:", grpc_core::StrError(errno))); + } + return EventEngine::ResolvedAddress(addr.address(), len); +} + +} // namespace + +void CFStreamEndpoint::Connect(EventEngine::OnConnectCallback on_connect, + EventEngine::ResolvedAddress addr, + EventEngine::Duration timeout) { + peer_address_ = std::move(addr); + std::string host_port = + grpc_sockaddr_to_string(reinterpret_cast( + peer_address_.address()), + true) + .value(); + gpr_log(GPR_INFO, "CFStreamClient::connect, host_port: %s", + host_port.c_str()); + std::string host_string; + std::string port_string; + grpc_core::SplitHostPort(host_port, &host_string, &port_string); + CFStringRef host = CFStringCreateWithCString(NULL, host_string.c_str(), + kCFStringEncodingUTF8); + int port = ResolvedAddressGetPort(peer_address_); + CFStreamCreatePairWithSocketToHost(NULL, host, port, &cf_read_stream_, + &cf_write_stream_); + + CFStreamClientContext cf_context = {0, static_cast(this), nullptr, + nullptr, nullptr}; + CFReadStreamSetClient( + cf_read_stream_, + kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | + kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, + ReadCallback, &cf_context); + CFWriteStreamSetClient( + cf_write_stream_, + kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes | + kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, + WriteCallback, &cf_context); + CFReadStreamSetDispatchQueue(cf_read_stream_, + dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)); + CFWriteStreamSetDispatchQueue( + cf_write_stream_, dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)); + + if (!CFReadStreamOpen(cf_read_stream_)) { + auto status = CFErrorToStatus(CFReadStreamCopyError(cf_read_stream_)); + on_connect(std::move(status)); + delete this; + return; + } + + if (!CFWriteStreamOpen(cf_write_stream_)) { + auto status = CFErrorToStatus(CFWriteStreamCopyError(cf_write_stream_)); + on_connect(std::move(status)); + delete this; + return; + } + + auto connect_timeout_timer = engine_->RunAfter(timeout, [this]() { + open_event_.SetShutdown(absl::DeadlineExceededError("Connect timed out")); + }); + + open_event_.NotifyOn(new PosixEngineClosure( + [this, on_connect = std::move(on_connect), + connect_timeout_timer](absl::Status status) mutable { + engine_->Cancel(connect_timeout_timer); + + if (!status.ok()) { + on_connect(std::move(status)); + delete this; + return; + } + + auto status_or_local_addr = CFReadStreamLocallAddress(cf_read_stream_); + if (!status_or_local_addr.ok()) { + on_connect(std::move(status_or_local_addr).status()); + delete this; + return; + } + + local_address_ = status_or_local_addr.value(); + on_connect(std::unique_ptr(this)); + }, + false /* is_permanent */)); +} + +/* static */ void CFStreamEndpoint::ReadCallback(CFReadStreamRef stream, + CFStreamEventType type, + void* client_callback_info) { + auto self = static_cast(client_callback_info); + + switch (type) { + case kCFStreamEventOpenCompleted: + break; + case kCFStreamEventHasBytesAvailable: + case kCFStreamEventEndEncountered: + self->read_event_.SetReady(); + break; + case kCFStreamEventErrorOccurred: { + auto status = CFErrorToStatus(CFReadStreamCopyError(stream)); + gpr_log(GPR_ERROR, "CFStream Read error: %s", status.ToString().c_str()); + self->open_event_.SetShutdown(status); + self->read_event_.SetShutdown(status); + self->write_event_.SetShutdown(status); + } break; + default: + GPR_UNREACHABLE_CODE(return); + } +} + +/* static */ +void CFStreamEndpoint::WriteCallback(CFWriteStreamRef stream, + CFStreamEventType type, + void* client_callback_info) { + auto self = static_cast(client_callback_info); + + switch (type) { + case kCFStreamEventOpenCompleted: + self->open_event_.SetReady(); + break; + case kCFStreamEventCanAcceptBytes: + case kCFStreamEventEndEncountered: + self->write_event_.SetReady(); + break; + case kCFStreamEventErrorOccurred: { + auto status = CFErrorToStatus(CFWriteStreamCopyError(stream)); + gpr_log(GPR_ERROR, "CFStream Write error: %s", status.ToString().c_str()); + self->open_event_.SetShutdown(status); + self->read_event_.SetShutdown(status); + self->write_event_.SetShutdown(status); + } break; + default: + GPR_UNREACHABLE_CODE(return); + } +} + +CFStreamEndpoint::CFStreamEndpoint(std::shared_ptr engine, + MemoryAllocator memory_allocator) + : engine_(std::move(engine)), + memory_allocator_(std::move(memory_allocator)), + open_event_(engine_.get()), + read_event_(engine_.get()), + write_event_(engine_.get()) { + open_event_.InitEvent(); + read_event_.InitEvent(); + write_event_.InitEvent(); +} + +CFStreamEndpoint::~CFStreamEndpoint() { + CFReadStreamClose(cf_read_stream_); + CFWriteStreamClose(cf_write_stream_); + + open_event_.SetShutdown(absl::OkStatus()); + read_event_.SetShutdown(absl::OkStatus()); + write_event_.SetShutdown(absl::OkStatus()); + open_event_.DestroyEvent(); + read_event_.DestroyEvent(); + write_event_.DestroyEvent(); +} + +void CFStreamEndpoint::Read(absl::AnyInvocable on_read, + SliceBuffer* buffer, const ReadArgs* /* args */) { + read_event_.NotifyOn(new PosixEngineClosure( + [this, on_read = std::move(on_read), + buffer](absl::Status status) mutable { + if (status.ok()) { + DoRead(std::move(on_read), buffer); + } else { + on_read(status); + } + }, + false /* is_permanent*/)); +} + +void CFStreamEndpoint::DoRead(absl::AnyInvocable on_read, + SliceBuffer* buffer) { + int buffer_size = 8192; + auto buffer_index = + buffer->AppendIndexed(Slice(memory_allocator_.MakeSlice(buffer_size))); + + CFIndex read_size = CFReadStreamRead( + cf_read_stream_, + internal::SliceCast(buffer->MutableSliceAt(buffer_index)) + .begin(), + buffer_size); + + if (read_size < 0) { + auto status = CFErrorToStatus(CFReadStreamCopyError(cf_read_stream_)); + gpr_log(GPR_ERROR, "CFStream read error: %s", status.ToString().c_str()); + on_read(status); + return; + } + + buffer->RemoveLastNBytes(buffer->Length() - read_size); + on_read(absl::OkStatus()); +} + +void CFStreamEndpoint::Write(absl::AnyInvocable on_writable, + SliceBuffer* data, const WriteArgs* /* args */) { + write_event_.NotifyOn(new PosixEngineClosure( + [this, on_writable = std::move(on_writable), + data](absl::Status status) mutable { + if (status.ok()) { + DoWrite(std::move(on_writable), data); + } else { + on_writable(status); + } + }, + false /* is_permanent*/)); +} + +void CFStreamEndpoint::DoWrite( + absl::AnyInvocable on_writable, SliceBuffer* data) { + size_t total_written_size = 0; + for (size_t i = 0; i < data->Count(); i++) { + auto slice = data->RefSlice(i); + size_t written_size = + CFWriteStreamWrite(cf_write_stream_, slice.begin(), slice.size()); + + total_written_size += written_size; + if (written_size < slice.size()) { + SliceBuffer written; + data->MoveFirstNBytesIntoSliceBuffer(total_written_size, written); + + write_event_.NotifyOn(new PosixEngineClosure( + [this, on_writable = std::move(on_writable), + data](absl::Status status) mutable { + if (status.ok()) { + DoWrite(std::move(on_writable), data); + } else { + on_writable(status); + } + }, + false /* is_permanent*/)); + return; + } + } + on_writable(absl::OkStatus()); +} + +} // namespace experimental +} // namespace grpc_event_engine + +#endif // GPR_APPLE diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h new file mode 100644 index 0000000000000..955eb9728281d --- /dev/null +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h @@ -0,0 +1,92 @@ +// Copyright 2022 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef GRPC_SRC_CORE_LIB_EVENT_ENGINE_CF_ENGINE_CFSTREAM_ENDPOINT_H +#define GRPC_SRC_CORE_LIB_EVENT_ENGINE_CF_ENGINE_CFSTREAM_ENDPOINT_H +#include + +#ifdef GPR_APPLE + +#include + +#include "absl/strings/str_format.h" + +#include + +#include "src/core/lib/address_utils/sockaddr_utils.h" +#include "src/core/lib/event_engine/cf_engine/cf_engine.h" +#include "src/core/lib/event_engine/cf_engine/cftype_unique_ref.h" +#include "src/core/lib/event_engine/posix_engine/lockfree_event.h" +#include "src/core/lib/event_engine/tcp_socket_utils.h" +#include "src/core/lib/gprpp/host_port.h" + +namespace grpc_event_engine { +namespace experimental { + +class CFStreamEndpoint : public EventEngine::Endpoint { + public: + CFStreamEndpoint(std::shared_ptr engine, + MemoryAllocator memory_allocator); + ~CFStreamEndpoint() override; + + void Read(absl::AnyInvocable on_read, SliceBuffer* buffer, + const ReadArgs* args) override; + void Write(absl::AnyInvocable on_writable, + SliceBuffer* data, const WriteArgs* args) override; + + const EventEngine::ResolvedAddress& GetPeerAddress() const override { + return peer_address_; + } + const EventEngine::ResolvedAddress& GetLocalAddress() const override { + return local_address_; + } + + public: + void Connect(EventEngine::OnConnectCallback on_connect, + EventEngine::ResolvedAddress addr, + EventEngine::Duration timeout); + bool CancelConnect() { return false; } + + private: + void DoWrite(absl::AnyInvocable on_writable, + SliceBuffer* data); + void DoRead(absl::AnyInvocable on_read, + SliceBuffer* buffer); + + private: + static void ReadCallback(CFReadStreamRef stream, CFStreamEventType type, + void* client_callback_info); + static void WriteCallback(CFWriteStreamRef stream, CFStreamEventType type, + void* client_callback_info); + + private: + CFTypeUniqueRef cf_read_stream_; + CFTypeUniqueRef cf_write_stream_; + + std::shared_ptr engine_; + + EventEngine::ResolvedAddress peer_address_; + EventEngine::ResolvedAddress local_address_; + MemoryAllocator memory_allocator_; + + LockfreeEvent open_event_; + LockfreeEvent read_event_; + LockfreeEvent write_event_; +}; + +} // namespace experimental +} // namespace grpc_event_engine + +#endif // GPR_APPLE + +#endif // GRPC_SRC_CORE_LIB_EVENT_ENGINE_CF_ENGINE_CFSTREAM_ENDPOINT_H diff --git a/src/core/lib/event_engine/cf_engine/cftype_unique_ref.h b/src/core/lib/event_engine/cf_engine/cftype_unique_ref.h new file mode 100644 index 0000000000000..7474906fb0e75 --- /dev/null +++ b/src/core/lib/event_engine/cf_engine/cftype_unique_ref.h @@ -0,0 +1,76 @@ +// Copyright 2022 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef GRPC_SRC_CORE_LIB_EVENT_ENGINE_CF_ENGINE_CFTYPE_UNIQUE_REF_H +#define GRPC_SRC_CORE_LIB_EVENT_ENGINE_CF_ENGINE_CFTYPE_UNIQUE_REF_H +#include + +#ifdef GPR_APPLE + +#include + +namespace grpc_event_engine { +namespace experimental { + +template +class CFTypeUniqueRef { + static_assert(std::is_convertible::value, + "T should be `CFXxxRef` type"); + + public: + /* implicit */ + CFTypeUniqueRef(T cf_type_ref = nullptr) : cf_type_ref_(cf_type_ref) {} + ~CFTypeUniqueRef() { reset(); } + + CFTypeUniqueRef(CFTypeUniqueRef const&) = delete; + CFTypeUniqueRef& operator=(CFTypeUniqueRef const&) = delete; + + CFTypeUniqueRef(CFTypeUniqueRef&& other) : cf_type_ref_(other.release()){}; + CFTypeUniqueRef& operator=(CFTypeUniqueRef&& other) { + reset(other.release()); + return *this; + } + + operator T() { return cf_type_ref_; } + + // Note: this is for passing a CFTypeRef as output parameter to a CF API, the + // current ref is released (if any) regardless of whether new value is set + T* operator&() { + reset(); + return &cf_type_ref_; + } + + T release() { + T old = cf_type_ref_; + cf_type_ref_ = nullptr; + return old; + } + + void reset(T other = nullptr) { + T old = cf_type_ref_; + cf_type_ref_ = other; + if (old) { + CFRelease(old); + } + } + + private: + T cf_type_ref_; +}; + +} // namespace experimental +} // namespace grpc_event_engine + +#endif // GPR_APPLE + +#endif // GRPC_SRC_CORE_LIB_EVENT_ENGINE_CF_ENGINE_CFTYPE_UNIQUE_REF_H diff --git a/test/core/event_engine/test_suite/BUILD b/test/core/event_engine/test_suite/BUILD index 620380bd94c2c..e2462d4594dec 100644 --- a/test/core/event_engine/test_suite/BUILD +++ b/test/core/event_engine/test_suite/BUILD @@ -98,9 +98,11 @@ grpc_cc_test( "no_linux", "no_windows", ], - uses_polling = False, + uses_polling = True, deps = [ "//src/core:cf_event_engine", + "//test/core/event_engine/test_suite/posix:oracle_event_engine_posix", + "//test/core/event_engine/test_suite/tests:client", "//test/core/event_engine/test_suite/tests:timer", ], ) diff --git a/test/core/event_engine/test_suite/cf_event_engine_test.cc b/test/core/event_engine/test_suite/cf_event_engine_test.cc index 1f3c800902ccd..1d222514cb624 100644 --- a/test/core/event_engine/test_suite/cf_event_engine_test.cc +++ b/test/core/event_engine/test_suite/cf_event_engine_test.cc @@ -19,6 +19,8 @@ #include "src/core/lib/event_engine/cf_engine/cf_engine.h" #include "test/core/event_engine/test_suite/event_engine_test_framework.h" +#include "test/core/event_engine/test_suite/posix/oracle_event_engine_posix.h" +#include "test/core/event_engine/test_suite/tests/client_test.h" #include "test/core/event_engine/test_suite/tests/timer_test.h" #include "test/core/util/test_config.h" @@ -28,8 +30,15 @@ int main(int argc, char** argv) { auto factory = []() { return std::make_unique(); }; - SetEventEngineFactories(factory, factory); + auto oracle_factory = []() { + return std::make_unique< + grpc_event_engine::experimental::PosixOracleEventEngine>(); + }; + SetEventEngineFactories(factory, oracle_factory); grpc_event_engine::experimental::InitTimerTests(); + grpc_event_engine::experimental::InitClientTests(); + // TODO(vigneshbabu): remove when the experiment is over + grpc_core::ForceEnableExperiment("event_engine_client", true); // TODO(ctiller): EventEngine temporarily needs grpc to be initialized first // until we clear out the iomgr shutdown code. grpc_init(); From ffed18d8857f997d33b73a8e5684bfb2c209ed82 Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Mon, 16 Jan 2023 14:10:00 -0800 Subject: [PATCH 26/39] add cf_engine to default event engine factory --- BUILD | 22 ++++++++++++++++++- src/core/BUILD | 6 +++++ .../default_event_engine_factory.cc | 16 ++++++++++++-- test/core/event_engine/posix/BUILD | 1 + test/core/event_engine/test_suite/BUILD | 1 + 5 files changed, 43 insertions(+), 3 deletions(-) diff --git a/BUILD b/BUILD index 7c23227f0700e..f26f6f8d48acf 100644 --- a/BUILD +++ b/BUILD @@ -75,11 +75,26 @@ config_setting( values = {"crosstool_top": "//external:android/crosstool"}, ) +config_setting( + name = "macos", + values = {"apple_platform_type": "macos"}, +) + config_setting( name = "ios", values = {"apple_platform_type": "ios"}, ) +config_setting( + name = "tvos", + values = {"apple_platform_type": "tvos"}, +) + +config_setting( + name = "watchos", + values = {"apple_platform_type": "watchos"}, +) + config_setting( name = "systemd", values = {"define": "use_systemd=true"}, @@ -162,10 +177,15 @@ config_setting( ) config_setting( - name = "mac_x86_64", + name = "mac", values = {"cpu": "darwin"}, ) +config_setting( + name = "mac_x86_64", + values = {"cpu": "darwin_x86_64"}, +) + config_setting( name = "mac_arm64", values = {"cpu": "darwin_arm64"}, diff --git a/src/core/BUILD b/src/core/BUILD index 325cf522d3c98..d861a72eff158 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -2162,6 +2162,12 @@ grpc_cc_library( "//:windows": ["windows_event_engine"], "//:windows_msvc": ["windows_event_engine"], "//:windows_other": ["windows_event_engine"], + "//:mac": ["cf_event_engine"], + "//:mac_x86_64": ["cf_event_engine"], + "//:mac_arm64": ["cf_event_engine"], + "//:ios": ["cf_event_engine"], + "//:tvos": ["cf_event_engine"], + "//:watchos": ["cf_event_engine"], "//conditions:default": ["posix_event_engine"], }], deps = [ diff --git a/src/core/lib/event_engine/default_event_engine_factory.cc b/src/core/lib/event_engine/default_event_engine_factory.cc index a4311bc96688c..b1cf0d58147b9 100644 --- a/src/core/lib/event_engine/default_event_engine_factory.cc +++ b/src/core/lib/event_engine/default_event_engine_factory.cc @@ -20,7 +20,7 @@ #include -#ifdef GPR_WINDOWS +#if defined(GPR_WINDOWS) #include "src/core/lib/event_engine/windows/windows_engine.h" namespace grpc_event_engine { @@ -32,7 +32,19 @@ std::unique_ptr DefaultEventEngineFactory() { } // namespace experimental } // namespace grpc_event_engine -#else // not GPR_WINDOWS +#elif defined(GPR_APPLE) +#include "src/core/lib/event_engine/cf_engine/cf_engine.h" + +namespace grpc_event_engine { +namespace experimental { + +std::unique_ptr DefaultEventEngineFactory() { + return std::make_unique(); +} + +} // namespace experimental +} // namespace grpc_event_engine +#else #include "src/core/lib/event_engine/posix_engine/posix_engine.h" namespace grpc_event_engine { diff --git a/test/core/event_engine/posix/BUILD b/test/core/event_engine/posix/BUILD index 0d8ee42474b68..6576c2157eb8a 100644 --- a/test/core/event_engine/posix/BUILD +++ b/test/core/event_engine/posix/BUILD @@ -224,6 +224,7 @@ grpc_cc_test( external_deps = ["gtest"], language = "C++", tags = [ + "no_mac", "no_windows", ], uses_event_engine = True, diff --git a/test/core/event_engine/test_suite/BUILD b/test/core/event_engine/test_suite/BUILD index e2462d4594dec..8e8592896cf19 100644 --- a/test/core/event_engine/test_suite/BUILD +++ b/test/core/event_engine/test_suite/BUILD @@ -40,6 +40,7 @@ grpc_cc_test( name = "posix_event_engine_test", srcs = ["posix_event_engine_test.cc"], tags = [ + "no_mac", "no_windows", ], uses_event_engine = True, From 419dcd3cb74959f30f22092b7dc74971361607a2 Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Mon, 16 Jan 2023 16:56:59 -0800 Subject: [PATCH 27/39] generate projects --- CMakeLists.txt | 19 ++++++++++---- Makefile | 4 +++ build_autogenerated.yaml | 29 +++++++++++++++++---- config.m4 | 3 +++ config.w32 | 3 +++ gRPC-C++.podspec | 6 +++++ gRPC-Core.podspec | 8 ++++++ grpc.gemspec | 5 ++++ grpc.gyp | 6 +++++ package.xml | 5 ++++ src/core/BUILD | 3 +++ src/python/grpcio/grpc_core_dependencies.py | 2 ++ tools/doxygen/Doxyfile.c++.internal | 5 ++++ tools/doxygen/Doxyfile.core.internal | 5 ++++ tools/run_tests/generated/tests.json | 6 +---- 15 files changed, 94 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6707312c407b7..293e569293b7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1107,10 +1107,10 @@ if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx posix_engine_listener_utils_test) endif() - if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx posix_event_engine_connect_test) endif() - if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx posix_event_engine_test) endif() add_dependencies(buildtests_cxx promise_factory_test) @@ -2040,6 +2040,8 @@ add_library(grpc src/core/lib/debug/stats.cc src/core/lib/debug/stats_data.cc src/core/lib/debug/trace.cc + src/core/lib/event_engine/cf_engine/cf_engine.cc + src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc src/core/lib/event_engine/channel_args_endpoint_config.cc src/core/lib/event_engine/default_event_engine.cc src/core/lib/event_engine/default_event_engine_factory.cc @@ -2730,6 +2732,8 @@ add_library(grpc_unsecure src/core/lib/debug/stats.cc src/core/lib/debug/stats_data.cc src/core/lib/debug/trace.cc + src/core/lib/event_engine/cf_engine/cf_engine.cc + src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc src/core/lib/event_engine/channel_args_endpoint_config.cc src/core/lib/event_engine/default_event_engine.cc src/core/lib/event_engine/default_event_engine_factory.cc @@ -4252,6 +4256,8 @@ add_library(grpc_authorization_provider src/core/lib/debug/stats.cc src/core/lib/debug/stats_data.cc src/core/lib/debug/trace.cc + src/core/lib/event_engine/cf_engine/cf_engine.cc + src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc src/core/lib/event_engine/channel_args_endpoint_config.cc src/core/lib/event_engine/default_event_engine.cc src/core/lib/event_engine/default_event_engine_factory.cc @@ -7415,10 +7421,11 @@ if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_executable(cf_event_engine_test - src/core/lib/event_engine/cf_engine/cf_engine.cc test/core/event_engine/event_engine_test_utils.cc test/core/event_engine/test_suite/cf_event_engine_test.cc test/core/event_engine/test_suite/event_engine_test_framework.cc + test/core/event_engine/test_suite/posix/oracle_event_engine_posix.cc + test/core/event_engine/test_suite/tests/client_test.cc test/core/event_engine/test_suite/tests/timer_test.cc third_party/googletest/googletest/src/gtest-all.cc third_party/googletest/googlemock/src/gmock-all.cc @@ -11451,6 +11458,8 @@ add_executable(frame_test src/core/lib/debug/stats.cc src/core/lib/debug/stats_data.cc src/core/lib/debug/trace.cc + src/core/lib/event_engine/cf_engine/cf_engine.cc + src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc src/core/lib/event_engine/channel_args_endpoint_config.cc src/core/lib/event_engine/default_event_engine.cc src/core/lib/event_engine/default_event_engine_factory.cc @@ -16611,7 +16620,7 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) endif() endif() if(gRPC_BUILD_TESTS) -if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) add_executable(posix_event_engine_connect_test test/core/event_engine/event_engine_test_utils.cc @@ -16654,7 +16663,7 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) endif() endif() if(gRPC_BUILD_TESTS) -if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) add_executable(posix_event_engine_test test/core/event_engine/event_engine_test_utils.cc diff --git a/Makefile b/Makefile index 8196861a80ea8..9c4cadab41a5b 100644 --- a/Makefile +++ b/Makefile @@ -1428,6 +1428,8 @@ LIBGRPC_SRC = \ src/core/lib/debug/stats.cc \ src/core/lib/debug/stats_data.cc \ src/core/lib/debug/trace.cc \ + src/core/lib/event_engine/cf_engine/cf_engine.cc \ + src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc \ src/core/lib/event_engine/channel_args_endpoint_config.cc \ src/core/lib/event_engine/default_event_engine.cc \ src/core/lib/event_engine/default_event_engine_factory.cc \ @@ -1972,6 +1974,8 @@ LIBGRPC_UNSECURE_SRC = \ src/core/lib/debug/stats.cc \ src/core/lib/debug/stats_data.cc \ src/core/lib/debug/trace.cc \ + src/core/lib/event_engine/cf_engine/cf_engine.cc \ + src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc \ src/core/lib/event_engine/channel_args_endpoint_config.cc \ src/core/lib/event_engine/default_event_engine.cc \ src/core/lib/event_engine/default_event_engine_factory.cc \ diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index b5df015c68132..2b9f242f3419a 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -665,6 +665,9 @@ libs: - src/core/lib/debug/stats.h - src/core/lib/debug/stats_data.h - src/core/lib/debug/trace.h + - src/core/lib/event_engine/cf_engine/cf_engine.h + - src/core/lib/event_engine/cf_engine/cfstream_endpoint.h + - src/core/lib/event_engine/cf_engine/cftype_unique_ref.h - src/core/lib/event_engine/channel_args_endpoint_config.h - src/core/lib/event_engine/common_closures.h - src/core/lib/event_engine/default_event_engine.h @@ -1456,6 +1459,8 @@ libs: - src/core/lib/debug/stats.cc - src/core/lib/debug/stats_data.cc - src/core/lib/debug/trace.cc + - src/core/lib/event_engine/cf_engine/cf_engine.cc + - src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc - src/core/lib/event_engine/channel_args_endpoint_config.cc - src/core/lib/event_engine/default_event_engine.cc - src/core/lib/event_engine/default_event_engine_factory.cc @@ -2014,6 +2019,9 @@ libs: - src/core/lib/debug/stats.h - src/core/lib/debug/stats_data.h - src/core/lib/debug/trace.h + - src/core/lib/event_engine/cf_engine/cf_engine.h + - src/core/lib/event_engine/cf_engine/cfstream_endpoint.h + - src/core/lib/event_engine/cf_engine/cftype_unique_ref.h - src/core/lib/event_engine/channel_args_endpoint_config.h - src/core/lib/event_engine/common_closures.h - src/core/lib/event_engine/default_event_engine.h @@ -2419,6 +2427,8 @@ libs: - src/core/lib/debug/stats.cc - src/core/lib/debug/stats_data.cc - src/core/lib/debug/trace.cc + - src/core/lib/event_engine/cf_engine/cf_engine.cc + - src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc - src/core/lib/event_engine/channel_args_endpoint_config.cc - src/core/lib/event_engine/default_event_engine.cc - src/core/lib/event_engine/default_event_engine_factory.cc @@ -3489,6 +3499,9 @@ libs: - src/core/lib/debug/stats.h - src/core/lib/debug/stats_data.h - src/core/lib/debug/trace.h + - src/core/lib/event_engine/cf_engine/cf_engine.h + - src/core/lib/event_engine/cf_engine/cfstream_endpoint.h + - src/core/lib/event_engine/cf_engine/cftype_unique_ref.h - src/core/lib/event_engine/channel_args_endpoint_config.h - src/core/lib/event_engine/common_closures.h - src/core/lib/event_engine/default_event_engine.h @@ -3774,6 +3787,8 @@ libs: - src/core/lib/debug/stats.cc - src/core/lib/debug/stats_data.cc - src/core/lib/debug/trace.cc + - src/core/lib/event_engine/cf_engine/cf_engine.cc + - src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc - src/core/lib/event_engine/channel_args_endpoint_config.cc - src/core/lib/event_engine/default_event_engine.cc - src/core/lib/event_engine/default_event_engine_factory.cc @@ -5388,15 +5403,17 @@ targets: build: test language: c++ headers: - - src/core/lib/event_engine/cf_engine/cf_engine.h - test/core/event_engine/event_engine_test_utils.h - test/core/event_engine/test_suite/event_engine_test_framework.h + - test/core/event_engine/test_suite/posix/oracle_event_engine_posix.h + - test/core/event_engine/test_suite/tests/client_test.h - test/core/event_engine/test_suite/tests/timer_test.h src: - - src/core/lib/event_engine/cf_engine/cf_engine.cc - test/core/event_engine/event_engine_test_utils.cc - test/core/event_engine/test_suite/cf_event_engine_test.cc - test/core/event_engine/test_suite/event_engine_test_framework.cc + - test/core/event_engine/test_suite/posix/oracle_event_engine_posix.cc + - test/core/event_engine/test_suite/tests/client_test.cc - test/core/event_engine/test_suite/tests/timer_test.cc deps: - grpc_unsecure @@ -5405,7 +5422,6 @@ targets: - linux - posix - mac - uses_polling: false - name: cfstream_test gtest: true build: test @@ -7425,6 +7441,9 @@ targets: - src/core/lib/debug/stats.h - src/core/lib/debug/stats_data.h - src/core/lib/debug/trace.h + - src/core/lib/event_engine/cf_engine/cf_engine.h + - src/core/lib/event_engine/cf_engine/cfstream_endpoint.h + - src/core/lib/event_engine/cf_engine/cftype_unique_ref.h - src/core/lib/event_engine/channel_args_endpoint_config.h - src/core/lib/event_engine/common_closures.h - src/core/lib/event_engine/default_event_engine.h @@ -7693,6 +7712,8 @@ targets: - src/core/lib/debug/stats.cc - src/core/lib/debug/stats_data.cc - src/core/lib/debug/trace.cc + - src/core/lib/event_engine/cf_engine/cf_engine.cc + - src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc - src/core/lib/event_engine/channel_args_endpoint_config.cc - src/core/lib/event_engine/default_event_engine.cc - src/core/lib/event_engine/default_event_engine_factory.cc @@ -10081,7 +10102,6 @@ targets: platforms: - linux - posix - - mac - name: posix_event_engine_test gtest: true build: test @@ -10107,7 +10127,6 @@ targets: platforms: - linux - posix - - mac - name: promise_factory_test gtest: true build: test diff --git a/config.m4 b/config.m4 index f6f6798832ba3..83a6f6f63a370 100644 --- a/config.m4 +++ b/config.m4 @@ -510,6 +510,8 @@ if test "$PHP_GRPC" != "no"; then src/core/lib/debug/stats.cc \ src/core/lib/debug/stats_data.cc \ src/core/lib/debug/trace.cc \ + src/core/lib/event_engine/cf_engine/cf_engine.cc \ + src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc \ src/core/lib/event_engine/channel_args_endpoint_config.cc \ src/core/lib/event_engine/default_event_engine.cc \ src/core/lib/event_engine/default_event_engine_factory.cc \ @@ -1424,6 +1426,7 @@ if test "$PHP_GRPC" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/config) PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/debug) PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/event_engine) + PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/event_engine/cf_engine) PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/event_engine/posix_engine) PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/event_engine/windows) PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/experiments) diff --git a/config.w32 b/config.w32 index c9008c9af327b..33fed33e26eaa 100644 --- a/config.w32 +++ b/config.w32 @@ -476,6 +476,8 @@ if (PHP_GRPC != "no") { "src\\core\\lib\\debug\\stats.cc " + "src\\core\\lib\\debug\\stats_data.cc " + "src\\core\\lib\\debug\\trace.cc " + + "src\\core\\lib\\event_engine\\cf_engine\\cf_engine.cc " + + "src\\core\\lib\\event_engine\\cf_engine\\cfstream_endpoint.cc " + "src\\core\\lib\\event_engine\\channel_args_endpoint_config.cc " + "src\\core\\lib\\event_engine\\default_event_engine.cc " + "src\\core\\lib\\event_engine\\default_event_engine_factory.cc " + @@ -1557,6 +1559,7 @@ if (PHP_GRPC != "no") { FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\config"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\debug"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\event_engine"); + FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\event_engine\\cf_engine"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\event_engine\\posix_engine"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\event_engine\\windows"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\experiments"); diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index deece3ee6f609..c210c5c84e9cb 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -738,6 +738,9 @@ Pod::Spec.new do |s| 'src/core/lib/debug/stats.h', 'src/core/lib/debug/stats_data.h', 'src/core/lib/debug/trace.h', + 'src/core/lib/event_engine/cf_engine/cf_engine.h', + 'src/core/lib/event_engine/cf_engine/cfstream_endpoint.h', + 'src/core/lib/event_engine/cf_engine/cftype_unique_ref.h', 'src/core/lib/event_engine/channel_args_endpoint_config.h', 'src/core/lib/event_engine/common_closures.h', 'src/core/lib/event_engine/default_event_engine.h', @@ -1679,6 +1682,9 @@ Pod::Spec.new do |s| 'src/core/lib/debug/stats.h', 'src/core/lib/debug/stats_data.h', 'src/core/lib/debug/trace.h', + 'src/core/lib/event_engine/cf_engine/cf_engine.h', + 'src/core/lib/event_engine/cf_engine/cfstream_endpoint.h', + 'src/core/lib/event_engine/cf_engine/cftype_unique_ref.h', 'src/core/lib/event_engine/channel_args_endpoint_config.h', 'src/core/lib/event_engine/common_closures.h', 'src/core/lib/event_engine/default_event_engine.h', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index c071ff7c43bac..21f36e1496498 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -1137,6 +1137,11 @@ Pod::Spec.new do |s| 'src/core/lib/debug/stats_data.h', 'src/core/lib/debug/trace.cc', 'src/core/lib/debug/trace.h', + 'src/core/lib/event_engine/cf_engine/cf_engine.cc', + 'src/core/lib/event_engine/cf_engine/cf_engine.h', + 'src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc', + 'src/core/lib/event_engine/cf_engine/cfstream_endpoint.h', + 'src/core/lib/event_engine/cf_engine/cftype_unique_ref.h', 'src/core/lib/event_engine/channel_args_endpoint_config.cc', 'src/core/lib/event_engine/channel_args_endpoint_config.h', 'src/core/lib/event_engine/common_closures.h', @@ -2369,6 +2374,9 @@ Pod::Spec.new do |s| 'src/core/lib/debug/stats.h', 'src/core/lib/debug/stats_data.h', 'src/core/lib/debug/trace.h', + 'src/core/lib/event_engine/cf_engine/cf_engine.h', + 'src/core/lib/event_engine/cf_engine/cfstream_endpoint.h', + 'src/core/lib/event_engine/cf_engine/cftype_unique_ref.h', 'src/core/lib/event_engine/channel_args_endpoint_config.h', 'src/core/lib/event_engine/common_closures.h', 'src/core/lib/event_engine/default_event_engine.h', diff --git a/grpc.gemspec b/grpc.gemspec index 0bca4f77b1464..af46e10204bad 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -1044,6 +1044,11 @@ Gem::Specification.new do |s| s.files += %w( src/core/lib/debug/stats_data.h ) s.files += %w( src/core/lib/debug/trace.cc ) s.files += %w( src/core/lib/debug/trace.h ) + s.files += %w( src/core/lib/event_engine/cf_engine/cf_engine.cc ) + s.files += %w( src/core/lib/event_engine/cf_engine/cf_engine.h ) + s.files += %w( src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc ) + s.files += %w( src/core/lib/event_engine/cf_engine/cfstream_endpoint.h ) + s.files += %w( src/core/lib/event_engine/cf_engine/cftype_unique_ref.h ) s.files += %w( src/core/lib/event_engine/channel_args_endpoint_config.cc ) s.files += %w( src/core/lib/event_engine/channel_args_endpoint_config.h ) s.files += %w( src/core/lib/event_engine/common_closures.h ) diff --git a/grpc.gyp b/grpc.gyp index d912b9ffd24d4..e5464964d453a 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -729,6 +729,8 @@ 'src/core/lib/debug/stats.cc', 'src/core/lib/debug/stats_data.cc', 'src/core/lib/debug/trace.cc', + 'src/core/lib/event_engine/cf_engine/cf_engine.cc', + 'src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc', 'src/core/lib/event_engine/channel_args_endpoint_config.cc', 'src/core/lib/event_engine/default_event_engine.cc', 'src/core/lib/event_engine/default_event_engine_factory.cc', @@ -1214,6 +1216,8 @@ 'src/core/lib/debug/stats.cc', 'src/core/lib/debug/stats_data.cc', 'src/core/lib/debug/trace.cc', + 'src/core/lib/event_engine/cf_engine/cf_engine.cc', + 'src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc', 'src/core/lib/event_engine/channel_args_endpoint_config.cc', 'src/core/lib/event_engine/default_event_engine.cc', 'src/core/lib/event_engine/default_event_engine_factory.cc', @@ -1725,6 +1729,8 @@ 'src/core/lib/debug/stats.cc', 'src/core/lib/debug/stats_data.cc', 'src/core/lib/debug/trace.cc', + 'src/core/lib/event_engine/cf_engine/cf_engine.cc', + 'src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc', 'src/core/lib/event_engine/channel_args_endpoint_config.cc', 'src/core/lib/event_engine/default_event_engine.cc', 'src/core/lib/event_engine/default_event_engine_factory.cc', diff --git a/package.xml b/package.xml index 18f013dffc670..ede0cb6c693f1 100644 --- a/package.xml +++ b/package.xml @@ -1026,6 +1026,11 @@ + + + + + diff --git a/src/core/BUILD b/src/core/BUILD index d861a72eff158..fd06f2af20cf3 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -2078,12 +2078,15 @@ grpc_cc_library( "lib/event_engine/cf_engine/cfstream_endpoint.h", "lib/event_engine/cf_engine/cftype_unique_ref.h", ], + external_deps = ["absl/strings:str_format"], deps = [ "event_engine_common", "event_engine_tcp_socket_utils", "event_engine_trace", "event_engine_utils", "init_internally", + "posix_event_engine_closure", + "posix_event_engine_event_poller", "posix_event_engine_lockfree_event", "posix_event_engine_timer_manager", "strerror", diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index dcab5dcd4556b..679fe839dcdab 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -485,6 +485,8 @@ 'src/core/lib/debug/stats.cc', 'src/core/lib/debug/stats_data.cc', 'src/core/lib/debug/trace.cc', + 'src/core/lib/event_engine/cf_engine/cf_engine.cc', + 'src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc', 'src/core/lib/event_engine/channel_args_endpoint_config.cc', 'src/core/lib/event_engine/default_event_engine.cc', 'src/core/lib/event_engine/default_event_engine_factory.cc', diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index ffc7d27ec2275..9aa7f9b99818a 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -2039,6 +2039,11 @@ src/core/lib/debug/stats_data.cc \ src/core/lib/debug/stats_data.h \ src/core/lib/debug/trace.cc \ src/core/lib/debug/trace.h \ +src/core/lib/event_engine/cf_engine/cf_engine.cc \ +src/core/lib/event_engine/cf_engine/cf_engine.h \ +src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc \ +src/core/lib/event_engine/cf_engine/cfstream_endpoint.h \ +src/core/lib/event_engine/cf_engine/cftype_unique_ref.h \ src/core/lib/event_engine/channel_args_endpoint_config.cc \ src/core/lib/event_engine/channel_args_endpoint_config.h \ src/core/lib/event_engine/common_closures.h \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index d1de4b40f2491..369e778e3eab3 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1818,6 +1818,11 @@ src/core/lib/debug/stats_data.cc \ src/core/lib/debug/stats_data.h \ src/core/lib/debug/trace.cc \ src/core/lib/debug/trace.h \ +src/core/lib/event_engine/cf_engine/cf_engine.cc \ +src/core/lib/event_engine/cf_engine/cf_engine.h \ +src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc \ +src/core/lib/event_engine/cf_engine/cfstream_endpoint.h \ +src/core/lib/event_engine/cf_engine/cftype_unique_ref.h \ src/core/lib/event_engine/channel_args_endpoint_config.cc \ src/core/lib/event_engine/channel_args_endpoint_config.h \ src/core/lib/event_engine/common_closures.h \ diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index a491456422eb1..ffa7d7ff65ed0 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -1365,7 +1365,7 @@ "mac", "posix" ], - "uses_polling": false + "uses_polling": true }, { "args": [], @@ -5546,7 +5546,6 @@ "benchmark": false, "ci_platforms": [ "linux", - "mac", "posix" ], "cpu_cost": 1.0, @@ -5558,7 +5557,6 @@ "name": "posix_event_engine_connect_test", "platforms": [ "linux", - "mac", "posix" ], "uses_polling": true @@ -5568,7 +5566,6 @@ "benchmark": false, "ci_platforms": [ "linux", - "mac", "posix" ], "cpu_cost": 1.0, @@ -5580,7 +5577,6 @@ "name": "posix_event_engine_test", "platforms": [ "linux", - "mac", "posix" ], "uses_polling": true From 354e2d4fa481cfd25be71752b9fc63828f5b28b8 Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Sat, 28 Jan 2023 12:10:22 -0800 Subject: [PATCH 28/39] address review comments --- .../cf_engine/cfstream_endpoint.cc | 30 +++++++++++-------- test/core/event_engine/test_suite/BUILD | 2 +- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc index c720ce58e2f99..a7e926c51d079 100644 --- a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc @@ -32,7 +32,7 @@ absl::Status CFErrorToStatus(CFTypeUniqueRef cf_error) { char desc_buf[256]; CFStringGetCString(cf_domain, domain_buf, 256, kCFStringEncodingUTF8); CFStringGetCString(cf_desc, desc_buf, 256, kCFStringEncodingUTF8); - return absl::Status(absl::StatusCode::kInternal, + return absl::Status(absl::StatusCode::kUnknown, absl::StrFormat("(domain:%s, code:%ld, description:%s)", domain_buf, code, desc_buf)); } @@ -59,16 +59,18 @@ void CFStreamEndpoint::Connect(EventEngine::OnConnectCallback on_connect, EventEngine::ResolvedAddress addr, EventEngine::Duration timeout) { peer_address_ = std::move(addr); - std::string host_port = - grpc_sockaddr_to_string(reinterpret_cast( - peer_address_.address()), - true) - .value(); + auto host_port = ResolvedAddressToNormalizedString(peer_address_); + if (!host_port.ok()) { + on_connect(std::move(host_port).status()); + delete this; + return; + } + gpr_log(GPR_INFO, "CFStreamClient::connect, host_port: %s", - host_port.c_str()); + host_port->c_str()); std::string host_string; std::string port_string; - grpc_core::SplitHostPort(host_port, &host_string, &port_string); + grpc_core::SplitHostPort(host_port.value(), &host_string, &port_string); CFStringRef host = CFStringCreateWithCString(NULL, host_string.c_str(), kCFStringEncodingUTF8); int port = ResolvedAddressGetPort(peer_address_); @@ -113,7 +115,7 @@ void CFStreamEndpoint::Connect(EventEngine::OnConnectCallback on_connect, open_event_.NotifyOn(new PosixEngineClosure( [this, on_connect = std::move(on_connect), connect_timeout_timer](absl::Status status) mutable { - engine_->Cancel(connect_timeout_timer); + bool canceled = engine_->Cancel(connect_timeout_timer); if (!status.ok()) { on_connect(std::move(status)); @@ -121,14 +123,14 @@ void CFStreamEndpoint::Connect(EventEngine::OnConnectCallback on_connect, return; } - auto status_or_local_addr = CFReadStreamLocallAddress(cf_read_stream_); - if (!status_or_local_addr.ok()) { - on_connect(std::move(status_or_local_addr).status()); + auto local_addr = CFReadStreamLocallAddress(cf_read_stream_); + if (!local_addr.ok()) { + on_connect(std::move(local_addr).status()); delete this; return; } - local_address_ = status_or_local_addr.value(); + local_address_ = local_addr.value(); on_connect(std::unique_ptr(this)); }, false /* is_permanent */)); @@ -143,6 +145,7 @@ void CFStreamEndpoint::Connect(EventEngine::OnConnectCallback on_connect, case kCFStreamEventOpenCompleted: break; case kCFStreamEventHasBytesAvailable: + ABSL_FALLTHROUGH_INTENDED; case kCFStreamEventEndEncountered: self->read_event_.SetReady(); break; @@ -169,6 +172,7 @@ void CFStreamEndpoint::WriteCallback(CFWriteStreamRef stream, self->open_event_.SetReady(); break; case kCFStreamEventCanAcceptBytes: + ABSL_FALLTHROUGH_INTENDED; case kCFStreamEventEndEncountered: self->write_event_.SetReady(); break; diff --git a/test/core/event_engine/test_suite/BUILD b/test/core/event_engine/test_suite/BUILD index 8e8592896cf19..f70302c4c965f 100644 --- a/test/core/event_engine/test_suite/BUILD +++ b/test/core/event_engine/test_suite/BUILD @@ -99,7 +99,7 @@ grpc_cc_test( "no_linux", "no_windows", ], - uses_polling = True, + uses_polling = False, deps = [ "//src/core:cf_event_engine", "//test/core/event_engine/test_suite/posix:oracle_event_engine_posix", From 6317cc6e67d6a73a501645a0b925076d79a37cf8 Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Tue, 14 Feb 2023 21:08:20 -0800 Subject: [PATCH 29/39] logging in cf_engine --- .../lib/event_engine/cf_engine/cf_engine.cc | 4 +- .../cf_engine/cfstream_endpoint.cc | 54 ++++++++++++++++--- .../cf_engine/cfstream_endpoint.h | 2 + 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/core/lib/event_engine/cf_engine/cf_engine.cc b/src/core/lib/event_engine/cf_engine/cf_engine.cc index 4cdc7fd7554be..7b7678c9e8595 100644 --- a/src/core/lib/event_engine/cf_engine/cf_engine.cc +++ b/src/core/lib/event_engine/cf_engine/cf_engine.cc @@ -80,7 +80,9 @@ CFEventEngine::ConnectionHandle CFEventEngine::Connect( const EndpointConfig& /* args */, MemoryAllocator memory_allocator, Duration timeout) { auto addr_uri = ResolvedAddressToURI(addr); - gpr_log(GPR_INFO, "CFEventEngine::connect: %s", addr_uri.value().c_str()); + GRPC_EVENT_ENGINE_TRACE("CFEventEngine::connect: %s", + addr_uri.value().c_str()); + if (!addr_uri.ok()) { Run([on_connect = std::move(on_connect), ep = absl::FailedPreconditionError(absl::StrCat( diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc index a7e926c51d079..58f2480b18919 100644 --- a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc @@ -17,6 +17,7 @@ #ifdef GPR_APPLE #include "src/core/lib/event_engine/cf_engine/cfstream_endpoint.h" +#include "src/core/lib/event_engine/trace.h" #include "src/core/lib/gprpp/strerror.h" namespace grpc_event_engine { @@ -66,8 +67,10 @@ void CFStreamEndpoint::Connect(EventEngine::OnConnectCallback on_connect, return; } - gpr_log(GPR_INFO, "CFStreamClient::connect, host_port: %s", - host_port->c_str()); + peer_address_string_ = host_port.value(); + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpoint::Connect, host_port: %s", + host_port->c_str()); + std::string host_string; std::string port_string; grpc_core::SplitHostPort(host_port.value(), &host_string, &port_string); @@ -115,7 +118,13 @@ void CFStreamEndpoint::Connect(EventEngine::OnConnectCallback on_connect, open_event_.NotifyOn(new PosixEngineClosure( [this, on_connect = std::move(on_connect), connect_timeout_timer](absl::Status status) mutable { - bool canceled = engine_->Cancel(connect_timeout_timer); + auto canceled = engine_->Cancel(connect_timeout_timer); + + if (!canceled) { + // timer cancelation failed: + // 1. will fire or is firing, cannot delete this + // 2. has fired and this may be it (open_event_ shutdown) + } if (!status.ok()) { on_connect(std::move(status)); @@ -131,6 +140,7 @@ void CFStreamEndpoint::Connect(EventEngine::OnConnectCallback on_connect, } local_address_ = local_addr.value(); + local_address_string_ = *ResolvedAddressToURI(local_address_); on_connect(std::unique_ptr(this)); }, false /* is_permanent */)); @@ -141,6 +151,9 @@ void CFStreamEndpoint::Connect(EventEngine::OnConnectCallback on_connect, void* client_callback_info) { auto self = static_cast(client_callback_info); + GRPC_EVENT_ENGINE_ENDPOINT_TRACE( + "CFStreamEndpoint::ReadCallback, type: %lu, this: %p", type, self); + switch (type) { case kCFStreamEventOpenCompleted: break; @@ -151,7 +164,9 @@ void CFStreamEndpoint::Connect(EventEngine::OnConnectCallback on_connect, break; case kCFStreamEventErrorOccurred: { auto status = CFErrorToStatus(CFReadStreamCopyError(stream)); - gpr_log(GPR_ERROR, "CFStream Read error: %s", status.ToString().c_str()); + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStream Read error: %s", + status.ToString().c_str()); + self->open_event_.SetShutdown(status); self->read_event_.SetShutdown(status); self->write_event_.SetShutdown(status); @@ -166,6 +181,8 @@ void CFStreamEndpoint::WriteCallback(CFWriteStreamRef stream, CFStreamEventType type, void* client_callback_info) { auto self = static_cast(client_callback_info); + GRPC_EVENT_ENGINE_ENDPOINT_TRACE( + "CFStreamEndpoint::WriteCallback, type: %lu, this: %p", type, self); switch (type) { case kCFStreamEventOpenCompleted: @@ -178,7 +195,9 @@ void CFStreamEndpoint::WriteCallback(CFWriteStreamRef stream, break; case kCFStreamEventErrorOccurred: { auto status = CFErrorToStatus(CFWriteStreamCopyError(stream)); - gpr_log(GPR_ERROR, "CFStream Write error: %s", status.ToString().c_str()); + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStream Write error: %s", + status.ToString().c_str()); + self->open_event_.SetShutdown(status); self->read_event_.SetShutdown(status); self->write_event_.SetShutdown(status); @@ -228,6 +247,8 @@ void CFStreamEndpoint::Read(absl::AnyInvocable on_read, void CFStreamEndpoint::DoRead(absl::AnyInvocable on_read, SliceBuffer* buffer) { + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpoint::DoRead, this: %p", this); + int buffer_size = 8192; auto buffer_index = buffer->AppendIndexed(Slice(memory_allocator_.MakeSlice(buffer_size))); @@ -240,12 +261,23 @@ void CFStreamEndpoint::DoRead(absl::AnyInvocable on_read, if (read_size < 0) { auto status = CFErrorToStatus(CFReadStreamCopyError(cf_read_stream_)); - gpr_log(GPR_ERROR, "CFStream read error: %s", status.ToString().c_str()); + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStream read error: %s", + status.ToString().c_str()); on_read(status); return; } buffer->RemoveLastNBytes(buffer->Length() - read_size); + + if (grpc_event_engine_endpoint_data_trace.enabled()) { + for (size_t i = 0; i < buffer->Count(); i++) { + auto str = buffer->RefSlice(i).as_string_view(); + gpr_log(GPR_INFO, "CFStreamEndpoint::DoRead, this: %p (peer=%s): %.*s", + this, peer_address_string_.c_str(), + static_cast(str.length()), str.data()); + } + } + on_read(absl::OkStatus()); } @@ -265,6 +297,16 @@ void CFStreamEndpoint::Write(absl::AnyInvocable on_writable, void CFStreamEndpoint::DoWrite( absl::AnyInvocable on_writable, SliceBuffer* data) { + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpoint::DoWrite, this: %p", this); + if (grpc_event_engine_endpoint_data_trace.enabled()) { + for (size_t i = 0; i < data->Count(); i++) { + auto str = data->RefSlice(i).as_string_view(); + gpr_log(GPR_INFO, "CFStreamEndpoint::DoWrite, this: %p (peer=%s): %.*s", + this, peer_address_string_.c_str(), + static_cast(str.length()), str.data()); + } + } + size_t total_written_size = 0; for (size_t i = 0; i < data->Count(); i++) { auto slice = data->RefSlice(i); diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h index 955eb9728281d..906bbc633167f 100644 --- a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h @@ -77,6 +77,8 @@ class CFStreamEndpoint : public EventEngine::Endpoint { EventEngine::ResolvedAddress peer_address_; EventEngine::ResolvedAddress local_address_; + std::string peer_address_string_; + std::string local_address_string_; MemoryAllocator memory_allocator_; LockfreeEvent open_event_; From b80b794486d301c5c77ad4adea6210df406671d3 Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Wed, 22 Feb 2023 08:28:40 -0800 Subject: [PATCH 30/39] add cancel connect support --- .../lib/event_engine/cf_engine/cf_engine.cc | 74 +++++++++++++++---- .../lib/event_engine/cf_engine/cf_engine.h | 7 ++ .../cf_engine/cfstream_endpoint.cc | 51 +++++++------ .../cf_engine/cfstream_endpoint.h | 7 +- 4 files changed, 94 insertions(+), 45 deletions(-) diff --git a/src/core/lib/event_engine/cf_engine/cf_engine.cc b/src/core/lib/event_engine/cf_engine/cf_engine.cc index 7b7678c9e8595..57ad268a910de 100644 --- a/src/core/lib/event_engine/cf_engine/cf_engine.cc +++ b/src/core/lib/event_engine/cf_engine/cf_engine.cc @@ -79,31 +79,75 @@ CFEventEngine::ConnectionHandle CFEventEngine::Connect( OnConnectCallback on_connect, const ResolvedAddress& addr, const EndpointConfig& /* args */, MemoryAllocator memory_allocator, Duration timeout) { - auto addr_uri = ResolvedAddressToURI(addr); - GRPC_EVENT_ENGINE_TRACE("CFEventEngine::connect: %s", - addr_uri.value().c_str()); - - if (!addr_uri.ok()) { - Run([on_connect = std::move(on_connect), - ep = absl::FailedPreconditionError(absl::StrCat( - "connect failed: ", "invalid addr: ", - addr_uri.value()))]() mutable { on_connect(std::move(ep)); }); - return {0, 0}; - } - auto endpoint_ptr = new CFStreamEndpoint( std::static_pointer_cast(shared_from_this()), std::move(memory_allocator)); - endpoint_ptr->Connect(std::move(on_connect), addr, std::move(timeout)); + ConnectionHandle handle{reinterpret_cast(endpoint_ptr), 0}; + { + grpc_core::MutexLock lock(&conn_mu_); + conn_handles_.insert(handle); + } + + auto deadline_timer = + RunAfter(timeout, [handle, that = std::static_pointer_cast( + shared_from_this())]() { + that->CancelConnectInternal( + handle, absl::DeadlineExceededError("Connect timed out")); + }); + + auto on_connect2 = + [that = std::static_pointer_cast(shared_from_this()), + deadline_timer = std::move(deadline_timer), handle, + on_connect = std::move(on_connect)](absl::Status status) mutable { + // best effort canceling deadline timer + that->Cancel(deadline_timer); + + { + grpc_core::MutexLock lock(&that->conn_mu_); + that->conn_handles_.erase(handle); + } + + auto endpoint_ptr = reinterpret_cast(handle.keys[0]); + + if (!status.ok()) { + on_connect(std::move(status)); + delete endpoint_ptr; + return; + } - return {reinterpret_cast(endpoint_ptr), 0}; + on_connect(std::unique_ptr(endpoint_ptr)); + }; + + endpoint_ptr->Connect(std::move(on_connect2), addr); + + return handle; } -bool CFEventEngine::CancelConnect(ConnectionHandle /* handle */) { +bool CFEventEngine::CancelConnect(ConnectionHandle handle) { + CancelConnectInternal(handle, absl::CancelledError("CancelConnect")); + // on_connect will always be called, even if cancellation is successful return false; } +bool CFEventEngine::CancelConnectInternal(ConnectionHandle handle, + absl::Status status) { + grpc_core::MutexLock lock(&conn_mu_); + + if (!conn_handles_.contains(handle)) { + GRPC_EVENT_ENGINE_TRACE( + "Unknown connection handle: %s", + HandleToString(handle).c_str()); + return false; + } + conn_handles_.erase(handle); + + // keep the `conn_mu_` lock to prevent endpoint_ptr from being deleted + + auto endpoint_ptr = reinterpret_cast(handle.keys[0]); + return endpoint_ptr->CancelConnect(status); +} + bool CFEventEngine::IsWorkerThread() { grpc_core::Crash("unimplemented"); } std::unique_ptr CFEventEngine::GetDNSResolver( diff --git a/src/core/lib/event_engine/cf_engine/cf_engine.h b/src/core/lib/event_engine/cf_engine/cf_engine.h index 18a021e03f794..3595484ed4588 100644 --- a/src/core/lib/event_engine/cf_engine/cf_engine.h +++ b/src/core/lib/event_engine/cf_engine/cf_engine.h @@ -64,9 +64,16 @@ class CFEventEngine : public EventEngine, struct Closure; EventEngine::TaskHandle RunAfterInternal(Duration when, absl::AnyInvocable cb); + + bool CancelConnectInternal(ConnectionHandle handle, absl::Status status); + grpc_core::Mutex mu_; TaskHandleSet known_handles_ ABSL_GUARDED_BY(mu_); std::atomic aba_token_{0}; + + grpc_core::Mutex conn_mu_; + ConnectionHandleSet conn_handles_ ABSL_GUARDED_BY(conn_mu_); + std::shared_ptr executor_; TimerManager timer_manager_; }; diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc index 58f2480b18919..29f3d97c695a5 100644 --- a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc @@ -56,14 +56,27 @@ absl::StatusOr CFReadStreamLocallAddress( } // namespace -void CFStreamEndpoint::Connect(EventEngine::OnConnectCallback on_connect, - EventEngine::ResolvedAddress addr, - EventEngine::Duration timeout) { +bool CFStreamEndpoint::CancelConnect(absl::Status status) { + return open_event_.SetShutdown(std::move(status)); +} + +void CFStreamEndpoint::Connect( + absl::AnyInvocable on_connect, + EventEngine::ResolvedAddress addr) { + auto addr_uri = ResolvedAddressToURI(addr); + + if (!addr_uri.ok()) { + on_connect(std::move(addr_uri).status()); + return; + } + + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpoint::Connect: %s", + addr_uri.value().c_str()); + peer_address_ = std::move(addr); auto host_port = ResolvedAddressToNormalizedString(peer_address_); if (!host_port.ok()) { on_connect(std::move(host_port).status()); - delete this; return; } @@ -100,48 +113,31 @@ void CFStreamEndpoint::Connect(EventEngine::OnConnectCallback on_connect, if (!CFReadStreamOpen(cf_read_stream_)) { auto status = CFErrorToStatus(CFReadStreamCopyError(cf_read_stream_)); on_connect(std::move(status)); - delete this; return; } if (!CFWriteStreamOpen(cf_write_stream_)) { auto status = CFErrorToStatus(CFWriteStreamCopyError(cf_write_stream_)); on_connect(std::move(status)); - delete this; return; } - auto connect_timeout_timer = engine_->RunAfter(timeout, [this]() { - open_event_.SetShutdown(absl::DeadlineExceededError("Connect timed out")); - }); - open_event_.NotifyOn(new PosixEngineClosure( - [this, on_connect = std::move(on_connect), - connect_timeout_timer](absl::Status status) mutable { - auto canceled = engine_->Cancel(connect_timeout_timer); - - if (!canceled) { - // timer cancelation failed: - // 1. will fire or is firing, cannot delete this - // 2. has fired and this may be it (open_event_ shutdown) - } - + [this, on_connect = std::move(on_connect)](absl::Status status) mutable { if (!status.ok()) { on_connect(std::move(status)); - delete this; return; } auto local_addr = CFReadStreamLocallAddress(cf_read_stream_); if (!local_addr.ok()) { on_connect(std::move(local_addr).status()); - delete this; return; } local_address_ = local_addr.value(); local_address_string_ = *ResolvedAddressToURI(local_address_); - on_connect(std::unique_ptr(this)); + on_connect(absl::OkStatus()); }, false /* is_permanent */)); } @@ -223,9 +219,12 @@ CFStreamEndpoint::~CFStreamEndpoint() { CFReadStreamClose(cf_read_stream_); CFWriteStreamClose(cf_write_stream_); - open_event_.SetShutdown(absl::OkStatus()); - read_event_.SetShutdown(absl::OkStatus()); - write_event_.SetShutdown(absl::OkStatus()); + auto shutdownStatus = + absl::Status(absl::StatusCode::kUnknown, + absl::StrFormat("Shutting down CFStreamEndpoint")); + open_event_.SetShutdown(shutdownStatus); + read_event_.SetShutdown(shutdownStatus); + write_event_.SetShutdown(shutdownStatus); open_event_.DestroyEvent(); read_event_.DestroyEvent(); write_event_.DestroyEvent(); diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h index 906bbc633167f..7deb68f2a9057 100644 --- a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h @@ -52,10 +52,9 @@ class CFStreamEndpoint : public EventEngine::Endpoint { } public: - void Connect(EventEngine::OnConnectCallback on_connect, - EventEngine::ResolvedAddress addr, - EventEngine::Duration timeout); - bool CancelConnect() { return false; } + void Connect(absl::AnyInvocable on_connect, + EventEngine::ResolvedAddress addr); + bool CancelConnect(absl::Status status); private: void DoWrite(absl::AnyInvocable on_writable, From cf1220c94f446bdb521e2a01dae32d3228df3182 Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Wed, 15 Feb 2023 14:02:00 -0800 Subject: [PATCH 31/39] fix error: unused parameter --- src/core/lib/iomgr/cfstream_handle.cc | 2 +- src/core/lib/iomgr/endpoint_cfstream.cc | 18 ++++++++-------- src/core/lib/iomgr/ev_apple.cc | 24 +++++++++++----------- src/core/lib/iomgr/iomgr_posix_cfstream.cc | 2 +- src/core/lib/iomgr/tcp_client_cfstream.cc | 2 +- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/core/lib/iomgr/cfstream_handle.cc b/src/core/lib/iomgr/cfstream_handle.cc index 434a0199e8ac0..2978139b606ac 100644 --- a/src/core/lib/iomgr/cfstream_handle.cc +++ b/src/core/lib/iomgr/cfstream_handle.cc @@ -188,7 +188,7 @@ void CFStreamHandle::Ref(const char* file, int line, const char* reason) { void CFStreamHandle::Unref(const char* file, int line, const char* reason) { if (grpc_tcp_trace.enabled()) { gpr_atm val = gpr_atm_no_barrier_load(&refcount_.count); - gpr_log(GPR_DEBUG, + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CFStream Handle unref %p : %s %" PRIdPTR " -> %" PRIdPTR, this, reason, val, val - 1); } diff --git a/src/core/lib/iomgr/endpoint_cfstream.cc b/src/core/lib/iomgr/endpoint_cfstream.cc index edfea7c802fd0..673a0b9f3685b 100644 --- a/src/core/lib/iomgr/endpoint_cfstream.cc +++ b/src/core/lib/iomgr/endpoint_cfstream.cc @@ -240,7 +240,7 @@ static void WriteAction(void* arg, grpc_error_handle error) { } static void CFStreamRead(grpc_endpoint* ep, grpc_slice_buffer* slices, - grpc_closure* cb, bool urgent, + grpc_closure* cb, bool /*urgent*/, int /*min_progress_size*/) { CFStreamEndpoint* ep_impl = reinterpret_cast(ep); if (grpc_tcp_trace.enabled()) { @@ -258,7 +258,8 @@ static void CFStreamRead(grpc_endpoint* ep, grpc_slice_buffer* slices, } static void CFStreamWrite(grpc_endpoint* ep, grpc_slice_buffer* slices, - grpc_closure* cb, void* arg, int /*max_frame_size*/) { + grpc_closure* cb, void* /*arg*/, + int /*max_frame_size*/) { CFStreamEndpoint* ep_impl = reinterpret_cast(ep); if (grpc_tcp_trace.enabled()) { gpr_log(GPR_DEBUG, "CFStream endpoint:%p write (%p, %p) length:%zu", @@ -304,14 +305,15 @@ absl::string_view CFStreamGetLocalAddress(grpc_endpoint* ep) { return ep_impl->local_address; } -int CFStreamGetFD(grpc_endpoint* ep) { return 0; } +int CFStreamGetFD(grpc_endpoint* /*ep*/) { return 0; } -bool CFStreamCanTrackErr(grpc_endpoint* ep) { return false; } +bool CFStreamCanTrackErr(grpc_endpoint* /*ep*/) { return false; } -void CFStreamAddToPollset(grpc_endpoint* ep, grpc_pollset* pollset) {} -void CFStreamAddToPollsetSet(grpc_endpoint* ep, grpc_pollset_set* pollset) {} -void CFStreamDeleteFromPollsetSet(grpc_endpoint* ep, - grpc_pollset_set* pollset) {} +void CFStreamAddToPollset(grpc_endpoint* /*ep*/, grpc_pollset* /*pollset*/) {} +void CFStreamAddToPollsetSet(grpc_endpoint* /*ep*/, + grpc_pollset_set* /*pollset*/) {} +void CFStreamDeleteFromPollsetSet(grpc_endpoint* /*ep*/, + grpc_pollset_set* /*pollset*/) {} static const grpc_endpoint_vtable vtable = {CFStreamRead, CFStreamWrite, diff --git a/src/core/lib/iomgr/ev_apple.cc b/src/core/lib/iomgr/ev_apple.cc index efc7639ac91a6..ba6c21d670cb0 100644 --- a/src/core/lib/iomgr/ev_apple.cc +++ b/src/core/lib/iomgr/ev_apple.cc @@ -118,7 +118,7 @@ static void grpc_apple_register_write_stream_queue( /// be issued to the run loop when a network event happens and will be driven by /// the global run loop thread gGlobalRunLoopThread. static void grpc_apple_register_read_stream_run_loop( - CFReadStreamRef read_stream, dispatch_queue_t dispatch_queue) { + CFReadStreamRef read_stream, dispatch_queue_t /*dispatch_queue*/) { GRPC_POLLING_TRACE("Register read stream: %p", read_stream); grpc_core::MutexLock lock(&gGlobalRunLoopContext->mu); CFReadStreamScheduleWithRunLoop(read_stream, gGlobalRunLoopContext->run_loop, @@ -131,7 +131,7 @@ static void grpc_apple_register_read_stream_run_loop( /// be issued to the run loop when a network event happens, and will be driven /// by the global run loop thread gGlobalRunLoopThread. static void grpc_apple_register_write_stream_run_loop( - CFWriteStreamRef write_stream, dispatch_queue_t dispatch_queue) { + CFWriteStreamRef write_stream, dispatch_queue_t /*dispatch_queue*/) { GRPC_POLLING_TRACE("Register write stream: %p", write_stream); grpc_core::MutexLock lock(&gGlobalRunLoopContext->mu); CFWriteStreamScheduleWithRunLoop( @@ -163,7 +163,7 @@ void grpc_apple_register_write_stream(CFWriteStreamRef write_stream, /// Drive the run loop in a global singleton thread until the global run loop is /// shutdown. -static void GlobalRunLoopFunc(void* arg) { +static void GlobalRunLoopFunc(void* /*arg*/) { grpc_core::LockableAndReleasableMutexLock lock(&gGlobalRunLoopContext->mu); gGlobalRunLoopContext->run_loop = CFRunLoopGetCurrent(); gGlobalRunLoopContext->init_cv.Signal(); @@ -342,15 +342,15 @@ grpc_pollset_vtable grpc_apple_pollset_vtable = { // pollset_set implementation grpc_pollset_set* pollset_set_create(void) { return nullptr; } -void pollset_set_destroy(grpc_pollset_set* pollset_set) {} -void pollset_set_add_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} -void pollset_set_del_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} -void pollset_set_add_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) {} -void pollset_set_del_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) {} +void pollset_set_destroy(grpc_pollset_set* /*pollset_set*/) {} +void pollset_set_add_pollset(grpc_pollset_set* /*pollset_set*/, + grpc_pollset* /*pollset*/) {} +void pollset_set_del_pollset(grpc_pollset_set* /*pollset_set*/, + grpc_pollset* /*pollset*/) {} +void pollset_set_add_pollset_set(grpc_pollset_set* /*bag*/, + grpc_pollset_set* /*item*/) {} +void pollset_set_del_pollset_set(grpc_pollset_set* /*bag*/, + grpc_pollset_set* /*item*/) {} grpc_pollset_set_vtable grpc_apple_pollset_set_vtable = { pollset_set_create, pollset_set_destroy, diff --git a/src/core/lib/iomgr/iomgr_posix_cfstream.cc b/src/core/lib/iomgr/iomgr_posix_cfstream.cc index ddb514000fe4a..8e2d2186664aa 100644 --- a/src/core/lib/iomgr/iomgr_posix_cfstream.cc +++ b/src/core/lib/iomgr/iomgr_posix_cfstream.cc @@ -73,7 +73,7 @@ static bool apple_iomgr_platform_is_any_background_poller_thread(void) { } static bool apple_iomgr_platform_add_closure_to_background_poller( - grpc_closure* closure, grpc_error_handle error) { + grpc_closure* /*closure*/, grpc_error_handle /*error*/) { return false; } diff --git a/src/core/lib/iomgr/tcp_client_cfstream.cc b/src/core/lib/iomgr/tcp_client_cfstream.cc index 7be3637ac42eb..e53d9f61890ed 100644 --- a/src/core/lib/iomgr/tcp_client_cfstream.cc +++ b/src/core/lib/iomgr/tcp_client_cfstream.cc @@ -149,7 +149,7 @@ static void ParseResolvedAddress(const grpc_resolved_address* addr, static int64_t CFStreamClientConnect( grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, + grpc_pollset_set* /*interested_parties*/, const grpc_event_engine::experimental::EndpointConfig& /*config*/, const grpc_resolved_address* resolved_addr, grpc_core::Timestamp deadline) { auto addr_uri = grpc_sockaddr_to_uri(resolved_addr); From a3177c097b2137f64affc8ab258e062a982e532c Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Mon, 27 Feb 2023 09:34:16 -0800 Subject: [PATCH 32/39] change event engine endpoing read/write return type --- src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc | 8 ++++++-- src/core/lib/event_engine/cf_engine/cfstream_endpoint.h | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc index 29f3d97c695a5..0f9a21412c105 100644 --- a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc @@ -230,7 +230,7 @@ CFStreamEndpoint::~CFStreamEndpoint() { write_event_.DestroyEvent(); } -void CFStreamEndpoint::Read(absl::AnyInvocable on_read, +bool CFStreamEndpoint::Read(absl::AnyInvocable on_read, SliceBuffer* buffer, const ReadArgs* /* args */) { read_event_.NotifyOn(new PosixEngineClosure( [this, on_read = std::move(on_read), @@ -242,6 +242,8 @@ void CFStreamEndpoint::Read(absl::AnyInvocable on_read, } }, false /* is_permanent*/)); + + return false; } void CFStreamEndpoint::DoRead(absl::AnyInvocable on_read, @@ -280,7 +282,7 @@ void CFStreamEndpoint::DoRead(absl::AnyInvocable on_read, on_read(absl::OkStatus()); } -void CFStreamEndpoint::Write(absl::AnyInvocable on_writable, +bool CFStreamEndpoint::Write(absl::AnyInvocable on_writable, SliceBuffer* data, const WriteArgs* /* args */) { write_event_.NotifyOn(new PosixEngineClosure( [this, on_writable = std::move(on_writable), @@ -292,6 +294,8 @@ void CFStreamEndpoint::Write(absl::AnyInvocable on_writable, } }, false /* is_permanent*/)); + + return false; } void CFStreamEndpoint::DoWrite( diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h index 7deb68f2a9057..1a6a40224099c 100644 --- a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h @@ -39,9 +39,9 @@ class CFStreamEndpoint : public EventEngine::Endpoint { MemoryAllocator memory_allocator); ~CFStreamEndpoint() override; - void Read(absl::AnyInvocable on_read, SliceBuffer* buffer, + bool Read(absl::AnyInvocable on_read, SliceBuffer* buffer, const ReadArgs* args) override; - void Write(absl::AnyInvocable on_writable, + bool Write(absl::AnyInvocable on_writable, SliceBuffer* data, const WriteArgs* args) override; const EventEngine::ResolvedAddress& GetPeerAddress() const override { From 0503b2d92081b13fd3d94a5becec31b9b1489070 Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Sun, 12 Mar 2023 13:21:28 -0700 Subject: [PATCH 33/39] enable cf_engine in ios --- src/core/BUILD | 3 --- .../event_engine/default_event_engine_factory.cc | 2 +- src/core/lib/iomgr/tcp_client_cfstream.cc | 15 +++++++++++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/core/BUILD b/src/core/BUILD index fd06f2af20cf3..2bbd53d6be66b 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -2165,9 +2165,6 @@ grpc_cc_library( "//:windows": ["windows_event_engine"], "//:windows_msvc": ["windows_event_engine"], "//:windows_other": ["windows_event_engine"], - "//:mac": ["cf_event_engine"], - "//:mac_x86_64": ["cf_event_engine"], - "//:mac_arm64": ["cf_event_engine"], "//:ios": ["cf_event_engine"], "//:tvos": ["cf_event_engine"], "//:watchos": ["cf_event_engine"], diff --git a/src/core/lib/event_engine/default_event_engine_factory.cc b/src/core/lib/event_engine/default_event_engine_factory.cc index b1cf0d58147b9..10bfe898650f7 100644 --- a/src/core/lib/event_engine/default_event_engine_factory.cc +++ b/src/core/lib/event_engine/default_event_engine_factory.cc @@ -32,7 +32,7 @@ std::unique_ptr DefaultEventEngineFactory() { } // namespace experimental } // namespace grpc_event_engine -#elif defined(GPR_APPLE) +#elif defined(GRPC_CFSTREAM) #include "src/core/lib/event_engine/cf_engine/cf_engine.h" namespace grpc_event_engine { diff --git a/src/core/lib/iomgr/tcp_client_cfstream.cc b/src/core/lib/iomgr/tcp_client_cfstream.cc index e53d9f61890ed..09e7c5d799ab5 100644 --- a/src/core/lib/iomgr/tcp_client_cfstream.cc +++ b/src/core/lib/iomgr/tcp_client_cfstream.cc @@ -33,6 +33,7 @@ #include #include "src/core/lib/address_utils/sockaddr_utils.h" +#include "src/core/lib/event_engine/shim.h" #include "src/core/lib/gprpp/crash.h" #include "src/core/lib/gprpp/host_port.h" #include "src/core/lib/iomgr/cfstream_handle.h" @@ -40,6 +41,7 @@ #include "src/core/lib/iomgr/endpoint_cfstream.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/iomgr/error_cfstream.h" +#include "src/core/lib/iomgr/event_engine_shims/tcp_client.h" #include "src/core/lib/iomgr/tcp_client.h" #include "src/core/lib/iomgr/timer.h" @@ -150,8 +152,13 @@ static void ParseResolvedAddress(const grpc_resolved_address* addr, static int64_t CFStreamClientConnect( grpc_closure* closure, grpc_endpoint** ep, grpc_pollset_set* /*interested_parties*/, - const grpc_event_engine::experimental::EndpointConfig& /*config*/, + const grpc_event_engine::experimental::EndpointConfig& config, const grpc_resolved_address* resolved_addr, grpc_core::Timestamp deadline) { + if (grpc_event_engine::experimental::UseEventEngineClient()) { + return grpc_event_engine::experimental::event_engine_tcp_client_connect( + closure, ep, config, resolved_addr, deadline); + } + auto addr_uri = grpc_sockaddr_to_uri(resolved_addr); if (!addr_uri.ok()) { grpc_error_handle error = GRPC_ERROR_CREATE(addr_uri.status().ToString()); @@ -198,7 +205,11 @@ static int64_t CFStreamClientConnect( return 0; } -static bool CFStreamClientCancelConnect(int64_t /*connection_handle*/) { +static bool CFStreamClientCancelConnect(int64_t connection_handle) { + if (grpc_event_engine::experimental::UseEventEngineClient()) { + return grpc_event_engine::experimental:: + event_engine_tcp_client_cancel_connect(connection_handle); + } return false; } From 076e6387f19e112380cede30784df362df5ccab1 Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Wed, 15 Mar 2023 14:45:19 -0700 Subject: [PATCH 34/39] add cf event engine test on connection timeout and cancel connection --- test/core/event_engine/cf/BUILD | 33 +++++++ test/core/event_engine/cf/cf_engine_test.cc | 96 +++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 test/core/event_engine/cf/BUILD create mode 100644 test/core/event_engine/cf/cf_engine_test.cc diff --git a/test/core/event_engine/cf/BUILD b/test/core/event_engine/cf/BUILD new file mode 100644 index 0000000000000..0c9aec0869937 --- /dev/null +++ b/test/core/event_engine/cf/BUILD @@ -0,0 +1,33 @@ +# Copyright 2023 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//bazel:grpc_build_system.bzl", "grpc_cc_test") + +licenses(["notice"]) + +grpc_cc_test( + name = "cf_engine_test", + timeout = "short", + srcs = ["cf_engine_test.cc"], + external_deps = ["gtest"], + language = "C++", + tags = [ + "no_linux", + "no_windows", + ], + deps = [ + "//src/core:cf_event_engine", + "//test/core/util:grpc_test_util", + ], +) diff --git a/test/core/event_engine/cf/cf_engine_test.cc b/test/core/event_engine/cf/cf_engine_test.cc new file mode 100644 index 0000000000000..23679f60bb194 --- /dev/null +++ b/test/core/event_engine/cf/cf_engine_test.cc @@ -0,0 +1,96 @@ +// Copyright 2023 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#ifdef GPR_APPLE + +#include + +#include "absl/status/status.h" +#include "gtest/gtest.h" + +#include +#include + +#include "src/core/lib/event_engine/cf_engine/cf_engine.h" +#include "src/core/lib/event_engine/channel_args_endpoint_config.h" +#include "src/core/lib/event_engine/tcp_socket_utils.h" +#include "src/core/lib/resource_quota/memory_quota.h" +#include "src/core/lib/resource_quota/resource_quota.h" +#include "test/core/util/port.h" + +using namespace std::chrono_literals; + +namespace grpc_event_engine { +namespace experimental { + +TEST(CFEventEngineTest, TestConnectionTimeout) { + // use a non-routable IP so connection will timeout + auto resolved_addr = URIToResolvedAddress("ipv4:10.255.255.255:1234"); + GPR_ASSERT(resolved_addr.ok()); + + grpc_core::MemoryQuota memory_quota("cf_engine_test"); + grpc_core::Notification client_signal; + auto cf_engine = std::make_shared(); + + ChannelArgsEndpointConfig config(grpc_core::ChannelArgs().Set( + GRPC_ARG_RESOURCE_QUOTA, grpc_core::ResourceQuota::Default())); + cf_engine->Connect( + [&client_signal](auto endpoint) { + EXPECT_EQ(endpoint.status().code(), + absl::StatusCode::kDeadlineExceeded); + client_signal.Notify(); + }, + *resolved_addr, config, memory_quota.CreateMemoryAllocator("conn1"), 1ms); + + client_signal.WaitForNotification(); +} + +TEST(CFEventEngineTest, TestConnectionCancelled) { + // use a non-routable IP so to cancel connection before timeout + auto resolved_addr = URIToResolvedAddress("ipv4:10.255.255.255:1234"); + GPR_ASSERT(resolved_addr.ok()); + + grpc_core::MemoryQuota memory_quota("cf_engine_test"); + grpc_core::Notification client_signal; + auto cf_engine = std::make_shared(); + + ChannelArgsEndpointConfig config(grpc_core::ChannelArgs().Set( + GRPC_ARG_RESOURCE_QUOTA, grpc_core::ResourceQuota::Default())); + auto conn_handle = cf_engine->Connect( + [&client_signal](auto endpoint) { + EXPECT_EQ(endpoint.status().code(), absl::StatusCode::kCancelled); + client_signal.Notify(); + }, + *resolved_addr, config, memory_quota.CreateMemoryAllocator("conn1"), 1h); + + cf_engine->CancelConnect(conn_handle); + client_signal.WaitForNotification(); +} + +} // namespace experimental +} // namespace grpc_event_engine + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + grpc_init(); + int status = RUN_ALL_TESTS(); + grpc_shutdown(); + return status; +} + +#else // not GPR_APPLE +int main(int /* argc */, char** /* argv */) { return 0; } +#endif \ No newline at end of file From 2212e915766cb124677cc975c07a66079731f2cc Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Thu, 30 Mar 2023 16:29:51 -0700 Subject: [PATCH 35/39] address review comments --- src/core/lib/event_engine/cf_engine/cf_engine.cc | 12 ++++++------ src/core/lib/event_engine/cf_engine/cf_engine.h | 6 +++--- .../lib/event_engine/cf_engine/cfstream_endpoint.cc | 12 +++++++----- .../lib/event_engine/cf_engine/cfstream_endpoint.h | 2 +- .../lib/event_engine/cf_engine/cftype_unique_ref.h | 5 ++++- test/core/event_engine/cf/cf_engine_test.cc | 2 +- test/core/event_engine/test_suite/BUILD | 2 +- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/core/lib/event_engine/cf_engine/cf_engine.cc b/src/core/lib/event_engine/cf_engine/cf_engine.cc index 57ad268a910de..11d933e9223ac 100644 --- a/src/core/lib/event_engine/cf_engine/cf_engine.cc +++ b/src/core/lib/event_engine/cf_engine/cf_engine.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The gRPC Authors +// Copyright 2023 The gRPC Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ struct CFEventEngine::Closure final : public EventEngine::Closure { GRPC_EVENT_ENGINE_TRACE("CFEventEngine:%p executing callback:%s", engine, HandleToString(handle).c_str()); { - grpc_core::MutexLock lock(&engine->mu_); + grpc_core::MutexLock lock(&engine->task_mu_); engine->known_handles_.erase(handle); } cb(); @@ -52,7 +52,7 @@ CFEventEngine::CFEventEngine() CFEventEngine::~CFEventEngine() { { - grpc_core::MutexLock lock(&mu_); + grpc_core::MutexLock lock(&task_mu_); if (GRPC_TRACE_FLAG_ENABLED(grpc_event_engine_trace)) { for (auto handle : known_handles_) { gpr_log(GPR_ERROR, @@ -98,7 +98,7 @@ CFEventEngine::ConnectionHandle CFEventEngine::Connect( auto on_connect2 = [that = std::static_pointer_cast(shared_from_this()), - deadline_timer = std::move(deadline_timer), handle, + deadline_timer, handle, on_connect = std::move(on_connect)](absl::Status status) mutable { // best effort canceling deadline timer that->Cancel(deadline_timer); @@ -174,7 +174,7 @@ EventEngine::TaskHandle CFEventEngine::RunAfter( } bool CFEventEngine::Cancel(TaskHandle handle) { - grpc_core::MutexLock lock(&mu_); + grpc_core::MutexLock lock(&task_mu_); if (!known_handles_.contains(handle)) return false; auto* cd = reinterpret_cast(handle.keys[0]); bool r = timer_manager_.TimerCancel(&cd->timer); @@ -191,7 +191,7 @@ EventEngine::TaskHandle CFEventEngine::RunAfterInternal( cd->engine = this; EventEngine::TaskHandle handle{reinterpret_cast(cd), aba_token_.fetch_add(1)}; - grpc_core::MutexLock lock(&mu_); + grpc_core::MutexLock lock(&task_mu_); known_handles_.insert(handle); cd->handle = handle; GRPC_EVENT_ENGINE_TRACE("CFEventEngine:%p scheduling callback:%s", this, diff --git a/src/core/lib/event_engine/cf_engine/cf_engine.h b/src/core/lib/event_engine/cf_engine/cf_engine.h index 3595484ed4588..57aaa698afdd1 100644 --- a/src/core/lib/event_engine/cf_engine/cf_engine.h +++ b/src/core/lib/event_engine/cf_engine/cf_engine.h @@ -1,4 +1,4 @@ -// Copyright 2022 The gRPC Authors +// Copyright 2023 The gRPC Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -67,8 +67,8 @@ class CFEventEngine : public EventEngine, bool CancelConnectInternal(ConnectionHandle handle, absl::Status status); - grpc_core::Mutex mu_; - TaskHandleSet known_handles_ ABSL_GUARDED_BY(mu_); + grpc_core::Mutex task_mu_; + TaskHandleSet known_handles_ ABSL_GUARDED_BY(task_mu_); std::atomic aba_token_{0}; grpc_core::Mutex conn_mu_; diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc index 0f9a21412c105..b036b1d2d2a02 100644 --- a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The gRPC Authors +// Copyright 2023 The gRPC Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,6 +25,8 @@ namespace experimental { namespace { +int kDefaultReadBufferSize = 8192; + absl::Status CFErrorToStatus(CFTypeUniqueRef cf_error) { CFErrorDomain cf_domain = CFErrorGetDomain((cf_error)); CFIndex code = CFErrorGetCode((cf_error)); @@ -152,6 +154,7 @@ void CFStreamEndpoint::Connect( switch (type) { case kCFStreamEventOpenCompleted: + // wait for write stream open completed to signal connection ready break; case kCFStreamEventHasBytesAvailable: ABSL_FALLTHROUGH_INTENDED; @@ -250,15 +253,14 @@ void CFStreamEndpoint::DoRead(absl::AnyInvocable on_read, SliceBuffer* buffer) { GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpoint::DoRead, this: %p", this); - int buffer_size = 8192; - auto buffer_index = - buffer->AppendIndexed(Slice(memory_allocator_.MakeSlice(buffer_size))); + auto buffer_index = buffer->AppendIndexed( + Slice(memory_allocator_.MakeSlice(kDefaultReadBufferSize))); CFIndex read_size = CFReadStreamRead( cf_read_stream_, internal::SliceCast(buffer->MutableSliceAt(buffer_index)) .begin(), - buffer_size); + kDefaultReadBufferSize); if (read_size < 0) { auto status = CFErrorToStatus(CFReadStreamCopyError(cf_read_stream_)); diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h index 1a6a40224099c..aa5dc878ba2cc 100644 --- a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h @@ -1,4 +1,4 @@ -// Copyright 2022 The gRPC Authors +// Copyright 2023 The gRPC Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/core/lib/event_engine/cf_engine/cftype_unique_ref.h b/src/core/lib/event_engine/cf_engine/cftype_unique_ref.h index 7474906fb0e75..c7915170f6220 100644 --- a/src/core/lib/event_engine/cf_engine/cftype_unique_ref.h +++ b/src/core/lib/event_engine/cf_engine/cftype_unique_ref.h @@ -1,4 +1,4 @@ -// Copyright 2022 The gRPC Authors +// Copyright 2023 The gRPC Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -57,6 +57,9 @@ class CFTypeUniqueRef { } void reset(T other = nullptr) { + if (cf_type_ref_ == other) { + return; + } T old = cf_type_ref_; cf_type_ref_ = other; if (old) { diff --git a/test/core/event_engine/cf/cf_engine_test.cc b/test/core/event_engine/cf/cf_engine_test.cc index 23679f60bb194..f50b40f82fa76 100644 --- a/test/core/event_engine/cf/cf_engine_test.cc +++ b/test/core/event_engine/cf/cf_engine_test.cc @@ -93,4 +93,4 @@ int main(int argc, char** argv) { #else // not GPR_APPLE int main(int /* argc */, char** /* argv */) { return 0; } -#endif \ No newline at end of file +#endif diff --git a/test/core/event_engine/test_suite/BUILD b/test/core/event_engine/test_suite/BUILD index f70302c4c965f..8e8592896cf19 100644 --- a/test/core/event_engine/test_suite/BUILD +++ b/test/core/event_engine/test_suite/BUILD @@ -99,7 +99,7 @@ grpc_cc_test( "no_linux", "no_windows", ], - uses_polling = False, + uses_polling = True, deps = [ "//src/core:cf_event_engine", "//test/core/event_engine/test_suite/posix:oracle_event_engine_posix", From 6b2e00ffae05cca88efa9477fbde285ead198888 Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Thu, 6 Apr 2023 20:48:04 -0700 Subject: [PATCH 36/39] use ref count for CFStreamEndpointImpl to keep it alive for callbacks --- .../cf_engine/cfstream_endpoint.cc | 139 ++++++++++-------- .../cf_engine/cfstream_endpoint.h | 69 ++++++++- 2 files changed, 135 insertions(+), 73 deletions(-) diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc index b036b1d2d2a02..f77a626d7f533 100644 --- a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.cc @@ -28,6 +28,9 @@ namespace { int kDefaultReadBufferSize = 8192; absl::Status CFErrorToStatus(CFTypeUniqueRef cf_error) { + if (cf_error == nullptr) { + return absl::OkStatus(); + } CFErrorDomain cf_domain = CFErrorGetDomain((cf_error)); CFIndex code = CFErrorGetCode((cf_error)); CFTypeUniqueRef cf_desc = CFErrorCopyDescription((cf_error)); @@ -58,11 +61,15 @@ absl::StatusOr CFReadStreamLocallAddress( } // namespace -bool CFStreamEndpoint::CancelConnect(absl::Status status) { +bool CFStreamEndpointImpl::CancelConnect(absl::Status status) { + GRPC_EVENT_ENGINE_ENDPOINT_TRACE( + "CFStreamEndpointImpl::CancelConnect: status: %s, this: %p", + status.ToString().c_str(), this); + return open_event_.SetShutdown(std::move(status)); } -void CFStreamEndpoint::Connect( +void CFStreamEndpointImpl::Connect( absl::AnyInvocable on_connect, EventEngine::ResolvedAddress addr) { auto addr_uri = ResolvedAddressToURI(addr); @@ -72,7 +79,7 @@ void CFStreamEndpoint::Connect( return; } - GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpoint::Connect: %s", + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpointImpl::Connect: %s", addr_uri.value().c_str()); peer_address_ = std::move(addr); @@ -83,8 +90,8 @@ void CFStreamEndpoint::Connect( } peer_address_string_ = host_port.value(); - GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpoint::Connect, host_port: %s", - host_port->c_str()); + GRPC_EVENT_ENGINE_ENDPOINT_TRACE( + "CFStreamEndpointImpl::Connect, host_port: %s", host_port->c_str()); std::string host_string; std::string port_string; @@ -95,8 +102,7 @@ void CFStreamEndpoint::Connect( CFStreamCreatePairWithSocketToHost(NULL, host, port, &cf_read_stream_, &cf_write_stream_); - CFStreamClientContext cf_context = {0, static_cast(this), nullptr, - nullptr, nullptr}; + CFStreamClientContext cf_context = {0, this, Retain, Release, nullptr}; CFReadStreamSetClient( cf_read_stream_, kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | @@ -125,32 +131,34 @@ void CFStreamEndpoint::Connect( } open_event_.NotifyOn(new PosixEngineClosure( - [this, on_connect = std::move(on_connect)](absl::Status status) mutable { + [that = Ref(), + on_connect = std::move(on_connect)](absl::Status status) mutable { if (!status.ok()) { on_connect(std::move(status)); return; } - auto local_addr = CFReadStreamLocallAddress(cf_read_stream_); + auto local_addr = CFReadStreamLocallAddress(that->cf_read_stream_); if (!local_addr.ok()) { on_connect(std::move(local_addr).status()); return; } - local_address_ = local_addr.value(); - local_address_string_ = *ResolvedAddressToURI(local_address_); + that->local_address_ = local_addr.value(); + that->local_address_string_ = + *ResolvedAddressToURI(that->local_address_); on_connect(absl::OkStatus()); }, false /* is_permanent */)); } -/* static */ void CFStreamEndpoint::ReadCallback(CFReadStreamRef stream, - CFStreamEventType type, - void* client_callback_info) { - auto self = static_cast(client_callback_info); +/* static */ void CFStreamEndpointImpl::ReadCallback( + CFReadStreamRef stream, CFStreamEventType type, + void* client_callback_info) { + auto self = static_cast(client_callback_info); GRPC_EVENT_ENGINE_ENDPOINT_TRACE( - "CFStreamEndpoint::ReadCallback, type: %lu, this: %p", type, self); + "CFStreamEndpointImpl::ReadCallback, type: %lu, this: %p", type, self); switch (type) { case kCFStreamEventOpenCompleted: @@ -176,12 +184,12 @@ void CFStreamEndpoint::Connect( } /* static */ -void CFStreamEndpoint::WriteCallback(CFWriteStreamRef stream, - CFStreamEventType type, - void* client_callback_info) { - auto self = static_cast(client_callback_info); +void CFStreamEndpointImpl::WriteCallback(CFWriteStreamRef stream, + CFStreamEventType type, + void* client_callback_info) { + auto self = static_cast(client_callback_info); GRPC_EVENT_ENGINE_ENDPOINT_TRACE( - "CFStreamEndpoint::WriteCallback, type: %lu, this: %p", type, self); + "CFStreamEndpointImpl::WriteCallback, type: %lu, this: %p", type, self); switch (type) { case kCFStreamEventOpenCompleted: @@ -206,8 +214,8 @@ void CFStreamEndpoint::WriteCallback(CFWriteStreamRef stream, } } -CFStreamEndpoint::CFStreamEndpoint(std::shared_ptr engine, - MemoryAllocator memory_allocator) +CFStreamEndpointImpl::CFStreamEndpointImpl( + std::shared_ptr engine, MemoryAllocator memory_allocator) : engine_(std::move(engine)), memory_allocator_(std::move(memory_allocator)), open_event_(engine_.get()), @@ -218,28 +226,41 @@ CFStreamEndpoint::CFStreamEndpoint(std::shared_ptr engine, write_event_.InitEvent(); } -CFStreamEndpoint::~CFStreamEndpoint() { - CFReadStreamClose(cf_read_stream_); - CFWriteStreamClose(cf_write_stream_); +CFStreamEndpointImpl::~CFStreamEndpointImpl() { + open_event_.DestroyEvent(); + read_event_.DestroyEvent(); + write_event_.DestroyEvent(); +} + +void CFStreamEndpointImpl::Shutdown() { + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpointImpl::Shutdown: this: %p", + this); auto shutdownStatus = absl::Status(absl::StatusCode::kUnknown, - absl::StrFormat("Shutting down CFStreamEndpoint")); + absl::StrFormat("Shutting down CFStreamEndpointImpl")); open_event_.SetShutdown(shutdownStatus); read_event_.SetShutdown(shutdownStatus); write_event_.SetShutdown(shutdownStatus); - open_event_.DestroyEvent(); - read_event_.DestroyEvent(); - write_event_.DestroyEvent(); + + CFReadStreamSetClient(cf_read_stream_, kCFStreamEventNone, nullptr, nullptr); + CFWriteStreamSetClient(cf_write_stream_, kCFStreamEventNone, nullptr, + nullptr); + CFReadStreamClose(cf_read_stream_); + CFWriteStreamClose(cf_write_stream_); } -bool CFStreamEndpoint::Read(absl::AnyInvocable on_read, - SliceBuffer* buffer, const ReadArgs* /* args */) { +bool CFStreamEndpointImpl::Read( + absl::AnyInvocable on_read, SliceBuffer* buffer, + const EventEngine::Endpoint::ReadArgs* /* args */) { + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpointImpl::Read, this: %p", + this); + read_event_.NotifyOn(new PosixEngineClosure( - [this, on_read = std::move(on_read), + [that = Ref(), on_read = std::move(on_read), buffer](absl::Status status) mutable { if (status.ok()) { - DoRead(std::move(on_read), buffer); + that->DoRead(std::move(on_read), buffer); } else { on_read(status); } @@ -249,9 +270,10 @@ bool CFStreamEndpoint::Read(absl::AnyInvocable on_read, return false; } -void CFStreamEndpoint::DoRead(absl::AnyInvocable on_read, - SliceBuffer* buffer) { - GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpoint::DoRead, this: %p", this); +void CFStreamEndpointImpl::DoRead( + absl::AnyInvocable on_read, SliceBuffer* buffer) { + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpointImpl::DoRead, this: %p", + this); auto buffer_index = buffer->AppendIndexed( Slice(memory_allocator_.MakeSlice(kDefaultReadBufferSize))); @@ -264,33 +286,27 @@ void CFStreamEndpoint::DoRead(absl::AnyInvocable on_read, if (read_size < 0) { auto status = CFErrorToStatus(CFReadStreamCopyError(cf_read_stream_)); - GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStream read error: %s", - status.ToString().c_str()); + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStream read error: %s, read_size: %ld", + status.ToString().c_str(), read_size); on_read(status); return; } buffer->RemoveLastNBytes(buffer->Length() - read_size); - - if (grpc_event_engine_endpoint_data_trace.enabled()) { - for (size_t i = 0; i < buffer->Count(); i++) { - auto str = buffer->RefSlice(i).as_string_view(); - gpr_log(GPR_INFO, "CFStreamEndpoint::DoRead, this: %p (peer=%s): %.*s", - this, peer_address_string_.c_str(), - static_cast(str.length()), str.data()); - } - } - on_read(absl::OkStatus()); } -bool CFStreamEndpoint::Write(absl::AnyInvocable on_writable, - SliceBuffer* data, const WriteArgs* /* args */) { +bool CFStreamEndpointImpl::Write( + absl::AnyInvocable on_writable, SliceBuffer* data, + const EventEngine::Endpoint::WriteArgs* /* args */) { + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpointImpl::Write, this: %p", + this); + write_event_.NotifyOn(new PosixEngineClosure( - [this, on_writable = std::move(on_writable), + [that = Ref(), on_writable = std::move(on_writable), data](absl::Status status) mutable { if (status.ok()) { - DoWrite(std::move(on_writable), data); + that->DoWrite(std::move(on_writable), data); } else { on_writable(status); } @@ -300,17 +316,10 @@ bool CFStreamEndpoint::Write(absl::AnyInvocable on_writable, return false; } -void CFStreamEndpoint::DoWrite( +void CFStreamEndpointImpl::DoWrite( absl::AnyInvocable on_writable, SliceBuffer* data) { - GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpoint::DoWrite, this: %p", this); - if (grpc_event_engine_endpoint_data_trace.enabled()) { - for (size_t i = 0; i < data->Count(); i++) { - auto str = data->RefSlice(i).as_string_view(); - gpr_log(GPR_INFO, "CFStreamEndpoint::DoWrite, this: %p (peer=%s): %.*s", - this, peer_address_string_.c_str(), - static_cast(str.length()), str.data()); - } - } + GRPC_EVENT_ENGINE_ENDPOINT_TRACE("CFStreamEndpointImpl::DoWrite, this: %p", + this); size_t total_written_size = 0; for (size_t i = 0; i < data->Count(); i++) { @@ -324,10 +333,10 @@ void CFStreamEndpoint::DoWrite( data->MoveFirstNBytesIntoSliceBuffer(total_written_size, written); write_event_.NotifyOn(new PosixEngineClosure( - [this, on_writable = std::move(on_writable), + [that = Ref(), on_writable = std::move(on_writable), data](absl::Status status) mutable { if (status.ok()) { - DoWrite(std::move(on_writable), data); + that->DoWrite(std::move(on_writable), data); } else { on_writable(status); } diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h index aa5dc878ba2cc..e8a4a8b1f0edb 100644 --- a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h @@ -29,25 +29,30 @@ #include "src/core/lib/event_engine/posix_engine/lockfree_event.h" #include "src/core/lib/event_engine/tcp_socket_utils.h" #include "src/core/lib/gprpp/host_port.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" namespace grpc_event_engine { namespace experimental { -class CFStreamEndpoint : public EventEngine::Endpoint { +class CFStreamEndpointImpl + : public grpc_core::RefCounted { public: - CFStreamEndpoint(std::shared_ptr engine, - MemoryAllocator memory_allocator); - ~CFStreamEndpoint() override; + CFStreamEndpointImpl(std::shared_ptr engine, + MemoryAllocator memory_allocator); + ~CFStreamEndpointImpl(); + + void Shutdown(); bool Read(absl::AnyInvocable on_read, SliceBuffer* buffer, - const ReadArgs* args) override; + const EventEngine::Endpoint::ReadArgs* args); bool Write(absl::AnyInvocable on_writable, - SliceBuffer* data, const WriteArgs* args) override; + SliceBuffer* data, const EventEngine::Endpoint::WriteArgs* args); - const EventEngine::ResolvedAddress& GetPeerAddress() const override { + const EventEngine::ResolvedAddress& GetPeerAddress() const { return peer_address_; } - const EventEngine::ResolvedAddress& GetLocalAddress() const override { + const EventEngine::ResolvedAddress& GetLocalAddress() const { return local_address_; } @@ -63,6 +68,16 @@ class CFStreamEndpoint : public EventEngine::Endpoint { SliceBuffer* buffer); private: + static void* Retain(void* info) { + auto that = static_cast(info); + return that->Ref().release(); + } + + static void Release(void* info) { + auto that = static_cast(info); + that->Unref(); + } + static void ReadCallback(CFReadStreamRef stream, CFStreamEventType type, void* client_callback_info); static void WriteCallback(CFWriteStreamRef stream, CFStreamEventType type, @@ -85,6 +100,44 @@ class CFStreamEndpoint : public EventEngine::Endpoint { LockfreeEvent write_event_; }; +class CFStreamEndpoint : public EventEngine::Endpoint { + public: + CFStreamEndpoint(std::shared_ptr engine, + MemoryAllocator memory_allocator) { + impl_ = grpc_core::MakeRefCounted( + std::move(engine), std::move(memory_allocator)); + } + ~CFStreamEndpoint() override { impl_->Shutdown(); } + + bool Read(absl::AnyInvocable on_read, SliceBuffer* buffer, + const ReadArgs* args) override { + return impl_->Read(std::move(on_read), buffer, args); + } + bool Write(absl::AnyInvocable on_writable, + SliceBuffer* data, const WriteArgs* args) override { + return impl_->Write(std::move(on_writable), data, args); + } + + const EventEngine::ResolvedAddress& GetPeerAddress() const override { + return impl_->GetPeerAddress(); + } + const EventEngine::ResolvedAddress& GetLocalAddress() const override { + return impl_->GetLocalAddress(); + } + + public: + void Connect(absl::AnyInvocable on_connect, + EventEngine::ResolvedAddress addr) { + impl_->Connect(std::move(on_connect), std::move(addr)); + } + bool CancelConnect(absl::Status status) { + return impl_->CancelConnect(std::move(status)); + } + + private: + grpc_core::RefCountedPtr impl_; +}; + } // namespace experimental } // namespace grpc_event_engine From e319495bb287ece0ca57a8645428a16b69cae483 Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Thu, 6 Apr 2023 20:49:20 -0700 Subject: [PATCH 37/39] generate projects --- CMakeLists.txt | 42 ++++++++++++++++++++++++++++ build_autogenerated.yaml | 13 +++++++++ src/core/BUILD | 2 ++ tools/run_tests/generated/tests.json | 22 +++++++++++++++ 4 files changed, 79 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 293e569293b7f..64d7ff97a35b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -863,6 +863,9 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx cel_authorization_engine_test) add_dependencies(buildtests_cxx certificate_provider_registry_test) add_dependencies(buildtests_cxx certificate_provider_store_test) + if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + add_dependencies(buildtests_cxx cf_engine_test) + endif() if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx cf_event_engine_test) endif() @@ -7416,6 +7419,45 @@ target_link_libraries(certificate_provider_store_test ) +endif() +if(gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + + add_executable(cf_engine_test + test/core/event_engine/cf/cf_engine_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc + ) + target_compile_features(cf_engine_test PUBLIC cxx_std_14) + target_include_directories(cf_engine_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(cf_engine_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util + ) + + +endif() endif() if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 2b9f242f3419a..f12e2c7a0b4d1 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -5398,6 +5398,19 @@ targets: - test/core/xds/certificate_provider_store_test.cc deps: - grpc_test_util +- name: cf_engine_test + gtest: true + build: test + language: c++ + headers: [] + src: + - test/core/event_engine/cf/cf_engine_test.cc + deps: + - grpc_test_util + platforms: + - linux + - posix + - mac - name: cf_event_engine_test gtest: true build: test diff --git a/src/core/BUILD b/src/core/BUILD index 2bbd53d6be66b..9f9062bbe39e2 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -2089,9 +2089,11 @@ grpc_cc_library( "posix_event_engine_event_poller", "posix_event_engine_lockfree_event", "posix_event_engine_timer_manager", + "ref_counted", "strerror", "//:event_engine_base_hdrs", "//:gpr", + "//:ref_counted_ptr", "//:sockaddr_utils", ], ) diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index ffa7d7ff65ed0..f4a07bbd72132 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -1345,6 +1345,28 @@ ], "uses_polling": true }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "cf_engine_test", + "platforms": [ + "linux", + "mac", + "posix" + ], + "uses_polling": true + }, { "args": [], "benchmark": false, From 6f7946fd8ac5f50de1b393ba168b06208bee1921 Mon Sep 17 00:00:00 2001 From: Hannah Shi Date: Mon, 3 Apr 2023 23:01:32 -0700 Subject: [PATCH 38/39] test cf event engine with ios --- src/objective-c/tests/run_one_test_bazel.sh | 5 +++++ tools/internal_ci/macos/grpc_objc_bazel_test.sh | 8 +++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/objective-c/tests/run_one_test_bazel.sh b/src/objective-c/tests/run_one_test_bazel.sh index 80f0133ac4581..e1882fb059a71 100755 --- a/src/objective-c/tests/run_one_test_bazel.sh +++ b/src/objective-c/tests/run_one_test_bazel.sh @@ -46,6 +46,11 @@ $INTEROP --port=$TLS_PORT --max_send_message_size=8388608 --use_tls & trap 'kill -9 `jobs -p` ; echo "EXIT TIME: $(date)"' EXIT time $BAZEL test --ios_multi_cpus=x86_64,sim_arm64 \ + --runs_per_test=500 \ + --config=dbg --cxxopt=-DGRPC_IOS_EVENT_ENGINE_CLIENT=1 \ + --test_output=errors --cache_test_results=no \ + --test_env GRPC_VERBOSITY=debug \ + --test_env GRPC_TRACE=all \ --test_env HOST_PORT_LOCALSSL=localhost:$TLS_PORT \ --test_env HOST_PORT_LOCAL=localhost:$PLAIN_PORT \ $SCHEME diff --git a/tools/internal_ci/macos/grpc_objc_bazel_test.sh b/tools/internal_ci/macos/grpc_objc_bazel_test.sh index a9b9e1c066bd2..05ab6300e6eea 100644 --- a/tools/internal_ci/macos/grpc_objc_bazel_test.sh +++ b/tools/internal_ci/macos/grpc_objc_bazel_test.sh @@ -56,10 +56,10 @@ TEST_TARGETS=( //src/objective-c/tests:InteropTestsRemote //src/objective-c/tests:MacTests //src/objective-c/tests:UnitTests - //src/objective-c/tests:CppCronetTests - //src/objective-c/tests:CronetTests + # //src/objective-c/tests:CppCronetTests + # //src/objective-c/tests:CronetTests //src/objective-c/tests:PerfTests - //src/objective-c/tests:CFStreamTests + # //src/objective-c/tests:CFStreamTests //src/objective-c/tests:tvtests_build_test # codegen plugin tests //src/objective-c/tests:objc_codegen_plugin_test @@ -113,6 +113,8 @@ objc_bazel_tests/bazel_wrapper \ --google_credentials="${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json" \ "${BAZEL_REMOTE_CACHE_ARGS[@]}" \ $BAZEL_FLAGS \ + --config=dbg --cxxopt=-DGRPC_IOS_EVENT_ENGINE_CLIENT=1 \ + --test_env=GRPC_VERBOSITY=debug --test_env=GRPC_TRACE=event_engine_endpoint,event_engine,api \ "${OBJC_TEST_ENV_ARGS[@]}" \ -- \ "${EXAMPLE_TARGETS[@]}" \ From 6781d091bf9ea2a2306b05724eccc125fbb13ac8 Mon Sep 17 00:00:00 2001 From: HannahShiSFB Date: Fri, 7 Apr 2023 04:37:46 +0000 Subject: [PATCH 39/39] Automated change: Fix sanity tests --- .../event_engine/cf_engine/cfstream_endpoint.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h index e8a4a8b1f0edb..832efda4412e2 100644 --- a/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h +++ b/src/core/lib/event_engine/cf_engine/cfstream_endpoint.h @@ -104,34 +104,34 @@ class CFStreamEndpoint : public EventEngine::Endpoint { public: CFStreamEndpoint(std::shared_ptr engine, MemoryAllocator memory_allocator) { - impl_ = grpc_core::MakeRefCounted( - std::move(engine), std::move(memory_allocator)); + impl_ = grpc_core::MakeRefCounted( + std::move(engine), std::move(memory_allocator)); } ~CFStreamEndpoint() override { impl_->Shutdown(); } bool Read(absl::AnyInvocable on_read, SliceBuffer* buffer, const ReadArgs* args) override { - return impl_->Read(std::move(on_read), buffer, args); + return impl_->Read(std::move(on_read), buffer, args); } bool Write(absl::AnyInvocable on_writable, SliceBuffer* data, const WriteArgs* args) override { - return impl_->Write(std::move(on_writable), data, args); + return impl_->Write(std::move(on_writable), data, args); } const EventEngine::ResolvedAddress& GetPeerAddress() const override { - return impl_->GetPeerAddress(); + return impl_->GetPeerAddress(); } const EventEngine::ResolvedAddress& GetLocalAddress() const override { - return impl_->GetLocalAddress(); + return impl_->GetLocalAddress(); } public: void Connect(absl::AnyInvocable on_connect, EventEngine::ResolvedAddress addr) { - impl_->Connect(std::move(on_connect), std::move(addr)); + impl_->Connect(std::move(on_connect), std::move(addr)); } bool CancelConnect(absl::Status status) { - return impl_->CancelConnect(std::move(status)); + return impl_->CancelConnect(std::move(status)); } private: