Imported Upstream version 1.39.0 upstream/1.39.0
authorJinWang An <jinwang.an@samsung.com>
Wed, 1 Dec 2021 08:53:22 +0000 (17:53 +0900)
committerJinWang An <jinwang.an@samsung.com>
Wed, 1 Dec 2021 08:53:22 +0000 (17:53 +0900)
519 files changed:
.github/ISSUE_TEMPLATE/bug_report.md
.github/ISSUE_TEMPLATE/cleanup_request.md
.github/ISSUE_TEMPLATE/feature_request.md
.github/ISSUE_TEMPLATE/question.md
.github/pull_request_template.md
BUILD
BUILD.gn
CMakeLists.txt
MAINTAINERS.md
Makefile
PYTHON-MANIFEST.in
Package.swift
README.md
_metadata.py [moved from src/proto/grpc/auth/v1/BUILD with 68% similarity]
bazel/cc_grpc_library.bzl
bazel/cython_library.bzl
bazel/generate_cc.bzl
bazel/generate_objc.bzl
bazel/grpc_build_system.bzl
bazel/grpc_deps.bzl
bazel/grpc_extra_deps.bzl
bazel/grpc_python_deps.bzl
bazel/grpc_util.bzl
bazel/objc_grpc_library.bzl
bazel/protobuf.bzl
bazel/python_rules.bzl
build_autogenerated.yaml
build_config.rb
build_handwritten.yaml
config.m4
config.w32
doc/environment_variables.md
doc/g_stands_for.md
doc/grpc_xds_features.md
doc/xds-test-descriptions.md
examples/cpp/helloworld/BUILD
examples/cpp/helloworld/CMakeLists.txt
examples/cpp/helloworld/Makefile
examples/cpp/helloworld/greeter_callback_client.cc [new file with mode: 0644]
examples/cpp/helloworld/greeter_callback_server.cc [new file with mode: 0644]
examples/cpp/route_guide/BUILD
examples/cpp/route_guide/route_guide_callback_client.cc [new file with mode: 0644]
examples/cpp/route_guide/route_guide_callback_server.cc [new file with mode: 0644]
gRPC-C++.podspec
gRPC-Core.podspec
gRPC-ProtoRPC.podspec
gRPC-RxLibrary.podspec
gRPC.podspec
grpc.def
grpc.gemspec
grpc.gyp
include/grpc/event_engine/endpoint_config.h [new file with mode: 0644]
include/grpc/event_engine/event_engine.h
include/grpc/event_engine/port.h
include/grpc/event_engine/slice_allocator.h
include/grpc/grpc.h
include/grpc/grpc_security.h
include/grpc/grpc_security_constants.h
include/grpc/impl/codegen/grpc_types.h
include/grpc/impl/codegen/port_platform.h
include/grpcpp/alarm.h
include/grpcpp/generic/generic_stub.h
include/grpcpp/impl/codegen/async_generic_service.h
include/grpcpp/impl/codegen/byte_buffer.h
include/grpcpp/impl/codegen/callback_common.h
include/grpcpp/impl/codegen/client_callback.h
include/grpcpp/impl/codegen/client_context.h
include/grpcpp/impl/codegen/completion_queue.h
include/grpcpp/impl/codegen/message_allocator.h
include/grpcpp/impl/codegen/server_callback.h
include/grpcpp/impl/codegen/server_callback_handlers.h
include/grpcpp/impl/codegen/server_context.h
include/grpcpp/impl/codegen/server_interface.h
include/grpcpp/impl/codegen/service_type.h
include/grpcpp/impl/codegen/slice.h
include/grpcpp/security/authorization_policy_provider.h [new file with mode: 0644]
include/grpcpp/server.h
include/grpcpp/server_builder.h
include/grpcpp/support/channel_arguments.h
include/grpcpp/test/client_context_test_peer.h [new file with mode: 0644]
include/grpcpp/test/mock_stream.h
package.xml
requirements.bazel.txt
setup.py
src/boringssl/boringssl_prefix_symbols.h
src/compiler/cpp_generator.cc
src/compiler/csharp_generator.cc
src/compiler/csharp_generator_helpers.h
src/compiler/csharp_plugin.cc
src/core/ext/filters/client_channel/client_channel.cc
src/core/ext/filters/client_channel/health/health_check_client.cc
src/core/ext/filters/client_channel/health/health_check_client.h
src/core/ext/filters/client_channel/http_proxy.cc
src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.cc
src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.h
src/core/ext/filters/client_channel/lb_policy/xds/cds.cc
src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_resolver.cc
src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc [new file with mode: 0644]
src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc [new file with mode: 0644]
src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc
src/core/ext/filters/client_channel/resolver/google_c2p/google_c2p_resolver.cc
src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc
src/core/ext/filters/client_channel/retry_filter.cc
src/core/ext/filters/client_channel/retry_service_config.cc
src/core/ext/filters/client_channel/retry_service_config.h
src/core/ext/filters/client_idle/client_idle_filter.cc
src/core/ext/filters/fault_injection/fault_injection_filter.cc
src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc
src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc
src/core/ext/transport/chttp2/transport/chttp2_transport.cc
src/core/ext/transport/chttp2/transport/internal.h
src/core/ext/transport/chttp2/transport/parsing.cc
src/core/ext/transport/inproc/inproc_transport.cc
src/core/ext/upb-generated/src/proto/grpc/auth/v1/authz_policy.upb.c [deleted file]
src/core/ext/upb-generated/src/proto/grpc/auth/v1/authz_policy.upb.h [deleted file]
src/core/ext/upbdefs-generated/src/proto/grpc/auth/v1/authz_policy.upbdefs.c [deleted file]
src/core/ext/upbdefs-generated/src/proto/grpc/auth/v1/authz_policy.upbdefs.h [deleted file]
src/core/ext/xds/xds_api.cc
src/core/ext/xds/xds_api.h
src/core/lib/address_utils/sockaddr_utils.cc
src/core/lib/address_utils/sockaddr_utils.h
src/core/lib/channel/channelz.h
src/core/lib/event_engine/endpoint_config.cc [new file with mode: 0644]
src/core/lib/event_engine/endpoint_config_internal.h [new file with mode: 0644]
src/core/lib/event_engine/event_engine.cc [new file with mode: 0644]
src/core/lib/event_engine/slice_allocator.cc
src/core/lib/event_engine/sockaddr.cc
src/core/lib/event_engine/sockaddr.h [new file with mode: 0644]
src/core/lib/gpr/wrap_memcpy.cc
src/core/lib/gprpp/status_helper.h
src/core/lib/iomgr/endpoint_pair_event_engine.cc [new file with mode: 0644]
src/core/lib/iomgr/error.cc
src/core/lib/iomgr/error.h
src/core/lib/iomgr/event_engine/closure.cc [new file with mode: 0644]
src/core/lib/iomgr/event_engine/closure.h [new file with mode: 0644]
src/core/lib/iomgr/event_engine/endpoint.cc [new file with mode: 0644]
src/core/lib/iomgr/event_engine/endpoint.h [new file with mode: 0644]
src/core/lib/iomgr/event_engine/iomgr.cc [new file with mode: 0644]
src/core/lib/iomgr/event_engine/iomgr.h [new file with mode: 0644]
src/core/lib/iomgr/event_engine/pollset.cc [new file with mode: 0644]
src/core/lib/iomgr/event_engine/pollset.h [moved from include/grpc/event_engine/channel_args.h with 68% similarity]
src/core/lib/iomgr/event_engine/promise.h [new file with mode: 0644]
src/core/lib/iomgr/event_engine/resolved_address_internal.cc [new file with mode: 0644]
src/core/lib/iomgr/event_engine/resolved_address_internal.h [new file with mode: 0644]
src/core/lib/iomgr/event_engine/resolver.cc [new file with mode: 0644]
src/core/lib/iomgr/event_engine/tcp.cc [new file with mode: 0644]
src/core/lib/iomgr/event_engine/timer.cc [new file with mode: 0644]
src/core/lib/iomgr/exec_ctx.cc
src/core/lib/iomgr/exec_ctx.h
src/core/lib/iomgr/executor/threadpool.cc
src/core/lib/iomgr/executor/threadpool.h
src/core/lib/iomgr/iomgr.cc
src/core/lib/iomgr/iomgr_posix.cc
src/core/lib/iomgr/iomgr_posix_cfstream.cc
src/core/lib/iomgr/pollset_custom.cc
src/core/lib/iomgr/pollset_custom.h
src/core/lib/iomgr/pollset_uv.cc
src/core/lib/iomgr/pollset_uv.h
src/core/lib/iomgr/port.h
src/core/lib/iomgr/resolve_address.cc
src/core/lib/iomgr/resolve_address.h
src/core/lib/iomgr/sockaddr.h
src/core/lib/iomgr/socket_mutator.cc
src/core/lib/iomgr/socket_mutator.h
src/core/lib/iomgr/socket_utils_common_posix.cc
src/core/lib/iomgr/socket_utils_posix.h
src/core/lib/iomgr/tcp_client_posix.cc
src/core/lib/iomgr/tcp_posix.cc
src/core/lib/iomgr/tcp_posix.h
src/core/lib/iomgr/tcp_server_custom.cc
src/core/lib/iomgr/tcp_server_posix.cc
src/core/lib/iomgr/tcp_server_utils_posix_common.cc
src/core/lib/iomgr/timer.h
src/core/lib/security/authorization/authorization_engine.h
src/core/lib/security/authorization/authorization_policy_provider.h [new file with mode: 0644]
src/core/lib/security/authorization/authorization_policy_provider_null_vtable.cc [new file with mode: 0644]
src/core/lib/security/authorization/authorization_policy_provider_vtable.cc [new file with mode: 0644]
src/core/lib/security/authorization/cel_authorization_engine.cc
src/core/lib/security/authorization/evaluate_args.cc
src/core/lib/security/authorization/evaluate_args.h
src/core/lib/security/authorization/grpc_authorization_engine.h
src/core/lib/security/authorization/grpc_authorization_policy_provider.cc [new file with mode: 0644]
src/core/lib/security/authorization/grpc_authorization_policy_provider.h [new file with mode: 0644]
src/core/lib/security/authorization/matchers.cc
src/core/lib/security/authorization/matchers.h
src/core/lib/security/authorization/rbac_policy.cc
src/core/lib/security/authorization/rbac_policy.h
src/core/lib/security/credentials/google_default/google_default_credentials.cc
src/core/lib/security/credentials/tls/tls_utils.cc
src/core/lib/security/credentials/tls/tls_utils.h
src/core/lib/security/security_connector/local/local_security_connector.cc
src/core/lib/security/security_connector/ssl_utils.cc
src/core/lib/surface/call.cc
src/core/lib/surface/call.h
src/core/lib/surface/completion_queue.cc
src/core/lib/surface/completion_queue.h
src/core/lib/surface/completion_queue_factory.cc
src/core/lib/surface/init.cc
src/core/lib/surface/init.h
src/core/lib/surface/version.cc
src/core/lib/transport/error_utils.cc
src/core/lib/transport/transport.h
src/core/lib/transport/transport_op_string.cc
src/core/plugin_registry/grpc_plugin_registry.cc
src/core/plugin_registry/grpc_unsecure_plugin_registry.cc
src/core/tsi/alts/crypt/gsec.h
src/cpp/README.md
src/cpp/client/channel_cc.cc
src/cpp/client/client_callback.cc
src/cpp/client/client_context.cc
src/cpp/client/create_channel_internal.cc
src/cpp/client/create_channel_internal.h
src/cpp/common/channel_arguments.cc
src/cpp/common/completion_queue_cc.cc
src/cpp/common/version_cc.cc
src/cpp/server/authorization_policy_provider.cc [new file with mode: 0644]
src/cpp/server/server_builder.cc
src/cpp/server/server_cc.cc
src/cpp/server/server_context.cc
src/cpp/util/byte_buffer_cc.cc
src/csharp/Grpc.Core.Api/ChannelCredentials.cs
src/csharp/Grpc.Core.Api/VersionInfo.cs
src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
src/csharp/Grpc.Core/NativeDeps.Linux.csproj.include
src/csharp/Grpc.Examples/MathGrpc.cs
src/csharp/Grpc.HealthCheck/HealthGrpc.cs
src/csharp/Grpc.IntegrationTesting/BenchmarkServiceGrpc.cs
src/csharp/Grpc.IntegrationTesting/EmptyServiceGrpc.cs
src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
src/csharp/Grpc.IntegrationTesting/ReportQpsScenarioServiceGrpc.cs
src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
src/csharp/Grpc.IntegrationTesting/WorkerServiceGrpc.cs
src/csharp/Grpc.Reflection/ReflectionGrpc.cs
src/csharp/build/dependencies.props
src/csharp/experimental/README.md
src/csharp/experimental/build_native_ext_for_android.sh
src/csharp/experimental/build_native_ext_for_ios.sh
src/objective-c/!ProtoCompiler-gRPCCppPlugin.podspec
src/objective-c/!ProtoCompiler-gRPCPlugin.podspec
src/objective-c/BoringSSL-GRPC.podspec
src/objective-c/GRPCClient/version.h
src/objective-c/tests/version.h
src/php/bin/generate_proto_php.sh
src/php/composer.json
src/php/ext/grpc/php_grpc.c
src/php/ext/grpc/version.h
src/php/lib/Grpc/BaseStub.php
src/php/tests/generated_code/AbstractGeneratedCodeTest.php
src/php/tests/generated_code/Math/MathStub.php [new file with mode: 0644]
src/php/tests/generated_code/math_server.php [new file with mode: 0644]
src/proto/grpc/auth/v1/authz_policy.proto [deleted file]
src/proto/grpc/testing/echo.proto
src/proto/grpc/testing/proto2/BUILD.bazel
src/proto/grpc/testing/xds/v3/BUILD
src/proto/grpc/testing/xds/v3/address.proto
src/proto/grpc/testing/xds/v3/cluster.proto
src/proto/grpc/testing/xds/v3/endpoint.proto
src/proto/grpc/testing/xds/v3/regex.proto
src/proto/grpc/testing/xds/v3/route.proto
src/python/grpcio/grpc/BUILD.bazel
src/python/grpcio/grpc/_cython/BUILD.bazel
src/python/grpcio/grpc/_cython/_cygrpc/aio/callback_common.pxd.pxi
src/python/grpcio/grpc/_cython/_cygrpc/aio/callback_common.pyx.pxi
src/python/grpcio/grpc/_cython/_cygrpc/aio/completion_queue.pyx.pxi
src/python/grpcio/grpc/_cython/_cygrpc/aio/iomgr/iomgr.pyx.pxi
src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
src/python/grpcio/grpc/_cython/_cygrpc/grpc_gevent.pyx.pxi
src/python/grpcio/grpc/_cython/_cygrpc/iomgr.pxd.pxi
src/python/grpcio/grpc/_grpcio_metadata.py
src/python/grpcio/grpc/_plugin_wrapping.py
src/python/grpcio/grpc/aio/_interceptor.py
src/python/grpcio/grpc/experimental/BUILD.bazel
src/python/grpcio/grpc/framework/BUILD.bazel
src/python/grpcio/grpc/framework/common/BUILD.bazel
src/python/grpcio/grpc/framework/foundation/BUILD.bazel
src/python/grpcio/grpc/framework/interfaces/BUILD.bazel
src/python/grpcio/grpc/framework/interfaces/base/BUILD.bazel
src/python/grpcio/grpc/framework/interfaces/face/BUILD.bazel
src/python/grpcio/grpc_core_dependencies.py
src/python/grpcio/grpc_version.py
src/python/grpcio_admin/grpc_version.py
src/python/grpcio_channelz/grpc_channelz/v1/BUILD.bazel
src/python/grpcio_channelz/grpc_version.py
src/python/grpcio_csds/grpc_version.py
src/python/grpcio_health_checking/grpc_health/v1/BUILD.bazel
src/python/grpcio_health_checking/grpc_version.py
src/python/grpcio_reflection/grpc_reflection/v1alpha/BUILD.bazel
src/python/grpcio_reflection/grpc_version.py
src/python/grpcio_status/grpc_status/BUILD.bazel
src/python/grpcio_status/grpc_version.py
src/python/grpcio_testing/grpc_version.py
src/python/grpcio_tests/commands.py
src/python/grpcio_tests/grpc_version.py
src/python/grpcio_tests/tests/BUILD.bazel
src/python/grpcio_tests/tests/channelz/BUILD.bazel
src/python/grpcio_tests/tests/health_check/BUILD.bazel
src/python/grpcio_tests/tests/interop/BUILD.bazel
src/python/grpcio_tests/tests/interop/credentials/BUILD.bazel
src/python/grpcio_tests/tests/reflection/BUILD.bazel
src/python/grpcio_tests/tests/status/BUILD.bazel
src/python/grpcio_tests/tests/stress/BUILD.bazel
src/python/grpcio_tests/tests/testing/BUILD.bazel
src/python/grpcio_tests/tests/unit/BUILD.bazel
src/python/grpcio_tests/tests/unit/_contextvars_propagation_test.py
src/python/grpcio_tests/tests/unit/_cython/BUILD.bazel
src/python/grpcio_tests/tests/unit/credentials/BUILD.bazel
src/python/grpcio_tests/tests/unit/framework/common/BUILD.bazel
src/python/grpcio_tests/tests/unit/framework/foundation/BUILD.bazel
src/python/grpcio_tests/tests_aio/unit/channel_argument_test.py
src/python/grpcio_tests/tests_aio/unit/connectivity_test.py
src/python/grpcio_tests/tests_gevent/__init__.py [new file with mode: 0644]
src/python/grpcio_tests/tests_gevent/tests.json [new file with mode: 0644]
src/python/grpcio_tests/tests_gevent/unit/__init__.py [new file with mode: 0644]
src/python/grpcio_tests/tests_gevent/unit/_test_server.py [new file with mode: 0644]
src/python/grpcio_tests/tests_gevent/unit/close_channel_test.py [new file with mode: 0644]
src/python/grpcio_tests/tests_py3_only/interop/BUILD.bazel
src/python/grpcio_tests/tests_py3_only/interop/Dockerfile.client
src/python/grpcio_tests/tests_py3_only/interop/Dockerfile.server
src/python/grpcio_tests/tests_py3_only/interop/xds_interop_client.py
src/python/grpcio_tests/tests_py3_only/interop/xds_interop_server.py
src/ruby/ext/grpc/extconf.rb
src/ruby/ext/grpc/rb_grpc_imports.generated.c
src/ruby/ext/grpc/rb_grpc_imports.generated.h
src/ruby/lib/grpc/version.rb
src/ruby/pb/test/xds_client.rb
src/ruby/tools/version.rb
templates/CMakeLists.txt.template
templates/_metadata.py.template [new file with mode: 0644]
templates/config.m4.template
templates/gRPC-Core.podspec.template
templates/src/objective-c/BoringSSL-GRPC.podspec.template
templates/src/ruby/ext/grpc/extconf.rb.template [new file with mode: 0644]
templates/tools/dockerfile/test/cxx_buster_openssl102_x64/Dockerfile.template [new file with mode: 0644]
test/core/client_channel/service_config_test.cc
test/core/end2end/end2end_nosec_tests.cc
test/core/end2end/end2end_tests.cc
test/core/end2end/fixtures/h2_fd.cc
test/core/end2end/fixtures/h2_proxy.cc
test/core/end2end/fixtures/proxy.cc
test/core/end2end/generate_tests.bzl
test/core/end2end/inproc_callback_test.cc
test/core/end2end/tests/connectivity.cc
test/core/end2end/tests/retry_exceeds_buffer_size_in_delay.cc [new file with mode: 0644]
test/core/end2end/tests/retry_per_attempt_recv_timeout.cc [new file with mode: 0644]
test/core/end2end/tests/retry_recv_trailing_metadata_error.cc [new file with mode: 0644]
test/core/end2end/tests/retry_send_op_fails.cc [new file with mode: 0644]
test/core/event_engine/BUILD [new file with mode: 0644]
test/core/event_engine/endpoint_config_test.cc [new file with mode: 0644]
test/core/handshake/BUILD
test/core/handshake/client_ssl.cc
test/core/handshake/readahead_handshaker_server_ssl.cc
test/core/handshake/server_ssl.cc
test/core/handshake/server_ssl_common.cc
test/core/handshake/server_ssl_common.h
test/core/iomgr/BUILD
test/core/iomgr/resolve_address_test.cc
test/core/iomgr/socket_utils_test.cc
test/core/iomgr/threadpool_test.cc
test/core/security/BUILD
test/core/security/authorization_matchers_test.cc
test/core/security/evaluate_args_test.cc
test/core/security/grpc_authorization_engine_test.cc
test/core/security/grpc_authorization_policy_provider_test.cc [new file with mode: 0644]
test/core/security/security_connector_test.cc
test/core/surface/BUILD
test/core/surface/completion_queue_test.cc
test/core/surface/public_headers_must_be_c89.c
test/cpp/codegen/compiler_test_golden
test/cpp/common/alarm_test.cc
test/cpp/common/channel_arguments_test.cc
test/cpp/end2end/client_callback_end2end_test.cc
test/cpp/end2end/client_interceptors_end2end_test.cc
test/cpp/end2end/context_allocator_end2end_test.cc
test/cpp/end2end/end2end_test.cc
test/cpp/end2end/hybrid_end2end_test.cc
test/cpp/end2end/interceptors_util.cc
test/cpp/end2end/message_allocator_end2end_test.cc
test/cpp/end2end/mock_test.cc
test/cpp/end2end/test_service_impl.cc
test/cpp/end2end/test_service_impl.h
test/cpp/end2end/xds_end2end_test.cc
test/cpp/grpclb/grpclb_api_test.cc
test/cpp/interop/grpclb_fallback_test.cc
test/cpp/microbenchmarks/bm_chttp2_transport.cc
test/cpp/microbenchmarks/bm_cq.cc
test/cpp/microbenchmarks/bm_threadpool.cc
test/cpp/microbenchmarks/callback_streaming_ping_pong.h
test/cpp/microbenchmarks/callback_test_service.cc
test/cpp/microbenchmarks/callback_test_service.h
test/cpp/microbenchmarks/callback_unary_ping_pong.h
test/cpp/naming/dns_test_util.cc
test/cpp/naming/generate_resolver_component_tests.bzl
test/cpp/qps/client_callback.cc
test/cpp/qps/json_run_localhost_scenario_gen.py
test/cpp/qps/json_run_localhost_scenarios.bzl
test/cpp/qps/qps_json_driver_scenario_gen.py
test/cpp/qps/qps_json_driver_scenarios.bzl
test/cpp/qps/server_callback.cc
test/cpp/server/BUILD
test/cpp/server/authorization_policy_provider_test.cc [new file with mode: 0644]
test/cpp/server/server_builder_with_socket_mutator_test.cc
test/cpp/test/BUILD
test/cpp/test/client_context_test_peer_test.cc [new file with mode: 0644]
test/cpp/test/mock_stream_test.cc
test/cpp/util/byte_buffer_test.cc
test/cpp/util/grpc_tool_test.cc
test/cpp/util/slice_test.cc
test/spm_build/test.cc [new file with mode: 0644]
third_party/ABSEIL_MANUAL.md
third_party/six.BUILD
tools/codegen/core/gen_upb_api.sh
tools/distrib/check_copyright.py
tools/distrib/pylint_code.sh
tools/distrib/python/grpc_version.py
tools/distrib/python/grpcio_tools/grpc_version.py
tools/dockerfile/interoptest/grpc_interop_cxx_xds/Dockerfile.xds_client
tools/dockerfile/interoptest/grpc_interop_cxx_xds/Dockerfile.xds_server
tools/dockerfile/test/cxx_buster_openssl102_x64/Dockerfile [new file with mode: 0644]
tools/doxygen/Doxyfile.c++
tools/doxygen/Doxyfile.c++.internal
tools/doxygen/Doxyfile.core
tools/doxygen/Doxyfile.core.internal
tools/doxygen/Doxyfile.objc
tools/doxygen/Doxyfile.objc.internal
tools/doxygen/Doxyfile.php
tools/internal_ci/helper_scripts/list_leftover_loadtests.sh [new file with mode: 0644]
tools/internal_ci/helper_scripts/prepare_build_macos_rc
tools/internal_ci/linux/aws/grpc_aws_basictests_csharp.cfg [new file with mode: 0644]
tools/internal_ci/linux/aws/grpc_aws_basictests_php.cfg [new file with mode: 0644]
tools/internal_ci/linux/aws/grpc_aws_basictests_python.cfg [new file with mode: 0644]
tools/internal_ci/linux/aws/grpc_aws_basictests_ruby.cfg [new file with mode: 0644]
tools/internal_ci/linux/aws/grpc_aws_bazel_test_c_cpp.cfg [new file with mode: 0644]
tools/internal_ci/linux/aws/grpc_aws_experiment.cfg [new file with mode: 0644]
tools/internal_ci/linux/aws/grpc_aws_experiment_remote.sh [new file with mode: 0755]
tools/internal_ci/linux/aws/grpc_aws_run_remote_test.sh [new file with mode: 0755]
tools/internal_ci/linux/aws/grpc_aws_run_remote_test_8core.sh [new file with mode: 0755]
tools/internal_ci/linux/aws/grpc_bazel_test_c_cpp_aarch64.sh [new file with mode: 0755]
tools/internal_ci/linux/aws/grpc_run_basictests_csharp_aarch64.sh [new file with mode: 0755]
tools/internal_ci/linux/aws/grpc_run_basictests_php_aarch64.sh [new file with mode: 0755]
tools/internal_ci/linux/aws/grpc_run_basictests_python_aarch64.sh [new file with mode: 0755]
tools/internal_ci/linux/aws/grpc_run_basictests_ruby_aarch64.sh [new file with mode: 0755]
tools/internal_ci/linux/grpc_e2e_performance_gke.cfg [new file with mode: 0644]
tools/internal_ci/linux/grpc_e2e_performance_gke.sh [new file with mode: 0755]
tools/internal_ci/linux/grpc_e2e_performance_v2.cfg
tools/internal_ci/linux/grpc_e2e_performance_v2.sh
tools/internal_ci/linux/grpc_xds_bazel_python_test_in_docker.sh
tools/internal_ci/linux/grpc_xds_bazel_test_in_docker.sh
tools/internal_ci/linux/grpc_xds_csharp_test_in_docker.sh
tools/internal_ci/linux/grpc_xds_k8s_install_test_driver.sh
tools/internal_ci/linux/grpc_xds_php_test_in_docker.sh
tools/internal_ci/linux/grpc_xds_ruby_test_in_docker.sh
tools/internal_ci/linux/grpc_xds_url_map.cfg [new file with mode: 0644]
tools/internal_ci/linux/grpc_xds_url_map.sh [new file with mode: 0755]
tools/internal_ci/macos/grpc_build_artifacts.cfg
tools/interop_matrix/client_matrix.py
tools/release/release_notes.py
tools/release/verify_python_release.py
tools/run_tests/artifacts/build_artifact_csharp.bat
tools/run_tests/artifacts/build_artifact_csharp.sh
tools/run_tests/generated/tests.json
tools/run_tests/helper_scripts/pre_build_csharp.bat
tools/run_tests/helper_scripts/pre_build_csharp.sh
tools/run_tests/performance/README.md
tools/run_tests/performance/loadtest_config.py
tools/run_tests/performance/loadtest_examples.sh [new file with mode: 0755]
tools/run_tests/performance/loadtest_template.py
tools/run_tests/performance/scenario_config.py
tools/run_tests/performance/scenario_config_exporter.py
tools/run_tests/performance/templates/loadtest_template_basic_all_languages.yaml
tools/run_tests/performance/templates/loadtest_template_prebuilt_all_languages.yaml [new file with mode: 0644]
tools/run_tests/run_tests.py
tools/run_tests/run_tests_matrix.py
tools/run_tests/run_xds_tests.py
tools/run_tests/sanity/check_submodules.sh
tools/run_tests/xds_k8s_test_driver/README.md
tools/run_tests/xds_k8s_test_driver/bin/cleanup.sh [new file with mode: 0755]
tools/run_tests/xds_k8s_test_driver/bin/ensure_venv.sh [new file with mode: 0755]
tools/run_tests/xds_k8s_test_driver/bin/isort.sh [new file with mode: 0755]
tools/run_tests/xds_k8s_test_driver/bin/run_channelz.py
tools/run_tests/xds_k8s_test_driver/bin/run_td_setup.py
tools/run_tests/xds_k8s_test_driver/bin/run_test_client.py
tools/run_tests/xds_k8s_test_driver/bin/run_test_server.py
tools/run_tests/xds_k8s_test_driver/bin/yapf.sh [new file with mode: 0755]
tools/run_tests/xds_k8s_test_driver/config/grpc-testing.cfg
tools/run_tests/xds_k8s_test_driver/config/local-dev.cfg.example
tools/run_tests/xds_k8s_test_driver/framework/helpers/retryers.py
tools/run_tests/xds_k8s_test_driver/framework/infrastructure/gcp/__init__.py
tools/run_tests/xds_k8s_test_driver/framework/infrastructure/gcp/api.py
tools/run_tests/xds_k8s_test_driver/framework/infrastructure/gcp/compute.py
tools/run_tests/xds_k8s_test_driver/framework/infrastructure/gcp/iam.py [new file with mode: 0644]
tools/run_tests/xds_k8s_test_driver/framework/infrastructure/gcp/network_security.py
tools/run_tests/xds_k8s_test_driver/framework/infrastructure/gcp/network_services.py
tools/run_tests/xds_k8s_test_driver/framework/infrastructure/k8s.py
tools/run_tests/xds_k8s_test_driver/framework/infrastructure/traffic_director.py
tools/run_tests/xds_k8s_test_driver/framework/rpc/grpc.py
tools/run_tests/xds_k8s_test_driver/framework/rpc/grpc_csds.py [new file with mode: 0644]
tools/run_tests/xds_k8s_test_driver/framework/rpc/grpc_testing.py
tools/run_tests/xds_k8s_test_driver/framework/test_app/base_runner.py
tools/run_tests/xds_k8s_test_driver/framework/test_app/client_app.py
tools/run_tests/xds_k8s_test_driver/framework/test_app/server_app.py
tools/run_tests/xds_k8s_test_driver/framework/xds_flags.py
tools/run_tests/xds_k8s_test_driver/framework/xds_k8s_testcase.py
tools/run_tests/xds_k8s_test_driver/framework/xds_url_map_test_resources.py [new file with mode: 0644]
tools/run_tests/xds_k8s_test_driver/framework/xds_url_map_testcase.py [new file with mode: 0644]
tools/run_tests/xds_k8s_test_driver/kubernetes-manifests/server.deployment.yaml
tools/run_tests/xds_k8s_test_driver/requirements-dev.txt [new file with mode: 0644]
tools/run_tests/xds_k8s_test_driver/requirements.txt
tools/run_tests/xds_k8s_test_driver/run.sh [new file with mode: 0755]
tools/run_tests/xds_k8s_test_driver/tests/baseline_test.py
tools/run_tests/xds_k8s_test_driver/tests/url_map/__init__.py [new file with mode: 0644]
tools/run_tests/xds_k8s_test_driver/tests/url_map/__main__.py [new file with mode: 0644]
tools/run_tests/xds_k8s_test_driver/tests/url_map/csds_test.py [new file with mode: 0644]
tools/run_tests/xds_k8s_test_driver/tests/url_map/fault_injection_test.py [new file with mode: 0644]
tools/run_tests/xds_k8s_test_driver/tests/url_map/header_matching_test.py [new file with mode: 0644]
tools/run_tests/xds_k8s_test_driver/tests/url_map/path_matching_test.py [new file with mode: 0644]
tools/run_tests/xds_k8s_test_driver/tests/url_map/timeout_test.py [new file with mode: 0644]

index e69dc0b..a191005 100644 (file)
@@ -2,7 +2,7 @@
 name: Report a bug
 about: Create a report to help us improve
 labels: kind/bug, priority/P2
-assignees: nicolasnoble
+assignees: yashykt
 
 ---
 
index c9a6d3f..98b0a5a 100644 (file)
@@ -2,7 +2,7 @@
 name: Request a cleanup
 about: Suggest a cleanup in our repository
 labels: kind/internal cleanup, priority/P2
-assignees: nicolasnoble
+assignees: yashykt
 
 ---
 
index e313799..b6a5493 100644 (file)
@@ -2,7 +2,7 @@
 name: Request a feature
 about: Suggest an idea for this project
 labels: kind/enhancement, priority/P2
-assignees: nicolasnoble
+assignees: yashykt
 
 ---
 
index cfde18b..b92cd62 100644 (file)
@@ -2,7 +2,7 @@
 name: Ask a question
 about: Ask a question
 labels: kind/question, priority/P3
-assignees: nicolasnoble
+assignees: yashykt
 
 ---
 
index 57af6c2..380c2bd 100644 (file)
@@ -8,4 +8,4 @@ If you know who should review your pull request, please remove the mentioning be
 
 -->
 
-@nicolasnoble
+@yashykt
diff --git a/BUILD b/BUILD
index 138120e..2f69487 100644 (file)
--- a/BUILD
+++ b/BUILD
@@ -85,11 +85,11 @@ config_setting(
 python_config_settings()
 
 # This should be updated along with build_handwritten.yaml
-g_stands_for = "guadalupe_river_park_conservancy"  # @unused
+g_stands_for = "goofy"  # @unused
 
-core_version = "16.0.0"  # @unused
+core_version = "18.0.0"  # @unused
 
-version = "1.38.1"  # @unused
+version = "1.39.0"  # @unused
 
 GPR_PUBLIC_HDRS = [
     "include/grpc/support/alloc.h",
@@ -128,7 +128,7 @@ GRPC_PUBLIC_HDRS = [
 ]
 
 GRPC_PUBLIC_EVENT_ENGINE_HDRS = [
-    "include/grpc/event_engine/channel_args.h",
+    "include/grpc/event_engine/endpoint_config.h",
     "include/grpc/event_engine/event_engine.h",
     "include/grpc/event_engine/port.h",
     "include/grpc/event_engine/slice_allocator.h",
@@ -264,6 +264,7 @@ GRPCXX_PUBLIC_HDRS = [
     "include/grpcpp/security/credentials.h",
     "include/grpcpp/security/server_credentials.h",
     "include/grpcpp/security/tls_certificate_provider.h",
+    "include/grpcpp/security/authorization_policy_provider.h",
     "include/grpcpp/security/tls_credentials_options.h",
     "include/grpcpp/server.h",
     "include/grpcpp/server_builder.h",
@@ -306,6 +307,7 @@ grpc_cc_library(
 grpc_cc_library(
     name = "grpc_unsecure",
     srcs = [
+        "src/core/lib/security/authorization/authorization_policy_provider_null_vtable.cc",
         "src/core/lib/surface/init.cc",
         "src/core/lib/surface/init_unsecure.cc",
         "src/core/plugin_registry/grpc_unsecure_plugin_registry.cc",
@@ -313,6 +315,7 @@ grpc_cc_library(
     language = "c++",
     public_hdrs = GRPC_PUBLIC_HDRS,
     standalone = True,
+    visibility = ["@grpc:public"],
     deps = [
         "grpc_common",
         "grpc_lb_policy_grpclb",
@@ -344,6 +347,10 @@ grpc_cc_library(
         ],
     },
     standalone = True,
+    visibility = [
+        "@grpc:alt_grpc_legacy",
+        "@grpc:public",
+    ],
     deps = [
         "grpc_common",
         "grpc_lb_policy_grpclb_secure",
@@ -360,6 +367,7 @@ grpc_cc_library(
         "absl/synchronization",
         "protobuf_headers",
     ],
+    visibility = ["@grpc:public"],
 )
 
 grpc_cc_library(
@@ -380,6 +388,10 @@ grpc_cc_library(
         ],
     },
     standalone = True,
+    visibility = [
+        "@grpc:alt_grpc++_legacy",
+        "@grpc:public",
+    ],
     deps = [
         "grpc++_internals",
     ],
@@ -463,6 +475,7 @@ grpc_cc_library(
     ],
     language = "c++",
     standalone = True,
+    visibility = ["@grpc:public"],
     deps = [
         "gpr",
         "grpc++_base_unsecure",
@@ -779,6 +792,8 @@ grpc_cc_library(
         "src/core/lib/compression/stream_compression_identity.cc",
         "src/core/lib/debug/stats.cc",
         "src/core/lib/debug/stats_data.cc",
+        "src/core/lib/event_engine/endpoint_config.cc",
+        "src/core/lib/event_engine/event_engine.cc",
         "src/core/lib/event_engine/slice_allocator.cc",
         "src/core/lib/event_engine/sockaddr.cc",
         "src/core/lib/http/format_request.cc",
@@ -791,6 +806,7 @@ grpc_cc_library(
         "src/core/lib/iomgr/dualstack_socket_posix.cc",
         "src/core/lib/iomgr/endpoint.cc",
         "src/core/lib/iomgr/endpoint_cfstream.cc",
+        "src/core/lib/iomgr/endpoint_pair_event_engine.cc",
         "src/core/lib/iomgr/endpoint_pair_posix.cc",
         "src/core/lib/iomgr/endpoint_pair_uv.cc",
         "src/core/lib/iomgr/endpoint_pair_windows.cc",
@@ -802,6 +818,14 @@ grpc_cc_library(
         "src/core/lib/iomgr/ev_poll_posix.cc",
         "src/core/lib/iomgr/ev_posix.cc",
         "src/core/lib/iomgr/ev_windows.cc",
+        "src/core/lib/iomgr/event_engine/closure.cc",
+        "src/core/lib/iomgr/event_engine/endpoint.cc",
+        "src/core/lib/iomgr/event_engine/iomgr.cc",
+        "src/core/lib/iomgr/event_engine/pollset.cc",
+        "src/core/lib/iomgr/event_engine/resolved_address_internal.cc",
+        "src/core/lib/iomgr/event_engine/resolver.cc",
+        "src/core/lib/iomgr/event_engine/tcp.cc",
+        "src/core/lib/iomgr/event_engine/timer.cc",
         "src/core/lib/iomgr/exec_ctx.cc",
         "src/core/lib/iomgr/executor.cc",
         "src/core/lib/iomgr/executor/mpmcqueue.cc",
@@ -945,6 +969,8 @@ grpc_cc_library(
         "src/core/lib/compression/stream_compression_identity.h",
         "src/core/lib/debug/stats.h",
         "src/core/lib/debug/stats_data.h",
+        "src/core/lib/event_engine/endpoint_config_internal.h",
+        "src/core/lib/event_engine/sockaddr.h",
         "src/core/lib/http/format_request.h",
         "src/core/lib/http/httpcli.h",
         "src/core/lib/http/parser.h",
@@ -966,6 +992,12 @@ grpc_cc_library(
         "src/core/lib/iomgr/ev_epollex_linux.h",
         "src/core/lib/iomgr/ev_poll_posix.h",
         "src/core/lib/iomgr/ev_posix.h",
+        "src/core/lib/iomgr/event_engine/closure.h",
+        "src/core/lib/iomgr/event_engine/endpoint.h",
+        "src/core/lib/iomgr/event_engine/iomgr.h",
+        "src/core/lib/iomgr/event_engine/pollset.h",
+        "src/core/lib/iomgr/event_engine/promise.h",
+        "src/core/lib/iomgr/event_engine/resolved_address_internal.h",
         "src/core/lib/iomgr/exec_ctx.h",
         "src/core/lib/iomgr/executor.h",
         "src/core/lib/iomgr/executor/mpmcqueue.h",
@@ -1062,6 +1094,7 @@ grpc_cc_library(
     external_deps = [
         "madler_zlib",
         "absl/container:inlined_vector",
+        "absl/functional:bind_front",
         "absl/status",
         "absl/status:statusor",
         "absl/strings",
@@ -1104,6 +1137,7 @@ grpc_cc_library(
         "grpc_client_authority_filter",
         "grpc_lb_policy_pick_first",
         "grpc_lb_policy_priority",
+        "grpc_lb_policy_ring_hash",
         "grpc_lb_policy_round_robin",
         "grpc_lb_policy_weighted_target",
         "grpc_client_idle_filter",
@@ -1543,6 +1577,7 @@ grpc_cc_library(
         "grpc_base",
         "grpc_client_channel",
         "grpc_lb_address_filtering",
+        "grpc_lb_policy_ring_hash",
         "grpc_lb_xds_channel_args",
         "grpc_lb_xds_common",
         "grpc_resolver_fake",
@@ -1635,6 +1670,10 @@ grpc_cc_library(
     hdrs = [
         "src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.h",
     ],
+    external_deps = [
+        "absl/strings",
+        "xxhash",
+    ],
     language = "c++",
     deps = [
         "grpc_base",
@@ -1833,10 +1872,12 @@ grpc_cc_library(
     name = "grpc_resolver_dns_ares",
     srcs = [
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc",
@@ -1875,7 +1916,10 @@ grpc_cc_library(
     srcs = ["src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc"],
     hdrs = ["src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"],
     language = "c++",
-    visibility = ["//test:__subpackages__"],
+    visibility = [
+        "//test:__subpackages__",
+        "@grpc:grpc_resolver_fake",
+    ],
     deps = [
         "grpc_base",
         "grpc_client_channel",
@@ -1924,6 +1968,8 @@ grpc_cc_library(
     name = "grpc_secure",
     srcs = [
         "src/core/lib/http/httpcli_security_connector.cc",
+        "src/core/lib/security/authorization/authorization_policy_provider_vtable.cc",
+        "src/core/lib/security/authorization/evaluate_args.cc",
         "src/core/lib/security/context/security_context.cc",
         "src/core/lib/security/credentials/alts/alts_credentials.cc",
         "src/core/lib/security/credentials/composite/composite_credentials.cc",
@@ -1973,6 +2019,9 @@ grpc_cc_library(
     hdrs = [
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h",
         "src/core/ext/xds/xds_channel_args.h",
+        "src/core/lib/security/authorization/authorization_engine.h",
+        "src/core/lib/security/authorization/authorization_policy_provider.h",
+        "src/core/lib/security/authorization/evaluate_args.h",
         "src/core/lib/security/context/security_context.h",
         "src/core/lib/security/credentials/alts/alts_credentials.h",
         "src/core/lib/security/credentials/composite/composite_credentials.h",
@@ -2016,6 +2065,7 @@ grpc_cc_library(
     ],
     language = "c++",
     public_hdrs = GRPC_SECURE_PUBLIC_HDRS,
+    visibility = ["@grpc:public"],
     deps = [
         "alts_util",
         "grpc_base",
@@ -2064,14 +2114,11 @@ grpc_cc_library(
 grpc_cc_library(
     name = "grpc_rbac_engine",
     srcs = [
-        "src/core/lib/security/authorization/evaluate_args.cc",
         "src/core/lib/security/authorization/grpc_authorization_engine.cc",
         "src/core/lib/security/authorization/matchers.cc",
         "src/core/lib/security/authorization/rbac_policy.cc",
     ],
     hdrs = [
-        "src/core/lib/security/authorization/authorization_engine.h",
-        "src/core/lib/security/authorization/evaluate_args.h",
         "src/core/lib/security/authorization/grpc_authorization_engine.h",
         "src/core/lib/security/authorization/matchers.h",
         "src/core/lib/security/authorization/rbac_policy.h",
@@ -2088,20 +2135,39 @@ grpc_cc_library(
 grpc_cc_library(
     name = "grpc_authorization_provider",
     srcs = [
+        "src/core/lib/security/authorization/grpc_authorization_policy_provider.cc",
         "src/core/lib/security/authorization/rbac_translator.cc",
     ],
     hdrs = [
+        "src/core/lib/security/authorization/grpc_authorization_policy_provider.h",
         "src/core/lib/security/authorization/rbac_translator.h",
     ],
     language = "c++",
     deps = [
-        "grpc_matchers",
         "grpc_rbac_engine",
     ],
 )
 
 # This target pulls in a dependency on RE2 and should not be linked into grpc by default for binary-size reasons.
 grpc_cc_library(
+    name = "grpc++_authorization_provider",
+    srcs = [
+        "src/cpp/server/authorization_policy_provider.cc",
+    ],
+    external_deps = [
+        "absl/synchronization",
+        "protobuf_headers",
+    ],
+    language = "c++",
+    public_hdrs = GRPCXX_PUBLIC_HDRS + GRPC_SECURE_PUBLIC_HDRS,
+    deps = [
+        "grpc++_codegen_base",
+        "grpc_authorization_provider",
+    ],
+)
+
+# This target pulls in a dependency on RE2 and should not be linked into grpc by default for binary-size reasons.
+grpc_cc_library(
     name = "grpc_cel_engine",
     srcs = [
         "src/core/lib/security/authorization/cel_authorization_engine.cc",
@@ -2690,6 +2756,7 @@ grpc_cc_library(
         "include/grpc++/test/mock_stream.h",
         "include/grpc++/test/server_context_test_spouse.h",
         "include/grpcpp/test/channel_test_peer.h",
+        "include/grpcpp/test/client_context_test_peer.h",
         "include/grpcpp/test/default_reactor_test_peer.h",
         "include/grpcpp/test/mock_stream.h",
         "include/grpcpp/test/server_context_test_spouse.h",
@@ -2760,6 +2827,7 @@ grpc_cc_library(
         "opencensus-context",
     ],
     language = "c++",
+    visibility = ["@grpc:grpc_opencensus_plugin"],
     deps = [
         ":census",
         ":grpc++",
index 2a2889d..47ebfa3 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -225,7 +225,7 @@ config("grpc_config") {
         "include/grpc/byte_buffer_reader.h",
         "include/grpc/census.h",
         "include/grpc/compression.h",
-        "include/grpc/event_engine/channel_args.h",
+        "include/grpc/event_engine/endpoint_config.h",
         "include/grpc/event_engine/event_engine.h",
         "include/grpc/event_engine/port.h",
         "include/grpc/event_engine/slice_allocator.h",
@@ -308,11 +308,13 @@ config("grpc_config") {
         "src/core/ext/filters/client_channel/resolver.h",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc",
         "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc",
@@ -861,8 +863,12 @@ config("grpc_config") {
         "src/core/lib/debug/stats_data.h",
         "src/core/lib/debug/trace.cc",
         "src/core/lib/debug/trace.h",
+        "src/core/lib/event_engine/endpoint_config.cc",
+        "src/core/lib/event_engine/endpoint_config_internal.h",
+        "src/core/lib/event_engine/event_engine.cc",
         "src/core/lib/event_engine/slice_allocator.cc",
         "src/core/lib/event_engine/sockaddr.cc",
+        "src/core/lib/event_engine/sockaddr.h",
         "src/core/lib/gprpp/atomic.h",
         "src/core/lib/gprpp/dual_ref_counted.h",
         "src/core/lib/gprpp/orphanable.h",
@@ -892,6 +898,7 @@ config("grpc_config") {
         "src/core/lib/iomgr/endpoint_cfstream.cc",
         "src/core/lib/iomgr/endpoint_cfstream.h",
         "src/core/lib/iomgr/endpoint_pair.h",
+        "src/core/lib/iomgr/endpoint_pair_event_engine.cc",
         "src/core/lib/iomgr/endpoint_pair_posix.cc",
         "src/core/lib/iomgr/endpoint_pair_uv.cc",
         "src/core/lib/iomgr/endpoint_pair_windows.cc",
@@ -911,6 +918,20 @@ config("grpc_config") {
         "src/core/lib/iomgr/ev_posix.cc",
         "src/core/lib/iomgr/ev_posix.h",
         "src/core/lib/iomgr/ev_windows.cc",
+        "src/core/lib/iomgr/event_engine/closure.cc",
+        "src/core/lib/iomgr/event_engine/closure.h",
+        "src/core/lib/iomgr/event_engine/endpoint.cc",
+        "src/core/lib/iomgr/event_engine/endpoint.h",
+        "src/core/lib/iomgr/event_engine/iomgr.cc",
+        "src/core/lib/iomgr/event_engine/iomgr.h",
+        "src/core/lib/iomgr/event_engine/pollset.cc",
+        "src/core/lib/iomgr/event_engine/pollset.h",
+        "src/core/lib/iomgr/event_engine/promise.h",
+        "src/core/lib/iomgr/event_engine/resolved_address_internal.cc",
+        "src/core/lib/iomgr/event_engine/resolved_address_internal.h",
+        "src/core/lib/iomgr/event_engine/resolver.cc",
+        "src/core/lib/iomgr/event_engine/tcp.cc",
+        "src/core/lib/iomgr/event_engine/timer.cc",
         "src/core/lib/iomgr/exec_ctx.cc",
         "src/core/lib/iomgr/exec_ctx.h",
         "src/core/lib/iomgr/executor.cc",
@@ -1049,6 +1070,11 @@ config("grpc_config") {
         "src/core/lib/json/json_writer.cc",
         "src/core/lib/matchers/matchers.cc",
         "src/core/lib/matchers/matchers.h",
+        "src/core/lib/security/authorization/authorization_engine.h",
+        "src/core/lib/security/authorization/authorization_policy_provider.h",
+        "src/core/lib/security/authorization/authorization_policy_provider_vtable.cc",
+        "src/core/lib/security/authorization/evaluate_args.cc",
+        "src/core/lib/security/authorization/evaluate_args.h",
         "src/core/lib/security/context/security_context.cc",
         "src/core/lib/security/context/security_context.h",
         "src/core/lib/security/credentials/alts/alts_credentials.cc",
@@ -1456,6 +1482,7 @@ config("grpc_config") {
         "include/grpcpp/resource_quota.h",
         "include/grpcpp/security/auth_context.h",
         "include/grpcpp/security/auth_metadata_processor.h",
+        "include/grpcpp/security/authorization_policy_provider.h",
         "include/grpcpp/security/credentials.h",
         "include/grpcpp/security/server_credentials.h",
         "include/grpcpp/security/tls_certificate_provider.h",
index eeaf0b4..9dfad89 100644 (file)
 cmake_minimum_required(VERSION 3.5.1)
 
 set(PACKAGE_NAME          "grpc")
-set(PACKAGE_VERSION       "1.38.1")
-set(gRPC_CORE_VERSION     "16.0.0")
-set(gRPC_CORE_SOVERSION   "16")
-set(gRPC_CPP_VERSION      "1.38.1")
-set(gRPC_CPP_SOVERSION    "1.38")
-set(gRPC_CSHARP_VERSION   "2.38.1")
-set(gRPC_CSHARP_SOVERSION "2.38")
+set(PACKAGE_VERSION       "1.39.0")
+set(gRPC_CORE_VERSION     "18.0.0")
+set(gRPC_CORE_SOVERSION   "18")
+set(gRPC_CPP_VERSION      "1.39.0")
+set(gRPC_CPP_SOVERSION    "1.39")
+set(gRPC_CSHARP_VERSION   "2.39.0")
+set(gRPC_CSHARP_SOVERSION "2.39")
 set(PACKAGE_STRING        "${PACKAGE_NAME} ${PACKAGE_VERSION}")
 set(PACKAGE_TARNAME       "${PACKAGE_NAME}-${PACKAGE_VERSION}")
 set(PACKAGE_BUGREPORT     "https://github.com/grpc/grpc/issues/")
@@ -248,6 +248,12 @@ else()
   set(_gRPC_CORE_NOSTDCXX_FLAGS "")
 endif()
 
+if (gRPC_XDS_USER_AGENT_IS_CSHARP)
+  # The value of the defines needs to contain quotes.
+  # See https://github.com/grpc/grpc/blob/fbf32836a418cc84f58786700273b65cb9174e1d/src/core/ext/xds/xds_api.cc#L854
+  add_definitions("-DGRPC_XDS_USER_AGENT_NAME_SUFFIX=\"csharp\"" "-DGRPC_XDS_USER_AGENT_VERSION_SUFFIX=\"2.39.0\"")
+endif()
+
 include(cmake/abseil-cpp.cmake)
 include(cmake/address_sorting.cmake)
 include(cmake/benchmark.cmake)
@@ -319,7 +325,7 @@ function(protobuf_generate_grpc_cpp)
            --plugin=protoc-gen-grpc=${_gRPC_CPP_PLUGIN}
            ${_protobuf_include_path}
            ${REL_FIL}
-      DEPENDS ${ABS_FIL} ${_gRPC_PROTOBUF_PROTOC} grpc_cpp_plugin
+      DEPENDS ${ABS_FIL} ${_gRPC_PROTOBUF_PROTOC} ${_gRPC_CPP_PLUGIN}
       WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
       COMMENT "Running gRPC C++ protocol buffer compiler on ${FIL}"
       VERBATIM)
@@ -620,9 +626,6 @@ if(gRPC_BUILD_TESTS)
   if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
     add_dependencies(buildtests_c handshake_server_with_readahead_handshaker_test)
   endif()
-  if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
-    add_dependencies(buildtests_c handshake_verify_peer_options_test)
-  endif()
   add_dependencies(buildtests_c histogram_test)
   add_dependencies(buildtests_c host_port_test)
   add_dependencies(buildtests_c hpack_encoder_test)
@@ -738,6 +741,7 @@ if(gRPC_BUILD_TESTS)
   add_dependencies(buildtests_cxx async_end2end_test)
   add_dependencies(buildtests_cxx auth_property_iterator_test)
   add_dependencies(buildtests_cxx authorization_matchers_test)
+  add_dependencies(buildtests_cxx authorization_policy_provider_test)
   add_dependencies(buildtests_cxx aws_request_signer_test)
   add_dependencies(buildtests_cxx backoff_test)
   add_dependencies(buildtests_cxx bad_streaming_id_bad_client_test)
@@ -826,6 +830,7 @@ if(gRPC_BUILD_TESTS)
   if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
     add_dependencies(buildtests_cxx client_channel_stress_test)
   endif()
+  add_dependencies(buildtests_cxx client_context_test_peer_test)
   add_dependencies(buildtests_cxx client_interceptors_end2end_test)
   if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
     add_dependencies(buildtests_cxx client_lb_end2end_test)
@@ -841,6 +846,7 @@ if(gRPC_BUILD_TESTS)
   add_dependencies(buildtests_cxx dual_ref_counted_test)
   add_dependencies(buildtests_cxx duplicate_header_bad_client_test)
   add_dependencies(buildtests_cxx end2end_test)
+  add_dependencies(buildtests_cxx endpoint_config_test)
   add_dependencies(buildtests_cxx error_details_test)
   add_dependencies(buildtests_cxx error_utils_test)
   add_dependencies(buildtests_cxx evaluate_args_test)
@@ -858,6 +864,7 @@ if(gRPC_BUILD_TESTS)
   add_dependencies(buildtests_cxx global_config_test)
   add_dependencies(buildtests_cxx google_mesh_ca_certificate_provider_factory_test)
   add_dependencies(buildtests_cxx grpc_authorization_engine_test)
+  add_dependencies(buildtests_cxx grpc_authorization_policy_provider_test)
   add_dependencies(buildtests_cxx grpc_cli)
   add_dependencies(buildtests_cxx grpc_tls_certificate_distributor_test)
   add_dependencies(buildtests_cxx grpc_tls_certificate_provider_test)
@@ -1115,13 +1122,17 @@ add_library(end2end_nosec_tests
   test/core/end2end/tests/retry_cancel_during_delay.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_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_recv_initial_metadata.cc
   test/core/end2end/tests/retry_recv_message.cc
+  test/core/end2end/tests/retry_recv_trailing_metadata_error.cc
+  test/core/end2end/tests/retry_send_op_fails.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
@@ -1249,13 +1260,17 @@ add_library(end2end_tests
   test/core/end2end/tests/retry_cancel_during_delay.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_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_recv_initial_metadata.cc
   test/core/end2end/tests/retry_recv_message.cc
+  test/core/end2end/tests/retry_recv_trailing_metadata_error.cc
+  test/core/end2end/tests/retry_send_op_fails.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
@@ -1525,10 +1540,12 @@ add_library(grpc
   src/core/ext/filters/client_channel/proxy_mapper_registry.cc
   src/core/ext/filters/client_channel/resolver.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
+  src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
+  src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc
@@ -1809,6 +1826,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/endpoint_config.cc
+  src/core/lib/event_engine/event_engine.cc
   src/core/lib/event_engine/slice_allocator.cc
   src/core/lib/event_engine/sockaddr.cc
   src/core/lib/http/format_request.cc
@@ -1822,6 +1841,7 @@ add_library(grpc
   src/core/lib/iomgr/dualstack_socket_posix.cc
   src/core/lib/iomgr/endpoint.cc
   src/core/lib/iomgr/endpoint_cfstream.cc
+  src/core/lib/iomgr/endpoint_pair_event_engine.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
   src/core/lib/iomgr/endpoint_pair_uv.cc
   src/core/lib/iomgr/endpoint_pair_windows.cc
@@ -1833,6 +1853,14 @@ add_library(grpc
   src/core/lib/iomgr/ev_poll_posix.cc
   src/core/lib/iomgr/ev_posix.cc
   src/core/lib/iomgr/ev_windows.cc
+  src/core/lib/iomgr/event_engine/closure.cc
+  src/core/lib/iomgr/event_engine/endpoint.cc
+  src/core/lib/iomgr/event_engine/iomgr.cc
+  src/core/lib/iomgr/event_engine/pollset.cc
+  src/core/lib/iomgr/event_engine/resolved_address_internal.cc
+  src/core/lib/iomgr/event_engine/resolver.cc
+  src/core/lib/iomgr/event_engine/tcp.cc
+  src/core/lib/iomgr/event_engine/timer.cc
   src/core/lib/iomgr/exec_ctx.cc
   src/core/lib/iomgr/executor.cc
   src/core/lib/iomgr/executor/mpmcqueue.cc
@@ -1912,6 +1940,8 @@ add_library(grpc
   src/core/lib/json/json_util.cc
   src/core/lib/json/json_writer.cc
   src/core/lib/matchers/matchers.cc
+  src/core/lib/security/authorization/authorization_policy_provider_vtable.cc
+  src/core/lib/security/authorization/evaluate_args.cc
   src/core/lib/security/context/security_context.cc
   src/core/lib/security/credentials/alts/alts_credentials.cc
   src/core/lib/security/credentials/alts/check_gcp_environment.cc
@@ -2089,7 +2119,7 @@ foreach(_hdr
   include/grpc/byte_buffer_reader.h
   include/grpc/census.h
   include/grpc/compression.h
-  include/grpc/event_engine/channel_args.h
+  include/grpc/event_engine/endpoint_config.h
   include/grpc/event_engine/event_engine.h
   include/grpc/event_engine/port.h
   include/grpc/event_engine/slice_allocator.h
@@ -2326,6 +2356,7 @@ add_library(grpc_unsecure
   src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc
   src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
   src/core/ext/filters/client_channel/lb_policy/priority/priority.cc
+  src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.cc
   src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
   src/core/ext/filters/client_channel/lb_policy/weighted_target/weighted_target.cc
   src/core/ext/filters/client_channel/lb_policy_registry.cc
@@ -2333,10 +2364,12 @@ add_library(grpc_unsecure
   src/core/ext/filters/client_channel/proxy_mapper_registry.cc
   src/core/ext/filters/client_channel/resolver.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
+  src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
+  src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc
   src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc
@@ -2430,6 +2463,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/endpoint_config.cc
+  src/core/lib/event_engine/event_engine.cc
   src/core/lib/event_engine/slice_allocator.cc
   src/core/lib/event_engine/sockaddr.cc
   src/core/lib/http/format_request.cc
@@ -2442,6 +2477,7 @@ add_library(grpc_unsecure
   src/core/lib/iomgr/dualstack_socket_posix.cc
   src/core/lib/iomgr/endpoint.cc
   src/core/lib/iomgr/endpoint_cfstream.cc
+  src/core/lib/iomgr/endpoint_pair_event_engine.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
   src/core/lib/iomgr/endpoint_pair_uv.cc
   src/core/lib/iomgr/endpoint_pair_windows.cc
@@ -2453,6 +2489,14 @@ add_library(grpc_unsecure
   src/core/lib/iomgr/ev_poll_posix.cc
   src/core/lib/iomgr/ev_posix.cc
   src/core/lib/iomgr/ev_windows.cc
+  src/core/lib/iomgr/event_engine/closure.cc
+  src/core/lib/iomgr/event_engine/endpoint.cc
+  src/core/lib/iomgr/event_engine/iomgr.cc
+  src/core/lib/iomgr/event_engine/pollset.cc
+  src/core/lib/iomgr/event_engine/resolved_address_internal.cc
+  src/core/lib/iomgr/event_engine/resolver.cc
+  src/core/lib/iomgr/event_engine/tcp.cc
+  src/core/lib/iomgr/event_engine/timer.cc
   src/core/lib/iomgr/exec_ctx.cc
   src/core/lib/iomgr/executor.cc
   src/core/lib/iomgr/executor/mpmcqueue.cc
@@ -2531,6 +2575,7 @@ add_library(grpc_unsecure
   src/core/lib/json/json_reader.cc
   src/core/lib/json/json_util.cc
   src/core/lib/json/json_writer.cc
+  src/core/lib/security/authorization/authorization_policy_provider_null_vtable.cc
   src/core/lib/slice/b64.cc
   src/core/lib/slice/percent_encoding.cc
   src/core/lib/slice/slice.cc
@@ -2614,6 +2659,7 @@ target_link_libraries(grpc_unsecure
   ${_gRPC_ALLTARGETS_LIBRARIES}
   absl::flat_hash_map
   absl::inlined_vector
+  absl::bind_front
   absl::statusor
   gpr
   address_sorting
@@ -2627,7 +2673,7 @@ foreach(_hdr
   include/grpc/byte_buffer_reader.h
   include/grpc/census.h
   include/grpc/compression.h
-  include/grpc/event_engine/channel_args.h
+  include/grpc/event_engine/endpoint_config.h
   include/grpc/event_engine/event_engine.h
   include/grpc/event_engine/port.h
   include/grpc/event_engine/slice_allocator.h
@@ -2969,6 +3015,7 @@ foreach(_hdr
   include/grpcpp/resource_quota.h
   include/grpcpp/security/auth_context.h
   include/grpcpp/security/auth_metadata_processor.h
+  include/grpcpp/security/authorization_policy_provider.h
   include/grpcpp/security/credentials.h
   include/grpcpp/security/server_credentials.h
   include/grpcpp/security/tls_certificate_provider.h
@@ -3261,6 +3308,7 @@ foreach(_hdr
   include/grpc++/test/mock_stream.h
   include/grpc++/test/server_context_test_spouse.h
   include/grpcpp/test/channel_test_peer.h
+  include/grpcpp/test/client_context_test_peer.h
   include/grpcpp/test/default_reactor_test_peer.h
   include/grpcpp/test/mock_stream.h
   include/grpcpp/test/server_context_test_spouse.h
@@ -3616,6 +3664,7 @@ foreach(_hdr
   include/grpcpp/resource_quota.h
   include/grpcpp/security/auth_context.h
   include/grpcpp/security/auth_metadata_processor.h
+  include/grpcpp/security/authorization_policy_provider.h
   include/grpcpp/security/credentials.h
   include/grpcpp/security/server_credentials.h
   include/grpcpp/security/tls_certificate_provider.h
@@ -3722,7 +3771,7 @@ foreach(_hdr
 endforeach()
 
 
-if(gRPC_INSTALL)
+if(gRPC_INSTALL AND NOT CMAKE_CROSSCOMPILING)
   install(TARGETS grpc_plugin_support EXPORT gRPCTargets
     RUNTIME DESTINATION ${gRPC_INSTALL_BINDIR}
     LIBRARY DESTINATION ${gRPC_INSTALL_LIBDIR}
@@ -5701,35 +5750,6 @@ 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(handshake_verify_peer_options_test
-    test/core/handshake/verify_peer_options.cc
-  )
-
-  target_include_directories(handshake_verify_peer_options_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(handshake_verify_peer_options_test
-    ${_gRPC_ALLTARGETS_LIBRARIES}
-    grpc_test_util
-  )
-
-
-endif()
-endif()
-if(gRPC_BUILD_TESTS)
 
 add_executable(histogram_test
   test/core/util/histogram_test.cc
@@ -6485,6 +6505,11 @@ endif()
 if(gRPC_BUILD_TESTS)
 
 add_executable(public_headers_must_be_c89
+  src/core/lib/security/authorization/grpc_authorization_engine.cc
+  src/core/lib/security/authorization/grpc_authorization_policy_provider.cc
+  src/core/lib/security/authorization/matchers.cc
+  src/core/lib/security/authorization/rbac_policy.cc
+  src/core/lib/security/authorization/rbac_translator.cc
   test/core/surface/public_headers_must_be_c89.c
 )
 
@@ -8015,7 +8040,6 @@ endif()
 if(gRPC_BUILD_TESTS)
 
 add_executable(authorization_matchers_test
-  src/core/lib/security/authorization/evaluate_args.cc
   src/core/lib/security/authorization/grpc_authorization_engine.cc
   src/core/lib/security/authorization/matchers.cc
   src/core/lib/security/authorization/rbac_policy.cc
@@ -8053,6 +8077,48 @@ target_link_libraries(authorization_matchers_test
 endif()
 if(gRPC_BUILD_TESTS)
 
+add_executable(authorization_policy_provider_test
+  src/core/lib/security/authorization/grpc_authorization_engine.cc
+  src/core/lib/security/authorization/grpc_authorization_policy_provider.cc
+  src/core/lib/security/authorization/matchers.cc
+  src/core/lib/security/authorization/rbac_policy.cc
+  src/core/lib/security/authorization/rbac_translator.cc
+  src/cpp/server/authorization_policy_provider.cc
+  test/cpp/server/authorization_policy_provider_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+target_include_directories(authorization_policy_provider_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(authorization_policy_provider_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc++
+  grpc_test_util
+)
+
+
+endif()
+if(gRPC_BUILD_TESTS)
+
 add_executable(aws_request_signer_test
   test/core/security/aws_request_signer_test.cc
   third_party/googletest/googletest/src/gtest-all.cc
@@ -9130,7 +9196,6 @@ if(gRPC_BUILD_TESTS)
 
 add_executable(cel_authorization_engine_test
   src/core/lib/security/authorization/cel_authorization_engine.cc
-  src/core/lib/security/authorization/evaluate_args.cc
   src/core/lib/security/authorization/grpc_authorization_engine.cc
   src/core/lib/security/authorization/matchers.cc
   src/core/lib/security/authorization/rbac_policy.cc
@@ -9690,6 +9755,42 @@ endif()
 endif()
 if(gRPC_BUILD_TESTS)
 
+add_executable(client_context_test_peer_test
+  test/cpp/test/client_context_test_peer_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+target_include_directories(client_context_test_peer_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(client_context_test_peer_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc++_test
+  grpc++_test_util
+)
+
+
+endif()
+if(gRPC_BUILD_TESTS)
+
 add_executable(client_interceptors_end2end_test
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo.grpc.pb.cc
@@ -10234,6 +10335,41 @@ target_link_libraries(end2end_test
 endif()
 if(gRPC_BUILD_TESTS)
 
+add_executable(endpoint_config_test
+  test/core/event_engine/endpoint_config_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+target_include_directories(endpoint_config_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(endpoint_config_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_test_util
+)
+
+
+endif()
+if(gRPC_BUILD_TESTS)
+
 add_executable(error_details_test
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/status/status.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/status/status.grpc.pb.cc
@@ -10314,10 +10450,6 @@ endif()
 if(gRPC_BUILD_TESTS)
 
 add_executable(evaluate_args_test
-  src/core/lib/security/authorization/evaluate_args.cc
-  src/core/lib/security/authorization/grpc_authorization_engine.cc
-  src/core/lib/security/authorization/matchers.cc
-  src/core/lib/security/authorization/rbac_policy.cc
   test/core/security/evaluate_args_test.cc
   third_party/googletest/googletest/src/gtest-all.cc
   third_party/googletest/googlemock/src/gmock-all.cc
@@ -10730,7 +10862,6 @@ endif()
 if(gRPC_BUILD_TESTS)
 
 add_executable(grpc_authorization_engine_test
-  src/core/lib/security/authorization/evaluate_args.cc
   src/core/lib/security/authorization/grpc_authorization_engine.cc
   src/core/lib/security/authorization/matchers.cc
   src/core/lib/security/authorization/rbac_policy.cc
@@ -10768,6 +10899,46 @@ target_link_libraries(grpc_authorization_engine_test
 endif()
 if(gRPC_BUILD_TESTS)
 
+add_executable(grpc_authorization_policy_provider_test
+  src/core/lib/security/authorization/grpc_authorization_engine.cc
+  src/core/lib/security/authorization/grpc_authorization_policy_provider.cc
+  src/core/lib/security/authorization/matchers.cc
+  src/core/lib/security/authorization/rbac_policy.cc
+  src/core/lib/security/authorization/rbac_translator.cc
+  test/core/security/grpc_authorization_policy_provider_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+target_include_directories(grpc_authorization_policy_provider_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(grpc_authorization_policy_provider_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_test_util
+)
+
+
+endif()
+if(gRPC_BUILD_TESTS)
+
 add_executable(grpc_cli
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/reflection/v1alpha/reflection.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.cc
@@ -10843,7 +11014,7 @@ target_link_libraries(grpc_cpp_plugin
 
 
 
-if(gRPC_INSTALL)
+if(gRPC_INSTALL AND NOT CMAKE_CROSSCOMPILING)
   install(TARGETS grpc_cpp_plugin EXPORT gRPCTargets
     RUNTIME DESTINATION ${gRPC_INSTALL_BINDIR}
     LIBRARY DESTINATION ${gRPC_INSTALL_LIBDIR}
@@ -10882,7 +11053,7 @@ target_link_libraries(grpc_csharp_plugin
 
 
 
-if(gRPC_INSTALL)
+if(gRPC_INSTALL AND NOT CMAKE_CROSSCOMPILING)
   install(TARGETS grpc_csharp_plugin EXPORT gRPCTargets
     RUNTIME DESTINATION ${gRPC_INSTALL_BINDIR}
     LIBRARY DESTINATION ${gRPC_INSTALL_LIBDIR}
@@ -10921,7 +11092,7 @@ target_link_libraries(grpc_node_plugin
 
 
 
-if(gRPC_INSTALL)
+if(gRPC_INSTALL AND NOT CMAKE_CROSSCOMPILING)
   install(TARGETS grpc_node_plugin EXPORT gRPCTargets
     RUNTIME DESTINATION ${gRPC_INSTALL_BINDIR}
     LIBRARY DESTINATION ${gRPC_INSTALL_LIBDIR}
@@ -10960,7 +11131,7 @@ target_link_libraries(grpc_objective_c_plugin
 
 
 
-if(gRPC_INSTALL)
+if(gRPC_INSTALL AND NOT CMAKE_CROSSCOMPILING)
   install(TARGETS grpc_objective_c_plugin EXPORT gRPCTargets
     RUNTIME DESTINATION ${gRPC_INSTALL_BINDIR}
     LIBRARY DESTINATION ${gRPC_INSTALL_LIBDIR}
@@ -10999,7 +11170,7 @@ target_link_libraries(grpc_php_plugin
 
 
 
-if(gRPC_INSTALL)
+if(gRPC_INSTALL AND NOT CMAKE_CROSSCOMPILING)
   install(TARGETS grpc_php_plugin EXPORT gRPCTargets
     RUNTIME DESTINATION ${gRPC_INSTALL_BINDIR}
     LIBRARY DESTINATION ${gRPC_INSTALL_LIBDIR}
@@ -11038,7 +11209,7 @@ target_link_libraries(grpc_python_plugin
 
 
 
-if(gRPC_INSTALL)
+if(gRPC_INSTALL AND NOT CMAKE_CROSSCOMPILING)
   install(TARGETS grpc_python_plugin EXPORT gRPCTargets
     RUNTIME DESTINATION ${gRPC_INSTALL_BINDIR}
     LIBRARY DESTINATION ${gRPC_INSTALL_LIBDIR}
@@ -11077,7 +11248,7 @@ target_link_libraries(grpc_ruby_plugin
 
 
 
-if(gRPC_INSTALL)
+if(gRPC_INSTALL AND NOT CMAKE_CROSSCOMPILING)
   install(TARGETS grpc_ruby_plugin EXPORT gRPCTargets
     RUNTIME DESTINATION ${gRPC_INSTALL_BINDIR}
     LIBRARY DESTINATION ${gRPC_INSTALL_LIBDIR}
@@ -12797,8 +12968,8 @@ endif()
 if(gRPC_BUILD_TESTS)
 
 add_executable(rbac_translator_test
-  src/core/lib/security/authorization/evaluate_args.cc
   src/core/lib/security/authorization/grpc_authorization_engine.cc
+  src/core/lib/security/authorization/grpc_authorization_policy_provider.cc
   src/core/lib/security/authorization/matchers.cc
   src/core/lib/security/authorization/rbac_policy.cc
   src/core/lib/security/authorization/rbac_translator.cc
@@ -15706,7 +15877,7 @@ generate_pkgconfig(
   "gRPC unsecure"
   "high performance general RPC framework without SSL"
   "${gRPC_CORE_VERSION}"
-  "gpr absl_base absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time"
+  "gpr absl_base absl_bind_front absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time"
   "-lgrpc_unsecure"
   ""
   "grpc_unsecure.pc")
@@ -15726,7 +15897,7 @@ generate_pkgconfig(
   "gRPC++ unsecure"
   "C++ wrapper for gRPC without SSL"
   "${gRPC_CPP_VERSION}"
-  "grpc_unsecure absl_base absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time"
+  "grpc_unsecure absl_base absl_bind_front absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time"
   "-lgrpc++_unsecure"
   ""
   "grpc++_unsecure.pc")
index f338c6b..6f6c8a5 100644 (file)
@@ -13,6 +13,7 @@ for general contribution guidelines.
 - [arjunroy](https://github.com/arjunroy), Google LLC
 - [AspirinSJL](https://github.com/AspirinSJL), Google LLC
 - [bogdandrutu](https://github.com/bogdandrutu), Google LLC
+- [ctiller](https://github.com/ctiller), Google LLC
 - [daniel-j-born](https://github.com/daniel-j-born), Google LLC
 - [dapengzhang0](https://github.com/dapengzhang0), Google LLC
 - [dfawley](https://github.com/dfawley), Google LLC
@@ -64,7 +65,6 @@ for general contribution guidelines.
 ## Emeritus Maintainers (in alphabetical order)
 - [adelez](https://github.com/adelez), Google LLC
 - [billfeng327](https://github.com/billfeng327), Google LLC
-- [ctiller](https://github.com/ctiller), Google LLC
 - [dgquintas](https://github.com/dgquintas), Google LLC
 - [fengli79](https://github.com/fengli79), Google LLC
 - [jcanizales](https://github.com/jcanizales), Google LLC
index 5cc86d8..8b07294 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -454,9 +454,9 @@ E = @echo
 Q = @
 endif
 
-CORE_VERSION = 16.0.0
-CPP_VERSION = 1.38.1
-CSHARP_VERSION = 2.38.1
+CORE_VERSION = 18.0.0
+CPP_VERSION = 1.39.0
+CSHARP_VERSION = 2.39.0
 
 CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES))
 CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS)
@@ -492,7 +492,7 @@ SHARED_EXT_CORE = dll
 SHARED_EXT_CPP = dll
 SHARED_EXT_CSHARP = dll
 SHARED_PREFIX =
-SHARED_VERSION_CORE = -16
+SHARED_VERSION_CORE = -18
 SHARED_VERSION_CPP = -1
 SHARED_VERSION_CSHARP = -2
 else ifeq ($(SYSTEM),Darwin)
@@ -891,8 +891,8 @@ $(LIBDIR)/$(CONFIG)/libaddress_sorting$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE):
 ifeq ($(SYSTEM),Darwin)
        $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name $(SHARED_PREFIX)address_sorting$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libaddress_sorting$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBADDRESS_SORTING_OBJS) $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
 else
-       $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libaddress_sorting.so.16 -o $(LIBDIR)/$(CONFIG)/libaddress_sorting$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBADDRESS_SORTING_OBJS) $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
-       $(Q) ln -sf $(SHARED_PREFIX)address_sorting$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libaddress_sorting$(SHARED_VERSION_CORE).so.16
+       $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libaddress_sorting.so.18 -o $(LIBDIR)/$(CONFIG)/libaddress_sorting$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBADDRESS_SORTING_OBJS) $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
+       $(Q) ln -sf $(SHARED_PREFIX)address_sorting$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libaddress_sorting$(SHARED_VERSION_CORE).so.18
        $(Q) ln -sf $(SHARED_PREFIX)address_sorting$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libaddress_sorting$(SHARED_VERSION_CORE).so
 endif
 endif
@@ -1032,8 +1032,8 @@ $(LIBDIR)/$(CONFIG)/libgpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE): $(LIBGPR_OB
 ifeq ($(SYSTEM),Darwin)
        $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name $(SHARED_PREFIX)gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGPR_OBJS) $(LIBDIR)/$(CONFIG)/libupb.a $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
 else
-       $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgpr.so.16 -o $(LIBDIR)/$(CONFIG)/libgpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGPR_OBJS) $(LIBDIR)/$(CONFIG)/libupb.a $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
-       $(Q) ln -sf $(SHARED_PREFIX)gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgpr$(SHARED_VERSION_CORE).so.16
+       $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgpr.so.18 -o $(LIBDIR)/$(CONFIG)/libgpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGPR_OBJS) $(LIBDIR)/$(CONFIG)/libupb.a $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
+       $(Q) ln -sf $(SHARED_PREFIX)gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgpr$(SHARED_VERSION_CORE).so.18
        $(Q) ln -sf $(SHARED_PREFIX)gpr$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgpr$(SHARED_VERSION_CORE).so
 endif
 endif
@@ -1083,10 +1083,12 @@ LIBGRPC_SRC = \
     src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
     src/core/ext/filters/client_channel/resolver.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \
+    src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \
+    src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc \
@@ -1367,6 +1369,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/endpoint_config.cc \
+    src/core/lib/event_engine/event_engine.cc \
     src/core/lib/event_engine/slice_allocator.cc \
     src/core/lib/event_engine/sockaddr.cc \
     src/core/lib/http/format_request.cc \
@@ -1380,6 +1384,7 @@ LIBGRPC_SRC = \
     src/core/lib/iomgr/dualstack_socket_posix.cc \
     src/core/lib/iomgr/endpoint.cc \
     src/core/lib/iomgr/endpoint_cfstream.cc \
+    src/core/lib/iomgr/endpoint_pair_event_engine.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
     src/core/lib/iomgr/endpoint_pair_uv.cc \
     src/core/lib/iomgr/endpoint_pair_windows.cc \
@@ -1391,6 +1396,14 @@ LIBGRPC_SRC = \
     src/core/lib/iomgr/ev_poll_posix.cc \
     src/core/lib/iomgr/ev_posix.cc \
     src/core/lib/iomgr/ev_windows.cc \
+    src/core/lib/iomgr/event_engine/closure.cc \
+    src/core/lib/iomgr/event_engine/endpoint.cc \
+    src/core/lib/iomgr/event_engine/iomgr.cc \
+    src/core/lib/iomgr/event_engine/pollset.cc \
+    src/core/lib/iomgr/event_engine/resolved_address_internal.cc \
+    src/core/lib/iomgr/event_engine/resolver.cc \
+    src/core/lib/iomgr/event_engine/tcp.cc \
+    src/core/lib/iomgr/event_engine/timer.cc \
     src/core/lib/iomgr/exec_ctx.cc \
     src/core/lib/iomgr/executor.cc \
     src/core/lib/iomgr/executor/mpmcqueue.cc \
@@ -1470,6 +1483,8 @@ LIBGRPC_SRC = \
     src/core/lib/json/json_util.cc \
     src/core/lib/json/json_writer.cc \
     src/core/lib/matchers/matchers.cc \
+    src/core/lib/security/authorization/authorization_policy_provider_vtable.cc \
+    src/core/lib/security/authorization/evaluate_args.cc \
     src/core/lib/security/context/security_context.cc \
     src/core/lib/security/credentials/alts/alts_credentials.cc \
     src/core/lib/security/credentials/alts/check_gcp_environment.cc \
@@ -1597,7 +1612,7 @@ PUBLIC_HEADERS_C += \
     include/grpc/byte_buffer_reader.h \
     include/grpc/census.h \
     include/grpc/compression.h \
-    include/grpc/event_engine/channel_args.h \
+    include/grpc/event_engine/endpoint_config.h \
     include/grpc/event_engine/event_engine.h \
     include/grpc/event_engine/port.h \
     include/grpc/event_engine/slice_allocator.h \
@@ -1648,8 +1663,8 @@ $(LIBDIR)/$(CONFIG)/libgrpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE): $(LIBGRPC_
 ifeq ($(SYSTEM),Darwin)
        $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name $(SHARED_PREFIX)grpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(OPENSSL_MERGE_LIBS) $(LDLIBS_SECURE) $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
 else
-       $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc.so.16 -o $(LIBDIR)/$(CONFIG)/libgrpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(OPENSSL_MERGE_LIBS) $(LDLIBS_SECURE) $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
-       $(Q) ln -sf $(SHARED_PREFIX)grpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgrpc$(SHARED_VERSION_CORE).so.16
+       $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc.so.18 -o $(LIBDIR)/$(CONFIG)/libgrpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(OPENSSL_MERGE_LIBS) $(LDLIBS_SECURE) $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
+       $(Q) ln -sf $(SHARED_PREFIX)grpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgrpc$(SHARED_VERSION_CORE).so.18
        $(Q) ln -sf $(SHARED_PREFIX)grpc$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgrpc$(SHARED_VERSION_CORE).so
 endif
 endif
@@ -1696,8 +1711,8 @@ $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE):
 ifeq ($(SYSTEM),Darwin)
        $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name $(SHARED_PREFIX)grpc_csharp_ext$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_CSHARP_EXT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
 else
-       $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc_csharp_ext.so.16 -o $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_CSHARP_EXT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
-       $(Q) ln -sf $(SHARED_PREFIX)grpc_csharp_ext$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext$(SHARED_VERSION_CORE).so.16
+       $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc_csharp_ext.so.18 -o $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_CSHARP_EXT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
+       $(Q) ln -sf $(SHARED_PREFIX)grpc_csharp_ext$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext$(SHARED_VERSION_CORE).so.18
        $(Q) ln -sf $(SHARED_PREFIX)grpc_csharp_ext$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext$(SHARED_VERSION_CORE).so
 endif
 endif
@@ -1735,6 +1750,7 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc \
     src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc \
     src/core/ext/filters/client_channel/lb_policy/priority/priority.cc \
+    src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.cc \
     src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \
     src/core/ext/filters/client_channel/lb_policy/weighted_target/weighted_target.cc \
     src/core/ext/filters/client_channel/lb_policy_registry.cc \
@@ -1742,10 +1758,12 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
     src/core/ext/filters/client_channel/resolver.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \
+    src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \
+    src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc \
@@ -1839,6 +1857,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/endpoint_config.cc \
+    src/core/lib/event_engine/event_engine.cc \
     src/core/lib/event_engine/slice_allocator.cc \
     src/core/lib/event_engine/sockaddr.cc \
     src/core/lib/http/format_request.cc \
@@ -1851,6 +1871,7 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/lib/iomgr/dualstack_socket_posix.cc \
     src/core/lib/iomgr/endpoint.cc \
     src/core/lib/iomgr/endpoint_cfstream.cc \
+    src/core/lib/iomgr/endpoint_pair_event_engine.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
     src/core/lib/iomgr/endpoint_pair_uv.cc \
     src/core/lib/iomgr/endpoint_pair_windows.cc \
@@ -1862,6 +1883,14 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/lib/iomgr/ev_poll_posix.cc \
     src/core/lib/iomgr/ev_posix.cc \
     src/core/lib/iomgr/ev_windows.cc \
+    src/core/lib/iomgr/event_engine/closure.cc \
+    src/core/lib/iomgr/event_engine/endpoint.cc \
+    src/core/lib/iomgr/event_engine/iomgr.cc \
+    src/core/lib/iomgr/event_engine/pollset.cc \
+    src/core/lib/iomgr/event_engine/resolved_address_internal.cc \
+    src/core/lib/iomgr/event_engine/resolver.cc \
+    src/core/lib/iomgr/event_engine/tcp.cc \
+    src/core/lib/iomgr/event_engine/timer.cc \
     src/core/lib/iomgr/exec_ctx.cc \
     src/core/lib/iomgr/executor.cc \
     src/core/lib/iomgr/executor/mpmcqueue.cc \
@@ -1940,6 +1969,7 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/lib/json/json_reader.cc \
     src/core/lib/json/json_util.cc \
     src/core/lib/json/json_writer.cc \
+    src/core/lib/security/authorization/authorization_policy_provider_null_vtable.cc \
     src/core/lib/slice/b64.cc \
     src/core/lib/slice/percent_encoding.cc \
     src/core/lib/slice/slice.cc \
@@ -1988,7 +2018,7 @@ PUBLIC_HEADERS_C += \
     include/grpc/byte_buffer_reader.h \
     include/grpc/census.h \
     include/grpc/compression.h \
-    include/grpc/event_engine/channel_args.h \
+    include/grpc/event_engine/endpoint_config.h \
     include/grpc/event_engine/event_engine.h \
     include/grpc/event_engine/port.h \
     include/grpc/event_engine/slice_allocator.h \
@@ -2028,8 +2058,8 @@ $(LIBDIR)/$(CONFIG)/libgrpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE): $
 ifeq ($(SYSTEM),Darwin)
        $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name $(SHARED_PREFIX)grpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_UNSECURE_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
 else
-       $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc_unsecure.so.16 -o $(LIBDIR)/$(CONFIG)/libgrpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_UNSECURE_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
-       $(Q) ln -sf $(SHARED_PREFIX)grpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure$(SHARED_VERSION_CORE).so.16
+       $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc_unsecure.so.18 -o $(LIBDIR)/$(CONFIG)/libgrpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBGRPC_UNSECURE_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
+       $(Q) ln -sf $(SHARED_PREFIX)grpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure$(SHARED_VERSION_CORE).so.18
        $(Q) ln -sf $(SHARED_PREFIX)grpc_unsecure$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure$(SHARED_VERSION_CORE).so
 endif
 endif
@@ -2424,8 +2454,8 @@ $(LIBDIR)/$(CONFIG)/libupb$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE): $(LIBUPB_OB
 ifeq ($(SYSTEM),Darwin)
        $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name $(SHARED_PREFIX)upb$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libupb$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBUPB_OBJS) $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
 else
-       $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libupb.so.16 -o $(LIBDIR)/$(CONFIG)/libupb$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBUPB_OBJS) $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
-       $(Q) ln -sf $(SHARED_PREFIX)upb$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libupb$(SHARED_VERSION_CORE).so.16
+       $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libupb.so.18 -o $(LIBDIR)/$(CONFIG)/libupb$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBUPB_OBJS) $(ZLIB_MERGE_LIBS) $(CARES_MERGE_LIBS) $(ADDRESS_SORTING_MERGE_LIBS) $(RE2_MERGE_LIBS) $(UPB_MERGE_LIBS) $(GRPC_ABSEIL_MERGE_LIBS) $(LDLIBS)
+       $(Q) ln -sf $(SHARED_PREFIX)upb$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libupb$(SHARED_VERSION_CORE).so.18
        $(Q) ln -sf $(SHARED_PREFIX)upb$(SHARED_VERSION_CORE).$(SHARED_EXT_CORE) $(LIBDIR)/$(CONFIG)/libupb$(SHARED_VERSION_CORE).so
 endif
 endif
@@ -2669,7 +2699,6 @@ ifneq ($(OPENSSL_DEP),)
 # installing headers to their final destination on the drive. We need this
 # otherwise parallel compilation will fail if a source is compiled first.
 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc: $(OPENSSL_DEP)
-src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.cc: $(OPENSSL_DEP)
 src/core/ext/filters/client_channel/lb_policy/xds/cds.cc: $(OPENSSL_DEP)
 src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_impl.cc: $(OPENSSL_DEP)
 src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_manager.cc: $(OPENSSL_DEP)
@@ -2863,6 +2892,8 @@ src/core/ext/xds/xds_http_filters.cc: $(OPENSSL_DEP)
 src/core/ext/xds/xds_server_config_fetcher.cc: $(OPENSSL_DEP)
 src/core/lib/http/httpcli_security_connector.cc: $(OPENSSL_DEP)
 src/core/lib/matchers/matchers.cc: $(OPENSSL_DEP)
+src/core/lib/security/authorization/authorization_policy_provider_vtable.cc: $(OPENSSL_DEP)
+src/core/lib/security/authorization/evaluate_args.cc: $(OPENSSL_DEP)
 src/core/lib/security/context/security_context.cc: $(OPENSSL_DEP)
 src/core/lib/security/credentials/alts/alts_credentials.cc: $(OPENSSL_DEP)
 src/core/lib/security/credentials/alts/check_gcp_environment.cc: $(OPENSSL_DEP)
index 890a2fa..355f5bf 100644 (file)
@@ -24,3 +24,4 @@ include requirements.txt
 include etc/roots.pem
 include Makefile
 include LICENSE
+include _metadata.py
index 66e4a74..65a7a51 100644 (file)
@@ -22,6 +22,8 @@ let package = Package(
   dependencies: [
     .package(
       name: "abseil",
+      //TODO(yulinliang): Firebase abseil is too old, update the dependency
+      //when abseil repo supports SPM.
       url: "https://github.com/firebase/abseil-cpp-SwiftPM.git",
       .revision("05d8107f2971a37e6c77245b7c4c6b0a7e97bc99")
     ),
@@ -140,6 +142,13 @@ let package = Package(
         .unsafeFlags(["-Wno-module-import-in-extern-c"]),
       ]
     ),
+    .testTarget(
+      name: "build-test",
+      dependencies: [
+        "gRPC-cpp",
+      ],
+      path: "test/spm_build"
+    ),
   ],
   cLanguageStandard: .gnu11,
   cxxLanguageStandard: .cxx11
index a392de0..85a9d7b 100644 (file)
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@ Sometimes things go wrong. Please check out the [Troubleshooting guide](TROUBLES
 
 # Performance 
 
-See the [Performance dashboard](https://performance-dot-grpc-testing.appspot.com/explore?dashboard=5652536396611584) for performance numbers of master branch daily builds.
+See the [Performance dashboard](https://performance-dot-grpc-testing.appspot.com/explore?dashboard=5180705743044608) for performance numbers of master branch daily builds.
 
 # Concepts
 
similarity index 68%
rename from src/proto/grpc/auth/v1/BUILD
rename to _metadata.py
index 0e487b2..caebf64 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright 2021 gRPC authors.
+# Copyright 2021 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.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-licenses(["notice"])
+# AUTO-GENERATED FROM `$REPO_ROOT/templates/_metadata.py.template`!!!
 
-load("@rules_proto//proto:defs.bzl", "proto_library")
-
-proto_library(
-    name = "authz_policy_proto",
-    srcs = [
-        "authz_policy.proto",
-    ],
-    visibility = ["//visibility:public"],
-)
+__version__ = """1.39.0"""
index 7ec1a98..9c31f0c 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 """Generates and compiles C++ grpc stubs from proto_library rules."""
 
 load("@rules_proto//proto:defs.bzl", "proto_library")
index c9f8642..8e003c2 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 """Custom rules for gRPC Python"""
 
 # Adapted with modifications from
index a180863..b419e13 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 """Generates C++ grpc stubs from proto_library rules.
 
 This is an internal rule used by cc_grpc_library, and shouldn't be used
@@ -141,6 +154,7 @@ def generate_cc_impl(ctx):
         outputs = out_files,
         executable = ctx.executable._protoc,
         arguments = arguments,
+        use_default_shell_env = True,
     )
 
     return struct(files = depset(out_files))
index cffe404..f6424b3 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load("@rules_proto//proto:defs.bzl", "ProtoInfo")
 load(
     "//bazel:protobuf.bzl",
index 85ec2d6..a94f750 100644 (file)
@@ -64,6 +64,29 @@ def _get_external_deps(external_deps):
             ret += ["//external:" + dep]
     return ret
 
+def _update_visibility(visibility):
+    if visibility == None:
+        return None
+
+    # Visibility rules prefixed with '@grpc_' are used to flag different visibility rule
+    # classes upstream.
+    VISIBILITY_TARGETS = {
+        "alt_grpc_legacy": [],
+        "alt_grpc++_legacy": [],
+        "endpoint_tests": [],
+        "grpc_opencensus_plugin": ["//visibility:public"],
+        "grpc_resolver_fake": [],
+        "public": ["//visibility:public"],
+    }
+    final_visibility = []
+    for rule in visibility:
+        if rule.startswith("@grpc:"):
+            for replacement in VISIBILITY_TARGETS[rule[6:]]:
+                final_visibility.append(replacement)
+        else:
+            final_visibility.append(rule)
+    return [x for x in final_visibility]
+
 def grpc_cc_library(
         name,
         srcs = [],
@@ -82,6 +105,7 @@ def grpc_cc_library(
         use_cfstream = False,
         tags = [],
         linkstatic = False):
+    visibility = _update_visibility(visibility)
     copts = []
     if use_cfstream:
         copts = if_mac(["-DGRPC_CFSTREAM"])
index a463d01..105d8bd 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 """Load dependencies needed to compile and test the grpc library as a 3rd-party consumer."""
 
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
@@ -156,11 +169,11 @@ def grpc_deps():
             name = "boringssl",
             # Use github mirror instead of https://boringssl.googlesource.com/boringssl
             # to obtain a boringssl archive with consistent sha256
-            sha256 = "f8616dff15cb8aad6705af53c7caf7a5f1103b6aaf59c76b55995e179d47f89c",
-            strip_prefix = "boringssl-688fc5cf5428868679d2ae1072cad81055752068",
+            sha256 = "19870fcdbdfc61217ad814077483347a5b2bf4b3bbb5f6c983edac7856a40bbb",
+            strip_prefix = "boringssl-bcc01b6c66b1c6fa2816b108e50a544b757fbd7b",
             urls = [
-                "https://storage.googleapis.com/grpc-bazel-mirror/github.com/google/boringssl/archive/688fc5cf5428868679d2ae1072cad81055752068.tar.gz",
-                "https://github.com/google/boringssl/archive/688fc5cf5428868679d2ae1072cad81055752068.tar.gz",
+                "https://storage.googleapis.com/grpc-bazel-mirror/github.com/google/boringssl/archive/bcc01b6c66b1c6fa2816b108e50a544b757fbd7b.tar.gz",
+                "https://github.com/google/boringssl/archive/bcc01b6c66b1c6fa2816b108e50a544b757fbd7b.tar.gz",
             ],
         )
 
index 2338a14..ec17192 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 """Loads the dependencies necessary for the external repositories defined in grpc_deps.bzl."""
 
 load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
index 8f0c7db..4c47c5c 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 """Load dependencies needed to compile and test the grpc python library as a 3rd-party consumer."""
 
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
@@ -11,8 +24,8 @@ def grpc_python_deps():
         http_archive(
             name = "six",
             build_file = "@com_github_grpc_grpc//third_party:six.BUILD",
-            sha256 = "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73",
-            urls = ["https://files.pythonhosted.org/packages/dd/bf/4138e7bfb757de47d1f4b6994648ec67a51efe58fa907c1e11e350cddfca/six-1.12.0.tar.gz"],
+            sha256 = "1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
+            urls = ["https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz"],
         )
 
     if "enum34" not in native.existing_rules():
index 779747c..b85d9af 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 # Follows convention set in objectivec_helpers.cc in the protobuf ObjC compiler.
 _upper_segments_list = ["url", "http", "https"]
 
index 5b71256..bfc04d1 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load(
     "//bazel:generate_objc.bzl",
     "generate_objc",
index 6475d5b..ad304f4 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 """Utility functions for generating protobuf code."""
 
 load("@rules_proto//proto:defs.bzl", "ProtoInfo")
index 8418c29..df067d3 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 """Generates and compiles Python gRPC stubs from proto_library rules."""
 
 load("@rules_proto//proto:defs.bzl", "ProtoInfo")
index 31c9d54..def2ee1 100644 (file)
@@ -87,13 +87,17 @@ libs:
   - test/core/end2end/tests/retry_cancel_during_delay.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_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_recv_initial_metadata.cc
   - test/core/end2end/tests/retry_recv_message.cc
+  - test/core/end2end/tests/retry_recv_trailing_metadata_error.cc
+  - test/core/end2end/tests/retry_send_op_fails.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
@@ -196,13 +200,17 @@ libs:
   - test/core/end2end/tests/retry_cancel_during_delay.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_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_recv_initial_metadata.cc
   - test/core/end2end/tests/retry_recv_message.cc
+  - test/core/end2end/tests/retry_recv_trailing_metadata_error.cc
+  - test/core/end2end/tests/retry_send_op_fails.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
@@ -396,7 +404,7 @@ libs:
   - include/grpc/byte_buffer_reader.h
   - include/grpc/census.h
   - include/grpc/compression.h
-  - include/grpc/event_engine/channel_args.h
+  - include/grpc/event_engine/endpoint_config.h
   - include/grpc/event_engine/event_engine.h
   - include/grpc/event_engine/port.h
   - include/grpc/event_engine/slice_allocator.h
@@ -712,6 +720,8 @@ 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/endpoint_config_internal.h
+  - src/core/lib/event_engine/sockaddr.h
   - src/core/lib/gprpp/atomic.h
   - src/core/lib/gprpp/dual_ref_counted.h
   - src/core/lib/gprpp/orphanable.h
@@ -738,6 +748,12 @@ libs:
   - src/core/lib/iomgr/ev_epollex_linux.h
   - src/core/lib/iomgr/ev_poll_posix.h
   - src/core/lib/iomgr/ev_posix.h
+  - src/core/lib/iomgr/event_engine/closure.h
+  - src/core/lib/iomgr/event_engine/endpoint.h
+  - src/core/lib/iomgr/event_engine/iomgr.h
+  - src/core/lib/iomgr/event_engine/pollset.h
+  - src/core/lib/iomgr/event_engine/promise.h
+  - src/core/lib/iomgr/event_engine/resolved_address_internal.h
   - src/core/lib/iomgr/exec_ctx.h
   - src/core/lib/iomgr/executor.h
   - src/core/lib/iomgr/executor/mpmcqueue.h
@@ -797,6 +813,9 @@ libs:
   - src/core/lib/json/json.h
   - src/core/lib/json/json_util.h
   - src/core/lib/matchers/matchers.h
+  - src/core/lib/security/authorization/authorization_engine.h
+  - src/core/lib/security/authorization/authorization_policy_provider.h
+  - src/core/lib/security/authorization/evaluate_args.h
   - src/core/lib/security/context/security_context.h
   - src/core/lib/security/credentials/alts/alts_credentials.h
   - src/core/lib/security/credentials/alts/check_gcp_environment.h
@@ -940,10 +959,12 @@ libs:
   - src/core/ext/filters/client_channel/proxy_mapper_registry.cc
   - src/core/ext/filters/client_channel/resolver.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
+  - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
+  - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc
@@ -1224,6 +1245,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/endpoint_config.cc
+  - src/core/lib/event_engine/event_engine.cc
   - src/core/lib/event_engine/slice_allocator.cc
   - src/core/lib/event_engine/sockaddr.cc
   - src/core/lib/http/format_request.cc
@@ -1237,6 +1260,7 @@ libs:
   - src/core/lib/iomgr/dualstack_socket_posix.cc
   - src/core/lib/iomgr/endpoint.cc
   - src/core/lib/iomgr/endpoint_cfstream.cc
+  - src/core/lib/iomgr/endpoint_pair_event_engine.cc
   - src/core/lib/iomgr/endpoint_pair_posix.cc
   - src/core/lib/iomgr/endpoint_pair_uv.cc
   - src/core/lib/iomgr/endpoint_pair_windows.cc
@@ -1248,6 +1272,14 @@ libs:
   - src/core/lib/iomgr/ev_poll_posix.cc
   - src/core/lib/iomgr/ev_posix.cc
   - src/core/lib/iomgr/ev_windows.cc
+  - src/core/lib/iomgr/event_engine/closure.cc
+  - src/core/lib/iomgr/event_engine/endpoint.cc
+  - src/core/lib/iomgr/event_engine/iomgr.cc
+  - src/core/lib/iomgr/event_engine/pollset.cc
+  - src/core/lib/iomgr/event_engine/resolved_address_internal.cc
+  - src/core/lib/iomgr/event_engine/resolver.cc
+  - src/core/lib/iomgr/event_engine/tcp.cc
+  - src/core/lib/iomgr/event_engine/timer.cc
   - src/core/lib/iomgr/exec_ctx.cc
   - src/core/lib/iomgr/executor.cc
   - src/core/lib/iomgr/executor/mpmcqueue.cc
@@ -1327,6 +1359,8 @@ libs:
   - src/core/lib/json/json_util.cc
   - src/core/lib/json/json_writer.cc
   - src/core/lib/matchers/matchers.cc
+  - src/core/lib/security/authorization/authorization_policy_provider_vtable.cc
+  - src/core/lib/security/authorization/evaluate_args.cc
   - src/core/lib/security/context/security_context.cc
   - src/core/lib/security/credentials/alts/alts_credentials.cc
   - src/core/lib/security/credentials/alts/check_gcp_environment.cc
@@ -1583,7 +1617,7 @@ libs:
   - include/grpc/byte_buffer_reader.h
   - include/grpc/census.h
   - include/grpc/compression.h
-  - include/grpc/event_engine/channel_args.h
+  - include/grpc/event_engine/endpoint_config.h
   - include/grpc/event_engine/event_engine.h
   - include/grpc/event_engine/port.h
   - include/grpc/event_engine/slice_allocator.h
@@ -1618,6 +1652,7 @@ libs:
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
+  - src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.h
   - src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
   - src/core/ext/filters/client_channel/lb_policy_factory.h
   - src/core/ext/filters/client_channel/lb_policy_registry.h
@@ -1710,6 +1745,8 @@ 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/endpoint_config_internal.h
+  - src/core/lib/event_engine/sockaddr.h
   - src/core/lib/gprpp/atomic.h
   - src/core/lib/gprpp/dual_ref_counted.h
   - src/core/lib/gprpp/orphanable.h
@@ -1736,6 +1773,12 @@ libs:
   - src/core/lib/iomgr/ev_epollex_linux.h
   - src/core/lib/iomgr/ev_poll_posix.h
   - src/core/lib/iomgr/ev_posix.h
+  - src/core/lib/iomgr/event_engine/closure.h
+  - src/core/lib/iomgr/event_engine/endpoint.h
+  - src/core/lib/iomgr/event_engine/iomgr.h
+  - src/core/lib/iomgr/event_engine/pollset.h
+  - src/core/lib/iomgr/event_engine/promise.h
+  - src/core/lib/iomgr/event_engine/resolved_address_internal.h
   - src/core/lib/iomgr/exec_ctx.h
   - src/core/lib/iomgr/executor.h
   - src/core/lib/iomgr/executor/mpmcqueue.h
@@ -1828,6 +1871,7 @@ libs:
   - src/core/lib/transport/transport.h
   - src/core/lib/transport/transport_impl.h
   - src/core/lib/uri/uri_parser.h
+  - third_party/xxhash/xxhash.h
   src:
   - src/core/ext/filters/census/grpc_context.cc
   - src/core/ext/filters/client_channel/backend_metric.cc
@@ -1854,6 +1898,7 @@ libs:
   - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc
   - src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
   - src/core/ext/filters/client_channel/lb_policy/priority/priority.cc
+  - src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.cc
   - src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
   - src/core/ext/filters/client_channel/lb_policy/weighted_target/weighted_target.cc
   - src/core/ext/filters/client_channel/lb_policy_registry.cc
@@ -1861,10 +1906,12 @@ libs:
   - src/core/ext/filters/client_channel/proxy_mapper_registry.cc
   - src/core/ext/filters/client_channel/resolver.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
+  - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
+  - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc
   - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc
@@ -1958,6 +2005,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/endpoint_config.cc
+  - src/core/lib/event_engine/event_engine.cc
   - src/core/lib/event_engine/slice_allocator.cc
   - src/core/lib/event_engine/sockaddr.cc
   - src/core/lib/http/format_request.cc
@@ -1970,6 +2019,7 @@ libs:
   - src/core/lib/iomgr/dualstack_socket_posix.cc
   - src/core/lib/iomgr/endpoint.cc
   - src/core/lib/iomgr/endpoint_cfstream.cc
+  - src/core/lib/iomgr/endpoint_pair_event_engine.cc
   - src/core/lib/iomgr/endpoint_pair_posix.cc
   - src/core/lib/iomgr/endpoint_pair_uv.cc
   - src/core/lib/iomgr/endpoint_pair_windows.cc
@@ -1981,6 +2031,14 @@ libs:
   - src/core/lib/iomgr/ev_poll_posix.cc
   - src/core/lib/iomgr/ev_posix.cc
   - src/core/lib/iomgr/ev_windows.cc
+  - src/core/lib/iomgr/event_engine/closure.cc
+  - src/core/lib/iomgr/event_engine/endpoint.cc
+  - src/core/lib/iomgr/event_engine/iomgr.cc
+  - src/core/lib/iomgr/event_engine/pollset.cc
+  - src/core/lib/iomgr/event_engine/resolved_address_internal.cc
+  - src/core/lib/iomgr/event_engine/resolver.cc
+  - src/core/lib/iomgr/event_engine/tcp.cc
+  - src/core/lib/iomgr/event_engine/timer.cc
   - src/core/lib/iomgr/exec_ctx.cc
   - src/core/lib/iomgr/executor.cc
   - src/core/lib/iomgr/executor/mpmcqueue.cc
@@ -2059,6 +2117,7 @@ libs:
   - src/core/lib/json/json_reader.cc
   - src/core/lib/json/json_util.cc
   - src/core/lib/json/json_writer.cc
+  - src/core/lib/security/authorization/authorization_policy_provider_null_vtable.cc
   - src/core/lib/slice/b64.cc
   - src/core/lib/slice/percent_encoding.cc
   - src/core/lib/slice/slice.cc
@@ -2104,6 +2163,7 @@ libs:
   deps:
   - absl/container:flat_hash_map
   - absl/container:inlined_vector
+  - absl/functional:bind_front
   - absl/status:statusor
   - gpr
   - address_sorting
@@ -2286,6 +2346,7 @@ libs:
   - include/grpcpp/resource_quota.h
   - include/grpcpp/security/auth_context.h
   - include/grpcpp/security/auth_metadata_processor.h
+  - include/grpcpp/security/authorization_policy_provider.h
   - include/grpcpp/security/credentials.h
   - include/grpcpp/security/server_credentials.h
   - include/grpcpp/security/tls_certificate_provider.h
@@ -2428,6 +2489,7 @@ libs:
   - include/grpc++/test/mock_stream.h
   - include/grpc++/test/server_context_test_spouse.h
   - include/grpcpp/test/channel_test_peer.h
+  - include/grpcpp/test/client_context_test_peer.h
   - include/grpcpp/test/default_reactor_test_peer.h
   - include/grpcpp/test/mock_stream.h
   - include/grpcpp/test/server_context_test_spouse.h
@@ -2630,6 +2692,7 @@ libs:
   - include/grpcpp/resource_quota.h
   - include/grpcpp/security/auth_context.h
   - include/grpcpp/security/auth_metadata_processor.h
+  - include/grpcpp/security/authorization_policy_provider.h
   - include/grpcpp/security/credentials.h
   - include/grpcpp/security/server_credentials.h
   - include/grpcpp/security/tls_certificate_provider.h
@@ -3390,18 +3453,6 @@ targets:
   - linux
   - posix
   - mac
-- name: handshake_verify_peer_options_test
-  build: test
-  language: c
-  headers: []
-  src:
-  - test/core/handshake/verify_peer_options.cc
-  deps:
-  - grpc_test_util
-  platforms:
-  - linux
-  - posix
-  - mac
 - name: histogram_test
   build: test
   language: c
@@ -3678,8 +3729,18 @@ targets:
 - name: public_headers_must_be_c89
   build: test
   language: c
-  headers: []
+  headers:
+  - src/core/lib/security/authorization/grpc_authorization_engine.h
+  - src/core/lib/security/authorization/grpc_authorization_policy_provider.h
+  - src/core/lib/security/authorization/matchers.h
+  - src/core/lib/security/authorization/rbac_policy.h
+  - src/core/lib/security/authorization/rbac_translator.h
   src:
+  - src/core/lib/security/authorization/grpc_authorization_engine.cc
+  - src/core/lib/security/authorization/grpc_authorization_policy_provider.cc
+  - src/core/lib/security/authorization/matchers.cc
+  - src/core/lib/security/authorization/rbac_policy.cc
+  - src/core/lib/security/authorization/rbac_translator.cc
   - test/core/surface/public_headers_must_be_c89.c
   deps:
   - grpc_test_util
@@ -4229,19 +4290,37 @@ targets:
   build: test
   language: c++
   headers:
-  - src/core/lib/security/authorization/authorization_engine.h
-  - src/core/lib/security/authorization/evaluate_args.h
   - src/core/lib/security/authorization/grpc_authorization_engine.h
   - src/core/lib/security/authorization/matchers.h
   - src/core/lib/security/authorization/rbac_policy.h
   src:
-  - src/core/lib/security/authorization/evaluate_args.cc
   - src/core/lib/security/authorization/grpc_authorization_engine.cc
   - src/core/lib/security/authorization/matchers.cc
   - src/core/lib/security/authorization/rbac_policy.cc
   - test/core/security/authorization_matchers_test.cc
   deps:
   - grpc_test_util
+- name: authorization_policy_provider_test
+  gtest: true
+  build: test
+  language: c++
+  headers:
+  - src/core/lib/security/authorization/grpc_authorization_engine.h
+  - src/core/lib/security/authorization/grpc_authorization_policy_provider.h
+  - src/core/lib/security/authorization/matchers.h
+  - src/core/lib/security/authorization/rbac_policy.h
+  - src/core/lib/security/authorization/rbac_translator.h
+  src:
+  - src/core/lib/security/authorization/grpc_authorization_engine.cc
+  - src/core/lib/security/authorization/grpc_authorization_policy_provider.cc
+  - src/core/lib/security/authorization/matchers.cc
+  - src/core/lib/security/authorization/rbac_policy.cc
+  - src/core/lib/security/authorization/rbac_translator.cc
+  - src/cpp/server/authorization_policy_provider.cc
+  - test/cpp/server/authorization_policy_provider_test.cc
+  deps:
+  - grpc++
+  - grpc_test_util
 - name: aws_request_signer_test
   gtest: true
   build: test
@@ -4651,9 +4730,7 @@ targets:
   build: test
   language: c++
   headers:
-  - src/core/lib/security/authorization/authorization_engine.h
   - src/core/lib/security/authorization/cel_authorization_engine.h
-  - src/core/lib/security/authorization/evaluate_args.h
   - src/core/lib/security/authorization/grpc_authorization_engine.h
   - src/core/lib/security/authorization/matchers.h
   - src/core/lib/security/authorization/mock_cel/activation.h
@@ -4665,7 +4742,6 @@ targets:
   - src/core/lib/security/authorization/rbac_policy.h
   src:
   - src/core/lib/security/authorization/cel_authorization_engine.cc
-  - src/core/lib/security/authorization/evaluate_args.cc
   - src/core/lib/security/authorization/grpc_authorization_engine.cc
   - src/core/lib/security/authorization/matchers.cc
   - src/core/lib/security/authorization/rbac_policy.cc
@@ -4843,6 +4919,16 @@ targets:
   - linux
   - posix
   - mac
+- name: client_context_test_peer_test
+  gtest: true
+  build: test
+  language: c++
+  headers: []
+  src:
+  - test/cpp/test/client_context_test_peer_test.cc
+  deps:
+  - grpc++_test
+  - grpc++_test_util
 - name: client_fuzzer
   build: fuzzer
   language: c++
@@ -5029,6 +5115,15 @@ targets:
   deps:
   - grpc++_test
   - grpc++_test_util
+- name: endpoint_config_test
+  gtest: true
+  build: test
+  language: c++
+  headers: []
+  src:
+  - test/core/event_engine/endpoint_config_test.cc
+  deps:
+  - grpc_test_util
 - name: error_details_test
   gtest: true
   build: test
@@ -5054,17 +5149,8 @@ targets:
   gtest: true
   build: test
   language: c++
-  headers:
-  - src/core/lib/security/authorization/authorization_engine.h
-  - src/core/lib/security/authorization/evaluate_args.h
-  - src/core/lib/security/authorization/grpc_authorization_engine.h
-  - src/core/lib/security/authorization/matchers.h
-  - src/core/lib/security/authorization/rbac_policy.h
+  headers: []
   src:
-  - src/core/lib/security/authorization/evaluate_args.cc
-  - src/core/lib/security/authorization/grpc_authorization_engine.cc
-  - src/core/lib/security/authorization/matchers.cc
-  - src/core/lib/security/authorization/rbac_policy.cc
   - test/core/security/evaluate_args_test.cc
   deps:
   - grpc_test_util
@@ -5184,19 +5270,35 @@ targets:
   build: test
   language: c++
   headers:
-  - src/core/lib/security/authorization/authorization_engine.h
-  - src/core/lib/security/authorization/evaluate_args.h
   - src/core/lib/security/authorization/grpc_authorization_engine.h
   - src/core/lib/security/authorization/matchers.h
   - src/core/lib/security/authorization/rbac_policy.h
   src:
-  - src/core/lib/security/authorization/evaluate_args.cc
   - src/core/lib/security/authorization/grpc_authorization_engine.cc
   - src/core/lib/security/authorization/matchers.cc
   - src/core/lib/security/authorization/rbac_policy.cc
   - test/core/security/grpc_authorization_engine_test.cc
   deps:
   - grpc_test_util
+- name: grpc_authorization_policy_provider_test
+  gtest: true
+  build: test
+  language: c++
+  headers:
+  - src/core/lib/security/authorization/grpc_authorization_engine.h
+  - src/core/lib/security/authorization/grpc_authorization_policy_provider.h
+  - src/core/lib/security/authorization/matchers.h
+  - src/core/lib/security/authorization/rbac_policy.h
+  - src/core/lib/security/authorization/rbac_translator.h
+  src:
+  - src/core/lib/security/authorization/grpc_authorization_engine.cc
+  - src/core/lib/security/authorization/grpc_authorization_policy_provider.cc
+  - src/core/lib/security/authorization/matchers.cc
+  - src/core/lib/security/authorization/rbac_policy.cc
+  - src/core/lib/security/authorization/rbac_translator.cc
+  - test/core/security/grpc_authorization_policy_provider_test.cc
+  deps:
+  - grpc_test_util
 - name: grpc_cli
   build: test
   run: false
@@ -5958,15 +6060,14 @@ targets:
   build: test
   language: c++
   headers:
-  - src/core/lib/security/authorization/authorization_engine.h
-  - src/core/lib/security/authorization/evaluate_args.h
   - src/core/lib/security/authorization/grpc_authorization_engine.h
+  - src/core/lib/security/authorization/grpc_authorization_policy_provider.h
   - src/core/lib/security/authorization/matchers.h
   - src/core/lib/security/authorization/rbac_policy.h
   - src/core/lib/security/authorization/rbac_translator.h
   src:
-  - src/core/lib/security/authorization/evaluate_args.cc
   - src/core/lib/security/authorization/grpc_authorization_engine.cc
+  - src/core/lib/security/authorization/grpc_authorization_policy_provider.cc
   - src/core/lib/security/authorization/matchers.cc
   - src/core/lib/security/authorization/rbac_policy.cc
   - src/core/lib/security/authorization/rbac_translator.cc
index 98253e2..a824ddc 100644 (file)
@@ -13,5 +13,5 @@
 # limitations under the License.
 
 module GrpcBuildConfig
-  CORE_WINDOWS_DLL = '/tmp/libs/opt/grpc-16.dll'
+  CORE_WINDOWS_DLL = '/tmp/libs/opt/grpc-18.dll'
 end
index 9d60496..2479836 100644 (file)
@@ -12,11 +12,11 @@ settings:
   '#08': Use "-preN" suffixes to identify pre-release versions
   '#09': Per-language overrides are possible with (eg) ruby_version tag here
   '#10': See the expand_version.py for all the quirks here
-  core_version: 16.0.0
+  core_version: 18.0.0
   csharp_major_version: 2
   g_stands_for: guadalupe_river_park_conservancy
   protobuf_version: 3.15.8
-  version: 1.38.1
+  version: 1.39.0
 targets:
 - name: check_epollexclusive
   build: tool
index 5965358..02cfaed 100644 (file)
--- a/config.m4
+++ b/config.m4
@@ -77,10 +77,12 @@ if test "$PHP_GRPC" != "no"; then
     src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
     src/core/ext/filters/client_channel/resolver.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \
+    src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \
+    src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc \
     src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc \
@@ -374,6 +376,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/endpoint_config.cc \
+    src/core/lib/event_engine/event_engine.cc \
     src/core/lib/event_engine/slice_allocator.cc \
     src/core/lib/event_engine/sockaddr.cc \
     src/core/lib/gpr/alloc.cc \
@@ -431,6 +435,7 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/iomgr/dualstack_socket_posix.cc \
     src/core/lib/iomgr/endpoint.cc \
     src/core/lib/iomgr/endpoint_cfstream.cc \
+    src/core/lib/iomgr/endpoint_pair_event_engine.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
     src/core/lib/iomgr/endpoint_pair_uv.cc \
     src/core/lib/iomgr/endpoint_pair_windows.cc \
@@ -442,6 +447,14 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/iomgr/ev_poll_posix.cc \
     src/core/lib/iomgr/ev_posix.cc \
     src/core/lib/iomgr/ev_windows.cc \
+    src/core/lib/iomgr/event_engine/closure.cc \
+    src/core/lib/iomgr/event_engine/endpoint.cc \
+    src/core/lib/iomgr/event_engine/iomgr.cc \
+    src/core/lib/iomgr/event_engine/pollset.cc \
+    src/core/lib/iomgr/event_engine/resolved_address_internal.cc \
+    src/core/lib/iomgr/event_engine/resolver.cc \
+    src/core/lib/iomgr/event_engine/tcp.cc \
+    src/core/lib/iomgr/event_engine/timer.cc \
     src/core/lib/iomgr/exec_ctx.cc \
     src/core/lib/iomgr/executor.cc \
     src/core/lib/iomgr/executor/mpmcqueue.cc \
@@ -523,6 +536,8 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/matchers/matchers.cc \
     src/core/lib/profiling/basic_timers.cc \
     src/core/lib/profiling/stap_timers.cc \
+    src/core/lib/security/authorization/authorization_policy_provider_vtable.cc \
+    src/core/lib/security/authorization/evaluate_args.cc \
     src/core/lib/security/context/security_context.cc \
     src/core/lib/security/credentials/alts/alts_credentials.cc \
     src/core/lib/security/credentials/alts/check_gcp_environment.cc \
@@ -1036,7 +1051,9 @@ if test "$PHP_GRPC" != "no"; then
     , $ext_shared, , -fvisibility=hidden \
     -DOPENSSL_NO_ASM -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN \
     -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0 \
-    -DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1)
+    -DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1 \
+    -DGRPC_XDS_USER_AGENT_NAME_SUFFIX='"\"PHP\""' \
+    -DGRPC_XDS_USER_AGENT_VERSION_SUFFIX='"\"1.39.0\""')
 
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/census)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel)
@@ -1165,10 +1182,12 @@ if test "$PHP_GRPC" != "no"; then
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/gprpp)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/http)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/iomgr)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/iomgr/event_engine)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/iomgr/executor)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/json)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/matchers)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/profiling)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/authorization)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/context)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/alts)
index 1e65f5b..699e917 100644 (file)
@@ -43,10 +43,12 @@ if (PHP_GRPC != "no") {
     "src\\core\\ext\\filters\\client_channel\\proxy_mapper_registry.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\dns_resolver_ares.cc " +
+    "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_ev_driver_event_engine.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_ev_driver_libuv.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_ev_driver_posix.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_ev_driver_windows.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper.cc " +
+    "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper_event_engine.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper_libuv.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper_posix.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper_windows.cc " +
@@ -340,6 +342,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\\endpoint_config.cc " +
+    "src\\core\\lib\\event_engine\\event_engine.cc " +
     "src\\core\\lib\\event_engine\\slice_allocator.cc " +
     "src\\core\\lib\\event_engine\\sockaddr.cc " +
     "src\\core\\lib\\gpr\\alloc.cc " +
@@ -397,6 +401,7 @@ if (PHP_GRPC != "no") {
     "src\\core\\lib\\iomgr\\dualstack_socket_posix.cc " +
     "src\\core\\lib\\iomgr\\endpoint.cc " +
     "src\\core\\lib\\iomgr\\endpoint_cfstream.cc " +
+    "src\\core\\lib\\iomgr\\endpoint_pair_event_engine.cc " +
     "src\\core\\lib\\iomgr\\endpoint_pair_posix.cc " +
     "src\\core\\lib\\iomgr\\endpoint_pair_uv.cc " +
     "src\\core\\lib\\iomgr\\endpoint_pair_windows.cc " +
@@ -408,6 +413,14 @@ if (PHP_GRPC != "no") {
     "src\\core\\lib\\iomgr\\ev_poll_posix.cc " +
     "src\\core\\lib\\iomgr\\ev_posix.cc " +
     "src\\core\\lib\\iomgr\\ev_windows.cc " +
+    "src\\core\\lib\\iomgr\\event_engine\\closure.cc " +
+    "src\\core\\lib\\iomgr\\event_engine\\endpoint.cc " +
+    "src\\core\\lib\\iomgr\\event_engine\\iomgr.cc " +
+    "src\\core\\lib\\iomgr\\event_engine\\pollset.cc " +
+    "src\\core\\lib\\iomgr\\event_engine\\resolved_address_internal.cc " +
+    "src\\core\\lib\\iomgr\\event_engine\\resolver.cc " +
+    "src\\core\\lib\\iomgr\\event_engine\\tcp.cc " +
+    "src\\core\\lib\\iomgr\\event_engine\\timer.cc " +
     "src\\core\\lib\\iomgr\\exec_ctx.cc " +
     "src\\core\\lib\\iomgr\\executor.cc " +
     "src\\core\\lib\\iomgr\\executor\\mpmcqueue.cc " +
@@ -489,6 +502,8 @@ if (PHP_GRPC != "no") {
     "src\\core\\lib\\matchers\\matchers.cc " +
     "src\\core\\lib\\profiling\\basic_timers.cc " +
     "src\\core\\lib\\profiling\\stap_timers.cc " +
+    "src\\core\\lib\\security\\authorization\\authorization_policy_provider_vtable.cc " +
+    "src\\core\\lib\\security\\authorization\\evaluate_args.cc " +
     "src\\core\\lib\\security\\context\\security_context.cc " +
     "src\\core\\lib\\security\\credentials\\alts\\alts_credentials.cc " +
     "src\\core\\lib\\security\\credentials\\alts\\check_gcp_environment.cc " +
@@ -1266,11 +1281,13 @@ if (PHP_GRPC != "no") {
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\gprpp");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\http");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\iomgr");
+  FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\iomgr\\event_engine");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\iomgr\\executor");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\json");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\matchers");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\profiling");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security");
+  FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\authorization");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\context");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\credentials");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\credentials\\alts");
index bdb54a0..1aa0719 100644 (file)
@@ -75,6 +75,7 @@ some configuration as environment variables that can be set.
     in DEBUG)
   - priority_lb - traces priority LB policy
   - resource_quota - trace resource quota objects internals
+  - ring_hash_lb - traces the ring hash load balancing policy
   - round_robin - traces the round_robin load balancing policy
   - queue_pluck
   - server_channel - lightweight trace of significant server channel events
index 7a71931..8ff02d0 100644 (file)
@@ -38,3 +38,4 @@
 - 1.36 'g' stands for ['gummybear'](https://github.com/grpc/grpc/tree/v1.36.x)
 - 1.37 'g' stands for ['gilded'](https://github.com/grpc/grpc/tree/v1.37.x)
 - 1.38 'g' stands for ['guadalupe_river_park_conservancy'](https://github.com/grpc/grpc/tree/v1.38.x)
+- 1.39 'g' stands for ['goofy'](https://github.com/grpc/grpc/tree/v1.39.x)
index 8c3332a..1acb57b 100644 (file)
@@ -44,7 +44,7 @@ Features | gRFCs  | [C++, Python,<br> Ruby, PHP](https://github.com/grpc/grpc/re
 Request matching based on:<ul><li>[Path](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route_components.proto#route-routematch) (prefix, full path and safe regex)</li><ul><li>[case_sensitive](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route_components.proto#route-routematch) must be true else config is NACKed</li></ul><li>[Headers](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route_components.proto#route-headermatcher)</li></ul>Request routing to multiple clusters based on [weights](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route_components.proto#route-weightedcluster) | [A28](https://github.com/grpc/proposal/blob/master/A28-xds-traffic-splitting-and-routing.md) | v1.31.0 | v1.31.0 | v1.31.0 | v1.3.0 |
 Case insensitive prefix/full path matching:<ul><li>[case_sensitive](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route_components.proto#route-routematch) can be true or false</li></ul> | | v1.34.0 | v1.34.0 | v1.34.0 | v1.3.0 |
 Support for [xDS v3 APIs](https://www.envoyproxy.io/docs/envoy/latest/api-v3/api) | [A30](https://github.com/grpc/proposal/blob/master/A30-xds-v3.md) | v1.36.0 | v1.36.0 | v1.36.0 | |
-[Maximum Stream Duration](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#config-route-v3-routeaction-maxstreamduration):<ul><li>Only max_stream_duration is supported.</li></ul> | [A31](https://github.com/grpc/proposal/blob/master/A31-xds-timeout-support-and-config-selector.md) | v1.37.1  | v1.37.0 | v1.37.0 | |
-[Circuit Breaking](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/circuit_breaker.proto):<ul><li>Only max_requests is supported.</li></ul> | [A32](https://github.com/grpc/proposal/blob/master/A32-xds-circuit-breaking.md) | v1.37.1 (N/A for PHP) | v1.37.0 | v1.37.0 | |
-[Fault Injection](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/fault/v3/fault.proto):<br> Only the following fields are supported:<ul><li>delay</li><li>abort</li><li>max_active_faults</li><li>headers</li></ul> | [A33](https://github.com/grpc/proposal/blob/master/A33-Fault-Injection.md) | v1.37.1  | v1.37.0 | v1.37.0 | |
-[Client Status Discovery Service](https://github.com/envoyproxy/envoy/blob/main/api/envoy/service/status/v3/csds.proto) | [A40](https://github.com/grpc/proposal/blob/master/A40-csds-support.md) | v1.37.1 (Only C++)  | v1.37.0 | v1.37.0 | |
+[Maximum Stream Duration](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#config-route-v3-routeaction-maxstreamduration):<ul><li>Only max_stream_duration is supported.</li></ul> | [A31](https://github.com/grpc/proposal/blob/master/A31-xds-timeout-support-and-config-selector.md) | v1.37.1  | v1.37.1 | v1.37.0 | |
+[Circuit Breaking](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/circuit_breaker.proto):<ul><li>Only max_requests is supported.</li></ul> | [A32](https://github.com/grpc/proposal/blob/master/A32-xds-circuit-breaking.md) | v1.37.1 (N/A for PHP) | v1.37.1 | v1.37.0 | |
+[Fault Injection](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/fault/v3/fault.proto):<br> Only the following fields are supported:<ul><li>delay</li><li>abort</li><li>max_active_faults</li><li>headers</li></ul> | [A33](https://github.com/grpc/proposal/blob/master/A33-Fault-Injection.md) | v1.37.1  | v1.37.1 | v1.37.0 | |
+[Client Status Discovery Service](https://github.com/envoyproxy/envoy/blob/main/api/envoy/service/status/v3/csds.proto) | [A40](https://github.com/grpc/proposal/blob/master/A40-csds-support.md) | v1.37.1 (C++)<br>v1.38.0 (Python)  | v1.37.1 | v1.37.0 | |
index 6a82a7b..79c874d 100644 (file)
@@ -10,7 +10,12 @@ The code for the xDS test server can be found at:
 Server should accept these arguments:
 
 *   --port=PORT
-    *   The port the server will run on.
+    *   The port the test server will run on.
+*   --maintenance_port=PORT
+    *   The port for the maintenance server running health, channelz, and admin(CSDS) services.
+*   --secure_mode=BOOLEAN
+    *   When set to true it uses XdsServerCredentials with the test server for security test cases.
+        In case of secure mode, port and maintenance_port should be different.
 
 ## Client
 
@@ -41,6 +46,8 @@ Clients should accept these arguments:
         implementation.
 *   --rpc_timeout_sec=SEC
     *   The timeout to set on all outbound RPCs. Default is 20.
+*   --secure_mode=BOOLEAN
+    *   When set to true it uses XdsChannelCredentials with the test client for security test cases.
 
 ### XdsUpdateClientConfigureService
 
@@ -621,3 +628,110 @@ There are four sub-tests:
       `rpc-behavior: sleep-4`.
    1. Test driver asserts client recieves ~100% status `OK` for EmptyCall
       and ~100% status `DEADLINE_EXCEEDED` for UnaryCall.
+
+### api_listener
+The test case verifies a specific use case where it creates a second TD API 
+listener using the same name as the existing one and then delete the old one. 
+The test driver verifies this is a safe way to update the API listener 
+configuration while keep using the existing name.
+
+Client parameters:
+
+1.  --num_channels=1
+1.  --qps=100
+
+Load balancer configuration:
+
+1.  One MIG with two backends.
+
+Assert:
+
+The test driver configuration steps:
+1. The test driver creates the first set of forwarding rule + target proxy + 
+URL map with a test host name.
+1. Then the test driver creates a second set of forwarding rule + target proxy + 
+URL map with the same test host name.
+1. The test driver deletes the first set of configurations in step 1.
+
+The test driver verifies, at each configuration step, the traffic is always able 
+to reach the designated hosts.
+
+### metadata_filter
+This test case verifies that metadata filter configurations in URL map match 
+rule are effective at Traffic Director for routing selection against downstream
+node metadata.
+
+Client parameters:
+
+1.  --num_channels=1
+1.  --qps=100
+
+Load balancer configuration:
+
+1.  Two MIGs in the same zone, each having two backends.
+
+There are four test sub-cases:
+1. Test `MATCH_ALL` metadata filter criteria.
+1. Test `MATCH_ANY` metadata filter criteria.
+1. Test mixed `MATCH_ALL` and `MATCH_ANY` metadata filter criteria.
+1. Test when multiple match rules with metadata filter all match.
+
+Assert:
+
+At each test sub-case described above, the test driver configures
+and verifies:
+
+1. Set default URL map, and verify traffic goes to the original backend hosts. 
+1. Then patch URL map to update the match rule with metadata filter 
+configuration under test added.
+1. Then it verifies traffic switches to alternate backend service hosts. 
+
+This way, we test that TD correctly evaluates both matching and non-matching 
+configuration scenario.
+
+### forwarding_rule_port_match
+This test verifies that request server uri port should match with the GCP 
+forwarding rule configuration port.
+
+Client parameters:
+
+1.  --num_channels=1
+1.  --qps=100
+
+Load balancer configuration:
+
+1.  One MIG with two backends.
+
+Assert:
+1. The test driver configures matching port in the forwarding rule and in the
+request server uri, then verifies traffic reaches backend service instances.
+1. The test driver updates the forwarding rule to use a different port, then 
+verifies that the traffic stops going to those backend service instances.
+
+### forwarding_rule_default_port
+This test verifies that omitting port in the request server uri should only 
+match with the default port(80) configuration in the forwarding rule. 
+In addition, request server uri port should exactly match that in the URL map 
+host rule, as described in 
+[public doc](https://cloud.google.com/traffic-director/docs/proxyless-overview#proxyless-url-map).
+
+Client parameters:
+
+1.  --num_channels=1
+1.  --qps=100
+
+Load balancer configuration:
+
+1.  One MIG with two backends.
+
+Assert:
+
+Test driver configures and verifies:
+1. No traffic goes to backends when configuring the target URI 
+`xds:///myservice`, the forwarding rule with port *x != 80*, the URL map 
+host rule `myservice::x`.
+1. Traffic goes to backends when configuring the target URI `xds:///myservice`, 
+the forwarding rule port `80` and the URL map host rule `myservice`.
+1. No traffic goes to backends when configuring the target URI
+`xds:///myservice`, the forwarding rule port `80` and the host rule 
+`myservice::80`.
index 747f0b2..c9a0cc2 100644 (file)
@@ -45,6 +45,16 @@ cc_binary(
 )
 
 cc_binary(
+    name = "greeter_callback_client",
+    srcs = ["greeter_callback_client.cc"],
+    defines = ["BAZEL_BUILD"],
+    deps = [
+        "//:grpc++",
+        "//examples/protos:helloworld_cc_grpc",
+    ],
+)
+
+cc_binary(
     name = "xds_greeter_client",
     srcs = ["xds_greeter_client.cc"],
     defines = ["BAZEL_BUILD"],
@@ -78,6 +88,17 @@ cc_binary(
 )
 
 cc_binary(
+    name = "greeter_callback_server",
+    srcs = ["greeter_callback_server.cc"],
+    defines = ["BAZEL_BUILD"],
+    deps = [
+        "//:grpc++",
+        "//:grpc++_reflection",
+        "//examples/protos:helloworld_cc_grpc",
+    ],
+)
+
+cc_binary(
     name = "xds_greeter_server",
     srcs = ["xds_greeter_server.cc"],
     defines = ["BAZEL_BUILD"],
index f1c3147..0b466a4 100644 (file)
@@ -58,7 +58,8 @@ target_link_libraries(hw_grpc_proto
 
 # Targets greeter_[async_](client|server)
 foreach(_target
-  greeter_client greeter_server
+  greeter_client greeter_server 
+  greeter_callback_client greeter_callback_server 
   greeter_async_client greeter_async_client2 greeter_async_server)
   add_executable(${_target} "${_target}.cc")
   target_link_libraries(${_target}
index 8030ba2..cfd03ca 100644 (file)
@@ -38,7 +38,7 @@ PROTOS_PATH = ../../protos
 
 vpath %.proto $(PROTOS_PATH)
 
-all: system-check greeter_client greeter_server greeter_async_client greeter_async_client2 greeter_async_server
+all: system-check greeter_client greeter_server greeter_async_client greeter_async_client2 greeter_async_server greeter_callback_client greeter_callback_server 
 
 greeter_client: helloworld.pb.o helloworld.grpc.pb.o greeter_client.o
        $(CXX) $^ $(LDFLAGS) -o $@
@@ -55,6 +55,12 @@ greeter_async_client2: helloworld.pb.o helloworld.grpc.pb.o greeter_async_client
 greeter_async_server: helloworld.pb.o helloworld.grpc.pb.o greeter_async_server.o
        $(CXX) $^ $(LDFLAGS) -o $@
 
+greeter_callback_client: helloworld.pb.o helloworld.grpc.pb.o greeter_callback_client.o
+       $(CXX) $^ $(LDFLAGS) -o $@
+
+greeter_callback_server: helloworld.pb.o helloworld.grpc.pb.o greeter_callback_server.o
+       $(CXX) $^ $(LDFLAGS) -o $@
+
 .PRECIOUS: %.grpc.pb.cc
 %.grpc.pb.cc: %.proto
        $(PROTOC) -I $(PROTOS_PATH) --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $<
@@ -64,7 +70,7 @@ greeter_async_server: helloworld.pb.o helloworld.grpc.pb.o greeter_async_server.
        $(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $<
 
 clean:
-       rm -f *.o *.pb.cc *.pb.h greeter_client greeter_server greeter_async_client greeter_async_client2 greeter_async_server
+       rm -f *.o *.pb.cc *.pb.h greeter_client greeter_server greeter_async_client greeter_async_client2 greeter_async_server greeter_callback_client greeter_callback_server 
 
 
 # The following is to test your system and ensure a smoother experience.
diff --git a/examples/cpp/helloworld/greeter_callback_client.cc b/examples/cpp/helloworld/greeter_callback_client.cc
new file mode 100644 (file)
index 0000000..0e92cb9
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ *
+ * 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 <condition_variable>
+#include <iostream>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include <grpcpp/grpcpp.h>
+
+#ifdef BAZEL_BUILD
+#include "examples/protos/helloworld.grpc.pb.h"
+#else
+#include "helloworld.grpc.pb.h"
+#endif
+
+using grpc::Channel;
+using grpc::ClientContext;
+using grpc::Status;
+using helloworld::Greeter;
+using helloworld::HelloReply;
+using helloworld::HelloRequest;
+
+class GreeterClient {
+ public:
+  GreeterClient(std::shared_ptr<Channel> channel)
+      : stub_(Greeter::NewStub(channel)) {}
+
+  // Assembles the client's payload, sends it and presents the response back
+  // from the server.
+  std::string SayHello(const std::string& user) {
+    // Data we are sending to the server.
+    HelloRequest request;
+    request.set_name(user);
+
+    // Container for the data we expect from the server.
+    HelloReply reply;
+
+    // Context for the client. It could be used to convey extra information to
+    // the server and/or tweak certain RPC behaviors.
+    ClientContext context;
+
+    // The actual RPC.
+    std::mutex mu;
+    std::condition_variable cv;
+    bool done = false;
+    Status status;
+    stub_->async()->SayHello(&context, &request, &reply,
+                             [&mu, &cv, &done, &status](Status s) {
+                               status = std::move(s);
+                               std::lock_guard<std::mutex> lock(mu);
+                               done = true;
+                               cv.notify_one();
+                             });
+
+    std::unique_lock<std::mutex> lock(mu);
+    while (!done) {
+      cv.wait(lock);
+    }
+
+    // Act upon its status.
+    if (status.ok()) {
+      return reply.message();
+    } else {
+      std::cout << status.error_code() << ": " << status.error_message()
+                << std::endl;
+      return "RPC failed";
+    }
+  }
+
+ private:
+  std::unique_ptr<Greeter::Stub> stub_;
+};
+
+int main(int argc, char** argv) {
+  // Instantiate the client. It requires a channel, out of which the actual RPCs
+  // are created. This channel models a connection to an endpoint specified by
+  // the argument "--target=" which is the only expected argument.
+  // We indicate that the channel isn't authenticated (use of
+  // InsecureChannelCredentials()).
+  std::string target_str;
+  std::string arg_str("--target");
+  if (argc > 1) {
+    std::string arg_val = argv[1];
+    size_t start_pos = arg_val.find(arg_str);
+    if (start_pos != std::string::npos) {
+      start_pos += arg_str.size();
+      if (arg_val[start_pos] == '=') {
+        target_str = arg_val.substr(start_pos + 1);
+      } else {
+        std::cout << "The only correct argument syntax is --target="
+                  << std::endl;
+        return 0;
+      }
+    } else {
+      std::cout << "The only acceptable argument is --target=" << std::endl;
+      return 0;
+    }
+  } else {
+    target_str = "localhost:50051";
+  }
+  GreeterClient greeter(
+      grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()));
+  std::string user("world");
+  std::string reply = greeter.SayHello(user);
+  std::cout << "Greeter received: " << reply << std::endl;
+
+  return 0;
+}
diff --git a/examples/cpp/helloworld/greeter_callback_server.cc b/examples/cpp/helloworld/greeter_callback_server.cc
new file mode 100644 (file)
index 0000000..8f93561
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *
+ * 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 <iostream>
+#include <memory>
+#include <string>
+
+#include <grpcpp/ext/proto_server_reflection_plugin.h>
+#include <grpcpp/grpcpp.h>
+#include <grpcpp/health_check_service_interface.h>
+
+#ifdef BAZEL_BUILD
+#include "examples/protos/helloworld.grpc.pb.h"
+#else
+#include "helloworld.grpc.pb.h"
+#endif
+
+using grpc::CallbackServerContext;
+using grpc::Server;
+using grpc::ServerBuilder;
+using grpc::ServerUnaryReactor;
+using grpc::Status;
+using helloworld::Greeter;
+using helloworld::HelloReply;
+using helloworld::HelloRequest;
+
+// Logic and data behind the server's behavior.
+class GreeterServiceImpl final : public Greeter::CallbackService {
+  ServerUnaryReactor* SayHello(CallbackServerContext* context,
+                               const HelloRequest* request,
+                               HelloReply* reply) override {
+    std::string prefix("Hello ");
+    reply->set_message(prefix + request->name());
+
+    ServerUnaryReactor* reactor = context->DefaultReactor();
+    reactor->Finish(Status::OK);
+    return reactor;
+  }
+};
+
+void RunServer() {
+  std::string server_address("0.0.0.0:50051");
+  GreeterServiceImpl service;
+
+  grpc::EnableDefaultHealthCheckService(true);
+  grpc::reflection::InitProtoReflectionServerBuilderPlugin();
+  ServerBuilder builder;
+  // Listen on the given address without any authentication mechanism.
+  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
+  // Register "service" as the instance through which we'll communicate with
+  // clients. In this case it corresponds to an *synchronous* service.
+  builder.RegisterService(&service);
+  // Finally assemble the server.
+  std::unique_ptr<Server> server(builder.BuildAndStart());
+  std::cout << "Server listening on " << server_address << std::endl;
+
+  // Wait for the server to shutdown. Note that some other thread must be
+  // responsible for shutting down the server for this call to ever return.
+  server->Wait();
+}
+
+int main(int argc, char** argv) {
+  RunServer();
+
+  return 0;
+}
index 8aac557..6f93549 100644 (file)
@@ -54,3 +54,31 @@ cc_binary(
         "//examples/protos:route_guide",
     ],
 )
+
+cc_binary(
+    name = "route_guide_callback_client",
+    srcs = [
+        "route_guide_callback_client.cc",
+    ],
+    data = ["route_guide_db.json"],
+    defines = ["BAZEL_BUILD"],
+    deps = [
+        ":route_guide_helper",
+        "//:grpc++",
+        "//examples/protos:route_guide",
+    ],
+)
+
+cc_binary(
+    name = "route_guide_callback_server",
+    srcs = [
+        "route_guide_callback_server.cc",
+    ],
+    data = ["route_guide_db.json"],
+    defines = ["BAZEL_BUILD"],
+    deps = [
+        ":route_guide_helper",
+        "//:grpc++",
+        "//examples/protos:route_guide",
+    ],
+)
diff --git a/examples/cpp/route_guide/route_guide_callback_client.cc b/examples/cpp/route_guide/route_guide_callback_client.cc
new file mode 100644 (file)
index 0000000..93eeaec
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ *
+ * 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 <chrono>
+#include <condition_variable>
+#include <iostream>
+#include <memory>
+#include <mutex>
+#include <random>
+#include <string>
+#include <thread>
+
+#include <grpc/grpc.h>
+#include <grpcpp/alarm.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/client_context.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/security/credentials.h>
+#include "helper.h"
+#ifdef BAZEL_BUILD
+#include "examples/protos/route_guide.grpc.pb.h"
+#else
+#include "route_guide.grpc.pb.h"
+#endif
+
+using grpc::Channel;
+using grpc::ClientContext;
+using grpc::Status;
+using routeguide::Feature;
+using routeguide::Point;
+using routeguide::Rectangle;
+using routeguide::RouteGuide;
+using routeguide::RouteNote;
+using routeguide::RouteSummary;
+
+Point MakePoint(long latitude, long longitude) {
+  Point p;
+  p.set_latitude(latitude);
+  p.set_longitude(longitude);
+  return p;
+}
+
+Feature MakeFeature(const std::string& name, long latitude, long longitude) {
+  Feature f;
+  f.set_name(name);
+  f.mutable_location()->CopyFrom(MakePoint(latitude, longitude));
+  return f;
+}
+
+RouteNote MakeRouteNote(const std::string& message, long latitude,
+                        long longitude) {
+  RouteNote n;
+  n.set_message(message);
+  n.mutable_location()->CopyFrom(MakePoint(latitude, longitude));
+  return n;
+}
+
+class RouteGuideClient {
+ public:
+  RouteGuideClient(std::shared_ptr<Channel> channel, const std::string& db)
+      : stub_(RouteGuide::NewStub(channel)) {
+    routeguide::ParseDb(db, &feature_list_);
+  }
+
+  void GetFeature() {
+    Point point;
+    Feature feature;
+    point = MakePoint(409146138, -746188906);
+    GetOneFeature(point, &feature);
+    point = MakePoint(0, 0);
+    GetOneFeature(point, &feature);
+  }
+
+  void ListFeatures() {
+    routeguide::Rectangle rect;
+    Feature feature;
+
+    rect.mutable_lo()->set_latitude(400000000);
+    rect.mutable_lo()->set_longitude(-750000000);
+    rect.mutable_hi()->set_latitude(420000000);
+    rect.mutable_hi()->set_longitude(-730000000);
+    std::cout << "Looking for features between 40, -75 and 42, -73"
+              << std::endl;
+
+    class Reader : public grpc::ClientReadReactor<Feature> {
+     public:
+      Reader(RouteGuide::Stub* stub, float coord_factor,
+             const routeguide::Rectangle& rect)
+          : coord_factor_(coord_factor) {
+        stub->async()->ListFeatures(&context_, &rect, this);
+        StartRead(&feature_);
+        StartCall();
+      }
+      void OnReadDone(bool ok) override {
+        if (ok) {
+          std::cout << "Found feature called " << feature_.name() << " at "
+                    << feature_.location().latitude() / coord_factor_ << ", "
+                    << feature_.location().longitude() / coord_factor_
+                    << std::endl;
+          StartRead(&feature_);
+        }
+      }
+      void OnDone(const Status& s) override {
+        std::unique_lock<std::mutex> l(mu_);
+        status_ = s;
+        done_ = true;
+        cv_.notify_one();
+      }
+      Status Await() {
+        std::unique_lock<std::mutex> l(mu_);
+        cv_.wait(l, [this] { return done_; });
+        return std::move(status_);
+      }
+
+     private:
+      ClientContext context_;
+      float coord_factor_;
+      Feature feature_;
+      std::mutex mu_;
+      std::condition_variable cv_;
+      Status status_;
+      bool done_ = false;
+    };
+    Reader reader(stub_.get(), kCoordFactor_, rect);
+    Status status = std::move(reader.Await());
+    if (status.ok()) {
+      std::cout << "ListFeatures rpc succeeded." << std::endl;
+    } else {
+      std::cout << "ListFeatures rpc failed." << std::endl;
+    }
+  }
+
+  void RecordRoute() {
+    class Recorder : public grpc::ClientWriteReactor<Point> {
+     public:
+      Recorder(RouteGuide::Stub* stub, float coord_factor,
+               const std::vector<Feature>* feature_list)
+          : coord_factor_(coord_factor),
+            feature_list_(feature_list),
+            generator_(
+                std::chrono::system_clock::now().time_since_epoch().count()),
+            feature_distribution_(0, feature_list->size() - 1),
+            delay_distribution_(500, 1500) {
+        stub->async()->RecordRoute(&context_, &stats_, this);
+        // Use a hold since some StartWrites are invoked indirectly from a
+        // delayed lambda in OnWriteDone rather than directly from the reaction
+        // itself
+        AddHold();
+        NextWrite();
+        StartCall();
+      }
+      void OnWriteDone(bool ok) override {
+        // Delay and then do the next write or WritesDone
+        alarm_.Set(
+            std::chrono::system_clock::now() +
+                std::chrono::milliseconds(delay_distribution_(generator_)),
+            [this](bool /*ok*/) { NextWrite(); });
+      }
+      void OnDone(const Status& s) override {
+        std::unique_lock<std::mutex> l(mu_);
+        status_ = s;
+        done_ = true;
+        cv_.notify_one();
+      }
+      Status Await(RouteSummary* stats) {
+        std::unique_lock<std::mutex> l(mu_);
+        cv_.wait(l, [this] { return done_; });
+        *stats = stats_;
+        return std::move(status_);
+      }
+
+     private:
+      void NextWrite() {
+        if (points_remaining_ != 0) {
+          const Feature& f =
+              (*feature_list_)[feature_distribution_(generator_)];
+          std::cout << "Visiting point "
+                    << f.location().latitude() / coord_factor_ << ", "
+                    << f.location().longitude() / coord_factor_ << std::endl;
+          StartWrite(&f.location());
+          points_remaining_--;
+        } else {
+          StartWritesDone();
+          RemoveHold();
+        }
+      }
+      ClientContext context_;
+      float coord_factor_;
+      int points_remaining_ = 10;
+      Point point_;
+      RouteSummary stats_;
+      const std::vector<Feature>* feature_list_;
+      std::default_random_engine generator_;
+      std::uniform_int_distribution<int> feature_distribution_;
+      std::uniform_int_distribution<int> delay_distribution_;
+      grpc::Alarm alarm_;
+      std::mutex mu_;
+      std::condition_variable cv_;
+      Status status_;
+      bool done_ = false;
+    };
+    Recorder recorder(stub_.get(), kCoordFactor_, &feature_list_);
+    RouteSummary stats;
+    Status status = std::move(recorder.Await(&stats));
+    if (status.ok()) {
+      std::cout << "Finished trip with " << stats.point_count() << " points\n"
+                << "Passed " << stats.feature_count() << " features\n"
+                << "Travelled " << stats.distance() << " meters\n"
+                << "It took " << stats.elapsed_time() << " seconds"
+                << std::endl;
+    } else {
+      std::cout << "RecordRoute rpc failed." << std::endl;
+    }
+  }
+
+  void RouteChat() {
+    class Chatter : public grpc::ClientBidiReactor<RouteNote, RouteNote> {
+     public:
+      explicit Chatter(RouteGuide::Stub* stub)
+          : notes_{MakeRouteNote("First message", 0, 0),
+                   MakeRouteNote("Second message", 0, 1),
+                   MakeRouteNote("Third message", 1, 0),
+                   MakeRouteNote("Fourth message", 0, 0)},
+            notes_iterator_(notes_.begin()) {
+        stub->async()->RouteChat(&context_, this);
+        NextWrite();
+        StartRead(&server_note_);
+        StartCall();
+      }
+      void OnWriteDone(bool /*ok*/) override { NextWrite(); }
+      void OnReadDone(bool ok) override {
+        if (ok) {
+          std::cout << "Got message " << server_note_.message() << " at "
+                    << server_note_.location().latitude() << ", "
+                    << server_note_.location().longitude() << std::endl;
+          StartRead(&server_note_);
+        }
+      }
+      void OnDone(const Status& s) override {
+        std::unique_lock<std::mutex> l(mu_);
+        status_ = s;
+        done_ = true;
+        cv_.notify_one();
+      }
+      Status Await() {
+        std::unique_lock<std::mutex> l(mu_);
+        cv_.wait(l, [this] { return done_; });
+        return std::move(status_);
+      }
+
+     private:
+      void NextWrite() {
+        if (notes_iterator_ != notes_.end()) {
+          const auto& note = *notes_iterator_;
+          std::cout << "Sending message " << note.message() << " at "
+                    << note.location().latitude() << ", "
+                    << note.location().longitude() << std::endl;
+          StartWrite(&note);
+          notes_iterator_++;
+        } else {
+          StartWritesDone();
+        }
+      }
+      ClientContext context_;
+      const std::vector<RouteNote> notes_;
+      std::vector<RouteNote>::const_iterator notes_iterator_;
+      RouteNote server_note_;
+      std::mutex mu_;
+      std::condition_variable cv_;
+      Status status_;
+      bool done_ = false;
+    };
+
+    Chatter chatter(stub_.get());
+    Status status = std::move(chatter.Await());
+    if (!status.ok()) {
+      std::cout << "RouteChat rpc failed." << std::endl;
+    }
+  }
+
+ private:
+  bool GetOneFeature(const Point& point, Feature* feature) {
+    ClientContext context;
+    bool result;
+    std::mutex mu;
+    std::condition_variable cv;
+    bool done = false;
+    stub_->async()->GetFeature(
+        &context, &point, feature,
+        [&result, &mu, &cv, &done, feature, this](Status status) {
+          bool ret;
+          if (!status.ok()) {
+            std::cout << "GetFeature rpc failed." << std::endl;
+            ret = false;
+          } else if (!feature->has_location()) {
+            std::cout << "Server returns incomplete feature." << std::endl;
+            ret = false;
+          } else if (feature->name().empty()) {
+            std::cout << "Found no feature at "
+                      << feature->location().latitude() / kCoordFactor_ << ", "
+                      << feature->location().longitude() / kCoordFactor_
+                      << std::endl;
+            ret = true;
+          } else {
+            std::cout << "Found feature called " << feature->name() << " at "
+                      << feature->location().latitude() / kCoordFactor_ << ", "
+                      << feature->location().longitude() / kCoordFactor_
+                      << std::endl;
+            ret = true;
+          }
+          std::lock_guard<std::mutex> lock(mu);
+          result = ret;
+          done = true;
+          cv.notify_one();
+        });
+    std::unique_lock<std::mutex> lock(mu);
+    cv.wait(lock, [&done] { return done; });
+    return result;
+  }
+
+  const float kCoordFactor_ = 10000000.0;
+  std::unique_ptr<RouteGuide::Stub> stub_;
+  std::vector<Feature> feature_list_;
+};
+
+int main(int argc, char** argv) {
+  // Expect only arg: --db_path=path/to/route_guide_db.json.
+  std::string db = routeguide::GetDbFileContent(argc, argv);
+  RouteGuideClient guide(
+      grpc::CreateChannel("localhost:50051",
+                          grpc::InsecureChannelCredentials()),
+      db);
+
+  std::cout << "-------------- GetFeature --------------" << std::endl;
+  guide.GetFeature();
+  std::cout << "-------------- ListFeatures --------------" << std::endl;
+  guide.ListFeatures();
+  std::cout << "-------------- RecordRoute --------------" << std::endl;
+  guide.RecordRoute();
+  std::cout << "-------------- RouteChat --------------" << std::endl;
+  guide.RouteChat();
+
+  return 0;
+}
diff --git a/examples/cpp/route_guide/route_guide_callback_server.cc b/examples/cpp/route_guide/route_guide_callback_server.cc
new file mode 100644 (file)
index 0000000..cf99d05
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ *
+ * 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 <algorithm>
+#include <chrono>
+#include <cmath>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <thread>
+
+#include <grpc/grpc.h>
+#include <grpcpp/security/server_credentials.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_builder.h>
+#include <grpcpp/server_context.h>
+#include "helper.h"
+#ifdef BAZEL_BUILD
+#include "examples/protos/route_guide.grpc.pb.h"
+#else
+#include "route_guide.grpc.pb.h"
+#endif
+
+using grpc::CallbackServerContext;
+using grpc::Server;
+using grpc::ServerBuilder;
+using grpc::Status;
+using routeguide::Feature;
+using routeguide::Point;
+using routeguide::Rectangle;
+using routeguide::RouteGuide;
+using routeguide::RouteNote;
+using routeguide::RouteSummary;
+using std::chrono::system_clock;
+
+float ConvertToRadians(float num) { return num * 3.1415926 / 180; }
+
+// The formula is based on http://mathforum.org/library/drmath/view/51879.html
+float GetDistance(const Point& start, const Point& end) {
+  const float kCoordFactor = 10000000.0;
+  float lat_1 = start.latitude() / kCoordFactor;
+  float lat_2 = end.latitude() / kCoordFactor;
+  float lon_1 = start.longitude() / kCoordFactor;
+  float lon_2 = end.longitude() / kCoordFactor;
+  float lat_rad_1 = ConvertToRadians(lat_1);
+  float lat_rad_2 = ConvertToRadians(lat_2);
+  float delta_lat_rad = ConvertToRadians(lat_2 - lat_1);
+  float delta_lon_rad = ConvertToRadians(lon_2 - lon_1);
+
+  float a = pow(sin(delta_lat_rad / 2), 2) +
+            cos(lat_rad_1) * cos(lat_rad_2) * pow(sin(delta_lon_rad / 2), 2);
+  float c = 2 * atan2(sqrt(a), sqrt(1 - a));
+  int R = 6371000;  // metres
+
+  return R * c;
+}
+
+std::string GetFeatureName(const Point& point,
+                           const std::vector<Feature>& feature_list) {
+  for (const Feature& f : feature_list) {
+    if (f.location().latitude() == point.latitude() &&
+        f.location().longitude() == point.longitude()) {
+      return f.name();
+    }
+  }
+  return "";
+}
+
+class RouteGuideImpl final : public RouteGuide::CallbackService {
+ public:
+  explicit RouteGuideImpl(const std::string& db) {
+    routeguide::ParseDb(db, &feature_list_);
+  }
+
+  grpc::ServerUnaryReactor* GetFeature(CallbackServerContext* context,
+                                       const Point* point,
+                                       Feature* feature) override {
+    feature->set_name(GetFeatureName(*point, feature_list_));
+    feature->mutable_location()->CopyFrom(*point);
+    auto* reactor = context->DefaultReactor();
+    reactor->Finish(Status::OK);
+    return reactor;
+  }
+
+  grpc::ServerWriteReactor<Feature>* ListFeatures(
+      CallbackServerContext* context,
+      const routeguide::Rectangle* rectangle) override {
+    class Lister : public grpc::ServerWriteReactor<Feature> {
+     public:
+      Lister(const routeguide::Rectangle* rectangle,
+             const std::vector<Feature>* feature_list)
+          : left_((std::min)(rectangle->lo().longitude(),
+                             rectangle->hi().longitude())),
+            right_((std::max)(rectangle->lo().longitude(),
+                              rectangle->hi().longitude())),
+            top_((std::max)(rectangle->lo().latitude(),
+                            rectangle->hi().latitude())),
+            bottom_((std::min)(rectangle->lo().latitude(),
+                               rectangle->hi().latitude())),
+            feature_list_(feature_list),
+            next_feature_(feature_list_->begin()) {
+        NextWrite();
+      }
+      void OnDone() override { delete this; }
+      void OnWriteDone(bool /*ok*/) override { NextWrite(); }
+
+     private:
+      void NextWrite() {
+        while (next_feature_ != feature_list_->end()) {
+          const Feature& f = *next_feature_;
+          next_feature_++;
+          if (f.location().longitude() >= left_ &&
+              f.location().longitude() <= right_ &&
+              f.location().latitude() >= bottom_ &&
+              f.location().latitude() <= top_) {
+            StartWrite(&f);
+            return;
+          }
+        }
+        // Didn't write anything, all is done.
+        Finish(Status::OK);
+      }
+      const long left_;
+      const long right_;
+      const long top_;
+      const long bottom_;
+      const std::vector<Feature>* feature_list_;
+      std::vector<Feature>::const_iterator next_feature_;
+    };
+    return new Lister(rectangle, &feature_list_);
+  }
+
+  grpc::ServerReadReactor<Point>* RecordRoute(CallbackServerContext* context,
+                                              RouteSummary* summary) override {
+    class Recorder : public grpc::ServerReadReactor<Point> {
+     public:
+      Recorder(RouteSummary* summary, const std::vector<Feature>* feature_list)
+          : start_time_(system_clock::now()),
+            summary_(summary),
+            feature_list_(feature_list) {
+        StartRead(&point_);
+      }
+      void OnDone() { delete this; }
+      void OnReadDone(bool ok) {
+        if (ok) {
+          point_count_++;
+          if (!GetFeatureName(point_, *feature_list_).empty()) {
+            feature_count_++;
+          }
+          if (point_count_ != 1) {
+            distance_ += GetDistance(previous_, point_);
+          }
+          previous_ = point_;
+          StartRead(&point_);
+        } else {
+          summary_->set_point_count(point_count_);
+          summary_->set_feature_count(feature_count_);
+          summary_->set_distance(static_cast<long>(distance_));
+          auto secs = std::chrono::duration_cast<std::chrono::seconds>(
+              system_clock::now() - start_time_);
+          summary_->set_elapsed_time(secs.count());
+          Finish(Status::OK);
+        }
+      }
+
+     private:
+      system_clock::time_point start_time_;
+      RouteSummary* summary_;
+      const std::vector<Feature>* feature_list_;
+      Point point_;
+      int point_count_ = 0;
+      int feature_count_ = 0;
+      float distance_ = 0.0;
+      Point previous_;
+    };
+    return new Recorder(summary, &feature_list_);
+  }
+
+  grpc::ServerBidiReactor<RouteNote, RouteNote>* RouteChat(
+      CallbackServerContext* context) override {
+    class Chatter : public grpc::ServerBidiReactor<RouteNote, RouteNote> {
+     public:
+      Chatter(std::mutex* mu, std::vector<RouteNote>* received_notes)
+          : mu_(mu), received_notes_(received_notes) {
+        StartRead(&note_);
+      }
+      void OnDone() override {
+        // Collect the read_starter thread if needed
+        if (read_starter_.joinable()) {
+          read_starter_.join();
+        }
+        delete this;
+      }
+      void OnReadDone(bool ok) override {
+        if (ok) {
+          // We may need to wait an arbitary amount of time on this mutex
+          // and we cannot delay the reaction, so start it in a thread
+          // Collect the previous read_starter thread if needed
+          if (read_starter_.joinable()) {
+            read_starter_.join();
+          }
+          read_starter_ = std::thread([this] {
+            mu_->lock();
+            notes_iterator_ = received_notes_->begin();
+            NextWrite();
+          });
+        } else {
+          Finish(Status::OK);
+        }
+      }
+      void OnWriteDone(bool /*ok*/) override { NextWrite(); }
+
+     private:
+      void NextWrite() {
+        while (notes_iterator_ != received_notes_->end()) {
+          const RouteNote& n = *notes_iterator_;
+          notes_iterator_++;
+          if (n.location().latitude() == note_.location().latitude() &&
+              n.location().longitude() == note_.location().longitude()) {
+            StartWrite(&n);
+            return;
+          }
+        }
+        // Didn't write anything, so all done with this note
+        received_notes_->push_back(note_);
+        mu_->unlock();
+        StartRead(&note_);
+      }
+      RouteNote note_;
+      std::mutex* mu_;
+      std::vector<RouteNote>* received_notes_;
+      std::vector<RouteNote>::iterator notes_iterator_;
+      std::thread read_starter_;
+    };
+    return new Chatter(&mu_, &received_notes_);
+  }
+
+ private:
+  std::vector<Feature> feature_list_;
+  std::mutex mu_;
+  std::vector<RouteNote> received_notes_;
+};
+
+void RunServer(const std::string& db_path) {
+  std::string server_address("0.0.0.0:50051");
+  RouteGuideImpl service(db_path);
+
+  ServerBuilder builder;
+  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
+  builder.RegisterService(&service);
+  std::unique_ptr<Server> server(builder.BuildAndStart());
+  std::cout << "Server listening on " << server_address << std::endl;
+  server->Wait();
+}
+
+int main(int argc, char** argv) {
+  // Expect only arg: --db_path=path/to/route_guide_db.json.
+  std::string db = routeguide::GetDbFileContent(argc, argv);
+  RunServer(db);
+
+  return 0;
+}
index 57e8060..855ba9b 100644 (file)
@@ -22,7 +22,7 @@
 Pod::Spec.new do |s|
   s.name     = 'gRPC-C++'
   # TODO (mxyan): use version that match gRPC version when pod is stabilized
-  version = '1.38.1'
+  version = '1.39.0'
   s.version  = version
   s.summary  = 'gRPC C++ library'
   s.homepage = 'https://grpc.io'
@@ -149,6 +149,7 @@ Pod::Spec.new do |s|
                       'include/grpcpp/resource_quota.h',
                       'include/grpcpp/security/auth_context.h',
                       'include/grpcpp/security/auth_metadata_processor.h',
+                      'include/grpcpp/security/authorization_policy_provider.h',
                       'include/grpcpp/security/credentials.h',
                       'include/grpcpp/security/server_credentials.h',
                       'include/grpcpp/security/tls_certificate_provider.h',
@@ -514,6 +515,8 @@ 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/endpoint_config_internal.h',
+                      'src/core/lib/event_engine/sockaddr.h',
                       'src/core/lib/gpr/alloc.h',
                       'src/core/lib/gpr/arena.h',
                       'src/core/lib/gpr/env.h',
@@ -572,6 +575,12 @@ Pod::Spec.new do |s|
                       'src/core/lib/iomgr/ev_epollex_linux.h',
                       'src/core/lib/iomgr/ev_poll_posix.h',
                       'src/core/lib/iomgr/ev_posix.h',
+                      'src/core/lib/iomgr/event_engine/closure.h',
+                      'src/core/lib/iomgr/event_engine/endpoint.h',
+                      'src/core/lib/iomgr/event_engine/iomgr.h',
+                      'src/core/lib/iomgr/event_engine/pollset.h',
+                      'src/core/lib/iomgr/event_engine/promise.h',
+                      'src/core/lib/iomgr/event_engine/resolved_address_internal.h',
                       'src/core/lib/iomgr/exec_ctx.h',
                       'src/core/lib/iomgr/executor.h',
                       'src/core/lib/iomgr/executor/mpmcqueue.h',
@@ -632,6 +641,9 @@ Pod::Spec.new do |s|
                       'src/core/lib/json/json_util.h',
                       'src/core/lib/matchers/matchers.h',
                       'src/core/lib/profiling/timers.h',
+                      'src/core/lib/security/authorization/authorization_engine.h',
+                      'src/core/lib/security/authorization/authorization_policy_provider.h',
+                      'src/core/lib/security/authorization/evaluate_args.h',
                       'src/core/lib/security/context/security_context.h',
                       'src/core/lib/security/credentials/alts/alts_credentials.h',
                       'src/core/lib/security/credentials/alts/check_gcp_environment.h',
@@ -1155,6 +1167,8 @@ 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/endpoint_config_internal.h',
+                              'src/core/lib/event_engine/sockaddr.h',
                               'src/core/lib/gpr/alloc.h',
                               'src/core/lib/gpr/arena.h',
                               'src/core/lib/gpr/env.h',
@@ -1213,6 +1227,12 @@ Pod::Spec.new do |s|
                               'src/core/lib/iomgr/ev_epollex_linux.h',
                               'src/core/lib/iomgr/ev_poll_posix.h',
                               'src/core/lib/iomgr/ev_posix.h',
+                              'src/core/lib/iomgr/event_engine/closure.h',
+                              'src/core/lib/iomgr/event_engine/endpoint.h',
+                              'src/core/lib/iomgr/event_engine/iomgr.h',
+                              'src/core/lib/iomgr/event_engine/pollset.h',
+                              'src/core/lib/iomgr/event_engine/promise.h',
+                              'src/core/lib/iomgr/event_engine/resolved_address_internal.h',
                               'src/core/lib/iomgr/exec_ctx.h',
                               'src/core/lib/iomgr/executor.h',
                               'src/core/lib/iomgr/executor/mpmcqueue.h',
@@ -1273,6 +1293,9 @@ Pod::Spec.new do |s|
                               'src/core/lib/json/json_util.h',
                               'src/core/lib/matchers/matchers.h',
                               'src/core/lib/profiling/timers.h',
+                              'src/core/lib/security/authorization/authorization_engine.h',
+                              'src/core/lib/security/authorization/authorization_policy_provider.h',
+                              'src/core/lib/security/authorization/evaluate_args.h',
                               'src/core/lib/security/context/security_context.h',
                               'src/core/lib/security/credentials/alts/alts_credentials.h',
                               'src/core/lib/security/credentials/alts/check_gcp_environment.h',
index 2db7548..fbf0a27 100644 (file)
@@ -21,7 +21,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-Core'
-  version = '1.38.1'
+  version = '1.39.0'
   s.version  = version
   s.summary  = 'Core cross-platform gRPC library, written in C'
   s.homepage = 'https://grpc.io'
@@ -117,7 +117,10 @@ Pod::Spec.new do |s|
                       'include/grpc/byte_buffer_reader.h',
                       'include/grpc/census.h',
                       'include/grpc/compression.h',
+                      'include/grpc/event_engine/endpoint_config.h',
+                      'include/grpc/event_engine/event_engine.h',
                       'include/grpc/event_engine/port.h',
+                      'include/grpc/event_engine/slice_allocator.h',
                       'include/grpc/fork.h',
                       'include/grpc/grpc.h',
                       'include/grpc/grpc_posix.h',
@@ -174,7 +177,7 @@ Pod::Spec.new do |s|
     ss.header_mappings_dir = '.'
     ss.libraries = 'z'
     ss.dependency "#{s.name}/Interface", version
-    ss.dependency 'BoringSSL-GRPC', '0.0.18'
+    ss.dependency 'BoringSSL-GRPC', '0.0.19'
     ss.dependency 'abseil/base/base', abseil_version
     ss.dependency 'abseil/container/flat_hash_map', abseil_version
     ss.dependency 'abseil/container/inlined_vector', abseil_version
@@ -258,11 +261,13 @@ Pod::Spec.new do |s|
                       'src/core/ext/filters/client_channel/resolver.h',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h',
+                      'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h',
+                      'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc',
                       'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc',
@@ -837,6 +842,12 @@ 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/endpoint_config.cc',
+                      'src/core/lib/event_engine/endpoint_config_internal.h',
+                      'src/core/lib/event_engine/event_engine.cc',
+                      'src/core/lib/event_engine/slice_allocator.cc',
+                      'src/core/lib/event_engine/sockaddr.cc',
+                      'src/core/lib/event_engine/sockaddr.h',
                       'src/core/lib/gpr/alloc.cc',
                       'src/core/lib/gpr/alloc.h',
                       'src/core/lib/gpr/arena.h',
@@ -942,6 +953,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/iomgr/endpoint_cfstream.cc',
                       'src/core/lib/iomgr/endpoint_cfstream.h',
                       'src/core/lib/iomgr/endpoint_pair.h',
+                      'src/core/lib/iomgr/endpoint_pair_event_engine.cc',
                       'src/core/lib/iomgr/endpoint_pair_posix.cc',
                       'src/core/lib/iomgr/endpoint_pair_uv.cc',
                       'src/core/lib/iomgr/endpoint_pair_windows.cc',
@@ -961,6 +973,20 @@ Pod::Spec.new do |s|
                       'src/core/lib/iomgr/ev_posix.cc',
                       'src/core/lib/iomgr/ev_posix.h',
                       'src/core/lib/iomgr/ev_windows.cc',
+                      'src/core/lib/iomgr/event_engine/closure.cc',
+                      'src/core/lib/iomgr/event_engine/closure.h',
+                      'src/core/lib/iomgr/event_engine/endpoint.cc',
+                      'src/core/lib/iomgr/event_engine/endpoint.h',
+                      'src/core/lib/iomgr/event_engine/iomgr.cc',
+                      'src/core/lib/iomgr/event_engine/iomgr.h',
+                      'src/core/lib/iomgr/event_engine/pollset.cc',
+                      'src/core/lib/iomgr/event_engine/pollset.h',
+                      'src/core/lib/iomgr/event_engine/promise.h',
+                      'src/core/lib/iomgr/event_engine/resolved_address_internal.cc',
+                      'src/core/lib/iomgr/event_engine/resolved_address_internal.h',
+                      'src/core/lib/iomgr/event_engine/resolver.cc',
+                      'src/core/lib/iomgr/event_engine/tcp.cc',
+                      'src/core/lib/iomgr/event_engine/timer.cc',
                       'src/core/lib/iomgr/exec_ctx.cc',
                       'src/core/lib/iomgr/exec_ctx.h',
                       'src/core/lib/iomgr/executor.cc',
@@ -1102,6 +1128,11 @@ Pod::Spec.new do |s|
                       'src/core/lib/profiling/basic_timers.cc',
                       'src/core/lib/profiling/stap_timers.cc',
                       'src/core/lib/profiling/timers.h',
+                      'src/core/lib/security/authorization/authorization_engine.h',
+                      'src/core/lib/security/authorization/authorization_policy_provider.h',
+                      'src/core/lib/security/authorization/authorization_policy_provider_vtable.cc',
+                      'src/core/lib/security/authorization/evaluate_args.cc',
+                      'src/core/lib/security/authorization/evaluate_args.h',
                       'src/core/lib/security/context/security_context.cc',
                       'src/core/lib/security/context/security_context.h',
                       'src/core/lib/security/credentials/alts/alts_credentials.cc',
@@ -1716,6 +1747,8 @@ 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/endpoint_config_internal.h',
+                              'src/core/lib/event_engine/sockaddr.h',
                               'src/core/lib/gpr/alloc.h',
                               'src/core/lib/gpr/arena.h',
                               'src/core/lib/gpr/env.h',
@@ -1774,6 +1807,12 @@ Pod::Spec.new do |s|
                               'src/core/lib/iomgr/ev_epollex_linux.h',
                               'src/core/lib/iomgr/ev_poll_posix.h',
                               'src/core/lib/iomgr/ev_posix.h',
+                              'src/core/lib/iomgr/event_engine/closure.h',
+                              'src/core/lib/iomgr/event_engine/endpoint.h',
+                              'src/core/lib/iomgr/event_engine/iomgr.h',
+                              'src/core/lib/iomgr/event_engine/pollset.h',
+                              'src/core/lib/iomgr/event_engine/promise.h',
+                              'src/core/lib/iomgr/event_engine/resolved_address_internal.h',
                               'src/core/lib/iomgr/exec_ctx.h',
                               'src/core/lib/iomgr/executor.h',
                               'src/core/lib/iomgr/executor/mpmcqueue.h',
@@ -1834,6 +1873,9 @@ Pod::Spec.new do |s|
                               'src/core/lib/json/json_util.h',
                               'src/core/lib/matchers/matchers.h',
                               'src/core/lib/profiling/timers.h',
+                              'src/core/lib/security/authorization/authorization_engine.h',
+                              'src/core/lib/security/authorization/authorization_policy_provider.h',
+                              'src/core/lib/security/authorization/evaluate_args.h',
                               'src/core/lib/security/context/security_context.h',
                               'src/core/lib/security/credentials/alts/alts_credentials.h',
                               'src/core/lib/security/credentials/alts/check_gcp_environment.h',
@@ -2087,13 +2129,17 @@ Pod::Spec.new do |s|
                       'test/core/end2end/tests/retry_cancel_during_delay.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_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_recv_initial_metadata.cc',
                       'test/core/end2end/tests/retry_recv_message.cc',
+                      'test/core/end2end/tests/retry_recv_trailing_metadata_error.cc',
+                      'test/core/end2end/tests/retry_send_op_fails.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',
index 9fd51e6..9b9a548 100644 (file)
@@ -21,7 +21,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-ProtoRPC'
-  version = '1.38.1'
+  version = '1.39.0'
   s.version  = version
   s.summary  = 'RPC library for Protocol Buffers, based on gRPC'
   s.homepage = 'https://grpc.io'
index a1b4505..647db9d 100644 (file)
@@ -21,7 +21,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-RxLibrary'
-  version = '1.38.1'
+  version = '1.39.0'
   s.version  = version
   s.summary  = 'Reactive Extensions library for iOS/OSX.'
   s.homepage = 'https://grpc.io'
index 4f86b07..03463c7 100644 (file)
@@ -20,7 +20,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC'
-  version = '1.38.1'
+  version = '1.39.0'
   s.version  = version
   s.summary  = 'gRPC client library for iOS/OSX'
   s.homepage = 'https://grpc.io'
index 7be77a6..827aaf9 100644 (file)
--- a/grpc.def
+++ b/grpc.def
@@ -84,6 +84,7 @@ EXPORTS
     grpc_channelz_get_channel
     grpc_channelz_get_subchannel
     grpc_channelz_get_socket
+    grpc_authorization_policy_provider_arg_vtable
     grpc_insecure_channel_create_from_fd
     grpc_server_add_insecure_channel_from_fd
     grpc_auth_property_iterator_next
@@ -159,6 +160,8 @@ EXPORTS
     grpc_tls_server_authorization_check_config_release
     grpc_xds_credentials_create
     grpc_xds_server_credentials_create
+    grpc_authorization_policy_provider_static_data_create
+    grpc_authorization_policy_provider_release
     grpc_raw_byte_buffer_create
     grpc_raw_compressed_byte_buffer_create
     grpc_byte_buffer_copy
index 2185cbf..14bf3fe 100644 (file)
@@ -53,7 +53,7 @@ Gem::Specification.new do |s|
   s.files += %w( include/grpc/byte_buffer_reader.h )
   s.files += %w( include/grpc/census.h )
   s.files += %w( include/grpc/compression.h )
-  s.files += %w( include/grpc/event_engine/channel_args.h )
+  s.files += %w( include/grpc/event_engine/endpoint_config.h )
   s.files += %w( include/grpc/event_engine/event_engine.h )
   s.files += %w( include/grpc/event_engine/port.h )
   s.files += %w( include/grpc/event_engine/slice_allocator.h )
@@ -177,11 +177,13 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/ext/filters/client_channel/resolver.h )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h )
+  s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h )
+  s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc )
@@ -756,8 +758,12 @@ 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/endpoint_config.cc )
+  s.files += %w( src/core/lib/event_engine/endpoint_config_internal.h )
+  s.files += %w( src/core/lib/event_engine/event_engine.cc )
   s.files += %w( src/core/lib/event_engine/slice_allocator.cc )
   s.files += %w( src/core/lib/event_engine/sockaddr.cc )
+  s.files += %w( src/core/lib/event_engine/sockaddr.h )
   s.files += %w( src/core/lib/gpr/alloc.cc )
   s.files += %w( src/core/lib/gpr/alloc.h )
   s.files += %w( src/core/lib/gpr/arena.h )
@@ -863,6 +869,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/iomgr/endpoint_cfstream.cc )
   s.files += %w( src/core/lib/iomgr/endpoint_cfstream.h )
   s.files += %w( src/core/lib/iomgr/endpoint_pair.h )
+  s.files += %w( src/core/lib/iomgr/endpoint_pair_event_engine.cc )
   s.files += %w( src/core/lib/iomgr/endpoint_pair_posix.cc )
   s.files += %w( src/core/lib/iomgr/endpoint_pair_uv.cc )
   s.files += %w( src/core/lib/iomgr/endpoint_pair_windows.cc )
@@ -882,6 +889,20 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/iomgr/ev_posix.cc )
   s.files += %w( src/core/lib/iomgr/ev_posix.h )
   s.files += %w( src/core/lib/iomgr/ev_windows.cc )
+  s.files += %w( src/core/lib/iomgr/event_engine/closure.cc )
+  s.files += %w( src/core/lib/iomgr/event_engine/closure.h )
+  s.files += %w( src/core/lib/iomgr/event_engine/endpoint.cc )
+  s.files += %w( src/core/lib/iomgr/event_engine/endpoint.h )
+  s.files += %w( src/core/lib/iomgr/event_engine/iomgr.cc )
+  s.files += %w( src/core/lib/iomgr/event_engine/iomgr.h )
+  s.files += %w( src/core/lib/iomgr/event_engine/pollset.cc )
+  s.files += %w( src/core/lib/iomgr/event_engine/pollset.h )
+  s.files += %w( src/core/lib/iomgr/event_engine/promise.h )
+  s.files += %w( src/core/lib/iomgr/event_engine/resolved_address_internal.cc )
+  s.files += %w( src/core/lib/iomgr/event_engine/resolved_address_internal.h )
+  s.files += %w( src/core/lib/iomgr/event_engine/resolver.cc )
+  s.files += %w( src/core/lib/iomgr/event_engine/tcp.cc )
+  s.files += %w( src/core/lib/iomgr/event_engine/timer.cc )
   s.files += %w( src/core/lib/iomgr/exec_ctx.cc )
   s.files += %w( src/core/lib/iomgr/exec_ctx.h )
   s.files += %w( src/core/lib/iomgr/executor.cc )
@@ -1023,6 +1044,11 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/profiling/basic_timers.cc )
   s.files += %w( src/core/lib/profiling/stap_timers.cc )
   s.files += %w( src/core/lib/profiling/timers.h )
+  s.files += %w( src/core/lib/security/authorization/authorization_engine.h )
+  s.files += %w( src/core/lib/security/authorization/authorization_policy_provider.h )
+  s.files += %w( src/core/lib/security/authorization/authorization_policy_provider_vtable.cc )
+  s.files += %w( src/core/lib/security/authorization/evaluate_args.cc )
+  s.files += %w( src/core/lib/security/authorization/evaluate_args.h )
   s.files += %w( src/core/lib/security/context/security_context.cc )
   s.files += %w( src/core/lib/security/context/security_context.h )
   s.files += %w( src/core/lib/security/credentials/alts/alts_credentials.cc )
@@ -1719,7 +1745,6 @@ Gem::Specification.new do |s|
   s.files += %w( third_party/boringssl-with-bazel/src/crypto/fipsmodule/tls/kdf.c )
   s.files += %w( third_party/boringssl-with-bazel/src/crypto/hkdf/hkdf.c )
   s.files += %w( third_party/boringssl-with-bazel/src/crypto/hpke/hpke.c )
-  s.files += %w( third_party/boringssl-with-bazel/src/crypto/hpke/internal.h )
   s.files += %w( third_party/boringssl-with-bazel/src/crypto/hrss/hrss.c )
   s.files += %w( third_party/boringssl-with-bazel/src/crypto/hrss/internal.h )
   s.files += %w( third_party/boringssl-with-bazel/src/crypto/internal.h )
@@ -1786,7 +1811,6 @@ Gem::Specification.new do |s|
   s.files += %w( third_party/boringssl-with-bazel/src/crypto/x509/t_req.c )
   s.files += %w( third_party/boringssl-with-bazel/src/crypto/x509/t_x509.c )
   s.files += %w( third_party/boringssl-with-bazel/src/crypto/x509/t_x509a.c )
-  s.files += %w( third_party/boringssl-with-bazel/src/crypto/x509/vpm_int.h )
   s.files += %w( third_party/boringssl-with-bazel/src/crypto/x509/x509.c )
   s.files += %w( third_party/boringssl-with-bazel/src/crypto/x509/x509_att.c )
   s.files += %w( third_party/boringssl-with-bazel/src/crypto/x509/x509_cmp.c )
@@ -1895,6 +1919,7 @@ Gem::Specification.new do |s|
   s.files += %w( third_party/boringssl-with-bazel/src/include/openssl/ex_data.h )
   s.files += %w( third_party/boringssl-with-bazel/src/include/openssl/hkdf.h )
   s.files += %w( third_party/boringssl-with-bazel/src/include/openssl/hmac.h )
+  s.files += %w( third_party/boringssl-with-bazel/src/include/openssl/hpke.h )
   s.files += %w( third_party/boringssl-with-bazel/src/include/openssl/hrss.h )
   s.files += %w( third_party/boringssl-with-bazel/src/include/openssl/is_boringssl.h )
   s.files += %w( third_party/boringssl-with-bazel/src/include/openssl/lhash.h )
index 2418290..8a59392 100644 (file)
--- a/grpc.gyp
+++ b/grpc.gyp
         'test/core/end2end/tests/retry_cancel_during_delay.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_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_recv_initial_metadata.cc',
         'test/core/end2end/tests/retry_recv_message.cc',
+        'test/core/end2end/tests/retry_recv_trailing_metadata_error.cc',
+        'test/core/end2end/tests/retry_send_op_fails.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_cancel_during_delay.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_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_recv_initial_metadata.cc',
         'test/core/end2end/tests/retry_recv_message.cc',
+        'test/core/end2end/tests/retry_recv_trailing_metadata_error.cc',
+        'test/core/end2end/tests/retry_send_op_fails.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',
         'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
         'src/core/ext/filters/client_channel/resolver.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc',
+        'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc',
+        'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc',
         'src/core/lib/debug/stats.cc',
         'src/core/lib/debug/stats_data.cc',
         'src/core/lib/debug/trace.cc',
+        'src/core/lib/event_engine/endpoint_config.cc',
+        'src/core/lib/event_engine/event_engine.cc',
         'src/core/lib/event_engine/slice_allocator.cc',
         'src/core/lib/event_engine/sockaddr.cc',
         'src/core/lib/http/format_request.cc',
         'src/core/lib/iomgr/dualstack_socket_posix.cc',
         'src/core/lib/iomgr/endpoint.cc',
         'src/core/lib/iomgr/endpoint_cfstream.cc',
+        'src/core/lib/iomgr/endpoint_pair_event_engine.cc',
         'src/core/lib/iomgr/endpoint_pair_posix.cc',
         'src/core/lib/iomgr/endpoint_pair_uv.cc',
         'src/core/lib/iomgr/endpoint_pair_windows.cc',
         'src/core/lib/iomgr/ev_poll_posix.cc',
         'src/core/lib/iomgr/ev_posix.cc',
         'src/core/lib/iomgr/ev_windows.cc',
+        'src/core/lib/iomgr/event_engine/closure.cc',
+        'src/core/lib/iomgr/event_engine/endpoint.cc',
+        'src/core/lib/iomgr/event_engine/iomgr.cc',
+        'src/core/lib/iomgr/event_engine/pollset.cc',
+        'src/core/lib/iomgr/event_engine/resolved_address_internal.cc',
+        'src/core/lib/iomgr/event_engine/resolver.cc',
+        'src/core/lib/iomgr/event_engine/tcp.cc',
+        'src/core/lib/iomgr/event_engine/timer.cc',
         'src/core/lib/iomgr/exec_ctx.cc',
         'src/core/lib/iomgr/executor.cc',
         'src/core/lib/iomgr/executor/mpmcqueue.cc',
         'src/core/lib/json/json_util.cc',
         'src/core/lib/json/json_writer.cc',
         'src/core/lib/matchers/matchers.cc',
+        'src/core/lib/security/authorization/authorization_policy_provider_vtable.cc',
+        'src/core/lib/security/authorization/evaluate_args.cc',
         'src/core/lib/security/context/security_context.cc',
         'src/core/lib/security/credentials/alts/alts_credentials.cc',
         'src/core/lib/security/credentials/alts/check_gcp_environment.cc',
       'dependencies': [
         'absl/container:flat_hash_map',
         'absl/container:inlined_vector',
+        'absl/functional:bind_front',
         'absl/status:statusor',
         'gpr',
         'address_sorting',
         'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc',
         'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc',
         'src/core/ext/filters/client_channel/lb_policy/priority/priority.cc',
+        'src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.cc',
         'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc',
         'src/core/ext/filters/client_channel/lb_policy/weighted_target/weighted_target.cc',
         'src/core/ext/filters/client_channel/lb_policy_registry.cc',
         'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
         'src/core/ext/filters/client_channel/resolver.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc',
+        'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc',
+        'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc',
         'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc',
         'src/core/lib/debug/stats.cc',
         'src/core/lib/debug/stats_data.cc',
         'src/core/lib/debug/trace.cc',
+        'src/core/lib/event_engine/endpoint_config.cc',
+        'src/core/lib/event_engine/event_engine.cc',
         'src/core/lib/event_engine/slice_allocator.cc',
         'src/core/lib/event_engine/sockaddr.cc',
         'src/core/lib/http/format_request.cc',
         'src/core/lib/iomgr/dualstack_socket_posix.cc',
         'src/core/lib/iomgr/endpoint.cc',
         'src/core/lib/iomgr/endpoint_cfstream.cc',
+        'src/core/lib/iomgr/endpoint_pair_event_engine.cc',
         'src/core/lib/iomgr/endpoint_pair_posix.cc',
         'src/core/lib/iomgr/endpoint_pair_uv.cc',
         'src/core/lib/iomgr/endpoint_pair_windows.cc',
         'src/core/lib/iomgr/ev_poll_posix.cc',
         'src/core/lib/iomgr/ev_posix.cc',
         'src/core/lib/iomgr/ev_windows.cc',
+        'src/core/lib/iomgr/event_engine/closure.cc',
+        'src/core/lib/iomgr/event_engine/endpoint.cc',
+        'src/core/lib/iomgr/event_engine/iomgr.cc',
+        'src/core/lib/iomgr/event_engine/pollset.cc',
+        'src/core/lib/iomgr/event_engine/resolved_address_internal.cc',
+        'src/core/lib/iomgr/event_engine/resolver.cc',
+        'src/core/lib/iomgr/event_engine/tcp.cc',
+        'src/core/lib/iomgr/event_engine/timer.cc',
         'src/core/lib/iomgr/exec_ctx.cc',
         'src/core/lib/iomgr/executor.cc',
         'src/core/lib/iomgr/executor/mpmcqueue.cc',
         'src/core/lib/json/json_reader.cc',
         'src/core/lib/json/json_util.cc',
         'src/core/lib/json/json_writer.cc',
+        'src/core/lib/security/authorization/authorization_policy_provider_null_vtable.cc',
         'src/core/lib/slice/b64.cc',
         'src/core/lib/slice/percent_encoding.cc',
         'src/core/lib/slice/slice.cc',
diff --git a/include/grpc/event_engine/endpoint_config.h b/include/grpc/event_engine/endpoint_config.h
new file mode 100644 (file)
index 0000000..ea1e172
--- /dev/null
@@ -0,0 +1,48 @@
+// Copyright 2021 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_EVENT_ENGINE_ENDPOINT_CONFIG_H
+#define GRPC_EVENT_ENGINE_ENDPOINT_CONFIG_H
+
+#include <grpc/support/port_platform.h>
+
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/variant.h"
+
+namespace grpc_event_engine {
+namespace experimental {
+
+/// A set of parameters used to configure an endpoint, either when initiating a
+/// new connection on the client side or when listening for incoming connections
+/// on the server side. An EndpointConfig contains a set of zero or more
+/// Settings. Each setting has a unique name, which can be used to fetch that
+/// Setting via the Get() method. Each Setting has a value, which can be an
+/// integer, string, or void pointer. Each EE impl should define the set of
+/// Settings that it supports being passed into it, along with the corresponding
+/// type.
+class EndpointConfig {
+ public:
+  virtual ~EndpointConfig() = default;
+  using Setting = absl::variant<absl::monostate, int, absl::string_view, void*>;
+  /// Returns an EndpointConfig Setting. If there is no Setting associated with
+  /// \a key in the EndpointConfig, an \a absl::monostate type will be
+  /// returned. Caller does not take ownership of resulting value.
+  virtual Setting Get(absl::string_view key) const = 0;
+};
+
+}  // namespace experimental
+}  // namespace grpc_event_engine
+
+#endif  // GRPC_EVENT_ENGINE_ENDPOINT_CONFIG_H
index cdb5966..af932ae 100644 (file)
 #include "absl/status/statusor.h"
 #include "absl/time/time.h"
 
-#include "grpc/event_engine/channel_args.h"
+#include "grpc/event_engine/endpoint_config.h"
 #include "grpc/event_engine/port.h"
 #include "grpc/event_engine/slice_allocator.h"
 
-// TODO(hork): explicitly define lifetimes and ownership of all objects.
 // TODO(hork): Define the Endpoint::Write metrics collection system
-
 namespace grpc_event_engine {
 namespace experimental {
 
@@ -96,12 +94,14 @@ class EventEngine {
     static constexpr socklen_t MAX_SIZE_BYTES = 128;
 
     ResolvedAddress(const sockaddr* address, socklen_t size);
+    ResolvedAddress() = default;
+    ResolvedAddress(const ResolvedAddress&) = default;
     const struct sockaddr* address() const;
     socklen_t size() const;
 
    private:
     char address_[MAX_SIZE_BYTES];
-    socklen_t size_;
+    socklen_t size_ = 0;
   };
 
   /// An Endpoint represents one end of a connection between a gRPC client and
@@ -144,9 +144,6 @@ class EventEngine {
     /// on endpoint shutdown.
     virtual void Write(Callback on_writable, SliceBuffer* data,
                        absl::Time deadline) = 0;
-    // TODO(hork): define status codes for the callback
-    // TODO(hork): define cleanup operations, lifetimes, responsibilities.
-    virtual void Close(Callback on_close) = 0;
     /// These methods return an address in the format described in DNSResolver.
     /// The returned values are owned by the Endpoint and are expected to remain
     /// valid for the life of the Endpoint.
@@ -199,7 +196,7 @@ class EventEngine {
   /// for Endpoint construction.
   virtual absl::StatusOr<std::unique_ptr<Listener>> CreateListener(
       Listener::AcceptCallback on_accept, Callback on_shutdown,
-      const ChannelArgs& args,
+      const EndpointConfig& args,
       SliceAllocatorFactory slice_allocator_factory) = 0;
   /// Creates a client network connection to a remote network listener.
   ///
@@ -216,7 +213,7 @@ class EventEngine {
   /// SliceAllocator API for more information.
   virtual absl::Status Connect(OnConnectCallback on_connect,
                                const ResolvedAddress& addr,
-                               const ChannelArgs& args,
+                               const EndpointConfig& args,
                                SliceAllocator slice_allocator,
                                absl::Time deadline) = 0;
 
@@ -280,13 +277,16 @@ class EventEngine {
 
   virtual ~EventEngine() = default;
 
+  // TODO(nnoble): consider whether we can remove this method before we
+  // de-experimentalize this API.
+  virtual bool IsWorkerThread() = 0;
+
   // TODO(hork): define return status codes
   /// Retrieves an instance of a DNSResolver.
   virtual absl::StatusOr<std::unique_ptr<DNSResolver>> GetDNSResolver() = 0;
 
   /// Intended for future expansion of Task run functionality.
   struct RunOptions {};
-  // TODO(hork): consider recommendation to make TaskHandle an output arg
   /// Run a callback as soon as possible.
   ///
   /// The \a fn callback's \a status argument is used to indicate whether it was
@@ -324,11 +324,9 @@ class EventEngine {
   virtual void Shutdown(Callback on_shutdown_complete) = 0;
 };
 
-/// Lazily instantiate and return a default global EventEngine instance if no
-/// custom instance is provided. If a custom EventEngine is provided for every
-/// channel/server via ChannelArgs, this method should never be called, and the
-/// default instance will never be instantiated.
-std::shared_ptr<EventEngine> GetDefaultEventEngine();
+// TODO(hork): finalize the API and document it. We need to firm up the story
+// around user-provided EventEngines.
+std::shared_ptr<EventEngine> DefaultEventEngineFactory();
 
 }  // namespace experimental
 }  // namespace grpc_event_engine
index c24b8f9..d54e65e 100644 (file)
     defined(GPR_AIX) || defined(GPR_NACL) || defined(GPR_FUCHSIA) ||        \
     defined(GRPC_POSIX_SOCKET)
 #define GRPC_EVENT_ENGINE_POSIX
+#include <arpa/inet.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
+#include <unistd.h>
 #elif defined(GPR_WINDOWS)
 #include <winsock2.h>
 #include <ws2tcpip.h>
index 4370cd5..f752541 100644 (file)
 // forward-declaring an internal struct, not used publicly.
 struct grpc_resource_quota;
 struct grpc_resource_user;
+struct grpc_slice_buffer;
 
 namespace grpc_event_engine {
 namespace experimental {
 
-// TODO(nnoble): forward declared here, needs definition.
-class SliceBuffer;
+// TODO(hork): stubbed out here, to be replaced with a real version in next PR.
+class SliceBuffer {
+ public:
+  SliceBuffer() { abort(); }
+  explicit SliceBuffer(grpc_slice_buffer*) { abort(); }
+};
 
 class SliceAllocator {
  public:
@@ -38,15 +43,20 @@ class SliceAllocator {
   SliceAllocator(SliceAllocator& other) = delete;
   SliceAllocator& operator=(const SliceAllocator& other) = delete;
   // Moveable
-  SliceAllocator(SliceAllocator&& other) = default;
-  SliceAllocator& operator=(SliceAllocator&& other) = default;
+  SliceAllocator(SliceAllocator&& other) noexcept;
+  SliceAllocator& operator=(SliceAllocator&& other) noexcept;
   ~SliceAllocator();
 
   using AllocateCallback =
       std::function<void(absl::Status, SliceBuffer* buffer)>;
-  // TODO(hork): explain what happens under resource exhaustion.
   /// Requests \a size bytes from gRPC, and populates \a dest with the allocated
   /// slices. Ownership of the \a SliceBuffer is not transferred.
+  ///
+  /// gRPC provides a ResourceQuota system to cap the amount of memory used by
+  /// the library. When a memory limit has been reached, slice allocation is
+  /// interrupted to attempt to reclaim memory from participating gRPC
+  /// internals. When there is sufficient memory available, slice allocation
+  /// proceeds as normal.
   absl::Status Allocate(size_t size, SliceBuffer* dest,
                         SliceAllocator::AllocateCallback cb);
 
@@ -62,8 +72,8 @@ class SliceAllocatorFactory {
   SliceAllocatorFactory(SliceAllocatorFactory& other) = delete;
   SliceAllocatorFactory& operator=(const SliceAllocatorFactory& other) = delete;
   // Moveable
-  SliceAllocatorFactory(SliceAllocatorFactory&& other) = default;
-  SliceAllocatorFactory& operator=(SliceAllocatorFactory&& other) = default;
+  SliceAllocatorFactory(SliceAllocatorFactory&& other) noexcept;
+  SliceAllocatorFactory& operator=(SliceAllocatorFactory&& other) noexcept;
   ~SliceAllocatorFactory();
 
   /// On Endpoint creation, call \a CreateSliceAllocator with the name of the
index f4408e1..5e45b47 100644 (file)
@@ -115,8 +115,7 @@ GRPCAPI grpc_completion_queue* grpc_completion_queue_create_for_pluck(
     of GRPC_CQ_CALLBACK and grpc_cq_polling_type of GRPC_CQ_DEFAULT_POLLING.
     This function is experimental. */
 GRPCAPI grpc_completion_queue* grpc_completion_queue_create_for_callback(
-    grpc_experimental_completion_queue_functor* shutdown_callback,
-    void* reserved);
+    grpc_completion_queue_functor* shutdown_callback, void* reserved);
 
 /** Create a completion queue */
 GRPCAPI grpc_completion_queue* grpc_completion_queue_create(
@@ -555,6 +554,14 @@ GRPCAPI char* grpc_channelz_get_subchannel(intptr_t subchannel_id);
    is allocated and must be freed by the application. */
 GRPCAPI char* grpc_channelz_get_socket(intptr_t socket_id);
 
+/**
+ * EXPERIMENTAL - Subject to change.
+ * Fetch a vtable for grpc_channel_arg that points to
+ * grpc_authorization_policy_provider.
+ */
+GRPCAPI const grpc_arg_pointer_vtable*
+grpc_authorization_policy_provider_arg_vtable(void);
+
 #ifdef __cplusplus
 }
 #endif
index b5dafe1..9edd70f 100644 (file)
@@ -1085,6 +1085,38 @@ GRPCAPI grpc_channel_credentials* grpc_xds_credentials_create(
 GRPCAPI grpc_server_credentials* grpc_xds_server_credentials_create(
     grpc_server_credentials* fallback_credentials);
 
+/**
+ * EXPERIMENTAL - Subject to change.
+ * An opaque type that is responsible for providing authorization policies to
+ * gRPC.
+ */
+typedef struct grpc_authorization_policy_provider
+    grpc_authorization_policy_provider;
+
+/**
+ * EXPERIMENTAL - Subject to change.
+ * Creates a grpc_authorization_policy_provider using SDK authorization policy
+ * from static string.
+ * - authz_policy is the input SDK authorization policy.
+ * - code is the error status code on failure. On success, it equals
+ *   GRPC_STATUS_OK.
+ * - error_details contains details about the error if any. If the
+ *   initialization is successful, it will be null. Caller must use gpr_free to
+ *   destroy this string.
+ */
+GRPCAPI grpc_authorization_policy_provider*
+grpc_authorization_policy_provider_static_data_create(
+    const char* authz_policy, grpc_status_code* code,
+    const char** error_details);
+
+/**
+ * EXPERIMENTAL - Subject to change.
+ * Releases grpc_authorization_policy_provider object. The creator of
+ * grpc_authorization_policy_provider is responsible for its release.
+ */
+GRPCAPI void grpc_authorization_policy_provider_release(
+    grpc_authorization_policy_provider* provider);
+
 #ifdef __cplusplus
 }
 #endif
index 4d7f078..3b2d45a 100644 (file)
@@ -45,6 +45,7 @@ extern "C" {
 #define GRPC_TRANSPORT_SECURITY_LEVEL_PROPERTY_NAME "security_level"
 #define GRPC_PEER_DNS_PROPERTY_NAME "peer_dns"
 #define GRPC_PEER_SPIFFE_ID_PROPERTY_NAME "peer_spiffe_id"
+#define GRPC_PEER_URI_PROPERTY_NAME "peer_uri"
 #define GRPC_PEER_EMAIL_PROPERTY_NAME "peer_email"
 #define GRPC_PEER_IP_PROPERTY_NAME "peer_ip"
 
index 9cf6d83..cf909b6 100644 (file)
@@ -428,6 +428,10 @@ typedef struct {
 #define GRPC_ARG_CHANNEL_POOL_DOMAIN "grpc.channel_pooling_domain"
 /** gRPC Objective-C channel pooling id. */
 #define GRPC_ARG_CHANNEL_ID "grpc.channel_id"
+/** Channel argument for grpc_authorization_policy_provider. If present, enables
+    gRPC authorization check. */
+#define GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER \
+  "grpc.authorization_policy_provider"
 /** \} */
 
 /** Result of a grpc call. If the caller satisfies the prerequisites of a
@@ -742,21 +746,20 @@ typedef enum {
   /** Events are popped out by calling grpc_completion_queue_pluck() API ONLY*/
   GRPC_CQ_PLUCK,
 
-  /** EXPERIMENTAL: Events trigger a callback specified as the tag */
+  /** Events trigger a callback specified as the tag */
   GRPC_CQ_CALLBACK
 } grpc_cq_completion_type;
 
-/** EXPERIMENTAL: Specifies an interface class to be used as a tag
-    for callback-based completion queues. This can be used directly,
-    as the first element of a struct in C, or as a base class in C++.
-    Its "run" value should be assigned to some non-member function, such as
-    a static method. */
-typedef struct grpc_experimental_completion_queue_functor {
+/** Specifies an interface class to be used as a tag for callback-based
+ * completion queues. This can be used directly, as the first element of a
+ * struct in C, or as a base class in C++. Its "run" value should be assigned to
+ * some non-member function, such as a static method. */
+typedef struct grpc_completion_queue_functor {
   /** The run member specifies a function that will be called when this
       tag is extracted from the completion queue. Its arguments will be a
       pointer to this functor and a boolean that indicates whether the
       operation succeeded (non-zero) or failed (zero) */
-  void (*functor_run)(struct grpc_experimental_completion_queue_functor*, int);
+  void (*functor_run)(struct grpc_completion_queue_functor*, int);
 
   /** The inlineable member specifies whether this functor can be run inline.
       This should only be used for trivial internally-defined functors. */
@@ -764,10 +767,11 @@ typedef struct grpc_experimental_completion_queue_functor {
 
   /** The following fields are not API. They are meant for internal use. */
   int internal_success;
-  struct grpc_experimental_completion_queue_functor* internal_next;
-} grpc_experimental_completion_queue_functor;
+  struct grpc_completion_queue_functor* internal_next;
+} grpc_completion_queue_functor;
 
-/* The upgrade to version 2 is currently experimental. */
+typedef grpc_completion_queue_functor
+    grpc_experimental_completion_queue_functor;
 
 #define GRPC_CQ_CURRENT_VERSION 2
 #define GRPC_CQ_VERSION_MINIMUM_FOR_CALLBACKABLE 2
@@ -782,10 +786,10 @@ typedef struct grpc_completion_queue_attributes {
 
   /* END OF VERSION 1 CQ ATTRIBUTES */
 
-  /* EXPERIMENTAL: START OF VERSION 2 CQ ATTRIBUTES */
+  /* START OF VERSION 2 CQ ATTRIBUTES */
   /** When creating a callbackable CQ, pass in a functor to get invoked when
    * shutdown is complete */
-  grpc_experimental_completion_queue_functor* cq_shutdown_cb;
+  grpc_completion_queue_functor* cq_shutdown_cb;
 
   /* END OF VERSION 2 CQ ATTRIBUTES */
 } grpc_completion_queue_attributes;
index 387639b..8a0d84d 100644 (file)
@@ -664,4 +664,21 @@ typedef unsigned __int64 uint64_t;
 #define __STDC_FORMAT_MACROS
 #endif
 
+/* Selectively enable EventEngine on specific platforms. This default can be
+ * overridden using the GRPC_USE_EVENT_ENGINE compiler flag.
+ */
+#ifndef GRPC_USE_EVENT_ENGINE
+/* Not enabled by default on any platforms yet. (2021.06) */
+#elif GRPC_USE_EVENT_ENGINE == 0
+/* Building with `-DGRPC_USE_EVENT_ENGINE=0` will override the default. */
+#undef GRPC_USE_EVENT_ENGINE
+#endif /* GRPC_USE_EVENT_ENGINE */
+
+#ifdef GRPC_USE_EVENT_ENGINE
+#undef GPR_SUPPORT_CHANNELS_FROM_FD
+#define GRPC_ARES 0
+#endif /* GRPC_USE_EVENT_ENGINE */
+
+#define GRPC_CALLBACK_API_NONEXPERIMENTAL
+
 #endif /* GRPC_IMPL_CODEGEN_PORT_PLATFORM_H */
index 6723e13..7d0b4be 100644 (file)
@@ -81,7 +81,6 @@ class Alarm : private ::grpc::GrpcLibraryCodegen {
   /// has already fired has no effect.
   void Cancel();
 
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
   /// Set an alarm to invoke callback \a f. The argument to the callback
   /// states whether the alarm expired at \a deadline (true) or was cancelled
   /// (false)
@@ -89,32 +88,6 @@ class Alarm : private ::grpc::GrpcLibraryCodegen {
   void Set(const T& deadline, std::function<void(bool)> f) {
     SetInternal(::grpc::TimePoint<T>(deadline).raw_time(), std::move(f));
   }
-#endif
-
-  /// NOTE: class experimental_type is not part of the public API of this class
-  /// TODO(vjpai): Move these contents to the public API of Alarm when
-  ///              they are no longer experimental
-  class experimental_type {
-   public:
-    explicit experimental_type(Alarm* alarm) : alarm_(alarm) {}
-
-    /// Set an alarm to invoke callback \a f. The argument to the callback
-    /// states whether the alarm expired at \a deadline (true) or was cancelled
-    /// (false)
-    template <typename T>
-    void Set(const T& deadline, std::function<void(bool)> f) {
-      alarm_->SetInternal(::grpc::TimePoint<T>(deadline).raw_time(),
-                          std::move(f));
-    }
-
-   private:
-    Alarm* alarm_;
-  };
-
-  /// NOTE: The function experimental() is not stable public API. It is a view
-  /// to the experimental components of this class. It may be changed or removed
-  /// at any time.
-  experimental_type experimental() { return experimental_type(this); }
 
  private:
   void SetInternal(::grpc::CompletionQueue* cq, gpr_timespec deadline,
index e9564c3..ad479d9 100644 (file)
@@ -87,13 +87,13 @@ class TemplatedGenericStub final {
                         true, tag);
   }
 
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
   /// Setup and start a unary call to a named method \a method using
   /// \a context and specifying the \a request and \a response buffers.
   void UnaryCall(ClientContext* context, const std::string& method,
-                 const RequestType* request, ResponseType* response,
+                 StubOptions options, const RequestType* request,
+                 ResponseType* response,
                  std::function<void(grpc::Status)> on_completion) {
-    UnaryCallInternal(context, method, /*options=*/{}, request, response,
+    UnaryCallInternal(context, method, options, request, response,
                       std::move(on_completion));
   }
 
@@ -102,9 +102,9 @@ class TemplatedGenericStub final {
   /// Like any other reactor-based RPC, it will not be activated until
   /// StartCall is invoked on its reactor.
   void PrepareUnaryCall(ClientContext* context, const std::string& method,
-                        const RequestType* request, ResponseType* response,
-                        ClientUnaryReactor* reactor) {
-    PrepareUnaryCallInternal(context, method, /*options=*/{}, request, response,
+                        StubOptions options, const RequestType* request,
+                        ResponseType* response, ClientUnaryReactor* reactor) {
+    PrepareUnaryCallInternal(context, method, options, request, response,
                              reactor);
   }
 
@@ -112,58 +112,10 @@ class TemplatedGenericStub final {
   /// \a reactor . Like any other bidi streaming RPC, it will not be activated
   /// until StartCall is invoked on its reactor.
   void PrepareBidiStreamingCall(
-      ClientContext* context, const std::string& method,
+      ClientContext* context, const std::string& method, StubOptions options,
       ClientBidiReactor<RequestType, ResponseType>* reactor) {
-    PrepareBidiStreamingCallInternal(context, method, /*options=*/{}, reactor);
+    PrepareBidiStreamingCallInternal(context, method, options, reactor);
   }
-#endif
-
-  /// NOTE: class experimental_type is not part of the public API of this class
-  /// TODO(vjpai): Move these contents to the public API of GenericStub when
-  ///              they are no longer experimental
-  class experimental_type {
-   public:
-    explicit experimental_type(TemplatedGenericStub* stub) : stub_(stub) {}
-
-    /// Setup and start a unary call to a named method \a method using
-    /// \a context and specifying the \a request and \a response buffers.
-    void UnaryCall(ClientContext* context, const std::string& method,
-                   StubOptions options, const RequestType* request,
-                   ResponseType* response,
-                   std::function<void(grpc::Status)> on_completion) {
-      stub_->UnaryCallInternal(context, method, options, request, response,
-                               std::move(on_completion));
-    }
-
-    /// Setup a unary call to a named method \a method using
-    /// \a context and specifying the \a request and \a response buffers.
-    /// Like any other reactor-based RPC, it will not be activated until
-    /// StartCall is invoked on its reactor.
-    void PrepareUnaryCall(ClientContext* context, const std::string& method,
-                          StubOptions options, const RequestType* request,
-                          ResponseType* response, ClientUnaryReactor* reactor) {
-      stub_->PrepareUnaryCallInternal(context, method, options, request,
-                                      response, reactor);
-    }
-
-    /// Setup a call to a named method \a method using \a context and tied to
-    /// \a reactor . Like any other bidi streaming RPC, it will not be activated
-    /// until StartCall is invoked on its reactor.
-    void PrepareBidiStreamingCall(
-        ClientContext* context, const std::string& method, StubOptions options,
-        ClientBidiReactor<RequestType, ResponseType>* reactor) {
-      stub_->PrepareBidiStreamingCallInternal(context, method, options,
-                                              reactor);
-    }
-
-   private:
-    TemplatedGenericStub* stub_;
-  };
-
-  /// NOTE: The function experimental() is not stable public API. It is a view
-  /// to the experimental components of this class. It may be changed or removed
-  /// at any time.
-  experimental_type experimental() { return experimental_type(this); }
 
  private:
   std::shared_ptr<grpc::ChannelInterface> channel_;
index 048af33..6cdd514 100644 (file)
@@ -79,10 +79,6 @@ class AsyncGenericService final {
   grpc::Server* server_;
 };
 
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-namespace experimental {
-#endif
-
 /// \a ServerGenericBidiReactor is the reactor class for bidi streaming RPCs
 /// invoked on a CallbackGenericService. It is just a ServerBidi reactor with
 /// ByteBuffer arguments.
@@ -134,9 +130,6 @@ class CallbackGenericService {
   grpc::Server* server_{nullptr};
 };
 
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-}  // namespace experimental
-#endif
 }  // namespace grpc
 
 #endif  // GRPCPP_IMPL_CODEGEN_ASYNC_GENERIC_SERVICE_H
index 2c015f2..6959fa2 100644 (file)
@@ -114,6 +114,13 @@ class ByteBuffer final {
     return *this;
   }
 
+  // If this ByteBuffer's representation is a single flat slice, returns a
+  // slice referencing that array.
+  Status TrySingleSlice(Slice* slice) const;
+
+  /// Dump (read) the buffer contents into \a slics.
+  Status DumpToSingleSlice(Slice* slice) const;
+
   /// Dump (read) the buffer contents into \a slices.
   Status Dump(std::vector<Slice>* slices) const;
 
index 086a179..d670786 100644 (file)
@@ -66,8 +66,7 @@ Reactor* CatchingReactorGetter(Func&& func, Args&&... args) {
 // constructed and then fired at exactly one point. There is no expectation
 // that they can be reused without reconstruction.
 
-class CallbackWithStatusTag
-    : public grpc_experimental_completion_queue_functor {
+class CallbackWithStatusTag : public grpc_completion_queue_functor {
  public:
   // always allocated against a call arena, no memory free required
   static void operator delete(void* /*ptr*/, std::size_t size) {
@@ -108,8 +107,7 @@ class CallbackWithStatusTag
   CompletionQueueTag* ops_;
   Status status_;
 
-  static void StaticRun(grpc_experimental_completion_queue_functor* cb,
-                        int ok) {
+  static void StaticRun(grpc_completion_queue_functor* cb, int ok) {
     static_cast<CallbackWithStatusTag*>(cb)->Run(static_cast<bool>(ok));
   }
   void Run(bool ok) {
@@ -134,8 +132,7 @@ class CallbackWithStatusTag
 /// CallbackWithSuccessTag can be reused multiple times, and will be used in
 /// this fashion for streaming operations. As a result, it shouldn't clear
 /// anything up until its destructor
-class CallbackWithSuccessTag
-    : public grpc_experimental_completion_queue_functor {
+class CallbackWithSuccessTag : public grpc_completion_queue_functor {
  public:
   // always allocated against a call arena, no memory free required
   static void operator delete(void* /*ptr*/, std::size_t size) {
@@ -198,8 +195,7 @@ class CallbackWithSuccessTag
   std::function<void(bool)> func_;
   CompletionQueueTag* ops_;
 
-  static void StaticRun(grpc_experimental_completion_queue_functor* cb,
-                        int ok) {
+  static void StaticRun(grpc_completion_queue_functor* cb, int ok) {
     static_cast<CallbackWithSuccessTag*>(cb)->Run(static_cast<bool>(ok));
   }
   void Run(bool ok) {
index ba80290..7d38ea5 100644 (file)
@@ -130,6 +130,15 @@ class ClientReactor {
   /// heavyweight and the cost of the virtual call is not much in comparison.
   /// This function may be removed or devirtualized in the future.
   virtual void InternalScheduleOnDone(::grpc::Status s);
+
+  /// InternalTrailersOnly is not part of the API and is not meant to be
+  /// overridden. It is virtual to allow successful builds for certain bazel
+  /// build users that only want to depend on gRPC codegen headers and not the
+  /// full library (although this is not a generally-supported option). Although
+  /// the virtual call is slower than a direct call, this function is
+  /// heavyweight and the cost of the virtual call is not much in comparison.
+  /// This function may be removed or devirtualized in the future.
+  virtual bool InternalTrailersOnly(const grpc_call* call) const;
 };
 
 }  // namespace internal
@@ -594,7 +603,8 @@ class ClientCallbackReaderWriterImpl
     start_tag_.Set(
         call_.call(),
         [this](bool ok) {
-          reactor_->OnReadInitialMetadataDone(ok);
+          reactor_->OnReadInitialMetadataDone(
+              ok && !reactor_->InternalTrailersOnly(call_.call()));
           MaybeFinish(/*from_reaction=*/true);
         },
         &start_ops_, /*can_inline=*/false);
@@ -737,7 +747,8 @@ class ClientCallbackReaderImpl : public ClientCallbackReader<Response> {
     start_tag_.Set(
         call_.call(),
         [this](bool ok) {
-          reactor_->OnReadInitialMetadataDone(ok);
+          reactor_->OnReadInitialMetadataDone(
+              ok && !reactor_->InternalTrailersOnly(call_.call()));
           MaybeFinish(/*from_reaction=*/true);
         },
         &start_ops_, /*can_inline=*/false);
@@ -995,7 +1006,8 @@ class ClientCallbackWriterImpl : public ClientCallbackWriter<Request> {
     start_tag_.Set(
         call_.call(),
         [this](bool ok) {
-          reactor_->OnReadInitialMetadataDone(ok);
+          reactor_->OnReadInitialMetadataDone(
+              ok && !reactor_->InternalTrailersOnly(call_.call()));
           MaybeFinish(/*from_reaction=*/true);
         },
         &start_ops_, /*can_inline=*/false);
@@ -1121,7 +1133,8 @@ class ClientCallbackUnaryImpl final : public ClientCallbackUnary {
     start_tag_.Set(
         call_.call(),
         [this](bool ok) {
-          reactor_->OnReadInitialMetadataDone(ok);
+          reactor_->OnReadInitialMetadataDone(
+              ok && !reactor_->InternalTrailersOnly(call_.call()));
           MaybeFinish();
         },
         &start_ops_, /*can_inline=*/false);
index 952a7ba..c4df62a 100644 (file)
@@ -92,6 +92,7 @@ class ClientAsyncResponseReader;
 
 namespace testing {
 class InteropClientContextInspector;
+class ClientContextTestPeer;
 }  // namespace testing
 
 namespace internal {
@@ -205,7 +206,7 @@ class ClientContext {
   /// \return A newly constructed \a ClientContext instance based on \a
   /// server_context, with traits propagated (copied) according to \a options.
   static std::unique_ptr<ClientContext> FromServerContext(
-      const grpc::ServerContext& server_context,
+      const grpc::ServerContextBase& server_context,
       PropagationOptions options = PropagationOptions());
   static std::unique_ptr<ClientContext> FromCallbackServerContext(
       const grpc::CallbackServerContext& server_context,
@@ -423,6 +424,7 @@ class ClientContext {
   ClientContext& operator=(const ClientContext&);
 
   friend class ::grpc::testing::InteropClientContextInspector;
+  friend class ::grpc::testing::ClientContextTestPeer;
   friend class ::grpc::internal::CallOpClientRecvStatus;
   friend class ::grpc::internal::CallOpRecvInitialMetadata;
   friend class ::grpc::Channel;
index d23e0e2..654ff1c 100644 (file)
@@ -443,7 +443,7 @@ class ServerCompletionQueue : public CompletionQueue {
   /// \param shutdown_cb is the shutdown callback used for CALLBACK api queues
   ServerCompletionQueue(grpc_cq_completion_type completion_type,
                         grpc_cq_polling_type polling_type,
-                        grpc_experimental_completion_queue_functor* shutdown_cb)
+                        grpc_completion_queue_functor* shutdown_cb)
       : CompletionQueue(grpc_completion_queue_attributes{
             GRPC_CQ_CURRENT_VERSION, completion_type, polling_type,
             shutdown_cb}),
index 4048ea1..23b990e 100644 (file)
@@ -20,9 +20,6 @@
 #define GRPCPP_IMPL_CODEGEN_MESSAGE_ALLOCATOR_H
 
 namespace grpc {
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-namespace experimental {
-#endif
 
 // NOTE: This is an API for advanced users who need custom allocators.
 // Per rpc struct for the allocator. This is the interface to return to user.
@@ -69,25 +66,6 @@ class MessageAllocator {
   virtual MessageHolder<RequestT, ResponseT>* AllocateMessages() = 0;
 };
 
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-}  // namespace experimental
-#endif
-
-// TODO(vjpai): Remove namespace experimental when de-experimentalized fully.
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-namespace experimental {
-
-using ::grpc::RpcAllocatorState;
-
-template <typename RequestT, typename ResponseT>
-using MessageHolder = ::grpc::MessageHolder<RequestT, ResponseT>;
-
-template <typename RequestT, typename ResponseT>
-using MessageAllocator = ::grpc::MessageAllocator<RequestT, ResponseT>;
-
-}  // namespace experimental
-#endif
-
 }  // namespace grpc
 
 #endif  // GRPCPP_IMPL_CODEGEN_MESSAGE_ALLOCATOR_H
index 3ccabdb..513213d 100644 (file)
@@ -158,8 +158,7 @@ class ServerCallbackCall {
 };
 
 template <class Request, class Response>
-class DefaultMessageHolder
-    : public ::grpc::experimental::MessageHolder<Request, Response> {
+class DefaultMessageHolder : public MessageHolder<Request, Response> {
  public:
   DefaultMessageHolder() {
     this->set_request(&request_obj_);
index 76e655a..6c66c42 100644 (file)
@@ -37,17 +37,16 @@ class CallbackUnaryHandler : public ::grpc::internal::MethodHandler {
       : get_reactor_(std::move(get_reactor)) {}
 
   void SetMessageAllocator(
-      ::grpc::experimental::MessageAllocator<RequestType, ResponseType>*
-          allocator) {
+      MessageAllocator<RequestType, ResponseType>* allocator) {
     allocator_ = allocator;
   }
 
   void RunHandler(const HandlerParameter& param) final {
     // Arena allocate a controller structure (that includes request/response)
     ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call());
-    auto* allocator_state = static_cast<
-        ::grpc::experimental::MessageHolder<RequestType, ResponseType>*>(
-        param.internal_data);
+    auto* allocator_state =
+        static_cast<MessageHolder<RequestType, ResponseType>*>(
+            param.internal_data);
 
     auto* call = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc(
         param.call->call(), sizeof(ServerCallbackUnaryImpl)))
@@ -82,8 +81,7 @@ class CallbackUnaryHandler : public ::grpc::internal::MethodHandler {
     ::grpc::ByteBuffer buf;
     buf.set_buffer(req);
     RequestType* request = nullptr;
-    ::grpc::experimental::MessageHolder<RequestType, ResponseType>*
-        allocator_state = nullptr;
+    MessageHolder<RequestType, ResponseType>* allocator_state = nullptr;
     if (allocator_ != nullptr) {
       allocator_state = allocator_->AllocateMessages();
     } else {
@@ -109,8 +107,7 @@ class CallbackUnaryHandler : public ::grpc::internal::MethodHandler {
   std::function<ServerUnaryReactor*(::grpc::CallbackServerContext*,
                                     const RequestType*, ResponseType*)>
       get_reactor_;
-  ::grpc::experimental::MessageAllocator<RequestType, ResponseType>*
-      allocator_ = nullptr;
+  MessageAllocator<RequestType, ResponseType>* allocator_ = nullptr;
 
   class ServerCallbackUnaryImpl : public ServerCallbackUnary {
    public:
@@ -181,8 +178,7 @@ class CallbackUnaryHandler : public ::grpc::internal::MethodHandler {
 
     ServerCallbackUnaryImpl(
         ::grpc::CallbackServerContext* ctx, ::grpc::internal::Call* call,
-        ::grpc::experimental::MessageHolder<RequestType, ResponseType>*
-            allocator_state,
+        MessageHolder<RequestType, ResponseType>* allocator_state,
         std::function<void()> call_requester)
         : ctx_(ctx),
           call_(*call),
@@ -233,8 +229,7 @@ class CallbackUnaryHandler : public ::grpc::internal::MethodHandler {
 
     ::grpc::CallbackServerContext* const ctx_;
     ::grpc::internal::Call call_;
-    ::grpc::experimental::MessageHolder<RequestType, ResponseType>* const
-        allocator_state_;
+    MessageHolder<RequestType, ResponseType>* const allocator_state_;
     std::function<void()> call_requester_;
     // reactor_ can always be loaded/stored with relaxed memory ordering because
     // its value is only set once, independently of other data in the object,
@@ -384,6 +379,9 @@ class CallbackClientStreamingHandler : public ::grpc::internal::MethodHandler {
       read_tag_.Set(
           call_.call(),
           [this, reactor](bool ok) {
+            if (GPR_UNLIKELY(!ok)) {
+              ctx_->MaybeMarkCancelledOnRead();
+            }
             reactor->OnReadDone(ok);
             this->MaybeDone(/*inlineable_ondone=*/true);
           },
@@ -831,6 +829,9 @@ class CallbackBidiHandler : public ::grpc::internal::MethodHandler {
       read_tag_.Set(
           call_.call(),
           [this, reactor](bool ok) {
+            if (GPR_UNLIKELY(!ok)) {
+              ctx_->MaybeMarkCancelledOnRead();
+            }
             reactor->OnReadDone(ok);
             this->MaybeDone(/*inlineable_ondone=*/true);
           },
index cbbd229..3eaee75 100644 (file)
@@ -110,13 +110,8 @@ typedef ::grpc::CallbackServerContext CallbackServerContext;
 
 }  // namespace experimental
 
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-namespace experimental {
-#endif
 class GenericCallbackServerContext;
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-}  // namespace experimental
-#endif
+
 namespace internal {
 class Call;
 }  // namespace internal
@@ -299,10 +294,7 @@ class ServerContextBase {
   /// NOTE: This is an API for advanced users who need custom allocators.
   /// Get and maybe mutate the allocator state associated with the current RPC.
   /// Currently only applicable for callback unary RPC methods.
-  /// WARNING: This is experimental API and could be changed or removed.
-  ::grpc::experimental::RpcAllocatorState* GetRpcAllocatorState() {
-    return message_allocator_state_;
-  }
+  RpcAllocatorState* GetRpcAllocatorState() { return message_allocator_state_; }
 
   /// Get a library-owned default unary reactor for use in minimal reaction
   /// cases. This supports typical unary RPC usage of providing a response and
@@ -394,11 +386,7 @@ class ServerContextBase {
   friend class ::grpc::internal::FinishOnlyReactor;
   friend class ::grpc::ClientContext;
   friend class ::grpc::GenericServerContext;
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
   friend class ::grpc::GenericCallbackServerContext;
-#else
-  friend class ::grpc::experimental::GenericCallbackServerContext;
-#endif
 
   /// Prevent copying.
   ServerContextBase(const ServerContextBase&);
@@ -429,11 +417,12 @@ class ServerContextBase {
     return rpc_info_;
   }
 
-  void set_message_allocator_state(
-      ::grpc::experimental::RpcAllocatorState* allocator_state) {
+  void set_message_allocator_state(RpcAllocatorState* allocator_state) {
     message_allocator_state_ = allocator_state;
   }
 
+  void MaybeMarkCancelledOnRead();
+
   struct CallWrapper {
     ~CallWrapper();
 
@@ -469,7 +458,7 @@ class ServerContextBase {
   bool has_pending_ops_ = false;
 
   ::grpc::experimental::ServerRpcInfo* rpc_info_ = nullptr;
-  ::grpc::experimental::RpcAllocatorState* message_allocator_state_ = nullptr;
+  RpcAllocatorState* message_allocator_state_ = nullptr;
   ContextAllocator* context_allocator_ = nullptr;
 
   class Reactor : public ::grpc::ServerUnaryReactor {
@@ -524,6 +513,9 @@ class ServerContextBase {
   typename std::aligned_storage<sizeof(Reactor), alignof(Reactor)>::type
       default_reactor_;
   std::atomic_bool default_reactor_used_{false};
+
+  std::atomic_bool marked_cancelled_{false};
+
   std::unique_ptr<TestServerCallbackUnary> test_unary_;
 };
 
@@ -633,24 +625,13 @@ class ContextAllocator {
 
   virtual CallbackServerContext* NewCallbackServerContext() { return nullptr; }
 
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-  virtual experimental::GenericCallbackServerContext*
-  NewGenericCallbackServerContext() {
-    return nullptr;
-  }
-#else
   virtual GenericCallbackServerContext* NewGenericCallbackServerContext() {
     return nullptr;
   }
-#endif
 
   virtual void Release(CallbackServerContext*) {}
 
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-  virtual void Release(experimental::GenericCallbackServerContext*) {}
-#else
   virtual void Release(GenericCallbackServerContext*) {}
-#endif
 };
 
 }  // namespace grpc
index a44ab1f..ac87b27 100644 (file)
@@ -50,13 +50,7 @@ namespace internal {
 class ServerAsyncStreamingInterface;
 }  // namespace internal
 
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-namespace experimental {
-#endif
 class CallbackGenericService;
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-}  // namespace experimental
-#endif
 
 namespace experimental {
 class ServerInterceptorFactoryInterface;
@@ -130,35 +124,12 @@ class ServerInterface : public internal::CallHook {
   /// service. The service must exist for the lifetime of the Server instance.
   virtual void RegisterAsyncGenericService(AsyncGenericService* service) = 0;
 
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
   /// Register a callback generic service. This call does not take ownership of
   /// the  service. The service must exist for the lifetime of the Server
   /// instance. May not be abstract since this is a post-1.0 API addition.
 
   virtual void RegisterCallbackGenericService(CallbackGenericService*
                                               /*service*/) {}
-#else
-  /// NOTE: class experimental_registration_interface is not part of the public
-  /// API of this class
-  /// TODO(vjpai): Move these contents to public API when no longer experimental
-  class experimental_registration_interface {
-   public:
-    virtual ~experimental_registration_interface() {}
-    /// May not be abstract since this is a post-1.0 API addition
-    virtual void RegisterCallbackGenericService(
-        experimental::CallbackGenericService* /*service*/) {}
-    virtual void RegisterContextAllocator(
-        std::unique_ptr<ContextAllocator> context_allocator) {}
-  };
-
-  /// NOTE: The function experimental_registration() is not stable public API.
-  /// It is a view to the experimental components of this class. It may be
-  /// changed or removed at any time. May not be abstract since this is a
-  /// post-1.0 API addition
-  virtual experimental_registration_interface* experimental_registration() {
-    return nullptr;
-  }
-#endif
 
   /// Tries to bind \a server to the given \a addr.
   ///
index 57ca9f0..9b12545 100644 (file)
@@ -99,29 +99,6 @@ class Service {
   }
 
  protected:
-  // TODO(vjpai): Promote experimental contents once callback API is accepted
-  class experimental_type {
-   public:
-    explicit experimental_type(Service* service) : service_(service) {}
-
-    void MarkMethodCallback(int index, internal::MethodHandler* handler) {
-      service_->MarkMethodCallbackInternal(index, handler);
-    }
-
-    void MarkMethodRawCallback(int index, internal::MethodHandler* handler) {
-      service_->MarkMethodRawCallbackInternal(index, handler);
-    }
-
-    internal::MethodHandler* GetHandler(int index) {
-      return service_->GetHandlerInternal(index);
-    }
-
-   private:
-    Service* service_;
-  };
-
-  experimental_type experimental() { return experimental_type(this); }
-
   template <class Message>
   void RequestAsyncUnary(int index, ::grpc::ServerContext* context,
                          Message* request,
@@ -216,23 +193,7 @@ class Service {
     methods_[idx]->SetMethodType(internal::RpcMethod::BIDI_STREAMING);
   }
 
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
   void MarkMethodCallback(int index, internal::MethodHandler* handler) {
-    MarkMethodCallbackInternal(index, handler);
-  }
-
-  void MarkMethodRawCallback(int index, internal::MethodHandler* handler) {
-    MarkMethodRawCallbackInternal(index, handler);
-  }
-
-  internal::MethodHandler* GetHandler(int index) {
-    return GetHandlerInternal(index);
-  }
-#endif
- private:
-  // TODO(vjpai): migrate the Internal functions to mainline functions once
-  //              callback API is fully de-experimental
-  void MarkMethodCallbackInternal(int index, internal::MethodHandler* handler) {
     // This does not have to be a hard error, however no one has approached us
     // with a use case yet. Please file an issue if you believe you have one.
     size_t idx = static_cast<size_t>(index);
@@ -245,8 +206,7 @@ class Service {
         internal::RpcServiceMethod::ApiType::CALL_BACK);
   }
 
-  void MarkMethodRawCallbackInternal(int index,
-                                     internal::MethodHandler* handler) {
+  void MarkMethodRawCallback(int index, internal::MethodHandler* handler) {
     // This does not have to be a hard error, however no one has approached us
     // with a use case yet. Please file an issue if you believe you have one.
     size_t idx = static_cast<size_t>(index);
@@ -259,11 +219,12 @@ class Service {
         internal::RpcServiceMethod::ApiType::RAW_CALL_BACK);
   }
 
-  internal::MethodHandler* GetHandlerInternal(int index) {
+  internal::MethodHandler* GetHandler(int index) {
     size_t idx = static_cast<size_t>(index);
     return methods_[idx]->handler();
   }
 
+ private:
   friend class Server;
   friend class ServerInterface;
   ServerInterface* server_;
index ea55025..d84b842 100644 (file)
@@ -74,6 +74,11 @@ class Slice final {
   Slice(const Slice& other)
       : slice_(g_core_codegen_interface->grpc_slice_ref(other.slice_)) {}
 
+  /// Move constructor, steals a reference.
+  Slice(Slice&& other) noexcept : slice_(other.slice_) {
+    other.slice_ = g_core_codegen_interface->grpc_empty_slice();
+  }
+
   /// Assignment, reference count is unchanged.
   Slice& operator=(Slice other) {
     std::swap(slice_, other.slice_);
@@ -107,6 +112,12 @@ class Slice final {
   /// Raw pointer to the end (one byte \em past the last element) of the slice.
   const uint8_t* end() const { return GRPC_SLICE_END_PTR(slice_); }
 
+  /// Returns a substring of the `slice` as another slice.
+  Slice sub(size_t begin, size_t end) const {
+    return Slice(g_core_codegen_interface->grpc_slice_sub(slice_, begin, end),
+                 STEAL_REF);
+  }
+
   /// Raw C slice. Caller needs to call grpc_slice_unref when done.
   grpc_slice c_slice() const {
     return g_core_codegen_interface->grpc_slice_ref(slice_);
diff --git a/include/grpcpp/security/authorization_policy_provider.h b/include/grpcpp/security/authorization_policy_provider.h
new file mode 100644 (file)
index 0000000..9c3c6be
--- /dev/null
@@ -0,0 +1,67 @@
+// 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.
+
+#ifndef GRPCPP_SECURITY_AUTHORIZATION_POLICY_PROVIDER_H
+#define GRPCPP_SECURITY_AUTHORIZATION_POLICY_PROVIDER_H
+
+#include <grpc/status.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+
+#include <memory>
+
+// TODO(yihuazhang): remove the forward declaration here and include
+// <grpc/grpc_security.h> directly once the insecure builds are cleaned up.
+typedef struct grpc_authorization_policy_provider
+    grpc_authorization_policy_provider;
+
+namespace grpc {
+namespace experimental {
+
+// Wrapper around C-core grpc_authorization_policy_provider. Internally, it
+// handles creating and updating authorization engine objects, using SDK
+// authorization policy.
+class AuthorizationPolicyProviderInterface {
+ public:
+  virtual ~AuthorizationPolicyProviderInterface() = default;
+  virtual grpc_authorization_policy_provider* c_provider() = 0;
+};
+
+// Implementation obtains authorization policy from static string. This provider
+// will always return the same authorization engines.
+class StaticDataAuthorizationPolicyProvider
+    : public AuthorizationPolicyProviderInterface {
+ public:
+  static std::shared_ptr<StaticDataAuthorizationPolicyProvider> Create(
+      const std::string& authz_policy, grpc::Status* status);
+
+  // Use factory method "Create" to create an instance of
+  // StaticDataAuthorizationPolicyProvider.
+  explicit StaticDataAuthorizationPolicyProvider(
+      grpc_authorization_policy_provider* provider)
+      : c_provider_(provider) {}
+
+  ~StaticDataAuthorizationPolicyProvider() override;
+
+  grpc_authorization_policy_provider* c_provider() override {
+    return c_provider_;
+  }
+
+ private:
+  grpc_authorization_policy_provider* c_provider_ = nullptr;
+};
+
+}  // namespace experimental
+}  // namespace grpc
+
+#endif  // GRPCPP_SECURITY_AUTHORIZATION_POLICY_PROVIDER_H
index a69e64b..4c21508 100644 (file)
@@ -237,7 +237,6 @@ class Server : public ServerInterface, private GrpcLibraryCodegen {
   /// service. The service must exist for the lifetime of the Server instance.
   void RegisterAsyncGenericService(AsyncGenericService* service) override;
 
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
   /// Register a callback-based generic service. This call does not take
   /// ownership of theservice. The service must exist for the lifetime of the
   /// Server instance.
@@ -248,41 +247,6 @@ class Server : public ServerInterface, private GrpcLibraryCodegen {
     context_allocator_ = std::move(context_allocator);
   }
 
-#else
-  /// NOTE: class experimental_registration_type is not part of the public API
-  /// of this class
-  /// TODO(vjpai): Move these contents to the public API of Server when
-  ///              they are no longer experimental
-  class experimental_registration_type final
-      : public experimental_registration_interface {
-   public:
-    explicit experimental_registration_type(Server* server) : server_(server) {}
-    void RegisterCallbackGenericService(
-        experimental::CallbackGenericService* service) override {
-      server_->RegisterCallbackGenericService(service);
-    }
-
-    void RegisterContextAllocator(
-        std::unique_ptr<ContextAllocator> context_allocator) override {
-      server_->context_allocator_ = std::move(context_allocator);
-    }
-
-   private:
-    Server* server_;
-  };
-
-  /// TODO(vjpai): Mark this override when experimental type above is deleted
-  void RegisterCallbackGenericService(
-      experimental::CallbackGenericService* service);
-
-  /// NOTE: The function experimental_registration() is not stable public API.
-  /// It is a view to the experimental components of this class. It may be
-  /// changed or removed at any time.
-  experimental_registration_interface* experimental_registration() override {
-    return &experimental_registration_;
-  }
-#endif
-
   void PerformOpsOnCall(internal::CallOpSetInterface* ops,
                         internal::Call* call) override;
 
@@ -327,12 +291,6 @@ class Server : public ServerInterface, private GrpcLibraryCodegen {
   /// the \a sync_server_cqs)
   std::vector<std::unique_ptr<SyncRequestThreadManager>> sync_req_mgrs_;
 
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-  // For registering experimental callback generic service; remove when that
-  // method longer experimental
-  experimental_registration_type experimental_registration_{this};
-#endif
-
   // Server status
   internal::Mutex mu_;
   bool started_;
@@ -364,11 +322,7 @@ class Server : public ServerInterface, private GrpcLibraryCodegen {
 
   // When appropriate, use a default callback generic service to handle
   // unimplemented methods
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
   std::unique_ptr<CallbackGenericService> unimplemented_service_;
-#else
-  std::unique_ptr<experimental::CallbackGenericService> unimplemented_service_;
-#endif
 
   // A special handler for resource exhausted in sync case
   std::unique_ptr<internal::MethodHandler> resource_exhausted_handler_;
index 00e82a1..99d2ca3 100644 (file)
@@ -33,6 +33,7 @@
 #include <grpcpp/impl/codegen/server_interceptor.h>
 #include <grpcpp/impl/server_builder_option.h>
 #include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/security/authorization_policy_provider.h>
 #include <grpcpp/server.h>
 #include <grpcpp/support/config.h>
 
@@ -55,13 +56,7 @@ namespace internal {
 class ExternalConnectionAcceptorImpl;
 }  // namespace internal
 
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-namespace experimental {
-#endif
 class CallbackGenericService;
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-}  // namespace experimental
-#endif
 
 namespace experimental {
 // EXPERIMENTAL API:
@@ -121,8 +116,8 @@ class ServerBuilder {
   /// \param addr_uri The address to try to bind to the server in URI form. If
   /// the scheme name is omitted, "dns:///" is assumed. To bind to any address,
   /// please use IPv6 any, i.e., [::]:<port>, which also accepts IPv4
-  /// connections.  Valid values include dns:///localhost:1234, /
-  /// 192.168.1.1:31416, dns:///[::1]:27182, etc.).
+  /// connections.  Valid values include dns:///localhost:1234,
+  /// 192.168.1.1:31416, dns:///[::1]:27182, etc.
   /// \param creds The credentials associated with the server.
   /// \param[out] selected_port If not `nullptr`, gets populated with the port
   /// number bound to the \a grpc::Server for the corresponding endpoint after
@@ -269,20 +264,6 @@ class ServerBuilder {
       builder_->interceptor_creators_ = std::move(interceptor_creators);
     }
 
-    /// Set the allocator for creating and releasing callback server context.
-    /// Takes the owndership of the allocator.
-    ServerBuilder& SetContextAllocator(
-        std::unique_ptr<grpc::ContextAllocator> context_allocator);
-
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-    /// Register a generic service that uses the callback API.
-    /// Matches requests with any :authority
-    /// This is mostly useful for writing generic gRPC Proxies where the exact
-    /// serialization format is unknown
-    ServerBuilder& RegisterCallbackGenericService(
-        grpc::experimental::CallbackGenericService* service);
-#endif
-
     enum class ExternalConnectionType {
       FROM_FD = 0  // in the form of a file descriptor
     };
@@ -295,18 +276,27 @@ class ServerBuilder {
     AddExternalConnectionAcceptor(ExternalConnectionType type,
                                   std::shared_ptr<ServerCredentials> creds);
 
+    /// Sets server authorization policy provider in
+    /// GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER channel argument.
+    void SetAuthorizationPolicyProvider(
+        std::shared_ptr<experimental::AuthorizationPolicyProviderInterface>
+            provider);
+
    private:
     ServerBuilder* builder_;
   };
 
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
+  /// Set the allocator for creating and releasing callback server context.
+  /// Takes the owndership of the allocator.
+  ServerBuilder& SetContextAllocator(
+      std::unique_ptr<grpc::ContextAllocator> context_allocator);
+
   /// Register a generic service that uses the callback API.
   /// Matches requests with any :authority
   /// This is mostly useful for writing generic gRPC Proxies where the exact
   /// serialization format is unknown
   ServerBuilder& RegisterCallbackGenericService(
       grpc::CallbackGenericService* service);
-#endif
 
   /// NOTE: The function experimental() is not stable public API. It is a view
   /// to the experimental components of this class. It may be changed or removed
@@ -398,12 +388,7 @@ class ServerBuilder {
   grpc_resource_quota* resource_quota_;
   grpc::AsyncGenericService* generic_service_{nullptr};
   std::unique_ptr<ContextAllocator> context_allocator_;
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
   grpc::CallbackGenericService* callback_generic_service_{nullptr};
-#else
-  grpc::experimental::CallbackGenericService* callback_generic_service_{
-      nullptr};
-#endif
 
   struct {
     bool is_set;
@@ -420,6 +405,8 @@ class ServerBuilder {
   std::vector<std::shared_ptr<grpc::internal::ExternalConnectionAcceptorImpl>>
       acceptors_;
   grpc_server_config_fetcher* server_config_fetcher_ = nullptr;
+  std::shared_ptr<experimental::AuthorizationPolicyProviderInterface>
+      authorization_provider_;
 };
 
 }  // namespace grpc
index a9514bc..afee704 100644 (file)
@@ -70,13 +70,7 @@ class ChannelArguments {
   /// the resolver.
   void SetGrpclbFallbackTimeout(int fallback_timeout);
 
-  /// For client channel's, the socket mutator operates on
-  /// "channel" sockets. For server's, the socket mutator operates
-  /// only on "listen" sockets.
-  /// TODO(apolcyn): allow socket mutators to also operate
-  /// on server "channel" sockets, and adjust the socket mutator
-  /// object to be more speficic about which type of socket
-  /// it should operate on.
+  /// Set a mutator for the underlying socket.
   void SetSocketMutator(grpc_socket_mutator* mutator);
 
   /// Set the string to prepend to the user agent.
diff --git a/include/grpcpp/test/client_context_test_peer.h b/include/grpcpp/test/client_context_test_peer.h
new file mode 100644 (file)
index 0000000..4631873
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPCPP_TEST_CLIENT_CONTEXT_TEST_PEER_H
+#define GRPCPP_TEST_CLIENT_CONTEXT_TEST_PEER_H
+
+#include <grpcpp/client_context.h>
+
+#include <map>
+
+namespace grpc {
+namespace testing {
+
+/// A test-only class to access private members and methods of ClientContext.
+class ClientContextTestPeer {
+ public:
+  explicit ClientContextTestPeer(ClientContext* const ctx) : ctx_(ctx) {}
+
+  /// Inject metadata to the ClientContext for the test. The test peer
+  /// must be alive when a ClientContext::GetServerInitialMetadata is called.
+  void AddServerInitialMetadata(const std::string& key,
+                                const std::string& value) {
+    server_initial_metadata_storage_.insert(
+        std::pair<std::string, std::string>(key, value));
+    ctx_->initial_metadata_received_ = true;
+    ctx_->recv_initial_metadata_.map()->clear();
+    for (const auto& item : server_initial_metadata_storage_) {
+      ctx_->recv_initial_metadata_.map()->insert(
+          std::pair<grpc::string_ref, grpc::string_ref>(
+              item.first.c_str(),
+              grpc::string_ref(item.second.data(), item.second.size())));
+    }
+  }
+
+  std::multimap<std::string, std::string> GetSendInitialMetadata() const {
+    return ctx_->send_initial_metadata_;
+  }
+
+ private:
+  ClientContext* const ctx_;  // not owned
+  std::multimap<std::string, std::string> server_initial_metadata_storage_;
+};
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif  // GRPCPP_TEST_CLIENT_CONTEXT_TEST_PEER_H
index deffad3..9e12af9 100644 (file)
@@ -150,6 +150,47 @@ class MockClientAsyncReaderWriter
   MOCK_METHOD1_T(WritesDone, void(void*));
 };
 
+template <class R>
+class MockServerReader : public ::grpc::ServerReaderInterface<R> {
+ public:
+  MockServerReader() = default;
+
+  /// ServerStreamingInterface
+  MOCK_METHOD0_T(SendInitialMetadata, void());
+
+  /// ReaderInterface
+  MOCK_METHOD1_T(NextMessageSize, bool(uint32_t*));
+  MOCK_METHOD1_T(Read, bool(R*));
+};
+
+template <class W>
+class MockServerWriter : public ::grpc::ServerWriterInterface<W> {
+ public:
+  MockServerWriter() = default;
+
+  /// ServerStreamingInterface
+  MOCK_METHOD0_T(SendInitialMetadata, void());
+
+  /// WriterInterface
+  MOCK_METHOD2_T(Write, bool(const W&, const WriteOptions));
+};
+
+template <class W, class R>
+class MockServerReaderWriter : public grpc::ServerReaderWriterInterface<W, R> {
+ public:
+  MockServerReaderWriter() = default;
+
+  /// ServerStreamingInterface
+  MOCK_METHOD0_T(SendInitialMetadata, void());
+
+  /// ReaderInterface
+  MOCK_METHOD1_T(NextMessageSize, bool(uint32_t*));
+  MOCK_METHOD1_T(Read, bool(R*));
+
+  /// WriterInterface
+  MOCK_METHOD2_T(Write, bool(const W&, const WriteOptions));
+};
+
 }  // namespace testing
 }  // namespace grpc
 
index 3aa3c7f..5971194 100644 (file)
@@ -13,8 +13,8 @@
  <date>2019-09-24</date>
  <time>16:06:07</time>
  <version>
-  <release>1.38.1</release>
-  <api>1.38.1</api>
+  <release>1.39.0</release>
+  <api>1.39.0</api>
  </version>
  <stability>
   <release>stable</release>
@@ -22,7 +22,7 @@
  </stability>
  <license>Apache 2.0</license>
  <notes>
-- gRPC Core 1.38.1 update
+- gRPC Core 1.39.0 update
  </notes>
  <contents>
   <dir baseinstalldir="/" name="/">
@@ -33,7 +33,7 @@
     <file baseinstalldir="/" name="include/grpc/byte_buffer_reader.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/census.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/compression.h" role="src" />
-    <file baseinstalldir="/" name="include/grpc/event_engine/channel_args.h" role="src" />
+    <file baseinstalldir="/" name="include/grpc/event_engine/endpoint_config.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/event_engine/event_engine.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/event_engine/port.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/event_engine/slice_allocator.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/debug/stats_data.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/debug/trace.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/debug/trace.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/event_engine/endpoint_config.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/event_engine/endpoint_config_internal.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/event_engine/event_engine.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/event_engine/slice_allocator.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/event_engine/sockaddr.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/event_engine/sockaddr.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/alloc.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/alloc.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/arena.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_cfstream.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_cfstream.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_event_engine.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_uv.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_posix.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_windows.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/closure.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/closure.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/endpoint.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/endpoint.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/iomgr.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/iomgr.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/pollset.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/pollset.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/promise.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/resolved_address_internal.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/resolved_address_internal.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/resolver.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/tcp.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/event_engine/timer.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/exec_ctx.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/exec_ctx.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/executor.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/profiling/basic_timers.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/profiling/stap_timers.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/profiling/timers.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/authorization/authorization_engine.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/authorization/authorization_policy_provider.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/authorization/authorization_policy_provider_vtable.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/authorization/evaluate_args.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/authorization/evaluate_args.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/context/security_context.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/context/security_context.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/alts/alts_credentials.cc" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/fipsmodule/tls/kdf.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/hkdf/hkdf.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/hpke/hpke.c" role="src" />
-    <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/hpke/internal.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/hrss/hrss.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/hrss/internal.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/internal.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/x509/t_req.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/x509/t_x509.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/x509/t_x509a.c" role="src" />
-    <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/x509/vpm_int.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/x509/x509.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/x509/x509_att.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/crypto/x509/x509_cmp.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/include/openssl/ex_data.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/include/openssl/hkdf.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/include/openssl/hmac.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/include/openssl/hpke.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/include/openssl/hrss.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/include/openssl/is_boringssl.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl-with-bazel/src/include/openssl/lhash.h" role="src" />
index 4b7135b..417fe4f 100644 (file)
@@ -9,7 +9,7 @@ futures==3.1.1
 google-auth==1.24.0
 oauth2client==4.1.0
 requests==2.25.1
-urllib3==1.26.3
+urllib3==1.26.5
 chardet==3.0.4
 certifi==2017.4.17
 idna==2.7
index c93d419..4aad42e 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -41,6 +41,8 @@ from setuptools.command import egg_info
 import subprocess
 from subprocess import PIPE
 
+import _metadata
+
 # Redirect the manifest template from MANIFEST.in to PYTHON-MANIFEST.in.
 egg_info.manifest_maker.template = 'PYTHON-MANIFEST.in'
 
@@ -246,7 +248,7 @@ if EXTRA_ENV_COMPILE_ARGS is None:
     elif "linux" in sys.platform:
         EXTRA_ENV_COMPILE_ARGS += ' -std=gnu99 -fvisibility=hidden -fno-wrapv -fno-exceptions'
     elif "darwin" in sys.platform:
-        EXTRA_ENV_COMPILE_ARGS += ' -stdlib=libc++ -fvisibility=hidden -fno-wrapv -fno-exceptions'
+        EXTRA_ENV_COMPILE_ARGS += ' -stdlib=libc++ -fvisibility=hidden -fno-wrapv -fno-exceptions -DHAVE_UNISTD_H'
 
 if EXTRA_ENV_LINK_ARGS is None:
     EXTRA_ENV_LINK_ARGS = ''
@@ -329,6 +331,22 @@ if BUILD_WITH_SYSTEM_RE2:
 DEFINE_MACROS = (('_WIN32_WINNT', 0x600),)
 asm_files = []
 
+
+# Quotes on Windows build macros are evaluated differently from other platforms,
+# so we must apply quotes asymmetrically in order to yield the proper result in
+# the binary.
+def _quote_build_define(argument):
+    if "win32" in sys.platform:
+        return '"\\\"{}\\\""'.format(argument)
+    return '"{}"'.format(argument)
+
+
+DEFINE_MACROS += (
+    ("GRPC_XDS_USER_AGENT_NAME_SUFFIX", _quote_build_define("Python")),
+    ("GRPC_XDS_USER_AGENT_VERSION_SUFFIX",
+     _quote_build_define(_metadata.__version__)),
+)
+
 asm_key = ''
 if BUILD_WITH_BORING_SSL_ASM and not BUILD_WITH_SYSTEM_OPENSSL:
     boringssl_asm_platform = BUILD_OVERRIDE_BORING_SSL_ASM_PLATFORM if BUILD_OVERRIDE_BORING_SSL_ASM_PLATFORM else util.get_platform(
index 1991903..3c899d0 100644 (file)
@@ -1,4 +1,4 @@
-// generated by generate_boringssl_prefix_header.sh on BoringSSL commit: 688fc5cf5428868679d2ae1072cad81055752068
+// generated by generate_boringssl_prefix_header.sh on BoringSSL commit: bcc01b6c66b1c6fa2816b108e50a544b757fbd7b
 
 // Copyright (c) 2018, Google Inc.
 //
@@ -88,7 +88,6 @@
 #define SSL_CTX_get0_param BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_get0_param)
 #define SSL_CTX_get0_privatekey BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_get0_privatekey)
 #define SSL_CTX_get_cert_store BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_get_cert_store)
-#define SSL_CTX_get_channel_id_cb BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_get_channel_id_cb)
 #define SSL_CTX_get_ciphers BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_get_ciphers)
 #define SSL_CTX_get_client_CA_list BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_get_client_CA_list)
 #define SSL_CTX_get_default_passwd_cb BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_get_default_passwd_cb)
 #define SSL_CTX_set1_chain BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set1_chain)
 #define SSL_CTX_set1_curves BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set1_curves)
 #define SSL_CTX_set1_curves_list BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set1_curves_list)
-#define SSL_CTX_set1_ech_server_config_list BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set1_ech_server_config_list)
+#define SSL_CTX_set1_ech_keys BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set1_ech_keys)
 #define SSL_CTX_set1_param BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set1_param)
 #define SSL_CTX_set1_sigalgs BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set1_sigalgs)
 #define SSL_CTX_set1_sigalgs_list BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set1_sigalgs_list)
 #define SSL_CTX_set_cert_store BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_cert_store)
 #define SSL_CTX_set_cert_verify_callback BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_cert_verify_callback)
 #define SSL_CTX_set_chain_and_key BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_chain_and_key)
-#define SSL_CTX_set_channel_id_cb BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_channel_id_cb)
 #define SSL_CTX_set_cipher_list BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_cipher_list)
 #define SSL_CTX_set_client_CA_list BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_client_CA_list)
 #define SSL_CTX_set_client_cert_cb BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_client_cert_cb)
 #define SSL_CTX_set_next_protos_advertised_cb BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_next_protos_advertised_cb)
 #define SSL_CTX_set_ocsp_response BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_ocsp_response)
 #define SSL_CTX_set_options BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_options)
+#define SSL_CTX_set_permute_extensions BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_permute_extensions)
 #define SSL_CTX_set_private_key_method BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_private_key_method)
 #define SSL_CTX_set_psk_client_callback BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_psk_client_callback)
 #define SSL_CTX_set_psk_server_callback BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_set_psk_server_callback)
 #define SSL_CTX_use_certificate_chain_file BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_use_certificate_chain_file)
 #define SSL_CTX_use_certificate_file BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_use_certificate_file)
 #define SSL_CTX_use_psk_identity_hint BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_CTX_use_psk_identity_hint)
-#define SSL_ECH_SERVER_CONFIG_LIST_add BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_ECH_SERVER_CONFIG_LIST_add)
-#define SSL_ECH_SERVER_CONFIG_LIST_free BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_ECH_SERVER_CONFIG_LIST_free)
-#define SSL_ECH_SERVER_CONFIG_LIST_new BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_ECH_SERVER_CONFIG_LIST_new)
-#define SSL_ECH_SERVER_CONFIG_LIST_up_ref BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_ECH_SERVER_CONFIG_LIST_up_ref)
+#define SSL_ECH_KEYS_add BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_ECH_KEYS_add)
+#define SSL_ECH_KEYS_free BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_ECH_KEYS_free)
+#define SSL_ECH_KEYS_has_duplicate_config_id BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_ECH_KEYS_has_duplicate_config_id)
+#define SSL_ECH_KEYS_marshal_retry_configs BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_ECH_KEYS_marshal_retry_configs)
+#define SSL_ECH_KEYS_new BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_ECH_KEYS_new)
+#define SSL_ECH_KEYS_up_ref BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_ECH_KEYS_up_ref)
 #define SSL_SESSION_copy_without_early_data BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_SESSION_copy_without_early_data)
 #define SSL_SESSION_early_data_capable BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_SESSION_early_data_capable)
 #define SSL_SESSION_free BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_SESSION_free)
 #define SSL_alert_type_string BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_alert_type_string)
 #define SSL_alert_type_string_long BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_alert_type_string_long)
 #define SSL_cache_hit BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_cache_hit)
+#define SSL_can_release_private_key BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_can_release_private_key)
 #define SSL_certs_clear BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_certs_clear)
 #define SSL_check_private_key BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_check_private_key)
 #define SSL_clear BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_clear)
 #define SSL_early_callback_ctx_extension_get BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_early_callback_ctx_extension_get)
 #define SSL_early_data_accepted BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_early_data_accepted)
 #define SSL_early_data_reason_string BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_early_data_reason_string)
+#define SSL_ech_accepted BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_ech_accepted)
 #define SSL_enable_ocsp_stapling BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_enable_ocsp_stapling)
 #define SSL_enable_signed_cert_timestamps BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_enable_signed_cert_timestamps)
 #define SSL_enable_tls_channel_id BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_enable_tls_channel_id)
 #define SSL_get_max_proto_version BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_get_max_proto_version)
 #define SSL_get_min_proto_version BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_get_min_proto_version)
 #define SSL_get_mode BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_get_mode)
-#define SSL_get_negotiated_token_binding_param BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_get_negotiated_token_binding_param)
 #define SSL_get_options BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_get_options)
 #define SSL_get_peer_cert_chain BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_get_peer_cert_chain)
 #define SSL_get_peer_certificate BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_get_peer_certificate)
 #define SSL_is_init_finished BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_is_init_finished)
 #define SSL_is_server BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_is_server)
 #define SSL_is_signature_algorithm_rsa_pss BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_is_signature_algorithm_rsa_pss)
-#define SSL_is_token_binding_negotiated BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_is_token_binding_negotiated)
 #define SSL_key_update BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_key_update)
 #define SSL_library_init BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_library_init)
 #define SSL_load_client_CA_file BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_load_client_CA_file)
 #define SSL_load_error_strings BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_load_error_strings)
 #define SSL_magic_pending_session_ptr BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_magic_pending_session_ptr)
+#define SSL_marshal_ech_config BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_marshal_ech_config)
 #define SSL_max_seal_overhead BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_max_seal_overhead)
 #define SSL_need_tmp_RSA BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_need_tmp_RSA)
 #define SSL_new BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_new)
 #define SSL_set1_curves BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set1_curves)
 #define SSL_set1_curves_list BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set1_curves_list)
 #define SSL_set1_delegated_credential BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set1_delegated_credential)
+#define SSL_set1_ech_config_list BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set1_ech_config_list)
 #define SSL_set1_param BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set1_param)
 #define SSL_set1_sigalgs BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set1_sigalgs)
 #define SSL_set1_sigalgs_list BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set1_sigalgs_list)
 #define SSL_set_mtu BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_mtu)
 #define SSL_set_ocsp_response BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_ocsp_response)
 #define SSL_set_options BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_options)
+#define SSL_set_permute_extensions BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_permute_extensions)
 #define SSL_set_private_key_method BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_private_key_method)
 #define SSL_set_psk_client_callback BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_psk_client_callback)
 #define SSL_set_psk_server_callback BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_psk_server_callback)
 #define SSL_set_tmp_ecdh BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_tmp_ecdh)
 #define SSL_set_tmp_rsa BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_tmp_rsa)
 #define SSL_set_tmp_rsa_callback BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_tmp_rsa_callback)
-#define SSL_set_token_binding_params BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_token_binding_params)
 #define SSL_set_trust BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_trust)
 #define SSL_set_verify BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_verify)
 #define SSL_set_verify_algorithm_prefs BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, SSL_set_verify_algorithm_prefs)
 #define EVP_EncryptInit BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_EncryptInit)
 #define EVP_EncryptInit_ex BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_EncryptInit_ex)
 #define EVP_EncryptUpdate BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_EncryptUpdate)
+#define EVP_HPKE_AEAD_aead BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_AEAD_aead)
+#define EVP_HPKE_AEAD_id BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_AEAD_id)
+#define EVP_HPKE_CTX_aead BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_aead)
 #define EVP_HPKE_CTX_cleanup BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_cleanup)
 #define EVP_HPKE_CTX_export BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_export)
-#define EVP_HPKE_CTX_get_aead_id BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_get_aead_id)
-#define EVP_HPKE_CTX_get_kdf_id BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_get_kdf_id)
-#define EVP_HPKE_CTX_init BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_init)
+#define EVP_HPKE_CTX_kdf BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_kdf)
 #define EVP_HPKE_CTX_max_overhead BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_max_overhead)
 #define EVP_HPKE_CTX_open BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_open)
 #define EVP_HPKE_CTX_seal BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_seal)
-#define EVP_HPKE_CTX_setup_base_r_x25519 BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_setup_base_r_x25519)
-#define EVP_HPKE_CTX_setup_base_s_x25519 BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_setup_base_s_x25519)
-#define EVP_HPKE_CTX_setup_base_s_x25519_for_test BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_setup_base_s_x25519_for_test)
-#define EVP_HPKE_CTX_setup_psk_r_x25519 BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_setup_psk_r_x25519)
-#define EVP_HPKE_CTX_setup_psk_s_x25519 BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_setup_psk_s_x25519)
-#define EVP_HPKE_CTX_setup_psk_s_x25519_for_test BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_setup_psk_s_x25519_for_test)
-#define EVP_HPKE_get_aead BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_get_aead)
-#define EVP_HPKE_get_hkdf_md BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_get_hkdf_md)
+#define EVP_HPKE_CTX_setup_recipient BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_setup_recipient)
+#define EVP_HPKE_CTX_setup_sender BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_setup_sender)
+#define EVP_HPKE_CTX_setup_sender_with_seed_for_testing BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_setup_sender_with_seed_for_testing)
+#define EVP_HPKE_CTX_zero BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_CTX_zero)
+#define EVP_HPKE_KDF_id BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_KDF_id)
+#define EVP_HPKE_KEM_id BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_KEM_id)
+#define EVP_HPKE_KEY_cleanup BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_KEY_cleanup)
+#define EVP_HPKE_KEY_copy BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_KEY_copy)
+#define EVP_HPKE_KEY_generate BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_KEY_generate)
+#define EVP_HPKE_KEY_init BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_KEY_init)
+#define EVP_HPKE_KEY_kem BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_KEY_kem)
+#define EVP_HPKE_KEY_private_key BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_KEY_private_key)
+#define EVP_HPKE_KEY_public_key BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_KEY_public_key)
+#define EVP_HPKE_KEY_zero BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_HPKE_KEY_zero)
 #define EVP_MD_CTX_block_size BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_MD_CTX_block_size)
 #define EVP_MD_CTX_cleanup BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_MD_CTX_cleanup)
 #define EVP_MD_CTX_copy BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_MD_CTX_copy)
 #define EVP_MD_CTX_free BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_MD_CTX_free)
 #define EVP_MD_CTX_init BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_MD_CTX_init)
 #define EVP_MD_CTX_md BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_MD_CTX_md)
+#define EVP_MD_CTX_move BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_MD_CTX_move)
 #define EVP_MD_CTX_new BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_MD_CTX_new)
 #define EVP_MD_CTX_reset BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_MD_CTX_reset)
 #define EVP_MD_CTX_set_flags BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_MD_CTX_set_flags)
 #define EVP_PKEY_encrypt BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_PKEY_encrypt)
 #define EVP_PKEY_encrypt_init BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_PKEY_encrypt_init)
 #define EVP_PKEY_free BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_PKEY_free)
+#define EVP_PKEY_get0 BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_PKEY_get0)
 #define EVP_PKEY_get0_DH BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_PKEY_get0_DH)
 #define EVP_PKEY_get0_DSA BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_PKEY_get0_DSA)
 #define EVP_PKEY_get0_EC_KEY BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_PKEY_get0_EC_KEY)
 #define EVP_get_digestbynid BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_get_digestbynid)
 #define EVP_get_digestbyobj BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_get_digestbyobj)
 #define EVP_has_aes_hardware BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_has_aes_hardware)
+#define EVP_hpke_aes_128_gcm BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_hpke_aes_128_gcm)
+#define EVP_hpke_aes_256_gcm BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_hpke_aes_256_gcm)
+#define EVP_hpke_chacha20_poly1305 BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_hpke_chacha20_poly1305)
+#define EVP_hpke_hkdf_sha256 BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_hpke_hkdf_sha256)
+#define EVP_hpke_x25519_hkdf_sha256 BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_hpke_x25519_hkdf_sha256)
 #define EVP_marshal_digest_algorithm BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_marshal_digest_algorithm)
 #define EVP_marshal_private_key BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_marshal_private_key)
 #define EVP_marshal_public_key BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, EVP_marshal_public_key)
 #define X509_CRL_print_fp BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, X509_CRL_print_fp)
 #define X509_CRL_set1_lastUpdate BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, X509_CRL_set1_lastUpdate)
 #define X509_CRL_set1_nextUpdate BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, X509_CRL_set1_nextUpdate)
+#define X509_CRL_set1_signature_algo BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, X509_CRL_set1_signature_algo)
+#define X509_CRL_set1_signature_value BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, X509_CRL_set1_signature_value)
 #define X509_CRL_set_default_method BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, X509_CRL_set_default_method)
 #define X509_CRL_set_issuer_name BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, X509_CRL_set_issuer_name)
 #define X509_CRL_set_meth_data BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, X509_CRL_set_meth_data)
 #define a2i_GENERAL_NAME BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, a2i_GENERAL_NAME)
 #define a2i_IPADDRESS BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, a2i_IPADDRESS)
 #define a2i_IPADDRESS_NC BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, a2i_IPADDRESS_NC)
-#define a2i_ipadd BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, a2i_ipadd)
 #define abi_test_bad_unwind_temporary BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, abi_test_bad_unwind_temporary)
 #define abi_test_bad_unwind_wrong_register BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, abi_test_bad_unwind_wrong_register)
 #define abi_test_clobber_r10 BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, abi_test_clobber_r10)
 #define x509_print_rsa_pss_params BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, x509_print_rsa_pss_params)
 #define x509_rsa_ctx_to_pss BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, x509_rsa_ctx_to_pss)
 #define x509_rsa_pss_to_ctx BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, x509_rsa_pss_to_ctx)
+#define x509v3_a2i_ipadd BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, x509v3_a2i_ipadd)
 #define x509v3_bytes_to_hex BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, x509v3_bytes_to_hex)
 #define x509v3_cache_extensions BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, x509v3_cache_extensions)
 #define x509v3_hex_to_bytes BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, x509v3_hex_to_bytes)
index c8dff73..13d65d9 100644 (file)
@@ -136,12 +136,8 @@ std::string GetHeaderIncludes(grpc_generator::File* file,
       PrintIncludes(printer.get(), params.additional_header_includes, false,
                     "");
     }
-    // TODO(vjpai): Remove port_platform.h from header list when callback API is
-    //              fully de-experimentalized since we will no longer be using
-    //              macros from it.
     static const char* headers_strs[] = {
         "functional",
-        "grpc/impl/codegen/port_platform.h",
         "grpcpp/impl/codegen/async_generic_service.h",
         "grpcpp/impl/codegen/async_stream.h",
         "grpcpp/impl/codegen/async_unary_call.h",
@@ -584,12 +580,12 @@ void PrintHeaderClientMethodCallbackInterfacesStart(
   // are pure; even though this is new (post-1.0) API, it can be pure because
   // it is an entirely new interface that happens to be scoped within
   // StubInterface, not new additions to StubInterface itself
-  printer->Print("class experimental_async_interface {\n");
+  printer->Print("class async_interface {\n");
   // All methods in this new interface are public. There is no need for private
   // "Raw" methods since the callback-based API returns unowned raw pointers
   printer->Print(" public:\n");
   printer->Indent();
-  printer->Print("virtual ~experimental_async_interface() {}\n");
+  printer->Print("virtual ~async_interface() {}\n");
 }
 
 void PrintHeaderClientMethodCallbackInterfaces(
@@ -604,55 +600,27 @@ void PrintHeaderClientMethodCallbackInterfaces(
                    "virtual void $Method$(::grpc::ClientContext* context, "
                    "const $Request$* request, $Response$* response, "
                    "std::function<void(::grpc::Status)>) = 0;\n");
-    // TODO(vjpai): Remove experimental versions and macros when callback API is
-    //              fully de-experimentalized.
     printer->Print(*vars,
-                   "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
                    "virtual void $Method$(::grpc::ClientContext* context, "
                    "const $Request$* request, $Response$* response, "
-                   "::grpc::ClientUnaryReactor* reactor) = 0;\n"
-                   "#else\n"
-                   "virtual void $Method$(::grpc::ClientContext* context, "
-                   "const $Request$* request, $Response$* response, "
-                   "::grpc::experimental::ClientUnaryReactor* reactor) = 0;\n"
-                   "#endif\n");
+                   "::grpc::ClientUnaryReactor* reactor) = 0;\n");
   } else if (ClientOnlyStreaming(method)) {
     printer->Print(*vars,
-                   "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
                    "virtual void $Method$(::grpc::ClientContext* context, "
                    "$Response$* response, "
                    "::grpc::ClientWriteReactor< $Request$>* "
-                   "reactor) = 0;\n"
-                   "#else\n"
-                   "virtual void $Method$(::grpc::ClientContext* context, "
-                   "$Response$* response, "
-                   "::grpc::experimental::ClientWriteReactor< $Request$>* "
-                   "reactor) = 0;\n"
-                   "#endif\n");
+                   "reactor) = 0;\n");
   } else if (ServerOnlyStreaming(method)) {
     printer->Print(*vars,
-                   "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
                    "virtual void $Method$(::grpc::ClientContext* context, "
                    "const $Request$* request, "
                    "::grpc::ClientReadReactor< $Response$>* "
-                   "reactor) = 0;\n"
-                   "#else\n"
-                   "virtual void $Method$(::grpc::ClientContext* context, "
-                   "const $Request$* request, "
-                   "::grpc::experimental::ClientReadReactor< $Response$>* "
-                   "reactor) = 0;\n"
-                   "#endif\n");
+                   "reactor) = 0;\n");
   } else if (method->BidiStreaming()) {
     printer->Print(*vars,
-                   "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
                    "virtual void $Method$(::grpc::ClientContext* context, "
                    "::grpc::ClientBidiReactor< "
-                   "$Request$,$Response$>* reactor) = 0;\n"
-                   "#else\n"
-                   "virtual void $Method$(::grpc::ClientContext* context, "
-                   "::grpc::experimental::ClientBidiReactor< "
-                   "$Request$,$Response$>* reactor) = 0;\n"
-                   "#endif\n");
+                   "$Request$,$Response$>* reactor) = 0;\n");
   }
 }
 
@@ -661,31 +629,30 @@ void PrintHeaderClientMethodCallbackInterfacesEnd(
     std::map<std::string, std::string>* /*vars*/) {
   printer->Outdent();
   printer->Print("};\n");
+  // TODO: Remove typedef when all uses of experimental_async are migrated off.
   printer->Print(
-      "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-      "typedef class experimental_async_interface async_interface;\n"
-      "#endif\n");
+      "typedef class async_interface experimental_async_interface;\n");
 
   // Declare a function to give the async stub contents. It can't be pure
   // since this is a new API in StubInterface, but it is meaningless by default
   // (since any stub that wants to use it must have its own implementation of
   // the callback functions therein), so make the default return value nullptr.
   // Intentionally include the word "class" to avoid possible shadowing.
+  // TODO: Remove experimental_async call when possible, replace with nullptr.
   printer->Print(
-      "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-      "async_interface* async() { return experimental_async(); }\n"
-      "#endif\n");
+      "virtual class async_interface* async() { return nullptr; }\n");
+
+  // TODO: Remove experimental_async call when possible.
   printer->Print(
-      "virtual class experimental_async_interface* experimental_async() { "
-      "return nullptr; }\n");
+      "class async_interface* experimental_async() { return async(); }\n");
 }
 
 void PrintHeaderClientMethodCallbackStart(
     grpc_generator::Printer* printer,
     std::map<std::string, std::string>* /*vars*/) {
   // This declares the stub entry for the callback-based API.
-  printer->Print("class experimental_async final :\n");
-  printer->Print("  public StubInterface::experimental_async_interface {\n");
+  printer->Print("class async final :\n");
+  printer->Print("  public StubInterface::async_interface {\n");
   printer->Print(" public:\n");
   printer->Indent();
 }
@@ -702,55 +669,28 @@ void PrintHeaderClientMethodCallback(grpc_generator::Printer* printer,
                    "void $Method$(::grpc::ClientContext* context, "
                    "const $Request$* request, $Response$* response, "
                    "std::function<void(::grpc::Status)>) override;\n");
-    printer->Print(
-        *vars,
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "void $Method$(::grpc::ClientContext* context, "
-        "const $Request$* request, $Response$* response, "
-        "::grpc::ClientUnaryReactor* reactor) override;\n"
-        "#else\n"
-        "void $Method$(::grpc::ClientContext* context, "
-        "const $Request$* request, $Response$* response, "
-        "::grpc::experimental::ClientUnaryReactor* reactor) override;\n"
-        "#endif\n");
+    printer->Print(*vars,
+                   "void $Method$(::grpc::ClientContext* context, "
+                   "const $Request$* request, $Response$* response, "
+                   "::grpc::ClientUnaryReactor* reactor) override;\n");
   } else if (ClientOnlyStreaming(method)) {
     printer->Print(*vars,
-                   "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
                    "void $Method$(::grpc::ClientContext* context, "
                    "$Response$* response, "
                    "::grpc::ClientWriteReactor< $Request$>* "
-                   "reactor) override;\n"
-                   "#else\n"
-                   "void $Method$(::grpc::ClientContext* context, "
-                   "$Response$* response, "
-                   "::grpc::experimental::ClientWriteReactor< $Request$>* "
-                   "reactor) override;\n"
-                   "#endif\n");
+                   "reactor) override;\n");
   } else if (ServerOnlyStreaming(method)) {
     printer->Print(*vars,
-                   "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
                    "void $Method$(::grpc::ClientContext* context, "
                    "const $Request$* request, "
                    "::grpc::ClientReadReactor< $Response$>* "
-                   "reactor) override;\n"
-                   "#else\n"
-                   "void $Method$(::grpc::ClientContext* context, "
-                   "const $Request$* request, "
-                   "::grpc::experimental::ClientReadReactor< $Response$>* "
-                   "reactor) override;\n"
-                   "#endif\n");
+                   "reactor) override;\n");
 
   } else if (method->BidiStreaming()) {
     printer->Print(*vars,
-                   "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
                    "void $Method$(::grpc::ClientContext* context, "
                    "::grpc::ClientBidiReactor< "
-                   "$Request$,$Response$>* reactor) override;\n"
-                   "#else\n"
-                   "void $Method$(::grpc::ClientContext* context, "
-                   "::grpc::experimental::ClientBidiReactor< "
-                   "$Request$,$Response$>* reactor) override;\n"
-                   "#endif\n");
+                   "$Request$,$Response$>* reactor) override;\n");
   }
 }
 
@@ -761,7 +701,7 @@ void PrintHeaderClientMethodCallbackEnd(
   printer->Print(" private:\n");
   printer->Indent();
   printer->Print("friend class Stub;\n");
-  printer->Print("explicit experimental_async(Stub* stub): stub_(stub) { }\n");
+  printer->Print("explicit async(Stub* stub): stub_(stub) { }\n");
   // include a function with a phony use of stub_ to avoid an unused
   // private member warning for service with no methods
   printer->Print("Stub* stub() { return stub_; }\n");
@@ -770,7 +710,7 @@ void PrintHeaderClientMethodCallbackEnd(
   printer->Print("};\n");
 
   printer->Print(
-      "class experimental_async_interface* experimental_async() override { "
+      "class async* async() override { "
       "return &async_stub_; }\n");
 }
 
@@ -963,18 +903,10 @@ void PrintHeaderServerCallbackMethodsHelper(
         "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
         "}\n");
     printer->Print(*vars,
-                   "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
                    "virtual ::grpc::ServerUnaryReactor* $Method$(\n"
                    "  ::grpc::CallbackServerContext* /*context*/, "
                    "const $RealRequest$* /*request*/, "
-                   "$RealResponse$* /*response*/)\n"
-                   "#else\n"
-                   "virtual ::grpc::experimental::ServerUnaryReactor* "
-                   "$Method$(\n"
-                   "  ::grpc::experimental::CallbackServerContext* "
-                   "/*context*/, const $RealRequest$* /*request*/, "
-                   "$RealResponse$* /*response*/)\n"
-                   "#endif\n"
+                   "$RealResponse$* /*response*/)"
                    "  { return nullptr; }\n");
   } else if (ClientOnlyStreaming(method)) {
     printer->Print(
@@ -988,17 +920,10 @@ void PrintHeaderServerCallbackMethodsHelper(
         "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
         "}\n");
     printer->Print(*vars,
-                   "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
                    "virtual ::grpc::ServerReadReactor< "
                    "$RealRequest$>* $Method$(\n"
                    "  ::grpc::CallbackServerContext* "
-                   "/*context*/, $RealResponse$* /*response*/)\n"
-                   "#else\n"
-                   "virtual ::grpc::experimental::ServerReadReactor< "
-                   "$RealRequest$>* $Method$(\n"
-                   "  ::grpc::experimental::CallbackServerContext* "
-                   "/*context*/, $RealResponse$* /*response*/)\n"
-                   "#endif\n"
+                   "/*context*/, $RealResponse$* /*response*/)"
                    "  { return nullptr; }\n");
   } else if (ServerOnlyStreaming(method)) {
     printer->Print(
@@ -1013,16 +938,9 @@ void PrintHeaderServerCallbackMethodsHelper(
         "}\n");
     printer->Print(
         *vars,
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
         "virtual ::grpc::ServerWriteReactor< $RealResponse$>* $Method$(\n"
         "  ::grpc::CallbackServerContext* "
-        "/*context*/, const $RealRequest$* /*request*/)\n"
-        "#else\n"
-        "virtual ::grpc::experimental::ServerWriteReactor< $RealResponse$>* "
-        "$Method$(\n"
-        "  ::grpc::experimental::CallbackServerContext* "
-        "/*context*/, const $RealRequest$* /*request*/)\n"
-        "#endif\n"
+        "/*context*/, const $RealRequest$* /*request*/)"
         "  { return nullptr; }\n");
   } else if (method->BidiStreaming()) {
     printer->Print(
@@ -1037,16 +955,9 @@ void PrintHeaderServerCallbackMethodsHelper(
         "}\n");
     printer->Print(
         *vars,
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
         "virtual ::grpc::ServerBidiReactor< $RealRequest$, $RealResponse$>* "
         "$Method$(\n"
         "  ::grpc::CallbackServerContext* /*context*/)\n"
-        "#else\n"
-        "virtual ::grpc::experimental::ServerBidiReactor< "
-        "$RealRequest$, $RealResponse$>* "
-        "$Method$(\n"
-        "  ::grpc::experimental::CallbackServerContext* /*context*/)\n"
-        "#endif\n"
         "  { return nullptr; }\n");
   }
 }
@@ -1062,117 +973,70 @@ void PrintHeaderServerMethodCallback(grpc_generator::Printer* printer,
   (*vars)["RealRequest"] = method->input_type_name();
   (*vars)["RealResponse"] = method->output_type_name();
   printer->Print(*vars, "template <class BaseClass>\n");
-  printer->Print(
-      *vars,
-      "class ExperimentalWithCallbackMethod_$Method$ : public BaseClass {\n");
+  printer->Print(*vars,
+                 "class WithCallbackMethod_$Method$ : public BaseClass {\n");
   printer->Print(
       " private:\n"
       "  void BaseClassMustBeDerivedFromService(const Service* /*service*/) "
       "{}\n");
   printer->Print(" public:\n");
   printer->Indent();
-  printer->Print(*vars, "ExperimentalWithCallbackMethod_$Method$() {\n");
+  printer->Print(*vars, "WithCallbackMethod_$Method$() {\n");
   if (method->NoStreaming()) {
     printer->Print(
         *vars,
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "  ::grpc::Service::\n"
-        "#else\n"
-        "  ::grpc::Service::experimental().\n"
-        "#endif\n"
-        "    MarkMethodCallback($Idx$,\n"
+        "  ::grpc::Service::MarkMethodCallback($Idx$,\n"
         "      new ::grpc::internal::CallbackUnaryHandler< "
         "$RealRequest$, $RealResponse$>(\n"
         "        [this](\n"
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "               ::grpc::CallbackServerContext*\n"
-        "#else\n"
-        "               ::grpc::experimental::CallbackServerContext*\n"
-        "#endif\n"
-        "                 context, "
+        "               ::grpc::CallbackServerContext* context, "
         "const $RealRequest$* "
         "request, "
         "$RealResponse$* response) { "
         "return this->$Method$(context, request, response); }));}\n");
     printer->Print(*vars,
                    "void SetMessageAllocatorFor_$Method$(\n"
-                   "    ::grpc::experimental::MessageAllocator< "
+                   "    ::grpc::MessageAllocator< "
                    "$RealRequest$, $RealResponse$>* allocator) {\n"
-                   "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
                    "  ::grpc::internal::MethodHandler* const handler = "
                    "::grpc::Service::GetHandler($Idx$);\n"
-                   "#else\n"
-                   "  ::grpc::internal::MethodHandler* const handler = "
-                   "::grpc::Service::experimental().GetHandler($Idx$);\n"
-                   "#endif\n"
                    "  static_cast<::grpc::internal::CallbackUnaryHandler< "
                    "$RealRequest$, $RealResponse$>*>(handler)\n"
                    "          ->SetMessageAllocator(allocator);\n");
   } else if (ClientOnlyStreaming(method)) {
     printer->Print(
         *vars,
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "  ::grpc::Service::\n"
-        "#else\n"
-        "  ::grpc::Service::experimental().\n"
-        "#endif\n"
-        "    MarkMethodCallback($Idx$,\n"
+        "  ::grpc::Service::MarkMethodCallback($Idx$,\n"
         "      new ::grpc::internal::CallbackClientStreamingHandler< "
         "$RealRequest$, $RealResponse$>(\n"
         "        [this](\n"
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "               ::grpc::CallbackServerContext*\n"
-        "#else\n"
-        "               ::grpc::experimental::CallbackServerContext*\n"
-        "#endif\n"
-        "                 context, "
+        "               ::grpc::CallbackServerContext* context, "
         "$RealResponse$* "
         "response) { "
         "return this->$Method$(context, response); }));\n");
   } else if (ServerOnlyStreaming(method)) {
     printer->Print(
         *vars,
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "  ::grpc::Service::\n"
-        "#else\n"
-        "  ::grpc::Service::experimental().\n"
-        "#endif\n"
-        "    MarkMethodCallback($Idx$,\n"
+        "  ::grpc::Service::MarkMethodCallback($Idx$,\n"
         "      new ::grpc::internal::CallbackServerStreamingHandler< "
         "$RealRequest$, $RealResponse$>(\n"
         "        [this](\n"
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "               ::grpc::CallbackServerContext*\n"
-        "#else\n"
-        "               ::grpc::experimental::CallbackServerContext*\n"
-        "#endif\n"
-        "                 context, "
+        "               ::grpc::CallbackServerContext* context, "
         "const $RealRequest$* "
         "request) { "
         "return this->$Method$(context, request); }));\n");
   } else if (method->BidiStreaming()) {
-    printer->Print(
-        *vars,
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "  ::grpc::Service::\n"
-        "#else\n"
-        "  ::grpc::Service::experimental().\n"
-        "#endif\n"
-        "    MarkMethodCallback($Idx$,\n"
-        "      new ::grpc::internal::CallbackBidiHandler< "
-        "$RealRequest$, $RealResponse$>(\n"
-        "        [this](\n"
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "               ::grpc::CallbackServerContext*\n"
-        "#else\n"
-        "               ::grpc::experimental::CallbackServerContext*\n"
-        "#endif\n"
-        "                 context) "
-        "{ return this->$Method$(context); }));\n");
+    printer->Print(*vars,
+                   "  ::grpc::Service::MarkMethodCallback($Idx$,\n"
+                   "      new ::grpc::internal::CallbackBidiHandler< "
+                   "$RealRequest$, $RealResponse$>(\n"
+                   "        [this](\n"
+                   "               ::grpc::CallbackServerContext* context) "
+                   "{ return this->$Method$(context); }));\n");
   }
   printer->Print(*vars, "}\n");
   printer->Print(*vars,
-                 "~ExperimentalWithCallbackMethod_$Method$() override {\n"
+                 "~WithCallbackMethod_$Method$() override {\n"
                  "  BaseClassMustBeDerivedFromService(this);\n"
                  "}\n");
   PrintHeaderServerCallbackMethodsHelper(printer, method, vars);
@@ -1192,7 +1056,7 @@ void PrintHeaderServerMethodRawCallback(
   (*vars)["RealResponse"] = "::grpc::ByteBuffer";
   printer->Print(*vars, "template <class BaseClass>\n");
   printer->Print(*vars,
-                 "class ExperimentalWithRawCallbackMethod_$Method$ : public "
+                 "class WithRawCallbackMethod_$Method$ : public "
                  "BaseClass {\n");
   printer->Print(
       " private:\n"
@@ -1200,93 +1064,51 @@ void PrintHeaderServerMethodRawCallback(
       "{}\n");
   printer->Print(" public:\n");
   printer->Indent();
-  printer->Print(*vars, "ExperimentalWithRawCallbackMethod_$Method$() {\n");
+  printer->Print(*vars, "WithRawCallbackMethod_$Method$() {\n");
   if (method->NoStreaming()) {
-    printer->Print(
-        *vars,
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "  ::grpc::Service::\n"
-        "#else\n"
-        "  ::grpc::Service::experimental().\n"
-        "#endif\n"
-        "    MarkMethodRawCallback($Idx$,\n"
-        "      new ::grpc::internal::CallbackUnaryHandler< "
-        "$RealRequest$, $RealResponse$>(\n"
-        "        [this](\n"
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "               ::grpc::CallbackServerContext*\n"
-        "#else\n"
-        "               ::grpc::experimental::CallbackServerContext*\n"
-        "#endif\n"
-        "                 context, "
-        "const $RealRequest$* "
-        "request, "
-        "$RealResponse$* response) { return "
-        "this->$Method$(context, request, response); }));\n");
+    printer->Print(*vars,
+                   "  ::grpc::Service::MarkMethodRawCallback($Idx$,\n"
+                   "      new ::grpc::internal::CallbackUnaryHandler< "
+                   "$RealRequest$, $RealResponse$>(\n"
+                   "        [this](\n"
+                   "               ::grpc::CallbackServerContext* context, "
+                   "const $RealRequest$* "
+                   "request, "
+                   "$RealResponse$* response) { return "
+                   "this->$Method$(context, request, response); }));\n");
   } else if (ClientOnlyStreaming(method)) {
     printer->Print(
         *vars,
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "  ::grpc::Service::\n"
-        "#else\n"
-        "  ::grpc::Service::experimental().\n"
-        "#endif\n"
-        "    MarkMethodRawCallback($Idx$,\n"
+        "  ::grpc::Service::MarkMethodRawCallback($Idx$,\n"
         "      new ::grpc::internal::CallbackClientStreamingHandler< "
         "$RealRequest$, $RealResponse$>(\n"
         "        [this](\n"
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "               ::grpc::CallbackServerContext*\n"
-        "#else\n"
-        "               ::grpc::experimental::CallbackServerContext*\n"
-        "#endif\n"
-        "                 context, "
+        "               ::grpc::CallbackServerContext* context, "
         "$RealResponse$* response) "
         "{ return this->$Method$(context, response); }));\n");
   } else if (ServerOnlyStreaming(method)) {
     printer->Print(
         *vars,
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "  ::grpc::Service::\n"
-        "#else\n"
-        "  ::grpc::Service::experimental().\n"
-        "#endif\n"
-        "    MarkMethodRawCallback($Idx$,\n"
+        "  ::grpc::Service::MarkMethodRawCallback($Idx$,\n"
         "      new ::grpc::internal::CallbackServerStreamingHandler< "
         "$RealRequest$, $RealResponse$>(\n"
         "        [this](\n"
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "               ::grpc::CallbackServerContext*\n"
-        "#else\n"
-        "               ::grpc::experimental::CallbackServerContext*\n"
-        "#endif\n"
-        "                 context, "
+        "               ::grpc::CallbackServerContext* context, "
         "const"
         "$RealRequest$* request) { return "
         "this->$Method$(context, request); }));\n");
   } else if (method->BidiStreaming()) {
-    printer->Print(
-        *vars,
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "  ::grpc::Service::\n"
-        "#else\n"
-        "  ::grpc::Service::experimental().\n"
-        "#endif\n"
-        "    MarkMethodRawCallback($Idx$,\n"
-        "      new ::grpc::internal::CallbackBidiHandler< "
-        "$RealRequest$, $RealResponse$>(\n"
-        "        [this](\n"
-        "#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n"
-        "               ::grpc::CallbackServerContext*\n"
-        "#else\n"
-        "               ::grpc::experimental::CallbackServerContext*\n"
-        "#endif\n"
-        "                 context) "
-        "{ return this->$Method$(context); }));\n");
+    printer->Print(*vars,
+                   "  ::grpc::Service::MarkMethodRawCallback($Idx$,\n"
+                   "      new ::grpc::internal::CallbackBidiHandler< "
+                   "$RealRequest$, $RealResponse$>(\n"
+                   "        [this](\n"
+                   "               ::grpc::CallbackServerContext* context) "
+                   "{ return this->$Method$(context); }));\n");
   }
   printer->Print(*vars, "}\n");
   printer->Print(*vars,
-                 "~ExperimentalWithRawCallbackMethod_$Method$() override {\n"
+                 "~WithRawCallbackMethod_$Method$() override {\n"
                  "  BaseClassMustBeDerivedFromService(this);\n"
                  "}\n");
   PrintHeaderServerCallbackMethodsHelper(printer, method, vars);
@@ -1571,7 +1393,7 @@ void PrintHeaderService(grpc_generator::Printer* printer,
   printer->Print("\n private:\n");
   printer->Indent();
   printer->Print("std::shared_ptr< ::grpc::ChannelInterface> channel_;\n");
-  printer->Print("class experimental_async async_stub_{this};\n");
+  printer->Print("class async async_stub_{this};\n");
   for (int i = 0; i < service->method_count(); ++i) {
     PrintHeaderClientMethod(printer, service->method(i).get(), vars, false);
   }
@@ -1624,31 +1446,21 @@ void PrintHeaderService(grpc_generator::Printer* printer,
     PrintHeaderServerMethodCallback(printer, service->method(i).get(), vars);
   }
 
-  printer->Print("#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL\n");
   printer->Print("typedef ");
 
   for (int i = 0; i < service->method_count(); ++i) {
     (*vars)["method_name"] = service->method(i)->name();
-    printer->Print(*vars, "ExperimentalWithCallbackMethod_$method_name$<");
+    printer->Print(*vars, "WithCallbackMethod_$method_name$<");
   }
   printer->Print("Service");
   for (int i = 0; i < service->method_count(); ++i) {
     printer->Print(" >");
   }
   printer->Print(" CallbackService;\n");
-  printer->Print("#endif\n\n");
-
-  printer->Print("typedef ");
 
-  for (int i = 0; i < service->method_count(); ++i) {
-    (*vars)["method_name"] = service->method(i)->name();
-    printer->Print(*vars, "ExperimentalWithCallbackMethod_$method_name$<");
-  }
-  printer->Print("Service");
-  for (int i = 0; i < service->method_count(); ++i) {
-    printer->Print(" >");
-  }
-  printer->Print(" ExperimentalCallbackService;\n");
+  // TODO: Remove following typedef once all uses of ExperimentalCallbackService
+  //       are migrated to CallbackService
+  printer->Print("typedef CallbackService ExperimentalCallbackService;\n");
 
   // Server side - Generic
   for (int i = 0; i < service->method_count(); ++i) {
@@ -1894,7 +1706,7 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer,
                    "context, request, response);\n}\n\n");
 
     printer->Print(*vars,
-                   "void $ns$$Service$::Stub::experimental_async::$Method$("
+                   "void $ns$$Service$::Stub::async::$Method$("
                    "::grpc::ClientContext* context, "
                    "const $Request$* request, $Response$* response, "
                    "std::function<void(::grpc::Status)> f) {\n");
@@ -1906,10 +1718,10 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer,
                    "context, request, response, std::move(f));\n}\n\n");
 
     printer->Print(*vars,
-                   "void $ns$$Service$::Stub::experimental_async::$Method$("
+                   "void $ns$$Service$::Stub::async::$Method$("
                    "::grpc::ClientContext* context, "
                    "const $Request$* request, $Response$* response, "
-                   "::grpc::experimental::ClientUnaryReactor* reactor) {\n");
+                   "::grpc::ClientUnaryReactor* reactor) {\n");
     printer->Print(*vars,
                    "  ::grpc::internal::ClientCallbackUnaryFactory::Create"
                    "< ::grpc::protobuf::MessageLite, "
@@ -1956,12 +1768,11 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer,
                    "context, response);\n"
                    "}\n\n");
 
-    printer->Print(
-        *vars,
-        "void $ns$$Service$::"
-        "Stub::experimental_async::$Method$(::grpc::ClientContext* context, "
-        "$Response$* response, "
-        "::grpc::experimental::ClientWriteReactor< $Request$>* reactor) {\n");
+    printer->Print(*vars,
+                   "void $ns$$Service$::"
+                   "Stub::async::$Method$(::grpc::ClientContext* context, "
+                   "$Response$* response, "
+                   "::grpc::ClientWriteReactor< $Request$>* reactor) {\n");
     printer->Print(*vars,
                    "  ::grpc::internal::ClientCallbackWriterFactory< "
                    "$Request$>::Create("
@@ -2002,12 +1813,11 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer,
                    "context, request);\n"
                    "}\n\n");
 
-    printer->Print(
-        *vars,
-        "void $ns$$Service$::Stub::experimental_async::$Method$(::grpc::"
-        "ClientContext* context, "
-        "const $Request$* request, "
-        "::grpc::experimental::ClientReadReactor< $Response$>* reactor) {\n");
+    printer->Print(*vars,
+                   "void $ns$$Service$::Stub::async::$Method$(::grpc::"
+                   "ClientContext* context, "
+                   "const $Request$* request, "
+                   "::grpc::ClientReadReactor< $Response$>* reactor) {\n");
     printer->Print(*vars,
                    "  ::grpc::internal::ClientCallbackReaderFactory< "
                    "$Response$>::Create("
@@ -2048,12 +1858,11 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer,
                    "context);\n"
                    "}\n\n");
 
-    printer->Print(
-        *vars,
-        "void $ns$$Service$::Stub::experimental_async::$Method$(::grpc::"
-        "ClientContext* context, "
-        "::grpc::experimental::ClientBidiReactor< $Request$,$Response$>* "
-        "reactor) {\n");
+    printer->Print(*vars,
+                   "void $ns$$Service$::Stub::async::$Method$(::grpc::"
+                   "ClientContext* context, "
+                   "::grpc::ClientBidiReactor< $Request$,$Response$>* "
+                   "reactor) {\n");
     printer->Print(*vars,
                    "  ::grpc::internal::ClientCallbackReaderWriterFactory< "
                    "$Request$,$Response$>::Create("
index c7677c5..9c9830e 100644 (file)
@@ -88,6 +88,17 @@ bool GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer* printer,
   return true;
 }
 
+void GenerateGeneratedCodeAttribute(grpc::protobuf::io::Printer* printer) {
+  // Mark the code as generated using the [GeneratedCode] attribute.
+  // We don't provide plugin version info in attribute the because:
+  // * the version information is not readily available from the plugin's code.
+  // * it would cause a lot of churn in the pre-generated code
+  //   in this repository every time the version is updated.
+  printer->Print(
+      "[global::System.CodeDom.Compiler.GeneratedCode(\"grpc_csharp_plugin\", "
+      "null)]\n");
+}
+
 template <typename DescriptorType>
 bool GenerateDocCommentBody(grpc::protobuf::io::Printer* printer,
                             const DescriptorType* descriptor) {
@@ -304,6 +315,7 @@ void GenerateMarshallerFields(Printer* out, const ServiceDescriptor* service) {
   std::vector<const Descriptor*> used_messages = GetUsedMessages(service);
   if (used_messages.size() != 0) {
     // Generate static helper methods for serialization/deserialization
+    GenerateGeneratedCodeAttribute(out);
     out->Print(
         "static void __Helper_SerializeMessage("
         "global::Google.Protobuf.IMessage message, "
@@ -331,6 +343,7 @@ void GenerateMarshallerFields(Printer* out, const ServiceDescriptor* service) {
     out->Outdent();
     out->Print("}\n\n");
 
+    GenerateGeneratedCodeAttribute(out);
     out->Print(
         "static class __Helper_MessageCache<T>\n"
         "{\n");
@@ -343,6 +356,7 @@ void GenerateMarshallerFields(Printer* out, const ServiceDescriptor* service) {
     out->Outdent();
     out->Print("}\n\n");
 
+    GenerateGeneratedCodeAttribute(out);
     out->Print(
         "static T __Helper_DeserializeMessage<T>("
         "grpc::DeserializationContext context, "
@@ -368,6 +382,7 @@ void GenerateMarshallerFields(Printer* out, const ServiceDescriptor* service) {
 
   for (size_t i = 0; i < used_messages.size(); i++) {
     const Descriptor* message = used_messages[i];
+    GenerateGeneratedCodeAttribute(out);
     out->Print(
         "static readonly grpc::Marshaller<$type$> $fieldname$ = "
         "grpc::Marshallers.Create(__Helper_SerializeMessage, "
@@ -379,6 +394,7 @@ void GenerateMarshallerFields(Printer* out, const ServiceDescriptor* service) {
 }
 
 void GenerateStaticMethodField(Printer* out, const MethodDescriptor* method) {
+  GenerateGeneratedCodeAttribute(out);
   out->Print(
       "static readonly grpc::Method<$request$, $response$> $fieldname$ = new "
       "grpc::Method<$request$, $response$>(\n",
@@ -433,6 +449,7 @@ void GenerateServerClass(Printer* out, const ServiceDescriptor* service) {
   for (int i = 0; i < service->method_count(); i++) {
     const MethodDescriptor* method = service->method(i);
     GenerateDocCommentServerMethod(out, method);
+    GenerateGeneratedCodeAttribute(out);
     out->Print(
         "public virtual $returntype$ "
         "$methodname$($request$$response_stream_maybe$, "
@@ -468,6 +485,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
       "/// <param name=\"channel\">The channel to use to make remote "
       "calls.</param>\n",
       "servicename", GetServiceClassName(service));
+  GenerateGeneratedCodeAttribute(out);
   out->Print("public $name$(grpc::ChannelBase channel) : base(channel)\n",
              "name", GetClientClassName(service));
   out->Print("{\n");
@@ -478,6 +496,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
       "/// <param name=\"callInvoker\">The callInvoker to use to make remote "
       "calls.</param>\n",
       "servicename", GetServiceClassName(service));
+  GenerateGeneratedCodeAttribute(out);
   out->Print(
       "public $name$(grpc::CallInvoker callInvoker) : base(callInvoker)\n",
       "name", GetClientClassName(service));
@@ -486,6 +505,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
   out->Print(
       "/// <summary>Protected parameterless constructor to allow creation"
       " of test doubles.</summary>\n");
+  GenerateGeneratedCodeAttribute(out);
   out->Print("protected $name$() : base()\n", "name",
              GetClientClassName(service));
   out->Print("{\n");
@@ -494,6 +514,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
       "/// <summary>Protected constructor to allow creation of configured "
       "clients.</summary>\n"
       "/// <param name=\"configuration\">The client configuration.</param>\n");
+  GenerateGeneratedCodeAttribute(out);
   out->Print(
       "protected $name$(ClientBaseConfiguration configuration)"
       " : base(configuration)\n",
@@ -506,6 +527,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
     if (!method->client_streaming() && !method->server_streaming()) {
       // unary calls have an extra synchronous stub method
       GenerateDocCommentClientMethod(out, method, true, false);
+      GenerateGeneratedCodeAttribute(out);
       out->Print(
           "public virtual $response$ $methodname$($request$ request, "
           "grpc::Metadata "
@@ -528,6 +550,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
 
       // overload taking CallOptions as a param
       GenerateDocCommentClientMethod(out, method, true, true);
+      GenerateGeneratedCodeAttribute(out);
       out->Print(
           "public virtual $response$ $methodname$($request$ request, "
           "grpc::CallOptions options)\n",
@@ -549,6 +572,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
       method_name += "Async";  // prevent name clash with synchronous method.
     }
     GenerateDocCommentClientMethod(out, method, false, false);
+    GenerateGeneratedCodeAttribute(out);
     out->Print(
         "public virtual $returntype$ "
         "$methodname$($request_maybe$grpc::Metadata "
@@ -573,6 +597,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
 
     // overload taking CallOptions as a param
     GenerateDocCommentClientMethod(out, method, false, true);
+    GenerateGeneratedCodeAttribute(out);
     out->Print(
         "public virtual $returntype$ "
         "$methodname$($request_maybe$grpc::CallOptions "
@@ -615,6 +640,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
   out->Print(
       "/// <summary>Creates a new instance of client from given "
       "<c>ClientBaseConfiguration</c>.</summary>\n");
+  GenerateGeneratedCodeAttribute(out);
   out->Print(
       "protected override $name$ NewInstance(ClientBaseConfiguration "
       "configuration)\n",
@@ -638,6 +664,7 @@ void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor* service) {
   out->Print(
       "/// <param name=\"serviceImpl\">An object implementing the server-side"
       " handling logic.</param>\n");
+  GenerateGeneratedCodeAttribute(out);
   out->Print(
       "public static grpc::ServerServiceDefinition BindService($implclass$ "
       "serviceImpl)\n",
@@ -679,6 +706,7 @@ void GenerateBindServiceWithBinderMethod(Printer* out,
   out->Print(
       "/// <param name=\"serviceImpl\">An object implementing the server-side"
       " handling logic.</param>\n");
+  GenerateGeneratedCodeAttribute(out);
   out->Print(
       "public static void BindService(grpc::ServiceBinderBase serviceBinder, "
       "$implclass$ "
@@ -709,6 +737,7 @@ void GenerateService(Printer* out, const ServiceDescriptor* service,
                      bool generate_client, bool generate_server,
                      bool internal_access) {
   GenerateDocCommentBody(out, service);
+
   out->Print("$access_level$ static partial class $classname$\n",
              "access_level", GetAccessLevel(internal_access), "classname",
              GetServiceClassName(service));
index c269431..466c645 100644 (file)
 namespace grpc_csharp_generator {
 
 inline bool ServicesFilename(const grpc::protobuf::FileDescriptor* file,
-                             std::string* file_name_or_error) {
-  *file_name_or_error =
-      grpc_generator::FileNameInUpperCamel(file, false) + "Grpc.cs";
+                             const std::string& file_suffix,
+                             std::string& out_file_name_or_error) {
+  out_file_name_or_error =
+      grpc_generator::FileNameInUpperCamel(file, false) + file_suffix;
   return true;
 }
 
index cac50d2..dcf800a 100644 (file)
@@ -43,6 +43,9 @@ class CSharpGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
     bool generate_client = true;
     bool generate_server = true;
     bool internal_access = false;
+    // the suffix that will get appended to the name generated from the name
+    // of the original .proto file
+    std::string file_suffix = "Grpc.cs";
     for (size_t i = 0; i < options.size(); i++) {
       if (options[i].first == "no_client") {
         generate_client = false;
@@ -50,6 +53,9 @@ class CSharpGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
         generate_server = false;
       } else if (options[i].first == "internal_access") {
         internal_access = true;
+      }
+      if (options[i].first == "file_suffix") {
+        file_suffix = options[i].second;
       } else {
         *error = "Unknown generator option: " + options[i].first;
         return false;
@@ -64,7 +70,8 @@ class CSharpGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
 
     // Get output file name.
     std::string file_name;
-    if (!grpc_csharp_generator::ServicesFilename(file, &file_name)) {
+    if (!grpc_csharp_generator::ServicesFilename(file, file_suffix,
+                                                 file_name)) {
       return false;
     }
     std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> output(
index 3bbec5e..40318cb 100644 (file)
@@ -2121,7 +2121,7 @@ void ClientChannel::CallData::PendingBatchesResume(grpc_call_element* elem) {
       GRPC_CLOSURE_INIT(&batch->handler_private.closure,
                         ResumePendingBatchInCallCombiner, batch, nullptr);
       closures.Add(&batch->handler_private.closure, GRPC_ERROR_NONE,
-                   "PendingBatchesResume");
+                   "resuming pending batch from client channel call");
       batch = nullptr;
     }
   }
@@ -2670,7 +2670,7 @@ void ClientChannel::LoadBalancedCall::PendingBatchesResume() {
                         ResumePendingBatchInCallCombiner, batch,
                         grpc_schedule_on_exec_ctx);
       closures.Add(&batch->handler_private.closure, GRPC_ERROR_NONE,
-                   "PendingBatchesResume");
+                   "resuming pending batch from LB call");
       batch = nullptr;
     }
   }
index adde23d..d33366c 100644 (file)
@@ -352,6 +352,7 @@ void HealthCheckClient::CallState::StartCall() {
   batch_.recv_initial_metadata = true;
   // Add recv_message op.
   payload_.recv_message.recv_message = &recv_message_;
+  payload_.recv_message.call_failed_before_recv_message = nullptr;
   // recv_message callback takes ref, handled manually.
   call_->Ref(DEBUG_LOCATION, "recv_message_ready").release();
   payload_.recv_message.recv_message_ready = GRPC_CLOSURE_INIT(
@@ -478,6 +479,7 @@ void HealthCheckClient::CallState::DoneReadingRecvMessage(
   // callbacks from the original batch have completed yet.
   recv_message_batch_.payload = &payload_;
   payload_.recv_message.recv_message = &recv_message_;
+  payload_.recv_message.call_failed_before_recv_message = nullptr;
   payload_.recv_message.recv_message_ready = GRPC_CLOSURE_INIT(
       &recv_message_ready_, RecvMessageReady, this, grpc_schedule_on_exec_ctx);
   recv_message_batch_.recv_message = true;
index d1f063b..7f61d84 100644 (file)
@@ -128,14 +128,14 @@ class HealthCheckClient : public InternallyRefCounted<HealthCheckClient> {
     grpc_slice_buffer recv_message_buffer_;
     Atomic<bool> seen_response_{false};
 
+    // True if the cancel_stream batch has been started.
+    Atomic<bool> cancelled_{false};
+
     // recv_trailing_metadata
     grpc_metadata_batch recv_trailing_metadata_;
     grpc_transport_stream_stats collect_stats_;
     grpc_closure recv_trailing_metadata_ready_;
 
-    // True if the cancel_stream batch has been started.
-    Atomic<bool> cancelled_{false};
-
     // Closure for call stack destruction.
     grpc_closure after_call_stack_destruction_;
   };
index f061c42..4bc7b1b 100644 (file)
@@ -36,6 +36,7 @@
 #include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/host_port.h"
+#include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/slice/b64.h"
 #include "src/core/lib/uri/uri_parser.h"
 
@@ -107,6 +108,17 @@ done:
   return proxy_name;
 }
 
+// Adds the default port if target does not contain a port.
+std::string MaybeAddDefaultPort(absl::string_view target) {
+  absl::string_view host;
+  absl::string_view port;
+  SplitHostPort(target, &host, &port);
+  if (port.empty()) {
+    return JoinHostPort(host, kDefaultSecurePortInt);
+  }
+  return std::string(target);
+}
+
 class HttpProxyMapper : public ProxyMapperInterface {
  public:
   bool MapName(const char* server_uri, const grpc_channel_args* args,
@@ -118,6 +130,7 @@ class HttpProxyMapper : public ProxyMapperInterface {
     *name_to_resolve = GetHttpProxyServer(args, &user_cred);
     if (*name_to_resolve == nullptr) return false;
     char* no_proxy_str = nullptr;
+    std::string server_target;
     absl::StatusOr<URI> uri = URI::Parse(server_uri);
     if (!uri.ok() || uri->path().empty()) {
       gpr_log(GPR_ERROR,
@@ -173,10 +186,12 @@ class HttpProxyMapper : public ProxyMapperInterface {
         if (!use_proxy) goto no_use_proxy;
       }
     }
+    server_target =
+        MaybeAddDefaultPort(absl::StripPrefix(uri->path(), "/")).c_str();
     grpc_arg args_to_add[2];
     args_to_add[0] = grpc_channel_arg_string_create(
         const_cast<char*>(GRPC_ARG_HTTP_CONNECT_SERVER),
-        const_cast<char*>(absl::StripPrefix(uri->path(), "/").data()));
+        const_cast<char*>(server_target.c_str()));
     if (user_cred != nullptr) {
       /* Use base64 encoding for user credentials as stated in RFC 7617 */
       char* encoded_user_cred =
index 921bd2e..b59d97f 100644 (file)
 
 #include <grpc/support/port_platform.h>
 
+#include <stdlib.h>
+#include <string.h>
+
+#include "absl/strings/numbers.h"
+#include "absl/strings/str_cat.h"
+#define XXH_INLINE_ALL
+#include "xxhash.h"
+
+#include <grpc/support/alloc.h>
+#include "src/core/ext/filters/client_channel/lb_policy/subchannel_list.h"
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/ext/filters/client_channel/subchannel.h"
+#include "src/core/lib/address_utils/sockaddr_utils.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/gprpp/sync.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/error_utils.h"
+#include "src/core/lib/transport/static_metadata.h"
+
 namespace grpc_core {
 
 const char* kRequestRingHashAttribute = "request_ring_hash";
+TraceFlag grpc_lb_ring_hash_trace(false, "ring_hash_lb");
+
+// Helper Parser method
+void ParseRingHashLbConfig(const Json& json, size_t* min_ring_size,
+                           size_t* max_ring_size,
+                           std::vector<grpc_error_handle>* error_list) {
+  *min_ring_size = 1024;
+  *max_ring_size = 8388608;
+  if (json.type() != Json::Type::OBJECT) {
+    error_list->push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "ring_hash_experimental should be of type object"));
+    return;
+  }
+  const Json::Object& ring_hash = json.object_value();
+  auto ring_hash_it = ring_hash.find("min_ring_size");
+  if (ring_hash_it != ring_hash.end()) {
+    if (ring_hash_it->second.type() != Json::Type::NUMBER) {
+      error_list->push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "field:min_ring_size error: should be of type number"));
+    } else {
+      *min_ring_size = gpr_parse_nonnegative_int(
+          ring_hash_it->second.string_value().c_str());
+    }
+  }
+  ring_hash_it = ring_hash.find("max_ring_size");
+  if (ring_hash_it != ring_hash.end()) {
+    if (ring_hash_it->second.type() != Json::Type::NUMBER) {
+      error_list->push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "field:max_ring_size error: should be of type number"));
+    } else {
+      *max_ring_size = gpr_parse_nonnegative_int(
+          ring_hash_it->second.string_value().c_str());
+    }
+  }
+  if (*min_ring_size == 0 || *min_ring_size > 8388608 || *max_ring_size == 0 ||
+      *max_ring_size > 8388608 || *min_ring_size > *max_ring_size) {
+    error_list->push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "field:max_ring_size and or min_ring_size error: "
+        "values need to be in the range of 1 to 8388608 "
+        "and max_ring_size cannot be smaller than "
+        "min_ring_size"));
+  }
+}
+
+namespace {
+
+constexpr char kRingHash[] = "ring_hash_experimental";
+
+class RingHashLbConfig : public LoadBalancingPolicy::Config {
+ public:
+  RingHashLbConfig(size_t min_ring_size, size_t max_ring_size)
+      : min_ring_size_(min_ring_size), max_ring_size_(max_ring_size) {}
+  const char* name() const override { return kRingHash; }
+  size_t min_ring_size() const { return min_ring_size_; }
+  size_t max_ring_size() const { return max_ring_size_; }
+
+ private:
+  size_t min_ring_size_;
+  size_t max_ring_size_;
+};
+
+//
+// ring_hash LB policy
+//
+class RingHash : public LoadBalancingPolicy {
+ public:
+  explicit RingHash(Args args);
+
+  const char* name() const override { return kRingHash; }
+
+  void UpdateLocked(UpdateArgs args) override;
+  void ResetBackoffLocked() override;
+
+ private:
+  ~RingHash() override;
+
+  // Forward declaration.
+  class RingHashSubchannelList;
+
+  // Data for a particular subchannel in a subchannel list.
+  // This subclass adds the following functionality:
+  // - Tracks the previous connectivity state of the subchannel, so that
+  //   we know how many subchannels are in each state.
+  class RingHashSubchannelData
+      : public SubchannelData<RingHashSubchannelList, RingHashSubchannelData> {
+   public:
+    RingHashSubchannelData(
+        SubchannelList<RingHashSubchannelList, RingHashSubchannelData>*
+            subchannel_list,
+        const ServerAddress& address,
+        RefCountedPtr<SubchannelInterface> subchannel)
+        : SubchannelData(subchannel_list, address, std::move(subchannel)),
+          address_(address) {}
+
+    grpc_connectivity_state connectivity_state() const {
+      return last_connectivity_state_;
+    }
+    const ServerAddress& address() const { return address_; }
+
+    bool seen_failure_since_ready() const { return seen_failure_since_ready_; }
+
+    // Performs connectivity state updates that need to be done both when we
+    // first start watching and when a watcher notification is received.
+    void UpdateConnectivityStateLocked(
+        grpc_connectivity_state connectivity_state);
+
+   private:
+    // Performs connectivity state updates that need to be done only
+    // after we have started watching.
+    void ProcessConnectivityChangeLocked(
+        grpc_connectivity_state connectivity_state) override;
+
+    ServerAddress address_;
+    grpc_connectivity_state last_connectivity_state_ = GRPC_CHANNEL_SHUTDOWN;
+    bool seen_failure_since_ready_ = false;
+  };
+
+  // A list of subchannels.
+  class RingHashSubchannelList
+      : public SubchannelList<RingHashSubchannelList, RingHashSubchannelData> {
+   public:
+    RingHashSubchannelList(RingHash* policy, TraceFlag* tracer,
+                           ServerAddressList addresses,
+                           const grpc_channel_args& args)
+        : SubchannelList(policy, tracer, std::move(addresses),
+                         policy->channel_control_helper(), args) {
+      // Need to maintain a ref to the LB policy as long as we maintain
+      // any references to subchannels, since the subchannels'
+      // pollset_sets will include the LB policy's pollset_set.
+      policy->Ref(DEBUG_LOCATION, "subchannel_list").release();
+    }
+
+    ~RingHashSubchannelList() override {
+      RingHash* p = static_cast<RingHash*>(policy());
+      p->Unref(DEBUG_LOCATION, "subchannel_list");
+    }
+
+    // Starts watching the subchannels in this list.
+    void StartWatchingLocked();
+
+    // Updates the counters of subchannels in each state when a
+    // subchannel transitions from old_state to new_state.
+    void UpdateStateCountersLocked(grpc_connectivity_state old_state,
+                                   grpc_connectivity_state new_state);
+
+    // Updates the RH policy's connectivity state based on the
+    // subchannel list's state counters, creating new picker and new ring.
+    // Furthermore, return a bool indicating whether the aggregated state is
+    // Transient Failure.
+    bool UpdateRingHashConnectivityStateLocked();
+
+   private:
+    size_t num_idle_ = 0;
+    size_t num_ready_ = 0;
+    size_t num_connecting_ = 0;
+    size_t num_transient_failure_ = 0;
+  };
+
+  class Picker : public SubchannelPicker {
+   public:
+    Picker(RefCountedPtr<RingHash> parent,
+           RingHashSubchannelList* subchannel_list);
+
+    PickResult Pick(PickArgs args) override;
+
+   private:
+    struct RingEntry {
+      uint64_t hash;
+      RefCountedPtr<SubchannelInterface> subchannel;
+      grpc_connectivity_state connectivity_state;
+    };
+
+    // A fire-and-forget class that schedules subchannel connection attempts
+    // on the control plane WorkSerializer.
+    class SubchannelConnectionAttempter : public Orphanable {
+     public:
+      explicit SubchannelConnectionAttempter(
+          RefCountedPtr<RingHash> ring_hash_lb)
+          : ring_hash_lb_(std::move(ring_hash_lb)) {
+        GRPC_CLOSURE_INIT(&closure_, RunInExecCtx, this, nullptr);
+      }
+
+      void AddSubchannel(RefCountedPtr<SubchannelInterface> subchannel) {
+        subchannels_.push_back(std::move(subchannel));
+      }
+
+      void Orphan() override {
+        // Hop into ExecCtx, so that we're not holding the data plane mutex
+        // while we run control-plane code.
+        ExecCtx::Run(DEBUG_LOCATION, &closure_, GRPC_ERROR_NONE);
+      }
+
+     private:
+      static void RunInExecCtx(void* arg, grpc_error* /*error*/) {
+        auto* self = static_cast<SubchannelConnectionAttempter*>(arg);
+        self->ring_hash_lb_->work_serializer()->Run(
+            [self]() {
+              if (!self->ring_hash_lb_->shutdown_) {
+                for (auto& subchannel : self->subchannels_) {
+                  subchannel->AttemptToConnect();
+                }
+              }
+              delete self;
+            },
+            DEBUG_LOCATION);
+      }
+
+      RefCountedPtr<RingHash> ring_hash_lb_;
+      grpc_closure closure_;
+      absl::InlinedVector<RefCountedPtr<SubchannelInterface>, 10> subchannels_;
+    };
+
+    RefCountedPtr<RingHash> parent_;
+
+    // A ring of subchannels.
+    std::vector<RingEntry> ring_;
+  };
+
+  void ShutdownLocked() override;
+
+  // Current config from resolver.
+  RefCountedPtr<RingHashLbConfig> config_;
+
+  // list of subchannels.
+  OrphanablePtr<RingHashSubchannelList> subchannel_list_;
+  // indicating if we are shutting down.
+  bool shutdown_ = false;
+};
+
+//
+// RingHash::Picker
+//
+
+RingHash::Picker::Picker(RefCountedPtr<RingHash> parent,
+                         RingHashSubchannelList* subchannel_list)
+    : parent_(std::move(parent)) {
+  size_t num_subchannels = subchannel_list->num_subchannels();
+  // Store the weights while finding the sum.
+  struct AddressWeight {
+    std::string address;
+    // Default weight is 1 for the cases where a weight is not provided,
+    // each occurrence of the address will be counted a weight value of 1.
+    uint32_t weight = 1;
+    double normalized_weight;
+  };
+  std::vector<AddressWeight> address_weights;
+  size_t sum = 0;
+  address_weights.reserve(num_subchannels);
+  for (size_t i = 0; i < num_subchannels; ++i) {
+    RingHashSubchannelData* sd = subchannel_list->subchannel(i);
+    const ServerAddressWeightAttribute* weight_attribute = static_cast<
+        const ServerAddressWeightAttribute*>(sd->address().GetAttribute(
+        ServerAddressWeightAttribute::kServerAddressWeightAttributeKey));
+    AddressWeight address_weight;
+    address_weight.address =
+        grpc_sockaddr_to_string(&sd->address().address(), false);
+    if (weight_attribute != nullptr) {
+      GPR_ASSERT(weight_attribute->weight() != 0);
+      address_weight.weight = weight_attribute->weight();
+    }
+    sum += address_weight.weight;
+    address_weights.push_back(std::move(address_weight));
+  }
+  // Calculating normalized weights and find min and max.
+  double min_normalized_weight = 1.0;
+  double max_normalized_weight = 0.0;
+  for (auto& address : address_weights) {
+    address.normalized_weight = static_cast<double>(address.weight) / sum;
+    min_normalized_weight =
+        std::min(address.normalized_weight, min_normalized_weight);
+    max_normalized_weight =
+        std::max(address.normalized_weight, max_normalized_weight);
+  }
+  // Scale up the number of hashes per host such that the least-weighted host
+  // gets a whole number of hashes on the ring. Other hosts might not end up
+  // with whole numbers, and that's fine (the ring-building algorithm below can
+  // handle this). This preserves the original implementation's behavior: when
+  // weights aren't provided, all hosts should get an equal number of hashes. In
+  // the case where this number exceeds the max_ring_size, it's scaled back down
+  // to fit.
+  const size_t min_ring_size = parent_->config_->min_ring_size();
+  const size_t max_ring_size = parent_->config_->max_ring_size();
+  const double scale = std::min(
+      std::ceil(min_normalized_weight * min_ring_size) / min_normalized_weight,
+      static_cast<double>(max_ring_size));
+  // Reserve memory for the entire ring up front.
+  const uint64_t ring_size = std::ceil(scale);
+  ring_.reserve(ring_size);
+  // Populate the hash ring by walking through the (host, weight) pairs in
+  // normalized_host_weights, and generating (scale * weight) hashes for each
+  // host. Since these aren't necessarily whole numbers, we maintain running
+  // sums -- current_hashes and target_hashes -- which allows us to populate the
+  // ring in a mostly stable way.
+  absl::InlinedVector<char, 196> hash_key_buffer;
+  double current_hashes = 0.0;
+  double target_hashes = 0.0;
+  uint64_t min_hashes_per_host = ring_size;
+  uint64_t max_hashes_per_host = 0;
+  for (size_t i = 0; i < num_subchannels; ++i) {
+    const std::string& address_string = address_weights[i].address;
+    hash_key_buffer.assign(address_string.begin(), address_string.end());
+    hash_key_buffer.emplace_back('_');
+    auto offset_start = hash_key_buffer.end();
+    target_hashes += scale * address_weights[i].normalized_weight;
+    size_t count = 0;
+    auto current_state =
+        subchannel_list->subchannel(i)->subchannel()->CheckConnectivityState();
+    while (current_hashes < target_hashes) {
+      const std::string count_str = absl::StrCat(count);
+      hash_key_buffer.insert(offset_start, count_str.begin(), count_str.end());
+      absl::string_view hash_key(hash_key_buffer.data(),
+                                 hash_key_buffer.size());
+      const uint64_t hash = XXH64(hash_key.data(), hash_key.size(), 0);
+      ring_.push_back({hash,
+                       subchannel_list->subchannel(i)->subchannel()->Ref(),
+                       current_state});
+      ++count;
+      ++current_hashes;
+      hash_key_buffer.erase(offset_start, hash_key_buffer.end());
+    }
+    min_hashes_per_host =
+        std::min(static_cast<uint64_t>(i), min_hashes_per_host);
+    max_hashes_per_host =
+        std::max(static_cast<uint64_t>(i), max_hashes_per_host);
+  }
+  std::sort(ring_.begin(), ring_.end(),
+            [](const RingEntry& lhs, const RingEntry& rhs) -> bool {
+              return lhs.hash < rhs.hash;
+            });
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_ring_hash_trace)) {
+    gpr_log(GPR_INFO,
+            "[RH %p picker %p] created picker from subchannel_list=%p "
+            "with %" PRIuPTR " ring entries",
+            parent_.get(), this, subchannel_list, ring_.size());
+  }
+}
+
+RingHash::PickResult RingHash::Picker::Pick(PickArgs args) {
+  PickResult result;
+  // Initialize to PICK_FAILED.
+  result.type = PickResult::PICK_FAILED;
+  auto hash =
+      args.call_state->ExperimentalGetCallAttribute(kRequestRingHashAttribute);
+  uint64_t h;
+  if (!absl::SimpleAtoi(hash, &h)) {
+    result.error = grpc_error_set_int(
+        GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+            absl::StrCat("xds ring hash value is not a number").c_str()),
+        GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_INTERNAL);
+    return result;
+  }
+  // Ported from https://github.com/RJ/ketama/blob/master/libketama/ketama.c
+  // (ketama_get_server) NOTE: The algorithm depends on using signed integers
+  // for lowp, highp, and first_index. Do not change them!
+  int64_t lowp = 0;
+  int64_t highp = ring_.size();
+  int64_t first_index = 0;
+  while (true) {
+    first_index = (lowp + highp) / 2;
+    if (first_index == static_cast<int64_t>(ring_.size())) {
+      first_index = 0;
+      break;
+    }
+    uint64_t midval = ring_[first_index].hash;
+    uint64_t midval1 = first_index == 0 ? 0 : ring_[first_index - 1].hash;
+    if (h <= midval && h > midval1) {
+      break;
+    }
+    if (midval < h) {
+      lowp = first_index + 1;
+    } else {
+      highp = first_index - 1;
+    }
+    if (lowp > highp) {
+      first_index = 0;
+      break;
+    }
+  }
+  OrphanablePtr<SubchannelConnectionAttempter> subchannel_connection_attempter;
+  auto ScheduleSubchannelConnectionAttempt =
+      [&](RefCountedPtr<SubchannelInterface> subchannel) {
+        if (subchannel_connection_attempter == nullptr) {
+          subchannel_connection_attempter =
+              MakeOrphanable<SubchannelConnectionAttempter>(parent_);
+        }
+        subchannel_connection_attempter->AddSubchannel(std::move(subchannel));
+      };
+  switch (ring_[first_index].connectivity_state) {
+    case GRPC_CHANNEL_READY:
+      result.type = PickResult::PICK_COMPLETE;
+      result.subchannel = ring_[first_index].subchannel;
+      return result;
+    case GRPC_CHANNEL_IDLE:
+      ScheduleSubchannelConnectionAttempt(ring_[first_index].subchannel);
+      // fallthrough
+    case GRPC_CHANNEL_CONNECTING:
+      result.type = PickResult::PICK_QUEUE;
+      return result;
+    default:  // GRPC_CHANNEL_TRANSIENT_FAILURE
+      break;
+  }
+  ScheduleSubchannelConnectionAttempt(ring_[first_index].subchannel);
+  // Loop through remaining subchannels to find one in READY.
+  // On the way, we make sure the right set of connection attempts
+  // will happen.
+  bool found_second_subchannel = false;
+  bool found_first_non_failed = false;
+  for (size_t i = 1; i < ring_.size(); ++i) {
+    const RingEntry& entry = ring_[(first_index + i) % ring_.size()];
+    if (entry.subchannel == ring_[first_index].subchannel) {
+      continue;
+    }
+    if (entry.connectivity_state == GRPC_CHANNEL_READY) {
+      result.type = PickResult::PICK_COMPLETE;
+      result.subchannel = entry.subchannel;
+      return result;
+    }
+    if (!found_second_subchannel) {
+      switch (entry.connectivity_state) {
+        case GRPC_CHANNEL_IDLE:
+          ScheduleSubchannelConnectionAttempt(entry.subchannel);
+          // fallthrough
+        case GRPC_CHANNEL_CONNECTING:
+          result.type = PickResult::PICK_QUEUE;
+          return result;
+        default:
+          break;
+      }
+      found_second_subchannel = true;
+    }
+    if (!found_first_non_failed) {
+      if (entry.connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+        ScheduleSubchannelConnectionAttempt(entry.subchannel);
+      } else {
+        if (entry.connectivity_state == GRPC_CHANNEL_IDLE) {
+          ScheduleSubchannelConnectionAttempt(entry.subchannel);
+        }
+        found_first_non_failed = true;
+      }
+    }
+  }
+  result.error =
+      grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+                             absl::StrCat("xds ring hash found a subchannel "
+                                          "that is in TRANSIENT_FAILURE state")
+                                 .c_str()),
+                         GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_INTERNAL);
+  return result;
+}
+
+//
+// RingHash::RingHashSubchannelList
+//
+
+void RingHash::RingHashSubchannelList::StartWatchingLocked() {
+  if (num_subchannels() == 0) return;
+  // Check current state of each subchannel synchronously.
+  for (size_t i = 0; i < num_subchannels(); ++i) {
+    grpc_connectivity_state state =
+        subchannel(i)->CheckConnectivityStateLocked();
+    subchannel(i)->UpdateConnectivityStateLocked(state);
+  }
+  // Start connectivity watch for each subchannel.
+  for (size_t i = 0; i < num_subchannels(); i++) {
+    if (subchannel(i)->subchannel() != nullptr) {
+      subchannel(i)->StartConnectivityWatchLocked();
+    }
+  }
+  RingHash* p = static_cast<RingHash*>(policy());
+  // Sending up the initial picker while all subchannels are in IDLE state.
+  p->channel_control_helper()->UpdateState(
+      GRPC_CHANNEL_READY, absl::Status(),
+      absl::make_unique<Picker>(p->Ref(DEBUG_LOCATION, "RingHashPicker"),
+                                this));
+}
+
+void RingHash::RingHashSubchannelList::UpdateStateCountersLocked(
+    grpc_connectivity_state old_state, grpc_connectivity_state new_state) {
+  GPR_ASSERT(new_state != GRPC_CHANNEL_SHUTDOWN);
+  if (old_state == GRPC_CHANNEL_IDLE) {
+    GPR_ASSERT(num_idle_ > 0);
+    --num_idle_;
+  } else if (old_state == GRPC_CHANNEL_READY) {
+    GPR_ASSERT(num_ready_ > 0);
+    --num_ready_;
+  } else if (old_state == GRPC_CHANNEL_CONNECTING) {
+    GPR_ASSERT(num_connecting_ > 0);
+    --num_connecting_;
+  } else if (old_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+    GPR_ASSERT(num_transient_failure_ > 0);
+    --num_transient_failure_;
+  }
+  if (new_state == GRPC_CHANNEL_IDLE) {
+    ++num_idle_;
+  } else if (new_state == GRPC_CHANNEL_READY) {
+    ++num_ready_;
+  } else if (new_state == GRPC_CHANNEL_CONNECTING) {
+    ++num_connecting_;
+  } else if (new_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+    ++num_transient_failure_;
+  }
+}
+
+// Sets the RH policy's connectivity state and generates a new picker based
+// on the current subchannel list or requests an re-attempt by returning true..
+bool RingHash::RingHashSubchannelList::UpdateRingHashConnectivityStateLocked() {
+  RingHash* p = static_cast<RingHash*>(policy());
+  // Only set connectivity state if this is the current subchannel list.
+  if (p->subchannel_list_.get() != this) return false;
+  // The overall aggregation rules here are:
+  // 1. If there is at least one subchannel in READY state, report READY.
+  // 2. If there are 2 or more subchannels in TRANSIENT_FAILURE state, report
+  // TRANSIENT_FAILURE.
+  // 3. If there is at least one subchannel in CONNECTING state, report
+  // CONNECTING.
+  // 4. If there is at least one subchannel in IDLE state, report IDLE.
+  // 5. Otherwise, report TRANSIENT_FAILURE.
+  if (num_ready_ > 0) {
+    /* READY */
+    p->channel_control_helper()->UpdateState(
+        GRPC_CHANNEL_READY, absl::Status(),
+        absl::make_unique<Picker>(p->Ref(DEBUG_LOCATION, "RingHashPicker"),
+                                  this));
+    return false;
+  }
+  if (num_connecting_ > 0 && num_transient_failure_ < 2) {
+    p->channel_control_helper()->UpdateState(
+        GRPC_CHANNEL_CONNECTING, absl::Status(),
+        absl::make_unique<QueuePicker>(p->Ref(DEBUG_LOCATION, "QueuePicker")));
+    return false;
+  }
+  if (num_idle_ > 0 && num_transient_failure_ < 2) {
+    p->channel_control_helper()->UpdateState(
+        GRPC_CHANNEL_IDLE, absl::Status(),
+        absl::make_unique<Picker>(p->Ref(DEBUG_LOCATION, "RingHashPicker"),
+                                  this));
+    return false;
+  }
+  grpc_error* error =
+      grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                             "connections to backend failing or idle"),
+                         GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE);
+  p->channel_control_helper()->UpdateState(
+      GRPC_CHANNEL_TRANSIENT_FAILURE, grpc_error_to_absl_status(error),
+      absl::make_unique<TransientFailurePicker>(error));
+  return true;
+}
+
+//
+// RingHash::RingHashSubchannelData
+//
+
+void RingHash::RingHashSubchannelData::UpdateConnectivityStateLocked(
+    grpc_connectivity_state connectivity_state) {
+  RingHash* p = static_cast<RingHash*>(subchannel_list()->policy());
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_ring_hash_trace)) {
+    gpr_log(
+        GPR_INFO,
+        "[RR %p] connectivity changed for subchannel %p, subchannel_list %p "
+        "(index %" PRIuPTR " of %" PRIuPTR "): prev_state=%s new_state=%s",
+        p, subchannel(), subchannel_list(), Index(),
+        subchannel_list()->num_subchannels(),
+        ConnectivityStateName(last_connectivity_state_),
+        ConnectivityStateName(connectivity_state));
+  }
+  // Decide what state to report for aggregation purposes.
+  // If we haven't seen a failure since the last time we were in state
+  // READY, then we report the state change as-is.  However, once we do see
+  // a failure, we report TRANSIENT_FAILURE and do not report any subsequent
+  // state changes until we go back into state READY.
+  if (!seen_failure_since_ready_) {
+    if (connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+      seen_failure_since_ready_ = true;
+    }
+    subchannel_list()->UpdateStateCountersLocked(last_connectivity_state_,
+                                                 connectivity_state);
+  } else {
+    if (connectivity_state == GRPC_CHANNEL_READY) {
+      seen_failure_since_ready_ = false;
+      subchannel_list()->UpdateStateCountersLocked(
+          GRPC_CHANNEL_TRANSIENT_FAILURE, connectivity_state);
+    }
+  }
+  // Record last seen connectivity state.
+  last_connectivity_state_ = connectivity_state;
+}
+
+void RingHash::RingHashSubchannelData::ProcessConnectivityChangeLocked(
+    grpc_connectivity_state connectivity_state) {
+  RingHash* p = static_cast<RingHash*>(subchannel_list()->policy());
+  GPR_ASSERT(subchannel() != nullptr);
+  // If the new state is TRANSIENT_FAILURE, re-resolve.
+  // Only do this if we've started watching, not at startup time.
+  // Otherwise, if the subchannel was already in state TRANSIENT_FAILURE
+  // when the subchannel list was created, we'd wind up in a constant
+  // loop of re-resolution.
+  // Also attempt to reconnect.
+  if (connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_ring_hash_trace)) {
+      gpr_log(GPR_INFO,
+              "[RR %p] Subchannel %p has gone into TRANSIENT_FAILURE. "
+              "Requesting re-resolution",
+              p, subchannel());
+    }
+    p->channel_control_helper()->RequestReresolution();
+  }
+  // Update state counters.
+  UpdateConnectivityStateLocked(connectivity_state);
+  // Update the RH policy's connectivity state, creating new picker and new
+  // ring.
+  bool transient_failure =
+      subchannel_list()->UpdateRingHashConnectivityStateLocked();
+  // While the ring_hash policy is reporting TRANSIENT_FAILURE, it will
+  // not be getting any pick requests from the priority policy.
+  // However, because the ring_hash policy does not attempt to
+  // reconnect to subchannels unless it is getting pick requests,
+  // it will need special handling to ensure that it will eventually
+  // recover from TRANSIENT_FAILURE state once the problem is resolved.
+  // Specifically, it will make sure that it is attempting to connect to
+  // at least one subchannel at any given time.  After a given subchannel
+  // fails a connection attempt, it will move on to the next subchannel
+  // in the ring.  It will keep doing this until one of the subchannels
+  // successfully connects, at which point it will report READY and stop
+  // proactively trying to connect.  The policy will remain in
+  // TRANSIENT_FAILURE until at least one subchannel becomes connected,
+  // even if subchannels are in state CONNECTING during that time.
+  if (transient_failure &&
+      connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+    size_t next_index = (Index() + 1) % subchannel_list()->num_subchannels();
+    RingHashSubchannelData* next_sd = subchannel_list()->subchannel(next_index);
+    next_sd->subchannel()->AttemptToConnect();
+  }
+}
+
+//
+// RingHash
+//
+
+RingHash::RingHash(Args args) : LoadBalancingPolicy(std::move(args)) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_ring_hash_trace)) {
+    gpr_log(GPR_INFO, "[RH %p] Created", this);
+  }
+}
+
+RingHash::~RingHash() {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_ring_hash_trace)) {
+    gpr_log(GPR_INFO, "[RH %p] Destroying Ring Hash policy", this);
+  }
+  GPR_ASSERT(subchannel_list_ == nullptr);
+}
+
+void RingHash::ShutdownLocked() {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_ring_hash_trace)) {
+    gpr_log(GPR_INFO, "[RH %p] Shutting down", this);
+  }
+  shutdown_ = true;
+  subchannel_list_.reset();
+}
+
+void RingHash::ResetBackoffLocked() { subchannel_list_->ResetBackoffLocked(); }
+
+void RingHash::UpdateLocked(UpdateArgs args) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_ring_hash_trace)) {
+    gpr_log(GPR_INFO, "[RR %p] received update with %" PRIuPTR " addresses",
+            this, args.addresses.size());
+  }
+  config_ = std::move(args.config);
+  // Filter out any address with weight 0.
+  ServerAddressList addresses;
+  addresses.reserve(args.addresses.size());
+  for (ServerAddress& address : args.addresses) {
+    const ServerAddressWeightAttribute* weight_attribute =
+        static_cast<const ServerAddressWeightAttribute*>(address.GetAttribute(
+            ServerAddressWeightAttribute::kServerAddressWeightAttributeKey));
+    if (weight_attribute == nullptr || weight_attribute->weight() > 0) {
+      addresses.push_back(std::move(address));
+    }
+  }
+  subchannel_list_ = MakeOrphanable<RingHashSubchannelList>(
+      this, &grpc_lb_ring_hash_trace, std::move(addresses), *args.args);
+  if (subchannel_list_->num_subchannels() == 0) {
+    // If the new list is empty, immediately transition to TRANSIENT_FAILURE.
+    grpc_error* error =
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"),
+                           GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE);
+    channel_control_helper()->UpdateState(
+        GRPC_CHANNEL_TRANSIENT_FAILURE, grpc_error_to_absl_status(error),
+        absl::make_unique<TransientFailurePicker>(error));
+  } else {
+    // Start watching the new list.
+    subchannel_list_->StartWatchingLocked();
+  }
+}
+
+//
+// factory
+//
+
+class RingHashFactory : public LoadBalancingPolicyFactory {
+ public:
+  OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
+      LoadBalancingPolicy::Args args) const override {
+    return MakeOrphanable<RingHash>(std::move(args));
+  }
+
+  const char* name() const override { return kRingHash; }
+
+  RefCountedPtr<LoadBalancingPolicy::Config> ParseLoadBalancingConfig(
+      const Json& json, grpc_error** error) const override {
+    size_t min_ring_size;
+    size_t max_ring_size;
+    std::vector<grpc_error_handle> error_list;
+    ParseRingHashLbConfig(json, &min_ring_size, &max_ring_size, &error_list);
+    if (error_list.empty()) {
+      return MakeRefCounted<RingHashLbConfig>(min_ring_size, max_ring_size);
+    } else {
+      *error = GRPC_ERROR_CREATE_FROM_VECTOR(
+          "ring_hash_experimental LB policy config", &error_list);
+      return nullptr;
+    }
+  }
+};
+
+}  // namespace
+
+void GrpcLbPolicyRingHashInit() {
+  grpc_core::LoadBalancingPolicyRegistry::Builder::
+      RegisterLoadBalancingPolicyFactory(
+          absl::make_unique<grpc_core::RingHashFactory>());
+}
+
+void GrpcLbPolicyRingHashShutdown() {}
 
 }  // namespace grpc_core
index dc176c2..f0f86b2 100644 (file)
 
 #include <grpc/support/port_platform.h>
 
+#include <stdlib.h>
+
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/json/json.h"
+
 namespace grpc_core {
 extern const char* kRequestRingHashAttribute;
 
+// Helper Parsing method to parse ring hash policy configs; for example, ring
+// hash size validity.
+void ParseRingHashLbConfig(const Json& json, size_t* min_ring_size,
+                           size_t* max_ring_size,
+                           std::vector<grpc_error_handle>* error_list);
 }  // namespace grpc_core
 
 #endif  // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_RING_HASH_RING_HASH_H
index 49bff27..9097523 100644 (file)
@@ -392,26 +392,25 @@ bool CdsLb::GenerateDiscoveryMechanismForCluster(
     }
     return !missing_cluster;
   }
-  std::string type;
+  Json::Object mechanism = {
+      {"clusterName", name},
+      {"max_concurrent_requests", state.update->max_concurrent_requests},
+  };
   switch (state.update->cluster_type) {
     case XdsApi::CdsUpdate::ClusterType::EDS:
-      type = "EDS";
+      mechanism["type"] = "EDS";
+      if (!state.update->eds_service_name.empty()) {
+        mechanism["edsServiceName"] = state.update->eds_service_name;
+      }
       break;
     case XdsApi::CdsUpdate::ClusterType::LOGICAL_DNS:
-      type = "LOGICAL_DNS";
+      mechanism["type"] = "LOGICAL_DNS";
+      mechanism["dnsHostname"] = state.update->dns_hostname;
       break;
     default:
       GPR_ASSERT(0);
       break;
   }
-  Json::Object mechanism = {
-      {"clusterName", name},
-      {"max_concurrent_requests", state.update->max_concurrent_requests},
-      {"type", std::move(type)},
-  };
-  if (!state.update->eds_service_name.empty()) {
-    mechanism["edsServiceName"] = state.update->eds_service_name;
-  }
   if (state.update->lrs_load_reporting_server_name.has_value()) {
     mechanism["lrsLoadReportingServerName"] =
         state.update->lrs_load_reporting_server_name.value();
@@ -452,22 +451,9 @@ void CdsLb::OnClusterChanged(const std::string& name,
     // Construct config for child policy.
     Json::Object xds_lb_policy;
     if (cluster_data.lb_policy == "RING_HASH") {
-      std::string hash_function;
-      switch (cluster_data.hash_function) {
-        case XdsApi::CdsUpdate::HashFunction::XX_HASH:
-          hash_function = "XX_HASH";
-          break;
-        case XdsApi::CdsUpdate::HashFunction::MURMUR_HASH_2:
-          hash_function = "MURMUR_HASH_2";
-          break;
-        default:
-          GPR_ASSERT(0);
-          break;
-      }
       xds_lb_policy["RING_HASH"] = Json::Object{
           {"min_ring_size", cluster_data.min_ring_size},
           {"max_ring_size", cluster_data.max_ring_size},
-          {"hash_function", hash_function},
       };
     } else {
       xds_lb_policy["ROUND_ROBIN"] = Json::Object();
index bcb8194..8bdfd6c 100644 (file)
@@ -28,6 +28,7 @@
 #include "src/core/ext/filters/client_channel/lb_policy.h"
 #include "src/core/ext/filters/client_channel/lb_policy/address_filtering.h"
 #include "src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h"
+#include "src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.h"
 #include "src/core/ext/filters/client_channel/lb_policy/xds/xds.h"
 #include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_args.h"
 #include "src/core/ext/filters/client_channel/lb_policy_factory.h"
@@ -71,13 +72,16 @@ class XdsClusterResolverLbConfig : public LoadBalancingPolicy::Config {
     };
     DiscoveryMechanismType type;
     std::string eds_service_name;
+    std::string dns_hostname;
 
     bool operator==(const DiscoveryMechanism& other) const {
       return (cluster_name == other.cluster_name &&
               lrs_load_reporting_server_name ==
                   other.lrs_load_reporting_server_name &&
               max_concurrent_requests == other.max_concurrent_requests &&
-              type == other.type && eds_service_name == other.eds_service_name);
+              type == other.type &&
+              eds_service_name == other.eds_service_name &&
+              dns_hostname == other.dns_hostname);
     }
   };
 
@@ -131,17 +135,6 @@ class XdsClusterResolverLb : public LoadBalancingPolicy {
     virtual Json::Array override_child_policy() = 0;
     virtual bool disable_reresolution() = 0;
 
-    // Caller must ensure that config_ is set before calling.
-    absl::string_view GetXdsClusterResolverResourceName() const {
-      if (!parent_->is_xds_uri_) return parent_->server_name_;
-      if (!parent_->config_->discovery_mechanisms()[index_]
-               .eds_service_name.empty()) {
-        return parent_->config_->discovery_mechanisms()[index_]
-            .eds_service_name;
-      }
-      return parent_->config_->discovery_mechanisms()[index_].cluster_name;
-    }
-
     // Returns a pair containing the cluster and eds_service_name
     // to use for LRS load reporting. Caller must ensure that config_ is set
     // before calling.
@@ -218,6 +211,18 @@ class XdsClusterResolverLb : public LoadBalancingPolicy {
       RefCountedPtr<EdsDiscoveryMechanism> discovery_mechanism_;
     };
 
+    absl::string_view GetEdsResourceName() const {
+      if (!parent()->is_xds_uri_) return parent()->server_name_;
+      if (!parent()
+               ->config_->discovery_mechanisms()[index()]
+               .eds_service_name.empty()) {
+        return parent()
+            ->config_->discovery_mechanisms()[index()]
+            .eds_service_name;
+      }
+      return parent()->config_->discovery_mechanisms()[index()].cluster_name;
+    }
+
     // Note that this is not owned, so this pointer must never be dereferenced.
     EndpointWatcher* watcher_ = nullptr;
   };
@@ -255,9 +260,11 @@ class XdsClusterResolverLb : public LoadBalancingPolicy {
      private:
       RefCountedPtr<LogicalDNSDiscoveryMechanism> discovery_mechanism_;
     };
-    // This is only necessary because of a bug in msvc where nested class cannot
+
+    // This is necessary only because of a bug in msvc where nested class cannot
     // access protected member in base class.
     friend class ResolverResultHandler;
+
     OrphanablePtr<Resolver> resolver_;
   };
 
@@ -389,13 +396,12 @@ void XdsClusterResolverLb::EdsDiscoveryMechanism::Start() {
     gpr_log(GPR_INFO,
             "[xds_cluster_resolver_lb %p] eds discovery mechanism %" PRIuPTR
             ":%p starting xds watch for %s",
-            parent(), index(), this,
-            std::string(GetXdsClusterResolverResourceName()).c_str());
+            parent(), index(), this, std::string(GetEdsResourceName()).c_str());
   }
   auto watcher = absl::make_unique<EndpointWatcher>(
       Ref(DEBUG_LOCATION, "EdsDiscoveryMechanism"));
   watcher_ = watcher.get();
-  parent()->xds_client_->WatchEndpointData(GetXdsClusterResolverResourceName(),
+  parent()->xds_client_->WatchEndpointData(GetEdsResourceName(),
                                            std::move(watcher));
 }
 
@@ -404,11 +410,10 @@ void XdsClusterResolverLb::EdsDiscoveryMechanism::Orphan() {
     gpr_log(GPR_INFO,
             "[xds_cluster_resolver_lb %p] eds discovery mechanism %" PRIuPTR
             ":%p cancelling xds watch for %s",
-            parent(), index(), this,
-            std::string(GetXdsClusterResolverResourceName()).c_str());
+            parent(), index(), this, std::string(GetEdsResourceName()).c_str());
   }
-  parent()->xds_client_->CancelEndpointDataWatch(
-      GetXdsClusterResolverResourceName(), watcher_);
+  parent()->xds_client_->CancelEndpointDataWatch(GetEdsResourceName(),
+                                                 watcher_);
   Unref();
 }
 
@@ -477,7 +482,8 @@ void XdsClusterResolverLb::EdsDiscoveryMechanism::EndpointWatcher::Notifier::
 //
 
 void XdsClusterResolverLb::LogicalDNSDiscoveryMechanism::Start() {
-  std::string target = parent()->server_name_;
+  std::string target =
+      parent()->config_->discovery_mechanisms()[index()].dns_hostname;
   grpc_channel_args* args = nullptr;
   FakeResolverResponseGenerator* fake_resolver_response_generator =
       grpc_channel_args_find_pointer<FakeResolverResponseGenerator>(
@@ -489,6 +495,7 @@ void XdsClusterResolverLb::LogicalDNSDiscoveryMechanism::Start() {
         fake_resolver_response_generator);
     args = grpc_channel_args_copy_and_add(parent()->args_, &new_arg, 1);
   } else {
+    target = absl::StrCat("dns:", target);
     args = grpc_channel_args_copy(parent()->args_);
   }
   resolver_ = ResolverRegistry::CreateResolver(
@@ -834,6 +841,13 @@ ServerAddressList XdsClusterResolverLb::CreateChildPolicyAddressesLocked() {
       std::vector<std::string> hierarchical_path = {
           priority_child_name, locality_name->AsHumanReadableString()};
       for (const auto& endpoint : locality.endpoints) {
+        const ServerAddressWeightAttribute* weight_attribute = static_cast<
+            const ServerAddressWeightAttribute*>(endpoint.GetAttribute(
+            ServerAddressWeightAttribute::kServerAddressWeightAttributeKey));
+        uint32_t weight = locality.lb_weight;
+        if (weight_attribute != nullptr) {
+          weight = locality.lb_weight * weight_attribute->weight();
+        }
         addresses.emplace_back(
             endpoint
                 .WithAttribute(kHierarchicalPathAttributeKey,
@@ -841,10 +855,10 @@ ServerAddressList XdsClusterResolverLb::CreateChildPolicyAddressesLocked() {
                 .WithAttribute(kXdsLocalityNameAttributeKey,
                                absl::make_unique<XdsLocalityAttribute>(
                                    locality_name->Ref()))
-                .WithAttribute(ServerAddressWeightAttribute::
-                                   kServerAddressWeightAttributeKey,
-                               absl::make_unique<ServerAddressWeightAttribute>(
-                                   locality.lb_weight)));
+                .WithAttribute(
+                    ServerAddressWeightAttribute::
+                        kServerAddressWeightAttributeKey,
+                    absl::make_unique<ServerAddressWeightAttribute>(weight)));
       }
     }
   }
@@ -1100,7 +1114,7 @@ class XdsClusterResolverLbFactory : public LoadBalancingPolicyFactory {
     GPR_ASSERT(uri.ok() && !uri->path().empty());
     absl::string_view server_name = absl::StripPrefix(uri->path(), "/");
     // Determine if it's an xds URI.
-    bool is_xds_uri = uri->scheme() == "xds";
+    bool is_xds_uri = uri->scheme() == "xds" || uri->scheme() == "google-c2p";
     // Get XdsClient.
     RefCountedPtr<XdsClient> xds_client =
         XdsClient::GetFromChannelArgs(*args.args);
@@ -1201,65 +1215,11 @@ class XdsClusterResolverLbFactory : public LoadBalancingPolicyFactory {
           }
           policy_it = policy.find("RING_HASH");
           if (policy_it != policy.end()) {
-            if (policy_it->second.type() != Json::Type::OBJECT) {
-              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-                  "field:RING_HASH error:type should be object"));
-              continue;
-            }
-            // TODO(donnadionne): Move this to a method in
-            // ring_hash_experimental and call it here.
-            const Json::Object& ring_hash = policy_it->second.object_value();
             xds_lb_policy = array[i];
-            size_t min_ring_size = 1024;
-            size_t max_ring_size = 8388608;
-            auto ring_hash_it = ring_hash.find("min_ring_size");
-            if (ring_hash_it == ring_hash.end()) {
-              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-                  "field:min_ring_size missing"));
-            } else if (ring_hash_it->second.type() != Json::Type::NUMBER) {
-              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-                  "field:min_ring_size error: should be of "
-                  "number"));
-            } else {
-              min_ring_size = gpr_parse_nonnegative_int(
-                  ring_hash_it->second.string_value().c_str());
-            }
-            ring_hash_it = ring_hash.find("max_ring_size");
-            if (ring_hash_it == ring_hash.end()) {
-              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-                  "field:max_ring_size missing"));
-            } else if (ring_hash_it->second.type() != Json::Type::NUMBER) {
-              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-                  "field:max_ring_size error: should be of "
-                  "number"));
-            } else {
-              max_ring_size = gpr_parse_nonnegative_int(
-                  ring_hash_it->second.string_value().c_str());
-            }
-            if (min_ring_size <= 0 || min_ring_size > 8388608 ||
-                max_ring_size <= 0 || max_ring_size > 8388608 ||
-                min_ring_size > max_ring_size) {
-              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-                  "field:max_ring_size and or min_ring_size error: "
-                  "values need to be in the range of 1 to 8388608 "
-                  "and max_ring_size cannot be smaller than "
-                  "min_ring_size"));
-            }
-            ring_hash_it = ring_hash.find("hash_function");
-            if (ring_hash_it == ring_hash.end()) {
-              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-                  "field:hash_function missing"));
-            } else if (ring_hash_it->second.type() != Json::Type::STRING) {
-              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-                  "field:hash_function error: should be a "
-                  "string"));
-            } else if (ring_hash_it->second.string_value() != "XX_HASH" &&
-                       ring_hash_it->second.string_value() != "MURMUR_HASH_2") {
-              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-                  "field:hash_function error: unsupported "
-                  "hash_function"));
-            }
-            break;
+            size_t min_ring_size;
+            size_t max_ring_size;
+            ParseRingHashLbConfig(policy_it->second, &min_ring_size,
+                                  &max_ring_size, &error_list);
           }
         }
       }
@@ -1331,25 +1291,33 @@ class XdsClusterResolverLbFactory : public LoadBalancingPolicyFactory {
       if (it->second.string_value() == "EDS") {
         discovery_mechanism->type = XdsClusterResolverLbConfig::
             DiscoveryMechanism::DiscoveryMechanismType::EDS;
+        it = json.object_value().find("edsServiceName");
+        if (it != json.object_value().end()) {
+          if (it->second.type() != Json::Type::STRING) {
+            error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                "field:edsServiceName error:type should be string"));
+          } else {
+            discovery_mechanism->eds_service_name = it->second.string_value();
+          }
+        }
       } else if (it->second.string_value() == "LOGICAL_DNS") {
         discovery_mechanism->type = XdsClusterResolverLbConfig::
             DiscoveryMechanism::DiscoveryMechanismType::LOGICAL_DNS;
+        it = json.object_value().find("dnsHostname");
+        if (it == json.object_value().end()) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:dnsHostname error:required field missing"));
+        } else if (it->second.type() != Json::Type::STRING) {
+          error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "field:dnsHostname error:type should be string"));
+        } else {
+          discovery_mechanism->dns_hostname = it->second.string_value();
+        }
       } else {
         error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
             "field:type error:invalid type"));
       }
     }
-    // EDS service name.
-    it = json.object_value().find("edsServiceName");
-    if (it != json.object_value().end()) {
-      if (it->second.type() != Json::Type::STRING) {
-        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-            "field:xds_cluster_resolverServiceName error:type should be "
-            "string"));
-      } else {
-        discovery_mechanism->eds_service_name = it->second.string_value();
-      }
-    }
     return error_list;
   }
 
index 2911eae..337aa37 100644 (file)
@@ -60,8 +60,6 @@ namespace grpc_core {
 
 namespace {
 
-const char kDefaultPort[] = "https";
-
 class AresDnsResolver : public Resolver {
  public:
   explicit AresDnsResolver(ResolverArgs args);
@@ -431,7 +429,7 @@ void AresDnsResolver::StartResolvingLocked() {
   resolving_ = true;
   service_config_json_ = nullptr;
   pending_request_ = grpc_dns_lookup_ares_locked(
-      dns_server_.c_str(), name_to_resolve_.c_str(), kDefaultPort,
+      dns_server_.c_str(), name_to_resolve_.c_str(), kDefaultSecurePort,
       interested_parties_, &on_resolved_, &addresses_,
       enable_srv_queries_ ? &balancer_addresses_ : nullptr,
       request_service_config_ ? &service_config_json_ : nullptr,
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc
new file mode 100644 (file)
index 0000000..45c1464
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+#if GRPC_ARES == 1 && defined(GRPC_USE_EVENT_ENGINE)
+
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h"
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+
+namespace grpc_core {
+
+std::unique_ptr<GrpcPolledFdFactory> NewGrpcPolledFdFactory(
+    std::shared_ptr<WorkSerializer> /* work_serializer */) {
+  return nullptr;
+}
+
+}  // namespace grpc_core
+
+#endif /* GRPC_ARES == 1 && defined(GRPC_USE_EVENT_ENGINE) */
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc
new file mode 100644 (file)
index 0000000..fdd4e00
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+#if GRPC_ARES == 1 && defined(GRPC_USE_EVENT_ENGINE)
+
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+
+bool grpc_ares_query_ipv6() {
+  /* The libuv grpc code currently does not have the code to probe for this,
+   * so we assume for now that IPv6 is always available in contexts where this
+   * code will be used. */
+  return true;
+}
+
+#endif /* GRPC_ARES == 1 && defined(GRPC_USE_EVENT_ENGINE) */
index 3265e29..a2e1640 100644 (file)
@@ -48,8 +48,6 @@ namespace grpc_core {
 
 namespace {
 
-const char kDefaultPort[] = "https";
-
 class NativeDnsResolver : public Resolver {
  public:
   explicit NativeDnsResolver(ResolverArgs args);
@@ -276,7 +274,7 @@ void NativeDnsResolver::StartResolvingLocked() {
   addresses_ = nullptr;
   GRPC_CLOSURE_INIT(&on_resolved_, NativeDnsResolver::OnResolved, this,
                     grpc_schedule_on_exec_ctx);
-  grpc_resolve_address(name_to_resolve_.c_str(), kDefaultPort,
+  grpc_resolve_address(name_to_resolve_.c_str(), kDefaultSecurePort,
                        interested_parties_, &on_resolved_, &addresses_);
   last_resolution_timestamp_ = grpc_core::ExecCtx::Get()->Now();
 }
index 208ec67..ed2b402 100644 (file)
@@ -16,6 +16,8 @@
 
 #include <grpc/support/port_platform.h>
 
+#include <random>
+
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
 #include "src/core/ext/xds/xds_client.h"
 #include "src/core/lib/gpr/env.h"
@@ -195,7 +197,7 @@ void GoogleCloud2ProdResolver::ZoneQuery::OnDone(
       gpr_log(GPR_ERROR, "could not parse zone from metadata server: %s",
               std::string(body).c_str());
     } else {
-      zone = std::string(body.substr(i));
+      zone = std::string(body.substr(i + 1));
     }
   }
   resolver->ZoneQueryDone(std::move(zone));
@@ -297,8 +299,11 @@ void GoogleCloud2ProdResolver::IPv6QueryDone(bool ipv6_supported) {
 
 void GoogleCloud2ProdResolver::StartXdsResolver() {
   // Construct bootstrap JSON.
+  std::random_device rd;
+  std::mt19937 mt(rd());
+  std::uniform_int_distribution<uint64_t> dist(1, UINT64_MAX);
   Json::Object node = {
-      {"id", "C2P"},
+      {"id", absl::StrCat("C2P-", dist(mt))},
   };
   if (!zone_->empty()) {
     node["locality"] = Json::Object{
index 3a10a42..069292e 100644 (file)
@@ -568,6 +568,9 @@ absl::optional<uint64_t> HeaderHashHelper(
   std::string value_buffer;
   absl::optional<absl::string_view> header_value =
       GetHeaderValue(initial_metadata, policy.header_name, &value_buffer);
+  if (!header_value.has_value()) {
+    return absl::nullopt;
+  }
   if (policy.regex != nullptr) {
     // If GetHeaderValue() did not already store the value in
     // value_buffer, copy it there now, so we can modify it.
@@ -671,7 +674,12 @@ ConfigSelector::CallConfig XdsResolver::XdsConfigSelector::GetCallConfig(
     }
     if (!hash.has_value()) {
       // If there is no hash, we just choose a random value as a default.
-      hash = rand();
+      // We cannot directly use the result of rand() as the hash value,
+      // since it is a 32-bit number and not a 64-bit number and will
+      // therefore not be evenly distributed.
+      uint32_t upper = rand();
+      uint32_t lower = rand();
+      hash = (static_cast<uint64_t>(upper) << 32) | lower;
     }
     CallConfig call_config;
     if (method_config != nullptr) {
@@ -680,8 +688,12 @@ ConfigSelector::CallConfig XdsResolver::XdsConfigSelector::GetCallConfig(
       call_config.service_config = std::move(method_config);
     }
     call_config.call_attributes[kXdsClusterAttribute] = it->first;
-    call_config.call_attributes[kRequestRingHashAttribute] =
-        absl::StrFormat("%" PRIu64, hash.value());
+    std::string hash_string = absl::StrCat(hash.value());
+    char* hash_value =
+        static_cast<char*>(args.arena->Alloc(hash_string.size() + 1));
+    memcpy(hash_value, hash_string.c_str(), hash_string.size());
+    hash_value[hash_string.size()] = '\0';
+    call_config.call_attributes[kRequestRingHashAttribute] = hash_value;
     call_config.on_call_committed = [resolver, cluster_state]() {
       cluster_state->Unref();
       ExecCtx::Run(
index a03d2da..2f0432f 100644 (file)
@@ -200,7 +200,6 @@ class RetryFilter::CallData {
   static void SetPollent(grpc_call_element* elem, grpc_polling_entity* pollent);
 
  private:
-  class Canceller;
   class CallStackDestructionBarrier;
 
   // Pending batches stored in call data.
@@ -212,13 +211,10 @@ class RetryFilter::CallData {
   };
 
   // State associated with each call attempt.
-  // Allocated on the arena.
-  class CallAttempt
-      : public RefCounted<CallAttempt, PolymorphicRefCount, kUnrefCallDtor> {
+  class CallAttempt : public RefCounted<CallAttempt> {
    public:
     explicit CallAttempt(CallData* calld);
-
-    ClientChannel::LoadBalancedCall* lb_call() const { return lb_call_.get(); }
+    ~CallAttempt() override;
 
     // Constructs and starts whatever batches are needed on this call
     // attempt.
@@ -228,6 +224,9 @@ class RetryFilter::CallData {
     // committing the call.
     void FreeCachedSendOpDataAfterCommit();
 
+    // Cancels the call attempt.
+    void CancelFromSurface(grpc_transport_stream_op_batch* cancel_batch);
+
    private:
     // State used for starting a retryable batch on the call attempt's LB call.
     // This provides its own grpc_transport_stream_op_batch and other data
@@ -235,7 +234,7 @@ class RetryFilter::CallData {
     // We allocate one struct on the arena for each attempt at starting a
     // batch on a given LB call.
     class BatchData
-        : public RefCounted<CallAttempt, PolymorphicRefCount, kUnrefCallDtor> {
+        : public RefCounted<BatchData, PolymorphicRefCount, kUnrefCallDtor> {
      public:
       BatchData(RefCountedPtr<CallAttempt> call_attempt, int refcount,
                 bool set_on_complete);
@@ -243,24 +242,22 @@ class RetryFilter::CallData {
 
       grpc_transport_stream_op_batch* batch() { return &batch_; }
 
-      // Adds retriable send_initial_metadata op to batch_data.
+      // Adds retriable send_initial_metadata op.
       void AddRetriableSendInitialMetadataOp();
-      // Adds retriable send_message op to batch_data.
+      // Adds retriable send_message op.
       void AddRetriableSendMessageOp();
-      // Adds retriable send_trailing_metadata op to batch_data.
+      // Adds retriable send_trailing_metadata op.
       void AddRetriableSendTrailingMetadataOp();
-      // Adds retriable recv_initial_metadata op to batch_data.
+      // Adds retriable recv_initial_metadata op.
       void AddRetriableRecvInitialMetadataOp();
-      // Adds retriable recv_message op to batch_data.
+      // Adds retriable recv_message op.
       void AddRetriableRecvMessageOp();
-      // Adds retriable recv_trailing_metadata op to batch_data.
+      // Adds retriable recv_trailing_metadata op.
       void AddRetriableRecvTrailingMetadataOp();
+      // Adds cancel_stream op.
+      void AddCancelStreamOp();
 
      private:
-      // Returns true if the call is being retried.
-      bool MaybeRetry(grpc_status_code status, grpc_mdelem* server_pushback_md,
-                      bool is_lb_drop);
-
       // Frees cached send ops that were completed by the completed batch in
       // batch_data.  Used when batches are completed after the call is
       // committed.
@@ -282,9 +279,9 @@ class RetryFilter::CallData {
       // Adds recv_trailing_metadata_ready closure to closures.
       void AddClosureForRecvTrailingMetadataReady(
           grpc_error_handle error, CallCombinerClosureList* closures);
-      // Adds any necessary closures for deferred recv_initial_metadata and
-      // recv_message callbacks to closures.
-      void AddClosuresForDeferredRecvCallbacks(
+      // Adds any necessary closures for deferred batch completion
+      // callbacks to closures.
+      void AddClosuresForDeferredCompletionCallbacks(
           CallCombinerClosureList* closures);
       // For any pending batch containing an op that has not yet been started,
       // adds the pending batch's completion closures to closures.
@@ -322,7 +319,8 @@ class RetryFilter::CallData {
     // on_complete callback will be set to point to on_complete();
     // otherwise, the batch's on_complete callback will be null.
     BatchData* CreateBatch(int refcount, bool set_on_complete) {
-      return calld_->arena_->New<BatchData>(Ref(), refcount, set_on_complete);
+      return calld_->arena_->New<BatchData>(Ref(DEBUG_LOCATION, "CreateBatch"),
+                                            refcount, set_on_complete);
     }
 
     // If there are any cached send ops that need to be replayed on this
@@ -330,6 +328,11 @@ class RetryFilter::CallData {
     // Otherwise, returns nullptr.
     BatchData* MaybeCreateBatchForReplay();
 
+    // Adds a closure to closures that will execute batch in the call combiner.
+    void AddClosureForBatch(grpc_transport_stream_op_batch* batch,
+                            const char* reason,
+                            CallCombinerClosureList* closures);
+
     // Adds batches for pending batches to closures.
     void AddBatchesForPendingBatches(CallCombinerClosureList* closures);
 
@@ -339,15 +342,41 @@ class RetryFilter::CallData {
     // Returns true if any op in the batch was not yet started on this attempt.
     bool PendingBatchIsUnstarted(PendingBatch* pending);
 
+    // Returns true if there are cached send ops to replay.
+    bool HaveSendOpsToReplay();
+
+    // If our retry state is no longer needed, switch to fast path by moving
+    // our LB call into calld_->committed_call_ and having calld_ drop
+    // its ref to us.
+    void MaybeSwitchToFastPath();
+
     // Helper function used to start a recv_trailing_metadata batch.  This
     // is used in the case where a recv_initial_metadata or recv_message
     // op fails in a way that we know the call is over but when the application
     // has not yet started its own recv_trailing_metadata op.
     void StartInternalRecvTrailingMetadata();
 
+    // Returns true if the call should be retried.
+    // If server_pushback_md is non-null, sets *server_pushback_ms.
+    bool ShouldRetry(absl::optional<grpc_status_code> status, bool is_lb_drop,
+                     grpc_mdelem* server_pushback_md,
+                     grpc_millis* server_pushback_ms);
+
+    // Cancels the call attempt.  Unrefs any deferred batches.
+    // Adds a batch to closures to cancel this call attempt.
+    void Cancel(CallCombinerClosureList* closures);
+
+    static void OnPerAttemptRecvTimer(void* arg, grpc_error_handle error);
+    static void OnPerAttemptRecvTimerLocked(void* arg, grpc_error_handle error);
+    void MaybeCancelPerAttemptRecvTimer();
+
     CallData* calld_;
     RefCountedPtr<ClientChannel::LoadBalancedCall> lb_call_;
 
+    grpc_timer per_attempt_recv_timer_;
+    grpc_closure on_per_attempt_recv_timer_;
+    bool per_attempt_recv_timer_pending_ = false;
+
     // BatchData.batch.payload points to this.
     grpc_transport_stream_op_batch_payload batch_payload_;
     // For send_initial_metadata.
@@ -389,16 +418,20 @@ class RetryFilter::CallData {
     bool started_recv_trailing_metadata_ : 1;
     bool completed_recv_trailing_metadata_ : 1;
     // State for callback processing.
-    BatchData* recv_initial_metadata_ready_deferred_batch_ = nullptr;
+    RefCountedPtr<BatchData> recv_initial_metadata_ready_deferred_batch_;
     grpc_error_handle recv_initial_metadata_error_ = GRPC_ERROR_NONE;
-    BatchData* recv_message_ready_deferred_batch_ = nullptr;
+    RefCountedPtr<BatchData> recv_message_ready_deferred_batch_;
     grpc_error_handle recv_message_error_ = GRPC_ERROR_NONE;
-    BatchData* recv_trailing_metadata_internal_batch_ = nullptr;
+    RefCountedPtr<BatchData> on_complete_deferred_batch_;
+    grpc_error_handle on_complete_error_ = GRPC_ERROR_NONE;
+    RefCountedPtr<BatchData> recv_trailing_metadata_internal_batch_;
+    grpc_error_handle recv_trailing_metadata_error_ = GRPC_ERROR_NONE;
+    bool seen_recv_trailing_metadata_from_surface_ : 1;
     // NOTE: Do not move this next to the metadata bitfields above. That would
     //       save space but will also result in a data race because compiler
     //       will generate a 2 byte store which overwrites the meta-data
     //       fields upon setting this field.
-    bool retry_dispatched_ : 1;
+    bool cancelled_ : 1;
   };
 
   CallData(RetryFilter* chand, const grpc_call_element_args& args);
@@ -432,18 +465,17 @@ class RetryFilter::CallData {
   // Commits the call so that no further retry attempts will be performed.
   void RetryCommit(CallAttempt* call_attempt);
 
-  // Starts a retry after appropriate back-off.
-  void DoRetry(grpc_millis server_pushback_ms);
+  // Starts a timer to retry after appropriate back-off.
+  // If server_pushback_ms is -1, retry_backoff_ is used.
+  void StartRetryTimer(grpc_millis server_pushback_ms);
+
   static void OnRetryTimer(void* arg, grpc_error_handle error);
+  static void OnRetryTimerLocked(void* arg, grpc_error_handle error);
 
   RefCountedPtr<ClientChannel::LoadBalancedCall> CreateLoadBalancedCall();
 
   void CreateCallAttempt();
 
-  // Adds a closure to closures that will execute batch in the call combiner.
-  void AddClosureForBatch(grpc_transport_stream_op_batch* batch,
-                          CallCombinerClosureList* closures);
-
   RetryFilter* chand_;
   grpc_polling_entity* pollent_;
   RefCountedPtr<ServerRetryThrottleData> retry_throttle_data_;
@@ -465,12 +497,9 @@ class RetryFilter::CallData {
   // gets cancelled.
   RefCountedPtr<CallAttempt> call_attempt_;
 
-  // LB call used when the call is commited before any CallAttempt is
-  // created.
-  // TODO(roth): Change CallAttempt logic such that once we've committed
-  // and all cached send ops have been replayed, we move the LB call
-  // from the CallAttempt here, thus creating a fast path for the
-  // remainder of the streaming call.
+  // LB call used when we've committed to a call attempt and the retry
+  // state for that attempt is no longer needed.  This provides a fast
+  // path for long-running streaming calls that minimizes overhead.
   RefCountedPtr<ClientChannel::LoadBalancedCall> committed_call_;
 
   // When are are not yet fully committed to a particular call (i.e.,
@@ -486,23 +515,11 @@ class RetryFilter::CallData {
 
   // Retry state.
   bool retry_committed_ : 1;
-  bool last_attempt_got_server_pushback_ : 1;
+  bool retry_timer_pending_ : 1;
   int num_attempts_completed_ = 0;
-  Mutex timer_mu_;
-  Canceller* canceller_ ABSL_GUARDED_BY(timer_mu_);
-  grpc_timer retry_timer_ ABSL_GUARDED_BY(timer_mu_);
+  grpc_timer retry_timer_;
   grpc_closure retry_closure_;
 
-  // The number of batches containing send ops that are currently in-flight
-  // on any call attempt.
-  // We hold a ref to the call stack while this is non-zero, since replay
-  // batches may not complete until after all callbacks have been returned
-  // to the surface, and we need to make sure that the call is not destroyed
-  // until all of these batches have completed.
-  // Note that we actually only need to track replay batches, but it's
-  // easier to track all batches with send ops.
-  int num_in_flight_call_attempt_send_batches_ = 0;
-
   // Cached data for retrying send ops.
   // send_initial_metadata
   bool seen_send_initial_metadata_ = false;
@@ -513,7 +530,10 @@ class RetryFilter::CallData {
   // have the LB call set a value in CallAttempt and then propagate it
   // from CallAttempt to the parent call when we commit.  Otherwise, we
   // may leave this with a value for a peer other than the one we
-  // actually commit to.
+  // actually commit to.  Alternatively, maybe see if there's a way to
+  // change the surface API such that the peer isn't available until
+  // after initial metadata is received?  (Could even change the
+  // transport API to return this with the recv_initial_metadata op.)
   gpr_atm* peer_string_;
   // send_message
   // When we get a send_message op, we replace the original byte stream
@@ -522,6 +542,10 @@ class RetryFilter::CallData {
   // Note: We inline the cache for the first 3 send_message ops and use
   // dynamic allocation after that.  This number was essentially picked
   // at random; it could be changed in the future to tune performance.
+  // TODO(roth): As part of implementing hedging, we may need some
+  // synchronization here, since ByteStreamCache does not provide any
+  // synchronization, so it's not safe to have multiple
+  // CachingByteStreams read from the same ByteStreamCache concurrently.
   absl::InlinedVector<ByteStreamCache*, 3> send_messages_;
   // send_trailing_metadata
   bool seen_send_trailing_metadata_ = false;
@@ -583,51 +607,13 @@ class RetryFilter::CallData::CallStackDestructionBarrier
 };
 
 //
-// RetryFilter::CallData::Canceller
-//
-
-class RetryFilter::CallData::Canceller {
- public:
-  explicit Canceller(CallData* calld) : calld_(calld) {
-    GRPC_CALL_STACK_REF(calld_->owning_call_, "RetryCanceller");
-    GRPC_CLOSURE_INIT(&closure_, &Cancel, this, nullptr);
-    calld_->call_combiner_->SetNotifyOnCancel(&closure_);
-  }
-
- private:
-  static void Cancel(void* arg, grpc_error_handle error) {
-    auto* self = static_cast<Canceller*>(arg);
-    auto* calld = self->calld_;
-    {
-      MutexLock lock(&calld->timer_mu_);
-      if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-        gpr_log(GPR_INFO,
-                "calld=%p: cancelling retry timer: error=%s self=%p "
-                "calld->canceller_=%p",
-                calld, grpc_error_std_string(error).c_str(), self,
-                calld->canceller_);
-      }
-      if (calld->canceller_ == self && error != GRPC_ERROR_NONE) {
-        calld->canceller_ = nullptr;  // Checked by OnRetryTimer().
-        grpc_timer_cancel(&calld->retry_timer_);
-        calld->FreeAllCachedSendOpData();
-        GRPC_CALL_COMBINER_STOP(calld->call_combiner_, "Canceller");
-      }
-    }
-    GRPC_CALL_STACK_UNREF(calld->owning_call_, "RetryCanceller");
-    delete self;
-  }
-
-  CallData* calld_;
-  grpc_closure closure_;
-};
-
-//
 // RetryFilter::CallData::CallAttempt
 //
 
 RetryFilter::CallData::CallAttempt::CallAttempt(CallData* calld)
-    : calld_(calld),
+    : RefCounted(GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace) ? "CallAttempt"
+                                                           : nullptr),
+      calld_(calld),
       batch_payload_(calld->call_context_),
       started_send_initial_metadata_(false),
       completed_send_initial_metadata_(false),
@@ -637,12 +623,42 @@ RetryFilter::CallData::CallAttempt::CallAttempt(CallData* calld)
       completed_recv_initial_metadata_(false),
       started_recv_trailing_metadata_(false),
       completed_recv_trailing_metadata_(false),
-      retry_dispatched_(false) {
+      seen_recv_trailing_metadata_from_surface_(false),
+      cancelled_(false) {
   lb_call_ = calld->CreateLoadBalancedCall();
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-    gpr_log(GPR_INFO, "chand=%p calld=%p: attempt=%p: create lb_call=%p",
+    gpr_log(GPR_INFO, "chand=%p calld=%p attempt=%p: create lb_call=%p",
             calld->chand_, calld, this, lb_call_.get());
   }
+  // If per_attempt_recv_timeout is set, start a timer.
+  if (calld->retry_policy_ != nullptr &&
+      calld->retry_policy_->per_attempt_recv_timeout().has_value()) {
+    grpc_millis per_attempt_recv_deadline =
+        ExecCtx::Get()->Now() +
+        *calld->retry_policy_->per_attempt_recv_timeout();
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p attempt=%p: per-attempt timeout in %" PRId64
+              " ms",
+              calld->chand_, calld, this,
+              *calld->retry_policy_->per_attempt_recv_timeout());
+    }
+    // Schedule retry after computed delay.
+    GRPC_CLOSURE_INIT(&on_per_attempt_recv_timer_, OnPerAttemptRecvTimer, this,
+                      nullptr);
+    GRPC_CALL_STACK_REF(calld->owning_call_, "OnPerAttemptRecvTimer");
+    Ref(DEBUG_LOCATION, "OnPerAttemptRecvTimer").release();
+    per_attempt_recv_timer_pending_ = true;
+    grpc_timer_init(&per_attempt_recv_timer_, per_attempt_recv_deadline,
+                    &on_per_attempt_recv_timer_);
+  }
+}
+
+RetryFilter::CallData::CallAttempt::~CallAttempt() {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+    gpr_log(GPR_INFO, "chand=%p calld=%p attempt=%p: destroying call attempt",
+            calld_->chand_, calld_, this);
+  }
 }
 
 void RetryFilter::CallData::CallAttempt::FreeCachedSendOpDataAfterCommit() {
@@ -683,12 +699,48 @@ bool RetryFilter::CallData::CallAttempt::PendingBatchIsUnstarted(
   return false;
 }
 
+bool RetryFilter::CallData::CallAttempt::HaveSendOpsToReplay() {
+  // We don't check send_initial_metadata here, because that op will always
+  // be started as soon as it is received from the surface, so it will
+  // never need to be started at this point.
+  return started_send_message_count_ < calld_->send_messages_.size() ||
+         (calld_->seen_send_trailing_metadata_ &&
+          !started_send_trailing_metadata_);
+}
+
+void RetryFilter::CallData::CallAttempt::MaybeSwitchToFastPath() {
+  // If we're not yet committed, we can't switch yet.
+  // TODO(roth): As part of implementing hedging, this logic needs to
+  // check that *this* call attempt is the one that we've committed to.
+  // Might need to replace cancelled_ with an enum indicating whether we're
+  // in flight, cancelled, or the winning call attempt.
+  if (!calld_->retry_committed_) return;
+  // If we've already switched to fast path, there's nothing to do here.
+  if (calld_->committed_call_ != nullptr) return;
+  // If the perAttemptRecvTimeout timer is pending, we can't switch yet.
+  if (per_attempt_recv_timer_pending_) return;
+  // If there are still send ops to replay, we can't switch yet.
+  if (HaveSendOpsToReplay()) return;
+  // If we started an internal batch for recv_trailing_metadata but have not
+  // yet seen that op from the surface, we can't switch yet.
+  if (recv_trailing_metadata_internal_batch_ != nullptr) return;
+  // Switch to fast path.
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p attempt=%p: retry state no longer needed; "
+            "moving LB call to parent and unreffing the call attempt",
+            calld_->chand_, calld_, this);
+  }
+  calld_->committed_call_ = std::move(lb_call_);
+  calld_->call_attempt_.reset(DEBUG_LOCATION, "MaybeSwitchToFastPath");
+}
+
 void RetryFilter::CallData::CallAttempt::StartInternalRecvTrailingMetadata() {
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
     gpr_log(GPR_INFO,
-            "chand=%p calld=%p: call failed but recv_trailing_metadata not "
-            "started; starting it internally",
-            calld_->chand_, calld_);
+            "chand=%p calld=%p attempt=%p: call failed but "
+            "recv_trailing_metadata not started; starting it internally",
+            calld_->chand_, calld_, this);
   }
   // Create batch_data with 2 refs, since this batch will be unreffed twice:
   // once for the recv_trailing_metadata_ready callback when the batch
@@ -696,7 +748,7 @@ void RetryFilter::CallData::CallAttempt::StartInternalRecvTrailingMetadata() {
   // op from the surface.
   BatchData* batch_data = CreateBatch(2, false /* set_on_complete */);
   batch_data->AddRetriableRecvTrailingMetadataOp();
-  recv_trailing_metadata_internal_batch_ = batch_data;
+  recv_trailing_metadata_internal_batch_.reset(batch_data);
   // Note: This will release the call combiner.
   lb_call_->StartTransportStreamOpBatch(batch_data->batch());
 }
@@ -712,9 +764,9 @@ RetryFilter::CallData::CallAttempt::MaybeCreateBatchForReplay() {
       !calld_->pending_send_initial_metadata_) {
     if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
       gpr_log(GPR_INFO,
-              "chand=%p calld=%p: replaying previously completed "
+              "chand=%p calld=%p attempt=%p: replaying previously completed "
               "send_initial_metadata op",
-              calld_->chand_, calld_);
+              calld_->chand_, calld_, this);
     }
     replay_batch_data = CreateBatch(1, true /* set_on_complete */);
     replay_batch_data->AddRetriableSendInitialMetadataOp();
@@ -726,9 +778,9 @@ RetryFilter::CallData::CallAttempt::MaybeCreateBatchForReplay() {
       !calld_->pending_send_message_) {
     if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
       gpr_log(GPR_INFO,
-              "chand=%p calld=%p: replaying previously completed "
+              "chand=%p calld=%p attempt=%p: replaying previously completed "
               "send_message op",
-              calld_->chand_, calld_);
+              calld_->chand_, calld_, this);
     }
     if (replay_batch_data == nullptr) {
       replay_batch_data = CreateBatch(1, true /* set_on_complete */);
@@ -745,9 +797,9 @@ RetryFilter::CallData::CallAttempt::MaybeCreateBatchForReplay() {
       !calld_->pending_send_trailing_metadata_) {
     if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
       gpr_log(GPR_INFO,
-              "chand=%p calld=%p: replaying previously completed "
+              "chand=%p calld=%p attempt=%p: replaying previously completed "
               "send_trailing_metadata op",
-              calld_->chand_, calld_);
+              calld_->chand_, calld_, this);
     }
     if (replay_batch_data == nullptr) {
       replay_batch_data = CreateBatch(1, true /* set_on_complete */);
@@ -757,6 +809,33 @@ RetryFilter::CallData::CallAttempt::MaybeCreateBatchForReplay() {
   return replay_batch_data;
 }
 
+namespace {
+
+void StartBatchInCallCombiner(void* arg, grpc_error_handle /*ignored*/) {
+  grpc_transport_stream_op_batch* batch =
+      static_cast<grpc_transport_stream_op_batch*>(arg);
+  auto* lb_call = static_cast<ClientChannel::LoadBalancedCall*>(
+      batch->handler_private.extra_arg);
+  // Note: This will release the call combiner.
+  lb_call->StartTransportStreamOpBatch(batch);
+}
+
+}  // namespace
+
+void RetryFilter::CallData::CallAttempt::AddClosureForBatch(
+    grpc_transport_stream_op_batch* batch, const char* reason,
+    CallCombinerClosureList* closures) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+    gpr_log(GPR_INFO, "chand=%p calld=%p attempt=%p: adding batch (%s): %s",
+            calld_->chand_, calld_, this, reason,
+            grpc_transport_stream_op_batch_string(batch).c_str());
+  }
+  batch->handler_private.extra_arg = lb_call_.get();
+  GRPC_CLOSURE_INIT(&batch->handler_private.closure, StartBatchInCallCombiner,
+                    batch, grpc_schedule_on_exec_ctx);
+  closures->Add(&batch->handler_private.closure, GRPC_ERROR_NONE, reason);
+}
+
 void RetryFilter::CallData::CallAttempt::AddBatchesForPendingBatches(
     CallCombinerClosureList* closures) {
   for (size_t i = 0; i < GPR_ARRAY_SIZE(calld_->pending_batches_); ++i) {
@@ -797,6 +876,7 @@ void RetryFilter::CallData::CallAttempt::AddBatchesForPendingBatches(
       continue;
     }
     if (batch->recv_trailing_metadata && started_recv_trailing_metadata_) {
+      seen_recv_trailing_metadata_from_surface_ = true;
       // If we previously completed a recv_trailing_metadata op
       // initiated by StartInternalRecvTrailingMetadata(), use the
       // result of that instead of trying to re-start this op.
@@ -806,21 +886,30 @@ void RetryFilter::CallData::CallAttempt::AddBatchesForPendingBatches(
         // the application.  Otherwise, just unref the internally started
         // batch, since we'll propagate the completion when it completes.
         if (completed_recv_trailing_metadata_) {
-          // Batches containing recv_trailing_metadata always succeed.
           closures->Add(
-              &recv_trailing_metadata_ready_, GRPC_ERROR_NONE,
+              &recv_trailing_metadata_ready_, recv_trailing_metadata_error_,
               "re-executing recv_trailing_metadata_ready to propagate "
               "internally triggered result");
+          // Ref will be released by callback.
+          recv_trailing_metadata_internal_batch_.release();
         } else {
-          recv_trailing_metadata_internal_batch_->Unref();
+          recv_trailing_metadata_internal_batch_.reset(
+              DEBUG_LOCATION,
+              "internally started recv_trailing_metadata batch pending and "
+              "recv_trailing_metadata started from surface");
+          GRPC_ERROR_UNREF(recv_trailing_metadata_error_);
         }
-        recv_trailing_metadata_internal_batch_ = nullptr;
+        recv_trailing_metadata_error_ = GRPC_ERROR_NONE;
       }
       continue;
     }
-    // If we're already committed, just send the batch as-is.
-    if (calld_->retry_committed_) {
-      calld_->AddClosureForBatch(batch, closures);
+    // If we're already committed and these send ops aren't cached, just send
+    // the batch as-is.
+    if (calld_->retry_committed_ && !pending->send_ops_cached) {
+      AddClosureForBatch(
+          batch,
+          "start non-replayable pending batch on call attempt after commit",
+          closures);
       calld_->PendingBatchClear(pending);
       continue;
     }
@@ -831,7 +920,7 @@ void RetryFilter::CallData::CallAttempt::AddBatchesForPendingBatches(
     const int num_callbacks = has_send_ops + batch->recv_initial_metadata +
                               batch->recv_message +
                               batch->recv_trailing_metadata;
-    CallAttempt::BatchData* batch_data =
+    BatchData* batch_data =
         CreateBatch(num_callbacks, has_send_ops /* set_on_complete */);
     // Cache send ops if needed.
     calld_->MaybeCacheSendOpsForBatch(pending);
@@ -861,16 +950,9 @@ void RetryFilter::CallData::CallAttempt::AddBatchesForPendingBatches(
     if (batch->recv_trailing_metadata) {
       batch_data->AddRetriableRecvTrailingMetadataOp();
     }
-    calld_->AddClosureForBatch(batch_data->batch(), closures);
-    // Track number of in-flight send batches.
-    // If this is the first one, take a ref to the call stack.
-    if (batch->send_initial_metadata || batch->send_message ||
-        batch->send_trailing_metadata) {
-      if (calld_->num_in_flight_call_attempt_send_batches_ == 0) {
-        GRPC_CALL_STACK_REF(calld_->owning_call_, "retriable_send_batches");
-      }
-      ++calld_->num_in_flight_call_attempt_send_batches_;
-    }
+    AddClosureForBatch(batch_data->batch(),
+                       "start replayable pending batch on call attempt",
+                       closures);
   }
 }
 
@@ -879,13 +961,8 @@ void RetryFilter::CallData::CallAttempt::AddRetriableBatches(
   // Replay previously-returned send_* ops if needed.
   BatchData* replay_batch_data = MaybeCreateBatchForReplay();
   if (replay_batch_data != nullptr) {
-    calld_->AddClosureForBatch(replay_batch_data->batch(), closures);
-    // Track number of pending send batches.
-    // If this is the first one, take a ref to the call stack.
-    if (calld_->num_in_flight_call_attempt_send_batches_ == 0) {
-      GRPC_CALL_STACK_REF(calld_->owning_call_, "retriable_send_batches");
-    }
-    ++calld_->num_in_flight_call_attempt_send_batches_;
+    AddClosureForBatch(replay_batch_data->batch(),
+                       "start replay batch on call attempt", closures);
   }
   // Now add pending batches.
   AddBatchesForPendingBatches(closures);
@@ -893,8 +970,9 @@ void RetryFilter::CallData::CallAttempt::AddRetriableBatches(
 
 void RetryFilter::CallData::CallAttempt::StartRetriableBatches() {
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-    gpr_log(GPR_INFO, "chand=%p calld=%p: constructing retriable batches",
-            calld_->chand_, calld_);
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p attempt=%p: constructing retriable batches",
+            calld_->chand_, calld_, this);
   }
   // Construct list of closures to execute, one for each pending batch.
   CallCombinerClosureList closures;
@@ -903,28 +981,235 @@ void RetryFilter::CallData::CallAttempt::StartRetriableBatches() {
   // Start batches on LB call.
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
     gpr_log(GPR_INFO,
-            "chand=%p calld=%p: starting %" PRIuPTR
+            "chand=%p calld=%p attempt=%p: starting %" PRIuPTR
             " retriable batches on lb_call=%p",
-            calld_->chand_, calld_, closures.size(), lb_call());
+            calld_->chand_, calld_, this, closures.size(), lb_call_.get());
   }
   closures.RunClosures(calld_->call_combiner_);
 }
 
+void RetryFilter::CallData::CallAttempt::CancelFromSurface(
+    grpc_transport_stream_op_batch* cancel_batch) {
+  MaybeCancelPerAttemptRecvTimer();
+  // Propagate cancellation to LB call.
+  lb_call_->StartTransportStreamOpBatch(cancel_batch);
+}
+
+bool RetryFilter::CallData::CallAttempt::ShouldRetry(
+    absl::optional<grpc_status_code> status, bool is_lb_drop,
+    grpc_mdelem* server_pushback_md, grpc_millis* server_pushback_ms) {
+  // LB drops always inhibit retries.
+  if (is_lb_drop) return false;
+  // TODO(roth): Handle transparent retries here.
+  // If no retry policy, don't retry.
+  if (calld_->retry_policy_ == nullptr) return false;
+  // Check status.
+  if (status.has_value()) {
+    if (GPR_LIKELY(*status == GRPC_STATUS_OK)) {
+      if (calld_->retry_throttle_data_ != nullptr) {
+        calld_->retry_throttle_data_->RecordSuccess();
+      }
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+        gpr_log(GPR_INFO, "chand=%p calld=%p attempt=%p: call succeeded",
+                calld_->chand_, calld_, this);
+      }
+      return false;
+    }
+    // Status is not OK.  Check whether the status is retryable.
+    if (!calld_->retry_policy_->retryable_status_codes().Contains(*status)) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+        gpr_log(GPR_INFO,
+                "chand=%p calld=%p attempt=%p: status %s not configured as "
+                "retryable",
+                calld_->chand_, calld_, this,
+                grpc_status_code_to_string(*status));
+      }
+      return false;
+    }
+  }
+  // Record the failure and check whether retries are throttled.
+  // Note that it's important for this check to come after the status
+  // code check above, since we should only record failures whose statuses
+  // match the configured retryable status codes, so that we don't count
+  // things like failures due to malformed requests (INVALID_ARGUMENT).
+  // Conversely, it's important for this to come before the remaining
+  // checks, so that we don't fail to record failures due to other factors.
+  if (calld_->retry_throttle_data_ != nullptr &&
+      !calld_->retry_throttle_data_->RecordFailure()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p attempt=%p: retries throttled",
+              calld_->chand_, calld_, this);
+    }
+    return false;
+  }
+  // Check whether the call is committed.
+  if (calld_->retry_committed_) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p attempt=%p: retries already committed",
+              calld_->chand_, calld_, this);
+    }
+    return false;
+  }
+  // Check whether we have retries remaining.
+  ++calld_->num_attempts_completed_;
+  if (calld_->num_attempts_completed_ >=
+      calld_->retry_policy_->max_attempts()) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+      gpr_log(
+          GPR_INFO, "chand=%p calld=%p attempt=%p: exceeded %d retry attempts",
+          calld_->chand_, calld_, this, calld_->retry_policy_->max_attempts());
+    }
+    return false;
+  }
+  // Check server push-back.
+  if (server_pushback_md != nullptr) {
+    // If the value is "-1" or any other unparseable string, we do not retry.
+    uint32_t ms;
+    if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(*server_pushback_md), &ms)) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+        gpr_log(GPR_INFO,
+                "chand=%p calld=%p attempt=%p: not retrying due to server "
+                "push-back",
+                calld_->chand_, calld_, this);
+      }
+      return false;
+    } else {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+        gpr_log(
+            GPR_INFO,
+            "chand=%p calld=%p attempt=%p: server push-back: retry in %u ms",
+            calld_->chand_, calld_, this, ms);
+      }
+      *server_pushback_ms = static_cast<grpc_millis>(ms);
+    }
+  }
+  // We should retry.
+  return true;
+}
+
+void RetryFilter::CallData::CallAttempt::Cancel(
+    CallCombinerClosureList* closures) {
+  // Record that this attempt has been cancelled.
+  cancelled_ = true;
+  // Unref batches for deferred completion callbacks that will now never
+  // be invoked.
+  if (started_recv_trailing_metadata_ &&
+      !seen_recv_trailing_metadata_from_surface_) {
+    recv_trailing_metadata_internal_batch_.reset(
+        DEBUG_LOCATION,
+        "internal recv_trailing_metadata completed before that op was "
+        "started from the surface");
+  }
+  GRPC_ERROR_UNREF(recv_trailing_metadata_error_);
+  recv_trailing_metadata_error_ = GRPC_ERROR_NONE;
+  recv_initial_metadata_ready_deferred_batch_.reset(
+      DEBUG_LOCATION,
+      "unref deferred recv_initial_metadata_ready batch due to retry");
+  GRPC_ERROR_UNREF(recv_initial_metadata_error_);
+  recv_initial_metadata_error_ = GRPC_ERROR_NONE;
+  recv_message_ready_deferred_batch_.reset(
+      DEBUG_LOCATION, "unref deferred recv_message_ready batch due to retry");
+  GRPC_ERROR_UNREF(recv_message_error_);
+  recv_message_error_ = GRPC_ERROR_NONE;
+  on_complete_deferred_batch_.reset(
+      DEBUG_LOCATION, "unref deferred on_complete batch due to retry");
+  GRPC_ERROR_UNREF(on_complete_error_);
+  on_complete_error_ = GRPC_ERROR_NONE;
+  // Start a cancellation op on this call attempt to make sure the
+  // transport knows that this call should be cleaned up, even if it
+  // hasn't received any ops.
+  BatchData* cancel_batch_data = CreateBatch(1, /*set_on_complete=*/true);
+  cancel_batch_data->AddCancelStreamOp();
+  AddClosureForBatch(cancel_batch_data->batch(),
+                     "start cancellation batch on call attempt", closures);
+}
+
+void RetryFilter::CallData::CallAttempt::OnPerAttemptRecvTimer(
+    void* arg, grpc_error_handle error) {
+  auto* call_attempt = static_cast<CallAttempt*>(arg);
+  GRPC_CLOSURE_INIT(&call_attempt->on_per_attempt_recv_timer_,
+                    OnPerAttemptRecvTimerLocked, call_attempt, nullptr);
+  GRPC_CALL_COMBINER_START(call_attempt->calld_->call_combiner_,
+                           &call_attempt->on_per_attempt_recv_timer_,
+                           GRPC_ERROR_REF(error), "per-attempt timer fired");
+}
+
+void RetryFilter::CallData::CallAttempt::OnPerAttemptRecvTimerLocked(
+    void* arg, grpc_error_handle error) {
+  auto* call_attempt = static_cast<CallAttempt*>(arg);
+  auto* calld = call_attempt->calld_;
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p attempt=%p: perAttemptRecvTimeout timer fired: "
+            "error=%s, per_attempt_recv_timer_pending_=%d",
+            calld->chand_, calld, call_attempt,
+            grpc_error_std_string(error).c_str(),
+            call_attempt->per_attempt_recv_timer_pending_);
+  }
+  CallCombinerClosureList closures;
+  if (error == GRPC_ERROR_NONE &&
+      call_attempt->per_attempt_recv_timer_pending_) {
+    call_attempt->per_attempt_recv_timer_pending_ = false;
+    // Cancel this attempt.
+    // TODO(roth): When implementing hedging, we should not cancel the
+    // current attempt.
+    call_attempt->Cancel(&closures);
+    // Check whether we should retry.
+    if (call_attempt->ShouldRetry(
+            /*status=*/absl::nullopt, /*is_lb_drop=*/false,
+            /*server_pushback_md=*/nullptr, /*server_pushback_ms=*/nullptr)) {
+      // We are retrying.  Start backoff timer.
+      calld->StartRetryTimer(/*server_pushback_ms=*/-1);
+    } else {
+      // Not retrying, so commit the call.
+      calld->RetryCommit(call_attempt);
+      // If retry state is no longer needed, switch to fast path for
+      // subsequent batches.
+      call_attempt->MaybeSwitchToFastPath();
+    }
+  }
+  closures.RunClosures(calld->call_combiner_);
+  call_attempt->Unref(DEBUG_LOCATION, "OnPerAttemptRecvTimer");
+  GRPC_CALL_STACK_UNREF(calld->owning_call_, "OnPerAttemptRecvTimer");
+}
+
+void RetryFilter::CallData::CallAttempt::MaybeCancelPerAttemptRecvTimer() {
+  if (per_attempt_recv_timer_pending_) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p attempt=%p: cancelling "
+              "perAttemptRecvTimeout timer",
+              calld_->chand_, calld_, this);
+    }
+    per_attempt_recv_timer_pending_ = false;
+    grpc_timer_cancel(&per_attempt_recv_timer_);
+  }
+}
+
 //
 // RetryFilter::CallData::CallAttempt::BatchData
 //
 
 RetryFilter::CallData::CallAttempt::BatchData::BatchData(
     RefCountedPtr<CallAttempt> attempt, int refcount, bool set_on_complete)
-    : RefCounted(nullptr, refcount), call_attempt_(std::move(attempt)) {
-  // TODO(roth): Consider holding this ref on the call stack in
-  // CallAttempt instead of here in BatchData.  This would eliminate the
-  // need for CallData::num_in_flight_call_attempt_send_batches_.
-  // But it would require having a way to unref CallAttempt when it is
-  // no longer needed (i.e., when the call is committed and all cached
-  // send ops have been replayed and the LB call is moved into
-  // CallData::committed_call_).
-  GRPC_CALL_STACK_REF(call_attempt_->calld_->owning_call_, "CallAttempt");
+    : RefCounted(
+          GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace) ? "BatchData" : nullptr,
+          refcount),
+      call_attempt_(std::move(attempt)) {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+    gpr_log(GPR_INFO, "chand=%p calld=%p attempt=%p: creating batch %p",
+            call_attempt_->calld_->chand_, call_attempt_->calld_,
+            call_attempt_.get(), this);
+  }
+  // We hold a ref to the call stack for every batch sent on a call attempt.
+  // This is because some batches on the call attempt may not complete
+  // until after all of the batches are completed at the surface (because
+  // each batch that is pending at the surface holds a ref).  This
+  // can happen for replayed send ops, and it can happen for
+  // recv_initial_metadata and recv_message ops on a call attempt that has
+  // been abandoned.
+  GRPC_CALL_STACK_REF(call_attempt_->calld_->owning_call_, "Retry BatchData");
   batch_.payload = &call_attempt_->batch_payload_;
   if (set_on_complete) {
     GRPC_CLOSURE_INIT(&on_complete_, OnComplete, this,
@@ -934,6 +1219,11 @@ RetryFilter::CallData::CallAttempt::BatchData::BatchData(
 }
 
 RetryFilter::CallData::CallAttempt::BatchData::~BatchData() {
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+    gpr_log(GPR_INFO, "chand=%p calld=%p attempt=%p: destroying batch %p",
+            call_attempt_->calld_->chand_, call_attempt_->calld_,
+            call_attempt_.get(), this);
+  }
   if (batch_.send_initial_metadata) {
     grpc_metadata_batch_destroy(&call_attempt_->send_initial_metadata_);
   }
@@ -946,7 +1236,8 @@ RetryFilter::CallData::CallAttempt::BatchData::~BatchData() {
   if (batch_.recv_trailing_metadata) {
     grpc_metadata_batch_destroy(&call_attempt_->recv_trailing_metadata_);
   }
-  GRPC_CALL_STACK_UNREF(call_attempt_->calld_->owning_call_, "CallAttempt");
+  GRPC_CALL_STACK_UNREF(call_attempt_->calld_->owning_call_, "Retry BatchData");
+  call_attempt_.reset(DEBUG_LOCATION, "~BatchData");
 }
 
 void RetryFilter::CallData::CallAttempt::BatchData::
@@ -968,108 +1259,13 @@ void RetryFilter::CallData::CallAttempt::BatchData::
   }
 }
 
-bool RetryFilter::CallData::CallAttempt::BatchData::MaybeRetry(
-    grpc_status_code status, grpc_mdelem* server_pushback_md, bool is_lb_drop) {
-  auto* calld = call_attempt_->calld_;
-  // LB drops always inhibit retries.
-  if (is_lb_drop) return false;
-  // Get retry policy.
-  if (calld->retry_policy_ == nullptr) return false;
-  // If we've already dispatched a retry from this call, return true.
-  // This catches the case where the batch has multiple callbacks
-  // (i.e., it includes either recv_message or recv_initial_metadata).
-  if (call_attempt_->retry_dispatched_) {
-    if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-      gpr_log(GPR_INFO, "chand=%p calld=%p: retry already dispatched",
-              calld->chand_, calld);
-    }
-    return true;
-  }
-  // Check status.
-  if (GPR_LIKELY(status == GRPC_STATUS_OK)) {
-    if (calld->retry_throttle_data_ != nullptr) {
-      calld->retry_throttle_data_->RecordSuccess();
-    }
-    if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-      gpr_log(GPR_INFO, "chand=%p calld=%p: call succeeded", calld->chand_,
-              calld);
-    }
-    return false;
-  }
-  // Status is not OK.  Check whether the status is retryable.
-  if (!calld->retry_policy_->retryable_status_codes().Contains(status)) {
-    if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-      gpr_log(GPR_INFO,
-              "chand=%p calld=%p: status %s not configured as retryable",
-              calld->chand_, calld, grpc_status_code_to_string(status));
-    }
-    return false;
-  }
-  // Record the failure and check whether retries are throttled.
-  // Note that it's important for this check to come after the status
-  // code check above, since we should only record failures whose statuses
-  // match the configured retryable status codes, so that we don't count
-  // things like failures due to malformed requests (INVALID_ARGUMENT).
-  // Conversely, it's important for this to come before the remaining
-  // checks, so that we don't fail to record failures due to other factors.
-  if (calld->retry_throttle_data_ != nullptr &&
-      !calld->retry_throttle_data_->RecordFailure()) {
-    if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-      gpr_log(GPR_INFO, "chand=%p calld=%p: retries throttled", calld->chand_,
-              calld);
-    }
-    return false;
-  }
-  // Check whether the call is committed.
-  if (calld->retry_committed_) {
-    if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-      gpr_log(GPR_INFO, "chand=%p calld=%p: retries already committed",
-              calld->chand_, calld);
-    }
-    return false;
-  }
-  // Check whether we have retries remaining.
-  ++calld->num_attempts_completed_;
-  if (calld->num_attempts_completed_ >= calld->retry_policy_->max_attempts()) {
-    if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-      gpr_log(GPR_INFO, "chand=%p calld=%p: exceeded %d retry attempts",
-              calld->chand_, calld, calld->retry_policy_->max_attempts());
-    }
-    return false;
-  }
-  // Check server push-back.
-  grpc_millis server_pushback_ms = -1;
-  if (server_pushback_md != nullptr) {
-    // If the value is "-1" or any other unparseable string, we do not retry.
-    uint32_t ms;
-    if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(*server_pushback_md), &ms)) {
-      if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-        gpr_log(GPR_INFO,
-                "chand=%p calld=%p: not retrying due to server push-back",
-                calld->chand_, calld);
-      }
-      return false;
-    } else {
-      if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-        gpr_log(GPR_INFO, "chand=%p calld=%p: server push-back: retry in %u ms",
-                calld->chand_, calld, ms);
-      }
-      server_pushback_ms = static_cast<grpc_millis>(ms);
-    }
-  }
-  // Do retry.
-  call_attempt_->retry_dispatched_ = true;
-  calld->DoRetry(server_pushback_ms);
-  return true;
-}
-
 //
 // recv_initial_metadata callback handling
 //
 
 void RetryFilter::CallData::CallAttempt::BatchData::
     InvokeRecvInitialMetadataCallback(void* arg, grpc_error_handle error) {
-  auto* batch_data = static_cast<CallAttempt::BatchData*>(arg);
+  auto* batch_data = static_cast<BatchData*>(arg);
   auto* call_attempt = batch_data->call_attempt_.get();
   // Find pending batch.
   PendingBatch* pending = call_attempt->calld_->PendingBatchFind(
@@ -1101,24 +1297,27 @@ void RetryFilter::CallData::CallAttempt::BatchData::
 
 void RetryFilter::CallData::CallAttempt::BatchData::RecvInitialMetadataReady(
     void* arg, grpc_error_handle error) {
-  CallAttempt::BatchData* batch_data =
-      static_cast<CallAttempt::BatchData*>(arg);
+  RefCountedPtr<BatchData> batch_data(static_cast<BatchData*>(arg));
   CallAttempt* call_attempt = batch_data->call_attempt_.get();
   CallData* calld = call_attempt->calld_;
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
     gpr_log(GPR_INFO,
-            "chand=%p calld=%p: got recv_initial_metadata_ready, error=%s",
-            calld->chand_, calld, grpc_error_std_string(error).c_str());
+            "chand=%p calld=%p attempt=%p: got recv_initial_metadata_ready, "
+            "error=%s",
+            calld->chand_, calld, call_attempt,
+            grpc_error_std_string(error).c_str());
   }
   call_attempt->completed_recv_initial_metadata_ = true;
-  // If a retry was already dispatched, then we're not going to use the
+  // If this attempt has been cancelled, then we're not going to use the
   // result of this recv_initial_metadata op, so do nothing.
-  if (call_attempt->retry_dispatched_) {
-    GRPC_CALL_COMBINER_STOP(
-        calld->call_combiner_,
-        "recv_initial_metadata_ready after retry dispatched");
+  if (call_attempt->cancelled_) {
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner_,
+                            "recv_initial_metadata_ready after cancellation");
     return;
   }
+  // Cancel per-attempt recv timer, if any.
+  call_attempt->MaybeCancelPerAttemptRecvTimer();
+  // If we're not committed, check the response to see if we need to commit.
   if (!calld->retry_committed_) {
     // If we got an error or a Trailers-Only response and have not yet gotten
     // the recv_trailing_metadata_ready callback, then defer propagating this
@@ -1129,11 +1328,12 @@ void RetryFilter::CallData::CallAttempt::BatchData::RecvInitialMetadataReady(
                      !call_attempt->completed_recv_trailing_metadata_)) {
       if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
         gpr_log(GPR_INFO,
-                "chand=%p calld=%p: deferring recv_initial_metadata_ready "
-                "(Trailers-Only)",
-                calld->chand_, calld);
+                "chand=%p calld=%p attempt=%p: deferring "
+                "recv_initial_metadata_ready (Trailers-Only)",
+                calld->chand_, calld, call_attempt);
       }
-      call_attempt->recv_initial_metadata_ready_deferred_batch_ = batch_data;
+      call_attempt->recv_initial_metadata_ready_deferred_batch_ =
+          std::move(batch_data);
       call_attempt->recv_initial_metadata_error_ = GRPC_ERROR_REF(error);
       if (!call_attempt->started_recv_trailing_metadata_) {
         // recv_trailing_metadata not yet started by application; start it
@@ -1148,10 +1348,13 @@ void RetryFilter::CallData::CallAttempt::BatchData::RecvInitialMetadataReady(
     }
     // Received valid initial metadata, so commit the call.
     calld->RetryCommit(call_attempt);
+    // If retry state is no longer needed, switch to fast path for
+    // subsequent batches.
+    call_attempt->MaybeSwitchToFastPath();
   }
   // Invoke the callback to return the result to the surface.
   // Manually invoking a callback function; it does not take ownership of error.
-  InvokeRecvInitialMetadataCallback(batch_data, error);
+  InvokeRecvInitialMetadataCallback(batch_data.release(), error);
 }
 
 //
@@ -1160,8 +1363,7 @@ void RetryFilter::CallData::CallAttempt::BatchData::RecvInitialMetadataReady(
 
 void RetryFilter::CallData::CallAttempt::BatchData::InvokeRecvMessageCallback(
     void* arg, grpc_error_handle error) {
-  CallAttempt::BatchData* batch_data =
-      static_cast<CallAttempt::BatchData*>(arg);
+  auto* batch_data = static_cast<BatchData*>(arg);
   CallAttempt* call_attempt = batch_data->call_attempt_.get();
   CallData* calld = call_attempt->calld_;
   // Find pending op.
@@ -1189,22 +1391,26 @@ void RetryFilter::CallData::CallAttempt::BatchData::InvokeRecvMessageCallback(
 
 void RetryFilter::CallData::CallAttempt::BatchData::RecvMessageReady(
     void* arg, grpc_error_handle error) {
-  CallAttempt::BatchData* batch_data =
-      static_cast<CallAttempt::BatchData*>(arg);
+  RefCountedPtr<BatchData> batch_data(static_cast<BatchData*>(arg));
   CallAttempt* call_attempt = batch_data->call_attempt_.get();
   CallData* calld = call_attempt->calld_;
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-    gpr_log(GPR_INFO, "chand=%p calld=%p: got recv_message_ready, error=%s",
-            calld->chand_, calld, grpc_error_std_string(error).c_str());
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p attempt=%p: got recv_message_ready, error=%s",
+            calld->chand_, calld, call_attempt,
+            grpc_error_std_string(error).c_str());
   }
   ++call_attempt->completed_recv_message_count_;
-  // If a retry was already dispatched, then we're not going to use the
+  // If this attempt has been cancelled, then we're not going to use the
   // result of this recv_message op, so do nothing.
-  if (call_attempt->retry_dispatched_) {
+  if (call_attempt->cancelled_) {
     GRPC_CALL_COMBINER_STOP(calld->call_combiner_,
-                            "recv_message_ready after retry dispatched");
+                            "recv_message_ready after cancellation");
     return;
   }
+  // Cancel per-attempt recv timer, if any.
+  call_attempt->MaybeCancelPerAttemptRecvTimer();
+  // If we're not committed, check the response to see if we need to commit.
   if (!calld->retry_committed_) {
     // If we got an error or the payload was nullptr and we have not yet gotten
     // the recv_trailing_metadata_ready callback, then defer propagating this
@@ -1215,11 +1421,11 @@ void RetryFilter::CallData::CallAttempt::BatchData::RecvMessageReady(
                      !call_attempt->completed_recv_trailing_metadata_)) {
       if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
         gpr_log(GPR_INFO,
-                "chand=%p calld=%p: deferring recv_message_ready (nullptr "
-                "message and recv_trailing_metadata pending)",
-                calld->chand_, calld);
+                "chand=%p calld=%p attempt=%p: deferring recv_message_ready "
+                "(nullptr message and recv_trailing_metadata pending)",
+                calld->chand_, calld, call_attempt);
       }
-      call_attempt->recv_message_ready_deferred_batch_ = batch_data;
+      call_attempt->recv_message_ready_deferred_batch_ = std::move(batch_data);
       call_attempt->recv_message_error_ = GRPC_ERROR_REF(error);
       if (!call_attempt->started_recv_trailing_metadata_) {
         // recv_trailing_metadata not yet started by application; start it
@@ -1233,10 +1439,13 @@ void RetryFilter::CallData::CallAttempt::BatchData::RecvMessageReady(
     }
     // Received a valid message, so commit the call.
     calld->RetryCommit(call_attempt);
+    // If retry state is no longer needed, switch to fast path for
+    // subsequent batches.
+    call_attempt->MaybeSwitchToFastPath();
   }
   // Invoke the callback to return the result to the surface.
   // Manually invoking a callback function; it does not take ownership of error.
-  InvokeRecvMessageCallback(batch_data, error);
+  InvokeRecvMessageCallback(batch_data.release(), error);
 }
 
 //
@@ -1285,7 +1494,7 @@ void RetryFilter::CallData::CallAttempt::BatchData::
   // If we generated the recv_trailing_metadata op internally via
   // StartInternalRecvTrailingMetadata(), then there will be no pending batch.
   if (pending == nullptr) {
-    GRPC_ERROR_UNREF(error);
+    call_attempt_->recv_trailing_metadata_error_ = error;
     return;
   }
   // Return metadata.
@@ -1303,7 +1512,8 @@ void RetryFilter::CallData::CallAttempt::BatchData::
 }
 
 void RetryFilter::CallData::CallAttempt::BatchData::
-    AddClosuresForDeferredRecvCallbacks(CallCombinerClosureList* closures) {
+    AddClosuresForDeferredCompletionCallbacks(
+        CallCombinerClosureList* closures) {
   if (batch_.recv_trailing_metadata) {
     // Add closure for deferred recv_initial_metadata_ready.
     if (GPR_UNLIKELY(
@@ -1312,24 +1522,28 @@ void RetryFilter::CallData::CallAttempt::BatchData::
       GRPC_CLOSURE_INIT(
           &call_attempt_->recv_initial_metadata_ready_,
           InvokeRecvInitialMetadataCallback,
-          call_attempt_->recv_initial_metadata_ready_deferred_batch_,
+          call_attempt_->recv_initial_metadata_ready_deferred_batch_.release(),
           grpc_schedule_on_exec_ctx);
       closures->Add(&call_attempt_->recv_initial_metadata_ready_,
                     call_attempt_->recv_initial_metadata_error_,
                     "resuming recv_initial_metadata_ready");
-      call_attempt_->recv_initial_metadata_ready_deferred_batch_ = nullptr;
     }
     // Add closure for deferred recv_message_ready.
     if (GPR_UNLIKELY(call_attempt_->recv_message_ready_deferred_batch_ !=
                      nullptr)) {
-      GRPC_CLOSURE_INIT(&call_attempt_->recv_message_ready_,
-                        InvokeRecvMessageCallback,
-                        call_attempt_->recv_message_ready_deferred_batch_,
-                        grpc_schedule_on_exec_ctx);
+      GRPC_CLOSURE_INIT(
+          &call_attempt_->recv_message_ready_, InvokeRecvMessageCallback,
+          call_attempt_->recv_message_ready_deferred_batch_.release(),
+          grpc_schedule_on_exec_ctx);
       closures->Add(&call_attempt_->recv_message_ready_,
                     call_attempt_->recv_message_error_,
                     "resuming recv_message_ready");
-      call_attempt_->recv_message_ready_deferred_batch_ = nullptr;
+    }
+    // Add closure for deferred on_complete.
+    if (GPR_UNLIKELY(call_attempt_->on_complete_deferred_batch_ != nullptr)) {
+      closures->Add(&call_attempt_->on_complete_deferred_batch_->on_complete_,
+                    call_attempt_->on_complete_error_, "resuming on_complete");
+      call_attempt_->on_complete_deferred_batch_.release();
     }
   }
 }
@@ -1343,9 +1557,9 @@ void RetryFilter::CallData::CallAttempt::BatchData::
     if (call_attempt_->PendingBatchIsUnstarted(pending)) {
       if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
         gpr_log(GPR_INFO,
-                "chand=%p calld=%p: failing unstarted pending batch at "
-                "index %" PRIuPTR,
-                calld->chand_, calld, i);
+                "chand=%p calld=%p attempt=%p: failing unstarted pending "
+                "batch at index %" PRIuPTR,
+                calld->chand_, calld, call_attempt_.get(), i);
       }
       closures->Add(pending->batch->on_complete, GRPC_ERROR_REF(error),
                     "failing on_complete for pending batch");
@@ -1362,31 +1576,38 @@ void RetryFilter::CallData::CallAttempt::BatchData::RunClosuresForCompletedCall(
   CallCombinerClosureList closures;
   // First, add closure for recv_trailing_metadata_ready.
   AddClosureForRecvTrailingMetadataReady(GRPC_ERROR_REF(error), &closures);
-  // If there are deferred recv_initial_metadata_ready or recv_message_ready
-  // callbacks, add them to closures.
-  AddClosuresForDeferredRecvCallbacks(&closures);
+  // If there are deferred batch completion callbacks, add them to closures.
+  AddClosuresForDeferredCompletionCallbacks(&closures);
   // Add closures to fail any pending batches that have not yet been started.
   AddClosuresToFailUnstartedPendingBatches(GRPC_ERROR_REF(error), &closures);
   // Schedule all of the closures identified above.
   // Note: This will release the call combiner.
   closures.RunClosures(call_attempt_->calld_->call_combiner_);
-  // Don't need batch_data anymore.
-  Unref();
   GRPC_ERROR_UNREF(error);
 }
 
 void RetryFilter::CallData::CallAttempt::BatchData::RecvTrailingMetadataReady(
     void* arg, grpc_error_handle error) {
-  CallAttempt::BatchData* batch_data =
-      static_cast<CallAttempt::BatchData*>(arg);
+  RefCountedPtr<BatchData> batch_data(static_cast<BatchData*>(arg));
   CallAttempt* call_attempt = batch_data->call_attempt_.get();
   CallData* calld = call_attempt->calld_;
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
     gpr_log(GPR_INFO,
-            "chand=%p calld=%p: got recv_trailing_metadata_ready, error=%s",
-            calld->chand_, calld, grpc_error_std_string(error).c_str());
+            "chand=%p calld=%p attempt=%p: got recv_trailing_metadata_ready, "
+            "error=%s",
+            calld->chand_, calld, call_attempt,
+            grpc_error_std_string(error).c_str());
   }
   call_attempt->completed_recv_trailing_metadata_ = true;
+  // If this attempt has been cancelled, then we're not going to use the
+  // result of this recv_trailing_metadata op, so do nothing.
+  if (call_attempt->cancelled_) {
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner_,
+                            "recv_trailing_metadata_ready after cancellation");
+    return;
+  }
+  // Cancel per-attempt recv timer, if any.
+  call_attempt->MaybeCancelPerAttemptRecvTimer();
   // Get the call's status and check for server pushback metadata.
   grpc_status_code status = GRPC_STATUS_OK;
   grpc_mdelem* server_pushback_md = nullptr;
@@ -1397,26 +1618,29 @@ void RetryFilter::CallData::CallAttempt::BatchData::RecvTrailingMetadataReady(
                 &server_pushback_md, &is_lb_drop);
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
     gpr_log(
-        GPR_INFO, "chand=%p calld=%p: call finished, status=%s is_lb_drop=%d",
-        calld->chand_, calld, grpc_status_code_to_string(status), is_lb_drop);
+        GPR_INFO,
+        "chand=%p calld=%p attempt=%p: call finished, status=%s is_lb_drop=%d",
+        calld->chand_, calld, call_attempt, grpc_status_code_to_string(status),
+        is_lb_drop);
   }
   // Check if we should retry.
-  if (batch_data->MaybeRetry(status, server_pushback_md, is_lb_drop)) {
-    // Unref batch_data for deferred recv_initial_metadata_ready or
-    // recv_message_ready callbacks, if any.
-    if (call_attempt->recv_initial_metadata_ready_deferred_batch_ != nullptr) {
-      GRPC_ERROR_UNREF(call_attempt->recv_initial_metadata_error_);
-      batch_data->Unref();
-    }
-    if (call_attempt->recv_message_ready_deferred_batch_ != nullptr) {
-      GRPC_ERROR_UNREF(call_attempt->recv_message_error_);
-      batch_data->Unref();
-    }
-    batch_data->Unref();
+  grpc_millis server_pushback_ms = -1;
+  if (call_attempt->ShouldRetry(status, is_lb_drop, server_pushback_md,
+                                &server_pushback_ms)) {
+    // Start retry timer.
+    calld->StartRetryTimer(server_pushback_ms);
+    // Cancel call attempt.
+    CallCombinerClosureList closures;
+    call_attempt->Cancel(&closures);
+    // Yields call combiner.
+    closures.RunClosures(calld->call_combiner_);
     return;
   }
   // Not retrying, so commit the call.
   calld->RetryCommit(call_attempt);
+  // If retry state is no longer needed, switch to fast path for
+  // subsequent batches.
+  call_attempt->MaybeSwitchToFastPath();
   // Run any necessary closures.
   batch_data->RunClosuresForCompletedCall(GRPC_ERROR_REF(error));
 }
@@ -1454,31 +1678,27 @@ void RetryFilter::CallData::CallAttempt::BatchData::
 void RetryFilter::CallData::CallAttempt::BatchData::
     AddClosuresForReplayOrPendingSendOps(CallCombinerClosureList* closures) {
   auto* calld = call_attempt_->calld_;
+  bool have_pending_send_ops = call_attempt_->HaveSendOpsToReplay();
   // We don't check send_initial_metadata here, because that op will always
   // be started as soon as it is received from the surface, so it will
   // never need to be started at this point.
-  bool have_pending_send_message_ops =
-      call_attempt_->started_send_message_count_ < calld->send_messages_.size();
-  bool have_pending_send_trailing_metadata_op =
-      calld->seen_send_trailing_metadata_ &&
-      !call_attempt_->started_send_trailing_metadata_;
-  if (!have_pending_send_message_ops &&
-      !have_pending_send_trailing_metadata_op) {
+  if (!have_pending_send_ops) {
     for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches_); ++i) {
       PendingBatch* pending = &calld->pending_batches_[i];
       grpc_transport_stream_op_batch* batch = pending->batch;
       if (batch == nullptr || pending->send_ops_cached) continue;
-      if (batch->send_message) have_pending_send_message_ops = true;
-      if (batch->send_trailing_metadata) {
-        have_pending_send_trailing_metadata_op = true;
+      if (batch->send_message || batch->send_trailing_metadata) {
+        have_pending_send_ops = true;
+        break;
       }
     }
   }
-  if (have_pending_send_message_ops || have_pending_send_trailing_metadata_op) {
+  if (have_pending_send_ops) {
     if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
       gpr_log(GPR_INFO,
-              "chand=%p calld=%p: starting next batch for pending send op(s)",
-              calld->chand_, calld);
+              "chand=%p calld=%p attempt=%p: starting next batch for pending "
+              "send op(s)",
+              calld->chand_, calld, call_attempt_.get());
     }
     call_attempt_->AddRetriableBatches(closures);
   }
@@ -1486,15 +1706,46 @@ void RetryFilter::CallData::CallAttempt::BatchData::
 
 void RetryFilter::CallData::CallAttempt::BatchData::OnComplete(
     void* arg, grpc_error_handle error) {
-  CallAttempt::BatchData* batch_data =
-      static_cast<CallAttempt::BatchData*>(arg);
+  RefCountedPtr<BatchData> batch_data(static_cast<BatchData*>(arg));
   CallAttempt* call_attempt = batch_data->call_attempt_.get();
   CallData* calld = call_attempt->calld_;
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-    gpr_log(GPR_INFO, "chand=%p calld=%p: got on_complete, error=%s, batch=%s",
-            calld->chand_, calld, grpc_error_std_string(error).c_str(),
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p attempt=%p: got on_complete, error=%s, batch=%s",
+            calld->chand_, calld, call_attempt,
+            grpc_error_std_string(error).c_str(),
             grpc_transport_stream_op_batch_string(&batch_data->batch_).c_str());
   }
+  // If this attempt has been cancelled, then we're not going to propagate
+  // the completion of this batch, so do nothing.
+  if (call_attempt->cancelled_) {
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner_,
+                            "on_complete after cancellation");
+    return;
+  }
+  // If we got an error and have not yet gotten the
+  // recv_trailing_metadata_ready callback, then defer propagating this
+  // callback back to the surface.  We can evaluate whether to retry when
+  // recv_trailing_metadata comes back.
+  if (GPR_UNLIKELY(!calld->retry_committed_ && error != GRPC_ERROR_NONE &&
+                   !call_attempt->completed_recv_trailing_metadata_)) {
+    if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p attempt=%p: deferring on_complete",
+              calld->chand_, calld, call_attempt);
+    }
+    call_attempt->on_complete_deferred_batch_ = std::move(batch_data);
+    call_attempt->on_complete_error_ = GRPC_ERROR_REF(error);
+    if (!call_attempt->started_recv_trailing_metadata_) {
+      // recv_trailing_metadata not yet started by application; start it
+      // ourselves to get status.
+      call_attempt->StartInternalRecvTrailingMetadata();
+    } else {
+      GRPC_CALL_COMBINER_STOP(
+          calld->call_combiner_,
+          "on_complete failure before recv_trailing_metadata_ready");
+    }
+    return;
+  }
   // Update bookkeeping in call_attempt.
   if (batch_data->batch_.send_initial_metadata) {
     call_attempt->completed_send_initial_metadata_ = true;
@@ -1512,33 +1763,21 @@ void RetryFilter::CallData::CallAttempt::BatchData::OnComplete(
   }
   // Construct list of closures to execute.
   CallCombinerClosureList closures;
-  // If a retry was already dispatched, that means we saw
-  // recv_trailing_metadata before this, so we do nothing here.
-  // Otherwise, invoke the callback to return the result to the surface.
-  if (!call_attempt->retry_dispatched_) {
-    // Add closure for the completed pending batch, if any.
-    batch_data->AddClosuresForCompletedPendingBatch(GRPC_ERROR_REF(error),
-                                                    &closures);
-    // If needed, add a callback to start any replay or pending send ops on
-    // the LB call.
-    if (!call_attempt->completed_recv_trailing_metadata_) {
-      batch_data->AddClosuresForReplayOrPendingSendOps(&closures);
-    }
-  }
-  // Track number of in-flight send batches and determine if this was the
-  // last one.
-  --calld->num_in_flight_call_attempt_send_batches_;
-  const bool last_send_batch_complete =
-      calld->num_in_flight_call_attempt_send_batches_ == 0;
-  // Don't need batch_data anymore.
-  batch_data->Unref();
+  // Add closure for the completed pending batch, if any.
+  batch_data->AddClosuresForCompletedPendingBatch(GRPC_ERROR_REF(error),
+                                                  &closures);
+  // If needed, add a callback to start any replay or pending send ops on
+  // the LB call.
+  if (!call_attempt->completed_recv_trailing_metadata_) {
+    batch_data->AddClosuresForReplayOrPendingSendOps(&closures);
+  }
+  // If retry state is no longer needed (i.e., we're committed and there
+  // are no more send ops to replay), switch to fast path for subsequent
+  // batches.
+  call_attempt->MaybeSwitchToFastPath();
   // Schedule all of the closures identified above.
   // Note: This yields the call combiner.
   closures.RunClosures(calld->call_combiner_);
-  // If this was the last in-flight send batch, unref the call stack.
-  if (last_send_batch_complete) {
-    GRPC_CALL_STACK_UNREF(calld->owning_call_, "retriable_send_batches");
-  }
 }
 
 //
@@ -1598,9 +1837,12 @@ void RetryFilter::CallData::CallAttempt::BatchData::
     AddRetriableSendMessageOp() {
   auto* calld = call_attempt_->calld_;
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-    gpr_log(GPR_INFO,
-            "chand=%p calld=%p: starting calld->send_messages[%" PRIuPTR "]",
-            calld->chand_, calld, call_attempt_->started_send_message_count_);
+    gpr_log(
+        GPR_INFO,
+        "chand=%p calld=%p attempt=%p: starting calld->send_messages[%" PRIuPTR
+        "]",
+        calld->chand_, calld, call_attempt_.get(),
+        call_attempt_->started_send_message_count_);
   }
   ByteStreamCache* cache =
       calld->send_messages_[call_attempt_->started_send_message_count_];
@@ -1650,6 +1892,7 @@ void RetryFilter::CallData::CallAttempt::BatchData::
   ++call_attempt_->started_recv_message_count_;
   batch_.recv_message = true;
   batch_.payload->recv_message.recv_message = &call_attempt_->recv_message_;
+  batch_.payload->recv_message.call_failed_before_recv_message = nullptr;
   GRPC_CLOSURE_INIT(&call_attempt_->recv_message_ready_, RecvMessageReady, this,
                     grpc_schedule_on_exec_ctx);
   batch_.payload->recv_message.recv_message_ready =
@@ -1671,6 +1914,12 @@ void RetryFilter::CallData::CallAttempt::BatchData::
       &call_attempt_->recv_trailing_metadata_ready_;
 }
 
+void RetryFilter::CallData::CallAttempt::BatchData::AddCancelStreamOp() {
+  batch_.cancel_stream = true;
+  batch_.payload->cancel_stream.cancel_error =
+      GRPC_ERROR_CREATE_FROM_STATIC_STRING("retry attempt abandoned");
+}
+
 //
 // CallData vtable functions
 //
@@ -1680,7 +1929,8 @@ grpc_error_handle RetryFilter::CallData::Init(
   auto* chand = static_cast<RetryFilter*>(elem->channel_data);
   new (elem->call_data) CallData(chand, *args);
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-    gpr_log(GPR_INFO, "chand=%p: created call=%p", chand, elem->call_data);
+    gpr_log(GPR_INFO, "chand=%p calld=%p: created call", chand,
+            elem->call_data);
   }
   return GRPC_ERROR_NONE;
 }
@@ -1758,7 +2008,7 @@ RetryFilter::CallData::CallData(RetryFilter* chand,
       pending_send_message_(false),
       pending_send_trailing_metadata_(false),
       retry_committed_(false),
-      last_attempt_got_server_pushback_(false) {}
+      retry_timer_pending_(false) {}
 
 RetryFilter::CallData::~CallData() {
   grpc_slice_unref_internal(path_);
@@ -1788,10 +2038,26 @@ void RetryFilter::CallData::StartTransportStreamOpBatch(
     // will not be retried, because we have committed it here.
     if (call_attempt_ != nullptr) {
       RetryCommit(call_attempt_.get());
+      // TODO(roth): When implementing hedging, this will get more
+      // complex, because instead of just passing the batch down to a
+      // single call attempt, we'll need to cancel multiple call
+      // attempts and wait for the cancellation on_complete from each call
+      // attempt before we propagate the on_complete from this batch
+      // back to the surface.
       // Note: This will release the call combiner.
-      call_attempt_->lb_call()->StartTransportStreamOpBatch(batch);
+      call_attempt_->CancelFromSurface(batch);
       return;
     }
+    // Cancel retry timer.
+    if (retry_timer_pending_) {
+      if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
+        gpr_log(GPR_INFO, "chand=%p calld=%p: cancelling retry timer", chand_,
+                this);
+      }
+      retry_timer_pending_ = false;  // Lame timer callback.
+      grpc_timer_cancel(&retry_timer_);
+      FreeAllCachedSendOpData();
+    }
     // Fail pending batches.
     PendingBatchesFail(GRPC_ERROR_REF(cancel_error));
     // Note: This will release the call combiner.
@@ -1801,13 +2067,33 @@ void RetryFilter::CallData::StartTransportStreamOpBatch(
   }
   // Add the batch to the pending list.
   PendingBatch* pending = PendingBatchesAdd(batch);
+  // If the timer is pending, yield the call combiner and wait for it to
+  // run, since we don't want to start another call attempt until it does.
+  if (retry_timer_pending_) {
+    GRPC_CALL_COMBINER_STOP(call_combiner_,
+                            "added pending batch while retry timer pending");
+    return;
+  }
+  // If we do not yet have a call attempt, create one.
   if (call_attempt_ == nullptr) {
     // If this is the first batch and retries are already committed
     // (e.g., if this batch put the call above the buffer size limit), then
     // immediately create an LB call and delegate the batch to it.  This
     // avoids the overhead of unnecessarily allocating a CallAttempt
     // object or caching any of the send op data.
-    if (num_attempts_completed_ == 0 && retry_committed_) {
+    // Note that we would ideally like to do this also on subsequent
+    // attempts (e.g., if a batch puts the call above the buffer size
+    // limit since the last attempt was complete), but in practice that's
+    // not really worthwhile, because we will almost always have cached and
+    // completed at least the send_initial_metadata op on the previous
+    // attempt, which means that we'd need special logic to replay the
+    // batch anyway, which is exactly what the CallAttempt object provides.
+    // We also skip this optimization if perAttemptRecvTimeout is set in the
+    // retry policy, because we need the code in CallAttempt to handle
+    // the associated timer.
+    if (num_attempts_completed_ == 0 && retry_committed_ &&
+        (retry_policy_ == nullptr ||
+         !retry_policy_->per_attempt_recv_timeout().has_value())) {
       if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
         gpr_log(GPR_INFO,
                 "chand=%p calld=%p: retry committed before first attempt; "
@@ -1819,7 +2105,9 @@ void RetryFilter::CallData::StartTransportStreamOpBatch(
       committed_call_->StartTransportStreamOpBatch(batch);
       return;
     }
-    // We do not yet have a call attempt, so create one.
+    // Otherwise, create a call attempt.
+    // The attempt will automatically start any necessary replays or
+    // pending batches.
     if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
       gpr_log(GPR_INFO, "chand=%p calld=%p: creating call attempt", chand_,
               this);
@@ -1829,9 +2117,8 @@ void RetryFilter::CallData::StartTransportStreamOpBatch(
   }
   // Send batches to call attempt.
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-    gpr_log(GPR_INFO,
-            "chand=%p calld=%p: starting batch on attempt=%p lb_call=%p",
-            chand_, this, call_attempt_.get(), call_attempt_->lb_call());
+    gpr_log(GPR_INFO, "chand=%p calld=%p: starting batch on attempt=%p", chand_,
+            this, call_attempt_.get());
   }
   call_attempt_->StartRetriableBatches();
 }
@@ -1849,36 +2136,8 @@ RetryFilter::CallData::CreateLoadBalancedCall() {
 }
 
 void RetryFilter::CallData::CreateCallAttempt() {
-  call_attempt_.reset(arena_->New<CallAttempt>(this));
+  call_attempt_ = MakeRefCounted<CallAttempt>(this);
   call_attempt_->StartRetriableBatches();
-  // TODO(roth): When implementing hedging, change this to start a timer
-  // for the next hedging attempt.
-}
-
-namespace {
-
-void StartBatchInCallCombiner(void* arg, grpc_error_handle /*ignored*/) {
-  grpc_transport_stream_op_batch* batch =
-      static_cast<grpc_transport_stream_op_batch*>(arg);
-  auto* lb_call = static_cast<ClientChannel::LoadBalancedCall*>(
-      batch->handler_private.extra_arg);
-  // Note: This will release the call combiner.
-  lb_call->StartTransportStreamOpBatch(batch);
-}
-
-}  // namespace
-
-void RetryFilter::CallData::AddClosureForBatch(
-    grpc_transport_stream_op_batch* batch, CallCombinerClosureList* closures) {
-  batch->handler_private.extra_arg = call_attempt_->lb_call();
-  GRPC_CLOSURE_INIT(&batch->handler_private.closure, StartBatchInCallCombiner,
-                    batch, grpc_schedule_on_exec_ctx);
-  if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-    gpr_log(GPR_INFO, "chand=%p calld=%p: starting batch on LB call: %s",
-            chand_, this, grpc_transport_stream_op_batch_string(batch).c_str());
-  }
-  closures->Add(&batch->handler_private.closure, GRPC_ERROR_NONE,
-                "start_batch_on_lb_call");
 }
 
 //
@@ -1943,7 +2202,7 @@ void RetryFilter::CallData::FreeCachedSendMessage(size_t idx) {
 
 void RetryFilter::CallData::FreeCachedSendTrailingMetadata() {
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
-    gpr_log(GPR_INFO, "chand_=%p calld=%p: destroying send_trailing_metadata",
+    gpr_log(GPR_INFO, "chand=%p calld=%p: destroying send_trailing_metadata",
             chand_, this);
   }
   grpc_metadata_batch_destroy(&send_trailing_metadata_);
@@ -1982,7 +2241,7 @@ RetryFilter::CallData::PendingBatch* RetryFilter::CallData::PendingBatchesAdd(
   const size_t idx = GetBatchIndex(batch);
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
     gpr_log(GPR_INFO,
-            "chand_=%p calld=%p: adding pending batch at index %" PRIuPTR,
+            "chand=%p calld=%p: adding pending batch at index %" PRIuPTR,
             chand_, this, idx);
   }
   PendingBatch* pending = &pending_batches_[idx];
@@ -2006,6 +2265,9 @@ RetryFilter::CallData::PendingBatch* RetryFilter::CallData::PendingBatchesAdd(
   if (batch->send_trailing_metadata) {
     pending_send_trailing_metadata_ = true;
   }
+  // TODO(roth): When we implement hedging, if there are currently attempts
+  // in flight, we will need to pick the one on which the max number of send
+  // ops have already been sent, and we commit to that attempt.
   if (GPR_UNLIKELY(bytes_buffered_for_retry_ >
                    chand_->per_rpc_retry_buffer_size_)) {
     if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
@@ -2126,18 +2388,15 @@ void RetryFilter::CallData::RetryCommit(CallAttempt* call_attempt) {
   }
 }
 
-void RetryFilter::CallData::DoRetry(grpc_millis server_pushback_ms) {
+void RetryFilter::CallData::StartRetryTimer(grpc_millis server_pushback_ms) {
   // Reset call attempt.
-  call_attempt_.reset();
+  call_attempt_.reset(DEBUG_LOCATION, "StartRetryTimer");
   // Compute backoff delay.
   grpc_millis next_attempt_time;
   if (server_pushback_ms >= 0) {
     next_attempt_time = ExecCtx::Get()->Now() + server_pushback_ms;
-    last_attempt_got_server_pushback_ = true;
+    retry_backoff_.Reset();
   } else {
-    if (num_attempts_completed_ == 1 || last_attempt_got_server_pushback_) {
-      last_attempt_got_server_pushback_ = false;
-    }
     next_attempt_time = retry_backoff_.NextAttemptTime();
   }
   if (GRPC_TRACE_FLAG_ENABLED(grpc_retry_trace)) {
@@ -2148,23 +2407,25 @@ void RetryFilter::CallData::DoRetry(grpc_millis server_pushback_ms) {
   // Schedule retry after computed delay.
   GRPC_CLOSURE_INIT(&retry_closure_, OnRetryTimer, this, nullptr);
   GRPC_CALL_STACK_REF(owning_call_, "OnRetryTimer");
-  MutexLock lock(&timer_mu_);
-  canceller_ = new Canceller(this);
+  retry_timer_pending_ = true;
   grpc_timer_init(&retry_timer_, next_attempt_time, &retry_closure_);
 }
 
 void RetryFilter::CallData::OnRetryTimer(void* arg, grpc_error_handle error) {
   auto* calld = static_cast<CallData*>(arg);
-  if (error == GRPC_ERROR_NONE) {
-    bool start_attempt = false;
-    {
-      MutexLock lock(&calld->timer_mu_);
-      if (calld->canceller_ != nullptr) {
-        calld->canceller_ = nullptr;
-        start_attempt = true;
-      }
-    }
-    if (start_attempt) calld->CreateCallAttempt();
+  GRPC_CLOSURE_INIT(&calld->retry_closure_, OnRetryTimerLocked, calld, nullptr);
+  GRPC_CALL_COMBINER_START(calld->call_combiner_, &calld->retry_closure_,
+                           GRPC_ERROR_REF(error), "retry timer fired");
+}
+
+void RetryFilter::CallData::OnRetryTimerLocked(void* arg,
+                                               grpc_error_handle error) {
+  auto* calld = static_cast<CallData*>(arg);
+  if (error == GRPC_ERROR_NONE && calld->retry_timer_pending_) {
+    calld->retry_timer_pending_ = false;
+    calld->CreateCallAttempt();
+  } else {
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner_, "retry timer cancelled");
   }
   GRPC_CALL_STACK_UNREF(calld->owning_call_, "OnRetryTimer");
 }
index fc066e6..cb19d2e 100644 (file)
@@ -158,11 +158,11 @@ RetryServiceConfigParser::ParseGlobalParams(const grpc_channel_args* /*args*/,
 
 namespace {
 
-grpc_error_handle ParseRetryPolicy(const Json& json, int* max_attempts,
-                                   grpc_millis* initial_backoff,
-                                   grpc_millis* max_backoff,
-                                   float* backoff_multiplier,
-                                   StatusCodeSet* retryable_status_codes) {
+grpc_error_handle ParseRetryPolicy(
+    const Json& json, int* max_attempts, grpc_millis* initial_backoff,
+    grpc_millis* max_backoff, float* backoff_multiplier,
+    StatusCodeSet* retryable_status_codes,
+    absl::optional<grpc_millis>* per_attempt_recv_timeout) {
   if (json.type() != Json::Type::OBJECT) {
     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
         "field:retryPolicy error:should be of type object");
@@ -170,7 +170,10 @@ grpc_error_handle ParseRetryPolicy(const Json& json, int* max_attempts,
   std::vector<grpc_error_handle> error_list;
   // Parse maxAttempts.
   auto it = json.object_value().find("maxAttempts");
-  if (it != json.object_value().end()) {
+  if (it == json.object_value().end()) {
+    error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "field:maxAttempts error:required field missing"));
+  } else {
     if (it->second.type() != Json::Type::NUMBER) {
       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
           "field:maxAttempts error:should be of type number"));
@@ -200,11 +203,14 @@ grpc_error_handle ParseRetryPolicy(const Json& json, int* max_attempts,
                                      max_backoff, &error_list) &&
       *max_backoff == 0) {
     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-        "field:maxBackoff error:should be greater than 0"));
+        "field:maxBackoff error:must be greater than 0"));
   }
   // Parse backoffMultiplier.
   it = json.object_value().find("backoffMultiplier");
-  if (it != json.object_value().end()) {
+  if (it == json.object_value().end()) {
+    error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "field:backoffMultiplier error:required field missing"));
+  } else {
     if (it->second.type() != Json::Type::NUMBER) {
       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
           "field:backoffMultiplier error:should be of type number"));
@@ -215,7 +221,7 @@ grpc_error_handle ParseRetryPolicy(const Json& json, int* max_attempts,
             "field:backoffMultiplier error:failed to parse"));
       } else if (*backoff_multiplier <= 0) {
         error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-            "field:backoffMultiplier error:should be greater than 0"));
+            "field:backoffMultiplier error:must be greater than 0"));
       }
     }
   }
@@ -224,7 +230,7 @@ grpc_error_handle ParseRetryPolicy(const Json& json, int* max_attempts,
   if (it != json.object_value().end()) {
     if (it->second.type() != Json::Type::ARRAY) {
       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-          "field:retryableStatusCodes error:should be of type array"));
+          "field:retryableStatusCodes error:must be of type array"));
     } else {
       for (const Json& element : it->second.array_value()) {
         if (element.type() != Json::Type::STRING) {
@@ -242,19 +248,31 @@ grpc_error_handle ParseRetryPolicy(const Json& json, int* max_attempts,
         }
         retryable_status_codes->Add(status);
       }
-      if (retryable_status_codes->Empty()) {
-        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-            "field:retryableStatusCodes error:should be non-empty"));
-      };
     }
   }
-  // Make sure required fields are set.
-  if (error_list.empty()) {
-    if (*max_attempts == 0 || *initial_backoff == 0 || *max_backoff == 0 ||
-        *backoff_multiplier == 0 || retryable_status_codes->Empty()) {
-      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-          "field:retryPolicy error:Missing required field(s)");
+  // Parse perAttemptRecvTimeout.
+  it = json.object_value().find("perAttemptRecvTimeout");
+  if (it != json.object_value().end()) {
+    grpc_millis per_attempt_recv_timeout_value;
+    if (!ParseDurationFromJson(it->second, &per_attempt_recv_timeout_value)) {
+      error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "field:perAttemptRecvTimeout error:type must be STRING of the "
+          "form given by google.proto.Duration."));
+    } else {
+      *per_attempt_recv_timeout = per_attempt_recv_timeout_value;
+      // TODO(roth): As part of implementing hedging, relax this check such
+      // that we allow a value of 0 if a hedging policy is specified.
+      if (per_attempt_recv_timeout_value == 0) {
+        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "field:perAttemptRecvTimeout error:must be greater than 0"));
+      }
     }
+  } else if (retryable_status_codes->Empty()) {
+    // If perAttemptRecvTimeout not present, retryableStatusCodes must be
+    // non-empty.
+    error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "field:retryableStatusCodes error:must be non-empty if "
+        "perAttemptRecvTimeout not present"));
   }
   return GRPC_ERROR_CREATE_FROM_VECTOR("retryPolicy", &error_list);
 }
@@ -274,13 +292,14 @@ RetryServiceConfigParser::ParsePerMethodParams(
   grpc_millis max_backoff = 0;
   float backoff_multiplier = 0;
   StatusCodeSet retryable_status_codes;
+  absl::optional<grpc_millis> per_attempt_recv_timeout;
   *error = ParseRetryPolicy(it->second, &max_attempts, &initial_backoff,
                             &max_backoff, &backoff_multiplier,
-                            &retryable_status_codes);
+                            &retryable_status_codes, &per_attempt_recv_timeout);
   if (*error != GRPC_ERROR_NONE) return nullptr;
-  return absl::make_unique<RetryMethodConfig>(max_attempts, initial_backoff,
-                                              max_backoff, backoff_multiplier,
-                                              retryable_status_codes);
+  return absl::make_unique<RetryMethodConfig>(
+      max_attempts, initial_backoff, max_backoff, backoff_multiplier,
+      retryable_status_codes, per_attempt_recv_timeout);
 }
 
 }  // namespace internal
index 256183f..f349b1b 100644 (file)
@@ -47,12 +47,14 @@ class RetryMethodConfig : public ServiceConfigParser::ParsedConfig {
  public:
   RetryMethodConfig(int max_attempts, grpc_millis initial_backoff,
                     grpc_millis max_backoff, float backoff_multiplier,
-                    StatusCodeSet retryable_status_codes)
+                    StatusCodeSet retryable_status_codes,
+                    absl::optional<grpc_millis> per_attempt_recv_timeout)
       : max_attempts_(max_attempts),
         initial_backoff_(initial_backoff),
         max_backoff_(max_backoff),
         backoff_multiplier_(backoff_multiplier),
-        retryable_status_codes_(retryable_status_codes) {}
+        retryable_status_codes_(retryable_status_codes),
+        per_attempt_recv_timeout_(per_attempt_recv_timeout) {}
 
   int max_attempts() const { return max_attempts_; }
   grpc_millis initial_backoff() const { return initial_backoff_; }
@@ -61,6 +63,9 @@ class RetryMethodConfig : public ServiceConfigParser::ParsedConfig {
   StatusCodeSet retryable_status_codes() const {
     return retryable_status_codes_;
   }
+  absl::optional<grpc_millis> per_attempt_recv_timeout() const {
+    return per_attempt_recv_timeout_;
+  }
 
  private:
   int max_attempts_ = 0;
@@ -68,6 +73,7 @@ class RetryMethodConfig : public ServiceConfigParser::ParsedConfig {
   grpc_millis max_backoff_ = 0;
   float backoff_multiplier_ = 0;
   StatusCodeSet retryable_status_codes_;
+  absl::optional<grpc_millis> per_attempt_recv_timeout_;
 };
 
 class RetryServiceConfigParser : public ServiceConfigParser::Parser {
index 01abaab..6255d91 100644 (file)
@@ -187,7 +187,7 @@ void ChannelData::StartTransportOp(grpc_channel_element* elem,
                                    grpc_transport_op* op) {
   ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
   // Catch the disconnect_with_error transport op.
-  if (op->disconnect_with_error != nullptr) {
+  if (op->disconnect_with_error != GRPC_ERROR_NONE) {
     // IncreaseCallCount() introduces a phony call and prevent the timer from
     // being reset by other threads.
     chand->IncreaseCallCount();
index 5f8d779..6197e74 100644 (file)
@@ -428,6 +428,12 @@ void CallData::DelayBatch(grpc_call_element* elem,
   MutexLock lock(&delay_mu_);
   delayed_batch_ = batch;
   resume_batch_canceller_ = new ResumeBatchCanceller(elem);
+  // Without this line, ExecCtx::Get()->Now() will return a cached timestamp. If
+  // there are thousands of RPCs happen on one thread, we might observe ms-level
+  // error in Now(). This could mean the construction of RPC object is
+  // microseconds earlier than the filter execution. But we still haven't found
+  // the root cause. Read more: https://github.com/grpc/grpc/pull/25738.
+  ExecCtx::Get()->InvalidateNow();
   grpc_millis resume_time = ExecCtx::Get()->Now() + fi_policy_->delay;
   GRPC_CLOSURE_INIT(&batch->handler_private.closure, ResumeBatch, elem,
                     grpc_schedule_on_exec_ctx);
index 4700093..4ac3a24 100644 (file)
@@ -82,7 +82,8 @@ grpc_channel* grpc_insecure_channel_create_from_fd(
 #else  // !GPR_SUPPORT_CHANNELS_FROM_FD
 
 grpc_channel* grpc_insecure_channel_create_from_fd(
-    const char* target, int fd, const grpc_channel_args* args) {
+    const char* /* target */, int /* fd */,
+    const grpc_channel_args* /* args */) {
   GPR_ASSERT(0);
   return nullptr;
 }
index fd57939..836ade2 100644 (file)
@@ -68,8 +68,9 @@ void grpc_server_add_insecure_channel_from_fd(grpc_server* server,
 
 #else  // !GPR_SUPPORT_CHANNELS_FROM_FD
 
-void grpc_server_add_insecure_channel_from_fd(grpc_server* server,
-                                              void* reserved, int fd) {
+void grpc_server_add_insecure_channel_from_fd(grpc_server* /* server */,
+                                              void* /* reserved */,
+                                              int /* fd */) {
   GPR_ASSERT(0);
 }
 
index 1ff6475..ec789cb 100644 (file)
@@ -564,7 +564,7 @@ static void close_transport_locked(grpc_chttp2_transport* t,
                                  GRPC_STATUS_UNAVAILABLE);
     }
     if (t->write_state != GRPC_CHTTP2_WRITE_STATE_IDLE) {
-      if (t->close_transport_on_writes_finished == nullptr) {
+      if (t->close_transport_on_writes_finished == GRPC_ERROR_NONE) {
         t->close_transport_on_writes_finished =
             GRPC_ERROR_CREATE_FROM_STATIC_STRING(
                 "Delayed close due to in-progress write");
@@ -821,9 +821,9 @@ static void set_write_state(grpc_chttp2_transport* t,
   // from peer while we had some pending writes)
   if (st == GRPC_CHTTP2_WRITE_STATE_IDLE) {
     grpc_core::ExecCtx::RunList(DEBUG_LOCATION, &t->run_after_write);
-    if (t->close_transport_on_writes_finished != nullptr) {
+    if (t->close_transport_on_writes_finished != GRPC_ERROR_NONE) {
       grpc_error_handle err = t->close_transport_on_writes_finished;
-      t->close_transport_on_writes_finished = nullptr;
+      t->close_transport_on_writes_finished = GRPC_ERROR_NONE;
       close_transport_locked(t, err);
     }
   }
@@ -1582,6 +1582,8 @@ static void perform_stream_op_locked(void* stream_op,
     GPR_ASSERT(!s->pending_byte_stream);
     s->recv_message_ready = op_payload->recv_message.recv_message_ready;
     s->recv_message = op_payload->recv_message.recv_message;
+    s->call_failed_before_recv_message =
+        op_payload->recv_message.call_failed_before_recv_message;
     if (s->id != 0) {
       if (!s->read_closed) {
         before = s->frame_storage.length +
@@ -1795,7 +1797,7 @@ static void perform_transport_op_locked(void* stream_op,
   grpc_chttp2_transport* t =
       static_cast<grpc_chttp2_transport*>(op->handler_private.extra_arg);
 
-  if (op->goaway_error) {
+  if (op->goaway_error != GRPC_ERROR_NONE) {
     send_goaway(t, op->goaway_error);
   }
 
@@ -1947,6 +1949,10 @@ void grpc_chttp2_maybe_complete_recv_message(grpc_chttp2_transport* /*t*/,
       null_then_sched_closure(&s->recv_message_ready);
     } else if (s->published_metadata[1] != GRPC_METADATA_NOT_PUBLISHED) {
       *s->recv_message = nullptr;
+      if (s->call_failed_before_recv_message != nullptr) {
+        *s->call_failed_before_recv_message =
+            (s->published_metadata[1] != GRPC_METADATA_PUBLISHED_AT_CLOSE);
+      }
       null_then_sched_closure(&s->recv_message_ready);
     }
     GRPC_ERROR_UNREF(error);
index 9c7f5fc..0b79f2f 100644 (file)
@@ -556,6 +556,7 @@ struct grpc_chttp2_stream {
   grpc_closure* recv_initial_metadata_ready = nullptr;
   bool* trailing_metadata_available = nullptr;
   grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message;
+  bool* call_failed_before_recv_message = nullptr;
   grpc_closure* recv_message_ready = nullptr;
   grpc_metadata_batch* recv_trailing_metadata;
   grpc_closure* recv_trailing_metadata_finished = nullptr;
index 68e7c69..d588f55 100644 (file)
@@ -246,10 +246,10 @@ grpc_error_handle grpc_chttp2_perform_read(grpc_chttp2_transport* t,
         t->incoming_frame_size -= static_cast<uint32_t>(end - cur);
         return GRPC_ERROR_NONE;
       }
-      GPR_UNREACHABLE_CODE(return nullptr);
+      GPR_UNREACHABLE_CODE(return GRPC_ERROR_NONE);
   }
 
-  GPR_UNREACHABLE_CODE(return nullptr);
+  GPR_UNREACHABLE_CODE(return GRPC_ERROR_NONE);
 }
 
 static grpc_error_handle init_frame_parser(grpc_chttp2_transport* t) {
index 0c21db7..0abc2b3 100644 (file)
@@ -388,7 +388,8 @@ void complete_if_batch_end_locked(inproc_stream* s, grpc_error_handle error,
   int is_rtm = static_cast<int>(op == s->recv_trailing_md_op);
 
   if ((is_sm + is_stm + is_rim + is_rm + is_rtm) == 1) {
-    INPROC_LOG(GPR_INFO, "%s %p %p %p", msg, s, op, error);
+    INPROC_LOG(GPR_INFO, "%s %p %p %s", msg, s, op,
+               grpc_error_std_string(error).c_str());
     grpc_core::ExecCtx::Run(DEBUG_LOCATION, op->on_complete,
                             GRPC_ERROR_REF(error));
   }
@@ -468,8 +469,9 @@ void fail_helper_locked(inproc_stream* s, grpc_error_handle error) {
            .trailing_metadata_available = true;
     }
     INPROC_LOG(GPR_INFO,
-               "fail_helper %p scheduling initial-metadata-ready %p %p", s,
-               error, err);
+               "fail_helper %p scheduling initial-metadata-ready %s %s", s,
+               grpc_error_std_string(error).c_str(),
+               grpc_error_std_string(err).c_str());
     grpc_core::ExecCtx::Run(
         DEBUG_LOCATION,
         s->recv_initial_md_op->payload->recv_initial_metadata
@@ -483,8 +485,13 @@ void fail_helper_locked(inproc_stream* s, grpc_error_handle error) {
     s->recv_initial_md_op = nullptr;
   }
   if (s->recv_message_op) {
-    INPROC_LOG(GPR_INFO, "fail_helper %p scheduling message-ready %p", s,
-               error);
+    INPROC_LOG(GPR_INFO, "fail_helper %p scheduling message-ready %s", s,
+               grpc_error_std_string(error).c_str());
+    if (s->recv_message_op->payload->recv_message
+            .call_failed_before_recv_message != nullptr) {
+      *s->recv_message_op->payload->recv_message
+           .call_failed_before_recv_message = true;
+    }
     grpc_core::ExecCtx::Run(
         DEBUG_LOCATION,
         s->recv_message_op->payload->recv_message.recv_message_ready,
@@ -508,15 +515,15 @@ void fail_helper_locked(inproc_stream* s, grpc_error_handle error) {
     s->send_trailing_md_op = nullptr;
   }
   if (s->recv_trailing_md_op) {
-    INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-metadata-ready %p",
-               s, error);
+    INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-metadata-ready %s",
+               s, grpc_error_std_string(error).c_str());
     grpc_core::ExecCtx::Run(
         DEBUG_LOCATION,
         s->recv_trailing_md_op->payload->recv_trailing_metadata
             .recv_trailing_metadata_ready,
         GRPC_ERROR_REF(error));
-    INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-md-on-complete %p",
-               s, error);
+    INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-md-on-complete %s",
+               s, grpc_error_std_string(error).c_str());
     complete_if_batch_end_locked(
         s, error, s->recv_trailing_md_op,
         "fail_helper scheduling recv-trailing-metadata-on-complete");
@@ -685,8 +692,8 @@ void op_state_machine_locked(inproc_stream* s, grpc_error_handle error) {
       INPROC_LOG(
           GPR_INFO,
           "op_state_machine %p scheduling on_complete errors for already "
-          "recvd initial md %p",
-          s, new_err);
+          "recvd initial md %s",
+          s, grpc_error_std_string(new_err).c_str());
       fail_helper_locked(s, GRPC_ERROR_REF(new_err));
       goto done;
     }
@@ -710,8 +717,8 @@ void op_state_machine_locked(inproc_stream* s, grpc_error_handle error) {
       grpc_metadata_batch_clear(&s->to_read_initial_md);
       s->to_read_initial_md_filled = false;
       INPROC_LOG(GPR_INFO,
-                 "op_state_machine %p scheduling initial-metadata-ready %p", s,
-                 new_err);
+                 "op_state_machine %p scheduling initial-metadata-ready %s", s,
+                 grpc_error_std_string(new_err).c_str());
       grpc_core::ExecCtx::Run(
           DEBUG_LOCATION,
           s->recv_initial_md_op->payload->recv_initial_metadata
@@ -724,8 +731,8 @@ void op_state_machine_locked(inproc_stream* s, grpc_error_handle error) {
 
       if (new_err != GRPC_ERROR_NONE) {
         INPROC_LOG(GPR_INFO,
-                   "op_state_machine %p scheduling on_complete errors2 %p", s,
-                   new_err);
+                   "op_state_machine %p scheduling on_complete errors2 %s", s,
+                   grpc_error_std_string(new_err).c_str());
         fail_helper_locked(s, GRPC_ERROR_REF(new_err));
         goto done;
       }
@@ -753,8 +760,8 @@ void op_state_machine_locked(inproc_stream* s, grpc_error_handle error) {
         INPROC_LOG(
             GPR_INFO,
             "op_state_machine %p scheduling on_complete errors for already "
-            "recvd trailing md %p",
-            s, new_err);
+            "recvd trailing md %s",
+            s, grpc_error_std_string(new_err).c_str());
         fail_helper_locked(s, GRPC_ERROR_REF(new_err));
         goto done;
       }
@@ -801,8 +808,8 @@ void op_state_machine_locked(inproc_stream* s, grpc_error_handle error) {
       //     a final status, so don't mark this op complete)
       if (s->t->is_client || s->trailing_md_sent) {
         INPROC_LOG(GPR_INFO,
-                   "op_state_machine %p scheduling trailing-md-on-complete %p",
-                   s, new_err);
+                   "op_state_machine %p scheduling trailing-md-on-complete %s",
+                   s, grpc_error_std_string(new_err).c_str());
         grpc_core::ExecCtx::Run(
             DEBUG_LOCATION,
             s->recv_trailing_md_op->payload->recv_trailing_metadata
@@ -816,8 +823,8 @@ void op_state_machine_locked(inproc_stream* s, grpc_error_handle error) {
       } else {
         INPROC_LOG(GPR_INFO,
                    "op_state_machine %p server needs to delay handling "
-                   "trailing-md-on-complete %p",
-                   s, new_err);
+                   "trailing-md-on-complete %s",
+                   s, grpc_error_std_string(new_err).c_str());
       }
     } else if (!s->trailing_md_recvd) {
       INPROC_LOG(
@@ -830,8 +837,8 @@ void op_state_machine_locked(inproc_stream* s, grpc_error_handle error) {
     // In this case, we don't care to receive the write-close from the client
     // because we have already sent status and the RPC is over as far as we
     // are concerned.
-    INPROC_LOG(GPR_INFO, "op_state_machine %p scheduling trailing-md-ready %p",
-               s, new_err);
+    INPROC_LOG(GPR_INFO, "op_state_machine %p scheduling trailing-md-ready %s",
+               s, grpc_error_std_string(new_err).c_str());
     grpc_core::ExecCtx::Run(
         DEBUG_LOCATION,
         s->recv_trailing_md_op->payload->recv_trailing_metadata
@@ -1091,8 +1098,8 @@ void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
         }
         INPROC_LOG(
             GPR_INFO,
-            "perform_stream_op error %p scheduling initial-metadata-ready %p",
-            s, error);
+            "perform_stream_op error %p scheduling initial-metadata-ready %s",
+            s, grpc_error_std_string(error).c_str());
         grpc_core::ExecCtx::Run(
             DEBUG_LOCATION,
             op->payload->recv_initial_metadata.recv_initial_metadata_ready,
@@ -1101,8 +1108,12 @@ void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
       if (op->recv_message) {
         INPROC_LOG(
             GPR_INFO,
-            "perform_stream_op error %p scheduling recv message-ready %p", s,
-            error);
+            "perform_stream_op error %p scheduling recv message-ready %s", s,
+            grpc_error_std_string(error).c_str());
+        if (op->payload->recv_message.call_failed_before_recv_message !=
+            nullptr) {
+          *op->payload->recv_message.call_failed_before_recv_message = true;
+        }
         grpc_core::ExecCtx::Run(DEBUG_LOCATION,
                                 op->payload->recv_message.recv_message_ready,
                                 GRPC_ERROR_REF(error));
@@ -1110,16 +1121,16 @@ void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
       if (op->recv_trailing_metadata) {
         INPROC_LOG(
             GPR_INFO,
-            "perform_stream_op error %p scheduling trailing-metadata-ready %p",
-            s, error);
+            "perform_stream_op error %p scheduling trailing-metadata-ready %s",
+            s, grpc_error_std_string(error).c_str());
         grpc_core::ExecCtx::Run(
             DEBUG_LOCATION,
             op->payload->recv_trailing_metadata.recv_trailing_metadata_ready,
             GRPC_ERROR_REF(error));
       }
     }
-    INPROC_LOG(GPR_INFO, "perform_stream_op %p scheduling on_complete %p", s,
-               error);
+    INPROC_LOG(GPR_INFO, "perform_stream_op %p scheduling on_complete %s", s,
+               grpc_error_std_string(error).c_str());
     grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_complete, GRPC_ERROR_REF(error));
   }
   gpr_mu_unlock(mu);
diff --git a/src/core/ext/upb-generated/src/proto/grpc/auth/v1/authz_policy.upb.c b/src/core/ext/upb-generated/src/proto/grpc/auth/v1/authz_policy.upb.c
deleted file mode 100644 (file)
index 62947d6..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/* This file was generated by upbc (the upb compiler) from the input
- * file:
- *
- *     src/proto/grpc/auth/v1/authz_policy.proto
- *
- * Do not edit -- your changes will be discarded when the file is
- * regenerated. */
-
-#include <stddef.h>
-#include "upb/msg.h"
-#include "src/proto/grpc/auth/v1/authz_policy.upb.h"
-
-#include "upb/port_def.inc"
-
-static const upb_msglayout_field grpc_auth_v1_Peer__fields[1] = {
-  {1, UPB_SIZE(0, 0), 0, 0, 9, 3},
-};
-
-const upb_msglayout grpc_auth_v1_Peer_msginit = {
-  NULL,
-  &grpc_auth_v1_Peer__fields[0],
-  UPB_SIZE(8, 8), 1, false, 255,
-};
-
-static const upb_msglayout_field grpc_auth_v1_Header__fields[2] = {
-  {1, UPB_SIZE(0, 0), 0, 0, 9, 1},
-  {2, UPB_SIZE(8, 16), 0, 0, 9, 3},
-};
-
-const upb_msglayout grpc_auth_v1_Header_msginit = {
-  NULL,
-  &grpc_auth_v1_Header__fields[0],
-  UPB_SIZE(16, 32), 2, false, 255,
-};
-
-static const upb_msglayout *const grpc_auth_v1_Request_submsgs[1] = {
-  &grpc_auth_v1_Header_msginit,
-};
-
-static const upb_msglayout_field grpc_auth_v1_Request__fields[2] = {
-  {1, UPB_SIZE(0, 0), 0, 0, 9, 3},
-  {3, UPB_SIZE(4, 8), 0, 0, 11, 3},
-};
-
-const upb_msglayout grpc_auth_v1_Request_msginit = {
-  &grpc_auth_v1_Request_submsgs[0],
-  &grpc_auth_v1_Request__fields[0],
-  UPB_SIZE(8, 16), 2, false, 255,
-};
-
-static const upb_msglayout *const grpc_auth_v1_Rule_submsgs[2] = {
-  &grpc_auth_v1_Peer_msginit,
-  &grpc_auth_v1_Request_msginit,
-};
-
-static const upb_msglayout_field grpc_auth_v1_Rule__fields[3] = {
-  {1, UPB_SIZE(4, 8), 0, 0, 9, 1},
-  {2, UPB_SIZE(12, 24), 1, 0, 11, 1},
-  {3, UPB_SIZE(16, 32), 2, 1, 11, 1},
-};
-
-const upb_msglayout grpc_auth_v1_Rule_msginit = {
-  &grpc_auth_v1_Rule_submsgs[0],
-  &grpc_auth_v1_Rule__fields[0],
-  UPB_SIZE(24, 48), 3, false, 255,
-};
-
-static const upb_msglayout *const grpc_auth_v1_AuthorizationPolicy_submsgs[1] = {
-  &grpc_auth_v1_Rule_msginit,
-};
-
-static const upb_msglayout_field grpc_auth_v1_AuthorizationPolicy__fields[3] = {
-  {1, UPB_SIZE(0, 0), 0, 0, 9, 1},
-  {2, UPB_SIZE(8, 16), 0, 0, 11, 3},
-  {3, UPB_SIZE(12, 24), 0, 0, 11, 3},
-};
-
-const upb_msglayout grpc_auth_v1_AuthorizationPolicy_msginit = {
-  &grpc_auth_v1_AuthorizationPolicy_submsgs[0],
-  &grpc_auth_v1_AuthorizationPolicy__fields[0],
-  UPB_SIZE(16, 32), 3, false, 255,
-};
-
-#include "upb/port_undef.inc"
-
diff --git a/src/core/ext/upb-generated/src/proto/grpc/auth/v1/authz_policy.upb.h b/src/core/ext/upb-generated/src/proto/grpc/auth/v1/authz_policy.upb.h
deleted file mode 100644 (file)
index 2177b9d..0000000
+++ /dev/null
@@ -1,276 +0,0 @@
-/* This file was generated by upbc (the upb compiler) from the input
- * file:
- *
- *     src/proto/grpc/auth/v1/authz_policy.proto
- *
- * Do not edit -- your changes will be discarded when the file is
- * regenerated. */
-
-#ifndef SRC_PROTO_GRPC_AUTH_V1_AUTHZ_POLICY_PROTO_UPB_H_
-#define SRC_PROTO_GRPC_AUTH_V1_AUTHZ_POLICY_PROTO_UPB_H_
-
-#include "upb/msg.h"
-#include "upb/decode.h"
-#include "upb/decode_fast.h"
-#include "upb/encode.h"
-
-#include "upb/port_def.inc"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct grpc_auth_v1_Peer;
-struct grpc_auth_v1_Header;
-struct grpc_auth_v1_Request;
-struct grpc_auth_v1_Rule;
-struct grpc_auth_v1_AuthorizationPolicy;
-typedef struct grpc_auth_v1_Peer grpc_auth_v1_Peer;
-typedef struct grpc_auth_v1_Header grpc_auth_v1_Header;
-typedef struct grpc_auth_v1_Request grpc_auth_v1_Request;
-typedef struct grpc_auth_v1_Rule grpc_auth_v1_Rule;
-typedef struct grpc_auth_v1_AuthorizationPolicy grpc_auth_v1_AuthorizationPolicy;
-extern const upb_msglayout grpc_auth_v1_Peer_msginit;
-extern const upb_msglayout grpc_auth_v1_Header_msginit;
-extern const upb_msglayout grpc_auth_v1_Request_msginit;
-extern const upb_msglayout grpc_auth_v1_Rule_msginit;
-extern const upb_msglayout grpc_auth_v1_AuthorizationPolicy_msginit;
-
-
-/* grpc.auth.v1.Peer */
-
-UPB_INLINE grpc_auth_v1_Peer *grpc_auth_v1_Peer_new(upb_arena *arena) {
-  return (grpc_auth_v1_Peer *)_upb_msg_new(&grpc_auth_v1_Peer_msginit, arena);
-}
-UPB_INLINE grpc_auth_v1_Peer *grpc_auth_v1_Peer_parse(const char *buf, size_t size,
-                        upb_arena *arena) {
-  grpc_auth_v1_Peer *ret = grpc_auth_v1_Peer_new(arena);
-  return (ret && upb_decode(buf, size, ret, &grpc_auth_v1_Peer_msginit, arena)) ? ret : NULL;
-}
-UPB_INLINE grpc_auth_v1_Peer *grpc_auth_v1_Peer_parse_ex(const char *buf, size_t size,
-                           upb_arena *arena, int options) {
-  grpc_auth_v1_Peer *ret = grpc_auth_v1_Peer_new(arena);
-  return (ret && _upb_decode(buf, size, ret, &grpc_auth_v1_Peer_msginit, arena, options))
-      ? ret : NULL;
-}
-UPB_INLINE char *grpc_auth_v1_Peer_serialize(const grpc_auth_v1_Peer *msg, upb_arena *arena, size_t *len) {
-  return upb_encode(msg, &grpc_auth_v1_Peer_msginit, arena, len);
-}
-
-UPB_INLINE upb_strview const* grpc_auth_v1_Peer_principals(const grpc_auth_v1_Peer *msg, size_t *len) { return (upb_strview const*)_upb_array_accessor(msg, UPB_SIZE(0, 0), len); }
-
-UPB_INLINE upb_strview* grpc_auth_v1_Peer_mutable_principals(grpc_auth_v1_Peer *msg, size_t *len) {
-  return (upb_strview*)_upb_array_mutable_accessor(msg, UPB_SIZE(0, 0), len);
-}
-UPB_INLINE upb_strview* grpc_auth_v1_Peer_resize_principals(grpc_auth_v1_Peer *msg, size_t len, upb_arena *arena) {
-  return (upb_strview*)_upb_array_resize_accessor2(msg, UPB_SIZE(0, 0), len, UPB_SIZE(3, 4), arena);
-}
-UPB_INLINE bool grpc_auth_v1_Peer_add_principals(grpc_auth_v1_Peer *msg, upb_strview val, upb_arena *arena) {
-  return _upb_array_append_accessor2(msg, UPB_SIZE(0, 0), UPB_SIZE(3, 4), &val,
-      arena);
-}
-
-/* grpc.auth.v1.Header */
-
-UPB_INLINE grpc_auth_v1_Header *grpc_auth_v1_Header_new(upb_arena *arena) {
-  return (grpc_auth_v1_Header *)_upb_msg_new(&grpc_auth_v1_Header_msginit, arena);
-}
-UPB_INLINE grpc_auth_v1_Header *grpc_auth_v1_Header_parse(const char *buf, size_t size,
-                        upb_arena *arena) {
-  grpc_auth_v1_Header *ret = grpc_auth_v1_Header_new(arena);
-  return (ret && upb_decode(buf, size, ret, &grpc_auth_v1_Header_msginit, arena)) ? ret : NULL;
-}
-UPB_INLINE grpc_auth_v1_Header *grpc_auth_v1_Header_parse_ex(const char *buf, size_t size,
-                           upb_arena *arena, int options) {
-  grpc_auth_v1_Header *ret = grpc_auth_v1_Header_new(arena);
-  return (ret && _upb_decode(buf, size, ret, &grpc_auth_v1_Header_msginit, arena, options))
-      ? ret : NULL;
-}
-UPB_INLINE char *grpc_auth_v1_Header_serialize(const grpc_auth_v1_Header *msg, upb_arena *arena, size_t *len) {
-  return upb_encode(msg, &grpc_auth_v1_Header_msginit, arena, len);
-}
-
-UPB_INLINE upb_strview grpc_auth_v1_Header_key(const grpc_auth_v1_Header *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(0, 0), upb_strview); }
-UPB_INLINE upb_strview const* grpc_auth_v1_Header_values(const grpc_auth_v1_Header *msg, size_t *len) { return (upb_strview const*)_upb_array_accessor(msg, UPB_SIZE(8, 16), len); }
-
-UPB_INLINE void grpc_auth_v1_Header_set_key(grpc_auth_v1_Header *msg, upb_strview value) {
-  *UPB_PTR_AT(msg, UPB_SIZE(0, 0), upb_strview) = value;
-}
-UPB_INLINE upb_strview* grpc_auth_v1_Header_mutable_values(grpc_auth_v1_Header *msg, size_t *len) {
-  return (upb_strview*)_upb_array_mutable_accessor(msg, UPB_SIZE(8, 16), len);
-}
-UPB_INLINE upb_strview* grpc_auth_v1_Header_resize_values(grpc_auth_v1_Header *msg, size_t len, upb_arena *arena) {
-  return (upb_strview*)_upb_array_resize_accessor2(msg, UPB_SIZE(8, 16), len, UPB_SIZE(3, 4), arena);
-}
-UPB_INLINE bool grpc_auth_v1_Header_add_values(grpc_auth_v1_Header *msg, upb_strview val, upb_arena *arena) {
-  return _upb_array_append_accessor2(msg, UPB_SIZE(8, 16), UPB_SIZE(3, 4), &val,
-      arena);
-}
-
-/* grpc.auth.v1.Request */
-
-UPB_INLINE grpc_auth_v1_Request *grpc_auth_v1_Request_new(upb_arena *arena) {
-  return (grpc_auth_v1_Request *)_upb_msg_new(&grpc_auth_v1_Request_msginit, arena);
-}
-UPB_INLINE grpc_auth_v1_Request *grpc_auth_v1_Request_parse(const char *buf, size_t size,
-                        upb_arena *arena) {
-  grpc_auth_v1_Request *ret = grpc_auth_v1_Request_new(arena);
-  return (ret && upb_decode(buf, size, ret, &grpc_auth_v1_Request_msginit, arena)) ? ret : NULL;
-}
-UPB_INLINE grpc_auth_v1_Request *grpc_auth_v1_Request_parse_ex(const char *buf, size_t size,
-                           upb_arena *arena, int options) {
-  grpc_auth_v1_Request *ret = grpc_auth_v1_Request_new(arena);
-  return (ret && _upb_decode(buf, size, ret, &grpc_auth_v1_Request_msginit, arena, options))
-      ? ret : NULL;
-}
-UPB_INLINE char *grpc_auth_v1_Request_serialize(const grpc_auth_v1_Request *msg, upb_arena *arena, size_t *len) {
-  return upb_encode(msg, &grpc_auth_v1_Request_msginit, arena, len);
-}
-
-UPB_INLINE upb_strview const* grpc_auth_v1_Request_paths(const grpc_auth_v1_Request *msg, size_t *len) { return (upb_strview const*)_upb_array_accessor(msg, UPB_SIZE(0, 0), len); }
-UPB_INLINE bool grpc_auth_v1_Request_has_headers(const grpc_auth_v1_Request *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(4, 8)); }
-UPB_INLINE const grpc_auth_v1_Header* const* grpc_auth_v1_Request_headers(const grpc_auth_v1_Request *msg, size_t *len) { return (const grpc_auth_v1_Header* const*)_upb_array_accessor(msg, UPB_SIZE(4, 8), len); }
-
-UPB_INLINE upb_strview* grpc_auth_v1_Request_mutable_paths(grpc_auth_v1_Request *msg, size_t *len) {
-  return (upb_strview*)_upb_array_mutable_accessor(msg, UPB_SIZE(0, 0), len);
-}
-UPB_INLINE upb_strview* grpc_auth_v1_Request_resize_paths(grpc_auth_v1_Request *msg, size_t len, upb_arena *arena) {
-  return (upb_strview*)_upb_array_resize_accessor2(msg, UPB_SIZE(0, 0), len, UPB_SIZE(3, 4), arena);
-}
-UPB_INLINE bool grpc_auth_v1_Request_add_paths(grpc_auth_v1_Request *msg, upb_strview val, upb_arena *arena) {
-  return _upb_array_append_accessor2(msg, UPB_SIZE(0, 0), UPB_SIZE(3, 4), &val,
-      arena);
-}
-UPB_INLINE grpc_auth_v1_Header** grpc_auth_v1_Request_mutable_headers(grpc_auth_v1_Request *msg, size_t *len) {
-  return (grpc_auth_v1_Header**)_upb_array_mutable_accessor(msg, UPB_SIZE(4, 8), len);
-}
-UPB_INLINE grpc_auth_v1_Header** grpc_auth_v1_Request_resize_headers(grpc_auth_v1_Request *msg, size_t len, upb_arena *arena) {
-  return (grpc_auth_v1_Header**)_upb_array_resize_accessor2(msg, UPB_SIZE(4, 8), len, UPB_SIZE(2, 3), arena);
-}
-UPB_INLINE struct grpc_auth_v1_Header* grpc_auth_v1_Request_add_headers(grpc_auth_v1_Request *msg, upb_arena *arena) {
-  struct grpc_auth_v1_Header* sub = (struct grpc_auth_v1_Header*)_upb_msg_new(&grpc_auth_v1_Header_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;
-}
-
-/* grpc.auth.v1.Rule */
-
-UPB_INLINE grpc_auth_v1_Rule *grpc_auth_v1_Rule_new(upb_arena *arena) {
-  return (grpc_auth_v1_Rule *)_upb_msg_new(&grpc_auth_v1_Rule_msginit, arena);
-}
-UPB_INLINE grpc_auth_v1_Rule *grpc_auth_v1_Rule_parse(const char *buf, size_t size,
-                        upb_arena *arena) {
-  grpc_auth_v1_Rule *ret = grpc_auth_v1_Rule_new(arena);
-  return (ret && upb_decode(buf, size, ret, &grpc_auth_v1_Rule_msginit, arena)) ? ret : NULL;
-}
-UPB_INLINE grpc_auth_v1_Rule *grpc_auth_v1_Rule_parse_ex(const char *buf, size_t size,
-                           upb_arena *arena, int options) {
-  grpc_auth_v1_Rule *ret = grpc_auth_v1_Rule_new(arena);
-  return (ret && _upb_decode(buf, size, ret, &grpc_auth_v1_Rule_msginit, arena, options))
-      ? ret : NULL;
-}
-UPB_INLINE char *grpc_auth_v1_Rule_serialize(const grpc_auth_v1_Rule *msg, upb_arena *arena, size_t *len) {
-  return upb_encode(msg, &grpc_auth_v1_Rule_msginit, arena, len);
-}
-
-UPB_INLINE upb_strview grpc_auth_v1_Rule_name(const grpc_auth_v1_Rule *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview); }
-UPB_INLINE bool grpc_auth_v1_Rule_has_source(const grpc_auth_v1_Rule *msg) { return _upb_hasbit(msg, 1); }
-UPB_INLINE const grpc_auth_v1_Peer* grpc_auth_v1_Rule_source(const grpc_auth_v1_Rule *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(12, 24), const grpc_auth_v1_Peer*); }
-UPB_INLINE bool grpc_auth_v1_Rule_has_request(const grpc_auth_v1_Rule *msg) { return _upb_hasbit(msg, 2); }
-UPB_INLINE const grpc_auth_v1_Request* grpc_auth_v1_Rule_request(const grpc_auth_v1_Rule *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(16, 32), const grpc_auth_v1_Request*); }
-
-UPB_INLINE void grpc_auth_v1_Rule_set_name(grpc_auth_v1_Rule *msg, upb_strview value) {
-  *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview) = value;
-}
-UPB_INLINE void grpc_auth_v1_Rule_set_source(grpc_auth_v1_Rule *msg, grpc_auth_v1_Peer* value) {
-  _upb_sethas(msg, 1);
-  *UPB_PTR_AT(msg, UPB_SIZE(12, 24), grpc_auth_v1_Peer*) = value;
-}
-UPB_INLINE struct grpc_auth_v1_Peer* grpc_auth_v1_Rule_mutable_source(grpc_auth_v1_Rule *msg, upb_arena *arena) {
-  struct grpc_auth_v1_Peer* sub = (struct grpc_auth_v1_Peer*)grpc_auth_v1_Rule_source(msg);
-  if (sub == NULL) {
-    sub = (struct grpc_auth_v1_Peer*)_upb_msg_new(&grpc_auth_v1_Peer_msginit, arena);
-    if (!sub) return NULL;
-    grpc_auth_v1_Rule_set_source(msg, sub);
-  }
-  return sub;
-}
-UPB_INLINE void grpc_auth_v1_Rule_set_request(grpc_auth_v1_Rule *msg, grpc_auth_v1_Request* value) {
-  _upb_sethas(msg, 2);
-  *UPB_PTR_AT(msg, UPB_SIZE(16, 32), grpc_auth_v1_Request*) = value;
-}
-UPB_INLINE struct grpc_auth_v1_Request* grpc_auth_v1_Rule_mutable_request(grpc_auth_v1_Rule *msg, upb_arena *arena) {
-  struct grpc_auth_v1_Request* sub = (struct grpc_auth_v1_Request*)grpc_auth_v1_Rule_request(msg);
-  if (sub == NULL) {
-    sub = (struct grpc_auth_v1_Request*)_upb_msg_new(&grpc_auth_v1_Request_msginit, arena);
-    if (!sub) return NULL;
-    grpc_auth_v1_Rule_set_request(msg, sub);
-  }
-  return sub;
-}
-
-/* grpc.auth.v1.AuthorizationPolicy */
-
-UPB_INLINE grpc_auth_v1_AuthorizationPolicy *grpc_auth_v1_AuthorizationPolicy_new(upb_arena *arena) {
-  return (grpc_auth_v1_AuthorizationPolicy *)_upb_msg_new(&grpc_auth_v1_AuthorizationPolicy_msginit, arena);
-}
-UPB_INLINE grpc_auth_v1_AuthorizationPolicy *grpc_auth_v1_AuthorizationPolicy_parse(const char *buf, size_t size,
-                        upb_arena *arena) {
-  grpc_auth_v1_AuthorizationPolicy *ret = grpc_auth_v1_AuthorizationPolicy_new(arena);
-  return (ret && upb_decode(buf, size, ret, &grpc_auth_v1_AuthorizationPolicy_msginit, arena)) ? ret : NULL;
-}
-UPB_INLINE grpc_auth_v1_AuthorizationPolicy *grpc_auth_v1_AuthorizationPolicy_parse_ex(const char *buf, size_t size,
-                           upb_arena *arena, int options) {
-  grpc_auth_v1_AuthorizationPolicy *ret = grpc_auth_v1_AuthorizationPolicy_new(arena);
-  return (ret && _upb_decode(buf, size, ret, &grpc_auth_v1_AuthorizationPolicy_msginit, arena, options))
-      ? ret : NULL;
-}
-UPB_INLINE char *grpc_auth_v1_AuthorizationPolicy_serialize(const grpc_auth_v1_AuthorizationPolicy *msg, upb_arena *arena, size_t *len) {
-  return upb_encode(msg, &grpc_auth_v1_AuthorizationPolicy_msginit, arena, len);
-}
-
-UPB_INLINE upb_strview grpc_auth_v1_AuthorizationPolicy_name(const grpc_auth_v1_AuthorizationPolicy *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(0, 0), upb_strview); }
-UPB_INLINE bool grpc_auth_v1_AuthorizationPolicy_has_deny_rules(const grpc_auth_v1_AuthorizationPolicy *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(8, 16)); }
-UPB_INLINE const grpc_auth_v1_Rule* const* grpc_auth_v1_AuthorizationPolicy_deny_rules(const grpc_auth_v1_AuthorizationPolicy *msg, size_t *len) { return (const grpc_auth_v1_Rule* const*)_upb_array_accessor(msg, UPB_SIZE(8, 16), len); }
-UPB_INLINE bool grpc_auth_v1_AuthorizationPolicy_has_allow_rules(const grpc_auth_v1_AuthorizationPolicy *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(12, 24)); }
-UPB_INLINE const grpc_auth_v1_Rule* const* grpc_auth_v1_AuthorizationPolicy_allow_rules(const grpc_auth_v1_AuthorizationPolicy *msg, size_t *len) { return (const grpc_auth_v1_Rule* const*)_upb_array_accessor(msg, UPB_SIZE(12, 24), len); }
-
-UPB_INLINE void grpc_auth_v1_AuthorizationPolicy_set_name(grpc_auth_v1_AuthorizationPolicy *msg, upb_strview value) {
-  *UPB_PTR_AT(msg, UPB_SIZE(0, 0), upb_strview) = value;
-}
-UPB_INLINE grpc_auth_v1_Rule** grpc_auth_v1_AuthorizationPolicy_mutable_deny_rules(grpc_auth_v1_AuthorizationPolicy *msg, size_t *len) {
-  return (grpc_auth_v1_Rule**)_upb_array_mutable_accessor(msg, UPB_SIZE(8, 16), len);
-}
-UPB_INLINE grpc_auth_v1_Rule** grpc_auth_v1_AuthorizationPolicy_resize_deny_rules(grpc_auth_v1_AuthorizationPolicy *msg, size_t len, upb_arena *arena) {
-  return (grpc_auth_v1_Rule**)_upb_array_resize_accessor2(msg, UPB_SIZE(8, 16), len, UPB_SIZE(2, 3), arena);
-}
-UPB_INLINE struct grpc_auth_v1_Rule* grpc_auth_v1_AuthorizationPolicy_add_deny_rules(grpc_auth_v1_AuthorizationPolicy *msg, upb_arena *arena) {
-  struct grpc_auth_v1_Rule* sub = (struct grpc_auth_v1_Rule*)_upb_msg_new(&grpc_auth_v1_Rule_msginit, arena);
-  bool ok = _upb_array_append_accessor2(
-      msg, UPB_SIZE(8, 16), UPB_SIZE(2, 3), &sub, arena);
-  if (!ok) return NULL;
-  return sub;
-}
-UPB_INLINE grpc_auth_v1_Rule** grpc_auth_v1_AuthorizationPolicy_mutable_allow_rules(grpc_auth_v1_AuthorizationPolicy *msg, size_t *len) {
-  return (grpc_auth_v1_Rule**)_upb_array_mutable_accessor(msg, UPB_SIZE(12, 24), len);
-}
-UPB_INLINE grpc_auth_v1_Rule** grpc_auth_v1_AuthorizationPolicy_resize_allow_rules(grpc_auth_v1_AuthorizationPolicy *msg, size_t len, upb_arena *arena) {
-  return (grpc_auth_v1_Rule**)_upb_array_resize_accessor2(msg, UPB_SIZE(12, 24), len, UPB_SIZE(2, 3), arena);
-}
-UPB_INLINE struct grpc_auth_v1_Rule* grpc_auth_v1_AuthorizationPolicy_add_allow_rules(grpc_auth_v1_AuthorizationPolicy *msg, upb_arena *arena) {
-  struct grpc_auth_v1_Rule* sub = (struct grpc_auth_v1_Rule*)_upb_msg_new(&grpc_auth_v1_Rule_msginit, arena);
-  bool ok = _upb_array_append_accessor2(
-      msg, UPB_SIZE(12, 24), UPB_SIZE(2, 3), &sub, arena);
-  if (!ok) return NULL;
-  return sub;
-}
-
-#ifdef __cplusplus
-}  /* extern "C" */
-#endif
-
-#include "upb/port_undef.inc"
-
-#endif  /* SRC_PROTO_GRPC_AUTH_V1_AUTHZ_POLICY_PROTO_UPB_H_ */
diff --git a/src/core/ext/upbdefs-generated/src/proto/grpc/auth/v1/authz_policy.upbdefs.c b/src/core/ext/upbdefs-generated/src/proto/grpc/auth/v1/authz_policy.upbdefs.c
deleted file mode 100644 (file)
index 7f317ae..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/* This file was generated by upbc (the upb compiler) from the input
- * file:
- *
- *     src/proto/grpc/auth/v1/authz_policy.proto
- *
- * Do not edit -- your changes will be discarded when the file is
- * regenerated. */
-
-#include "upb/def.h"
-#include "src/proto/grpc/auth/v1/authz_policy.upbdefs.h"
-
-extern const upb_msglayout grpc_auth_v1_Peer_msginit;
-extern const upb_msglayout grpc_auth_v1_Header_msginit;
-extern const upb_msglayout grpc_auth_v1_Request_msginit;
-extern const upb_msglayout grpc_auth_v1_Rule_msginit;
-extern const upb_msglayout grpc_auth_v1_AuthorizationPolicy_msginit;
-
-static const upb_msglayout *layouts[5] = {
-  &grpc_auth_v1_Peer_msginit,
-  &grpc_auth_v1_Header_msginit,
-  &grpc_auth_v1_Request_msginit,
-  &grpc_auth_v1_Rule_msginit,
-  &grpc_auth_v1_AuthorizationPolicy_msginit,
-};
-
-static const char descriptor[507] = {'\n', ')', 's', 'r', 'c', '/', 'p', 'r', 'o', 't', 'o', '/', 'g', 'r', 'p', 'c', '/', 'a', 'u', 't', 'h', '/', 'v', '1', '/', 
-'a', 'u', 't', 'h', 'z', '_', 'p', 'o', 'l', 'i', 'c', 'y', '.', 'p', 'r', 'o', 't', 'o', '\022', '\014', 'g', 'r', 'p', 'c', '.', 
-'a', 'u', 't', 'h', '.', 'v', '1', '\"', '&', '\n', '\004', 'P', 'e', 'e', 'r', '\022', '\036', '\n', '\n', 'p', 'r', 'i', 'n', 'c', 'i', 
-'p', 'a', 'l', 's', '\030', '\001', ' ', '\003', '(', '\t', 'R', '\n', 'p', 'r', 'i', 'n', 'c', 'i', 'p', 'a', 'l', 's', '\"', '2', '\n', 
-'\006', 'H', 'e', 'a', 'd', 'e', 'r', '\022', '\020', '\n', '\003', 'k', 'e', 'y', '\030', '\001', ' ', '\001', '(', '\t', 'R', '\003', 'k', 'e', 'y', 
-'\022', '\026', '\n', '\006', 'v', 'a', 'l', 'u', 'e', 's', '\030', '\002', ' ', '\003', '(', '\t', 'R', '\006', 'v', 'a', 'l', 'u', 'e', 's', '\"', 
-'O', '\n', '\007', 'R', 'e', 'q', 'u', 'e', 's', 't', '\022', '\024', '\n', '\005', 'p', 'a', 't', 'h', 's', '\030', '\001', ' ', '\003', '(', '\t', 
-'R', '\005', 'p', 'a', 't', 'h', 's', '\022', '.', '\n', '\007', 'h', 'e', 'a', 'd', 'e', 'r', 's', '\030', '\003', ' ', '\003', '(', '\013', '2', 
-'\024', '.', 'g', 'r', 'p', 'c', '.', 'a', 'u', 't', 'h', '.', 'v', '1', '.', 'H', 'e', 'a', 'd', 'e', 'r', 'R', '\007', 'h', 'e', 
-'a', 'd', 'e', 'r', 's', '\"', 'w', '\n', '\004', 'R', 'u', 'l', 'e', '\022', '\022', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', 
-'(', '\t', 'R', '\004', 'n', 'a', 'm', 'e', '\022', '*', '\n', '\006', 's', 'o', 'u', 'r', 'c', 'e', '\030', '\002', ' ', '\001', '(', '\013', '2', 
-'\022', '.', 'g', 'r', 'p', 'c', '.', 'a', 'u', 't', 'h', '.', 'v', '1', '.', 'P', 'e', 'e', 'r', 'R', '\006', 's', 'o', 'u', 'r', 
-'c', 'e', '\022', '/', '\n', '\007', 'r', 'e', 'q', 'u', 'e', 's', 't', '\030', '\003', ' ', '\001', '(', '\013', '2', '\025', '.', 'g', 'r', 'p', 
-'c', '.', 'a', 'u', 't', 'h', '.', 'v', '1', '.', 'R', 'e', 'q', 'u', 'e', 's', 't', 'R', '\007', 'r', 'e', 'q', 'u', 'e', 's', 
-'t', '\"', '\221', '\001', '\n', '\023', 'A', 'u', 't', 'h', 'o', 'r', 'i', 'z', 'a', 't', 'i', 'o', 'n', 'P', 'o', 'l', 'i', 'c', 'y', 
-'\022', '\022', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'R', '\004', 'n', 'a', 'm', 'e', '\022', '1', '\n', '\n', 'd', 
-'e', 'n', 'y', '_', 'r', 'u', 'l', 'e', 's', '\030', '\002', ' ', '\003', '(', '\013', '2', '\022', '.', 'g', 'r', 'p', 'c', '.', 'a', 'u', 
-'t', 'h', '.', 'v', '1', '.', 'R', 'u', 'l', 'e', 'R', '\t', 'd', 'e', 'n', 'y', 'R', 'u', 'l', 'e', 's', '\022', '3', '\n', '\013', 
-'a', 'l', 'l', 'o', 'w', '_', 'r', 'u', 'l', 'e', 's', '\030', '\003', ' ', '\003', '(', '\013', '2', '\022', '.', 'g', 'r', 'p', 'c', '.', 
-'a', 'u', 't', 'h', '.', 'v', '1', '.', 'R', 'u', 'l', 'e', 'R', '\n', 'a', 'l', 'l', 'o', 'w', 'R', 'u', 'l', 'e', 's', 'b', 
-'\006', 'p', 'r', 'o', 't', 'o', '3', 
-};
-
-static upb_def_init *deps[1] = {
-  NULL
-};
-
-upb_def_init src_proto_grpc_auth_v1_authz_policy_proto_upbdefinit = {
-  deps,
-  layouts,
-  "src/proto/grpc/auth/v1/authz_policy.proto",
-  UPB_STRVIEW_INIT(descriptor, 507)
-};
diff --git a/src/core/ext/upbdefs-generated/src/proto/grpc/auth/v1/authz_policy.upbdefs.h b/src/core/ext/upbdefs-generated/src/proto/grpc/auth/v1/authz_policy.upbdefs.h
deleted file mode 100644 (file)
index c06fb25..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/* This file was generated by upbc (the upb compiler) from the input
- * file:
- *
- *     src/proto/grpc/auth/v1/authz_policy.proto
- *
- * Do not edit -- your changes will be discarded when the file is
- * regenerated. */
-
-#ifndef SRC_PROTO_GRPC_AUTH_V1_AUTHZ_POLICY_PROTO_UPBDEFS_H_
-#define SRC_PROTO_GRPC_AUTH_V1_AUTHZ_POLICY_PROTO_UPBDEFS_H_
-
-#include "upb/def.h"
-#include "upb/port_def.inc"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "upb/def.h"
-
-#include "upb/port_def.inc"
-
-extern upb_def_init src_proto_grpc_auth_v1_authz_policy_proto_upbdefinit;
-
-UPB_INLINE const upb_msgdef *grpc_auth_v1_Peer_getmsgdef(upb_symtab *s) {
-  _upb_symtab_loaddefinit(s, &src_proto_grpc_auth_v1_authz_policy_proto_upbdefinit);
-  return upb_symtab_lookupmsg(s, "grpc.auth.v1.Peer");
-}
-
-UPB_INLINE const upb_msgdef *grpc_auth_v1_Header_getmsgdef(upb_symtab *s) {
-  _upb_symtab_loaddefinit(s, &src_proto_grpc_auth_v1_authz_policy_proto_upbdefinit);
-  return upb_symtab_lookupmsg(s, "grpc.auth.v1.Header");
-}
-
-UPB_INLINE const upb_msgdef *grpc_auth_v1_Request_getmsgdef(upb_symtab *s) {
-  _upb_symtab_loaddefinit(s, &src_proto_grpc_auth_v1_authz_policy_proto_upbdefinit);
-  return upb_symtab_lookupmsg(s, "grpc.auth.v1.Request");
-}
-
-UPB_INLINE const upb_msgdef *grpc_auth_v1_Rule_getmsgdef(upb_symtab *s) {
-  _upb_symtab_loaddefinit(s, &src_proto_grpc_auth_v1_authz_policy_proto_upbdefinit);
-  return upb_symtab_lookupmsg(s, "grpc.auth.v1.Rule");
-}
-
-UPB_INLINE const upb_msgdef *grpc_auth_v1_AuthorizationPolicy_getmsgdef(upb_symtab *s) {
-  _upb_symtab_loaddefinit(s, &src_proto_grpc_auth_v1_authz_policy_proto_upbdefinit);
-  return upb_symtab_lookupmsg(s, "grpc.auth.v1.AuthorizationPolicy");
-}
-
-#ifdef __cplusplus
-}  /* extern "C" */
-#endif
-
-#include "upb/port_undef.inc"
-
-#endif  /* SRC_PROTO_GRPC_AUTH_V1_AUTHZ_POLICY_PROTO_UPBDEFS_H_ */
index e51bc07..36f2044 100644 (file)
@@ -697,10 +697,24 @@ std::string XdsApi::LdsUpdate::ToString() const {
 //
 
 std::string XdsApi::CdsUpdate::ToString() const {
-  absl::InlinedVector<std::string, 4> contents;
-  if (!eds_service_name.empty()) {
-    contents.push_back(
-        absl::StrFormat("eds_service_name=%s", eds_service_name));
+  absl::InlinedVector<std::string, 8> contents;
+  switch (cluster_type) {
+    case EDS:
+      contents.push_back("cluster_type=EDS");
+      if (!eds_service_name.empty()) {
+        contents.push_back(
+            absl::StrFormat("eds_service_name=%s", eds_service_name));
+      }
+      break;
+    case LOGICAL_DNS:
+      contents.push_back("cluster_type=LOGICAL_DNS");
+      contents.push_back(absl::StrFormat("dns_hostname=%s", dns_hostname));
+      break;
+    case AGGREGATE:
+      contents.push_back("cluster_type=AGGREGATE");
+      contents.push_back(
+          absl::StrFormat("prioritized_cluster_names=[%s]",
+                          absl::StrJoin(prioritized_cluster_names, ", ")));
   }
   if (!common_tls_context.Empty()) {
     contents.push_back(absl::StrFormat("common_tls_context=%s",
@@ -710,6 +724,11 @@ std::string XdsApi::CdsUpdate::ToString() const {
     contents.push_back(absl::StrFormat("lrs_load_reporting_server_name=%s",
                                        lrs_load_reporting_server_name.value()));
   }
+  contents.push_back(absl::StrCat("lb_policy=", lb_policy));
+  if (lb_policy == "RING_HASH") {
+    contents.push_back(absl::StrCat("min_ring_size=", min_ring_size));
+    contents.push_back(absl::StrCat("max_ring_size=", max_ring_size));
+  }
   contents.push_back(
       absl::StrFormat("max_concurrent_requests=%d", max_concurrent_requests));
   return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
@@ -830,14 +849,39 @@ bool IsEds(absl::string_view type_url) {
 
 }  // namespace
 
+// If gRPC is built with -DGRPC_XDS_USER_AGENT_NAME_SUFFIX="...", that string
+// will be appended to the user agent name reported to the xDS server.
+#ifdef GRPC_XDS_USER_AGENT_NAME_SUFFIX
+#define GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING \
+  " " GRPC_XDS_USER_AGENT_NAME_SUFFIX
+#else
+#define GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING ""
+#endif
+
+// If gRPC is built with -DGRPC_XDS_USER_AGENT_VERSION_SUFFIX="...", that string
+// will be appended to the user agent version reported to the xDS server.
+#ifdef GRPC_XDS_USER_AGENT_VERSION_SUFFIX
+#define GRPC_XDS_USER_AGENT_VERSION_SUFFIX_STRING \
+  " " GRPC_XDS_USER_AGENT_VERSION_SUFFIX
+#else
+#define GRPC_XDS_USER_AGENT_VERSION_SUFFIX_STRING ""
+#endif
+
 XdsApi::XdsApi(XdsClient* client, TraceFlag* tracer,
                const XdsBootstrap::Node* node)
     : client_(client),
       tracer_(tracer),
       node_(node),
       build_version_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING, " ",
-                                  grpc_version_string())),
-      user_agent_name_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING)) {
+                                  grpc_version_string(),
+                                  GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING,
+                                  GRPC_XDS_USER_AGENT_VERSION_SUFFIX_STRING)),
+      user_agent_name_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING,
+                                    GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING)),
+      user_agent_version_(
+          absl::StrCat("C-core ", grpc_version_string(),
+                       GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING,
+                       GRPC_XDS_USER_AGENT_VERSION_SUFFIX_STRING)) {
   // Populate upb symtab with xDS proto messages that we want to print
   // properly in logs.
   // Note: This won't actually work properly until upb adds support for
@@ -968,6 +1012,7 @@ void PopulateNode(const EncodingContext& context,
                   const XdsBootstrap::Node* node,
                   const std::string& build_version,
                   const std::string& user_agent_name,
+                  const std::string& user_agent_version,
                   envoy_config_core_v3_Node* node_msg) {
   if (node != nullptr) {
     if (!node->id.empty()) {
@@ -1007,7 +1052,7 @@ void PopulateNode(const EncodingContext& context,
   envoy_config_core_v3_Node_set_user_agent_name(
       node_msg, StdStringToUpbString(user_agent_name));
   envoy_config_core_v3_Node_set_user_agent_version(
-      node_msg, upb_strview_makez(grpc_version_string()));
+      node_msg, StdStringToUpbString(user_agent_version));
   envoy_config_core_v3_Node_add_client_features(
       node_msg, upb_strview_makez("envoy.lb.does_not_support_overprovisioning"),
       context.arena);
@@ -1113,7 +1158,8 @@ grpc_slice XdsApi::CreateAdsRequest(
     envoy_config_core_v3_Node* node_msg =
         envoy_service_discovery_v3_DiscoveryRequest_mutable_node(request,
                                                                  arena.ptr());
-    PopulateNode(context, node_, build_version_, user_agent_name_, node_msg);
+    PopulateNode(context, node_, build_version_, user_agent_name_,
+                 user_agent_version_, node_msg);
   }
   // Add resource_names.
   for (const auto& resource_name : resource_names) {
@@ -1605,40 +1651,35 @@ grpc_error_handle RouteActionParse(const EncodingContext& context,
             regex_rewrite =
                 envoy_config_route_v3_RouteAction_HashPolicy_Header_regex_rewrite(
                     header);
-        if (regex_rewrite == nullptr) {
-          gpr_log(
-              GPR_DEBUG,
-              "RouteAction HashPolicy contains policy specifier Header with "
-              "RegexMatchAndSubstitution but Regex is missing");
-          continue;
-        }
-        const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
-            envoy_type_matcher_v3_RegexMatchAndSubstitute_pattern(
-                regex_rewrite);
-        if (regex_matcher == nullptr) {
-          gpr_log(
-              GPR_DEBUG,
-              "RouteAction HashPolicy contains policy specifier Header with "
-              "RegexMatchAndSubstitution but RegexMatcher pattern is "
-              "missing");
-          continue;
-        }
-        RE2::Options options;
-        policy.regex = absl::make_unique<RE2>(
-            UpbStringToStdString(
-                envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)),
-            options);
-        if (!policy.regex->ok()) {
-          gpr_log(
-              GPR_DEBUG,
-              "RouteAction HashPolicy contains policy specifier Header with "
-              "RegexMatchAndSubstitution but RegexMatcher pattern does not "
-              "compile");
-          continue;
+        if (regex_rewrite != nullptr) {
+          const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
+              envoy_type_matcher_v3_RegexMatchAndSubstitute_pattern(
+                  regex_rewrite);
+          if (regex_matcher == nullptr) {
+            gpr_log(
+                GPR_DEBUG,
+                "RouteAction HashPolicy contains policy specifier Header with "
+                "RegexMatchAndSubstitution but RegexMatcher pattern is "
+                "missing");
+            continue;
+          }
+          RE2::Options options;
+          policy.regex = absl::make_unique<RE2>(
+              UpbStringToStdString(
+                  envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)),
+              options);
+          if (!policy.regex->ok()) {
+            gpr_log(
+                GPR_DEBUG,
+                "RouteAction HashPolicy contains policy specifier Header with "
+                "RegexMatchAndSubstitution but RegexMatcher pattern does not "
+                "compile");
+            continue;
+          }
+          policy.regex_substitution = UpbStringToStdString(
+              envoy_type_matcher_v3_RegexMatchAndSubstitute_substitution(
+                  regex_rewrite));
         }
-        policy.regex_substitution = UpbStringToStdString(
-            envoy_type_matcher_v3_RegexMatchAndSubstitute_substitution(
-                regex_rewrite));
       } else if ((filter_state =
                       envoy_config_route_v3_RouteAction_HashPolicy_filter_state(
                           hash_policy)) != nullptr) {
@@ -2707,10 +2748,11 @@ grpc_error_handle CdsResponseParse(
       resource_names_failed->insert(cluster_name);
       continue;
     }
-    // Serialize into JSON and store it in the CdsUpdateMap
+    // Add the cluster to cds_update_map.
     XdsApi::CdsResourceData& cds_resource_data =
         (*cds_update_map)[cluster_name];
     XdsApi::CdsUpdate& cds_update = cds_resource_data.resource;
+    // Store serialized proto.
     cds_resource_data.serialized_proto = UpbStringToStdString(encoded_cluster);
     // Check the cluster_discovery_type.
     if (!envoy_config_cluster_v3_Cluster_has_type(cluster) &&
@@ -2752,6 +2794,102 @@ grpc_error_handle CdsResponseParse(
     } else if (envoy_config_cluster_v3_Cluster_type(cluster) ==
                envoy_config_cluster_v3_Cluster_LOGICAL_DNS) {
       cds_update.cluster_type = XdsApi::CdsUpdate::ClusterType::LOGICAL_DNS;
+      const auto* load_assignment =
+          envoy_config_cluster_v3_Cluster_load_assignment(cluster);
+      if (load_assignment == nullptr) {
+        errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+            absl::StrCat(
+                cluster_name,
+                ": load_assignment not present for LOGICAL_DNS cluster")
+                .c_str()));
+        resource_names_failed->insert(cluster_name);
+        continue;
+      }
+      size_t num_localities;
+      const auto* const* localities =
+          envoy_config_endpoint_v3_ClusterLoadAssignment_endpoints(
+              load_assignment, &num_localities);
+      if (num_localities != 1) {
+        errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+            absl::StrCat(cluster_name,
+                         ": load_assignment for LOGICAL_DNS cluster must have "
+                         "exactly one locality, found ",
+                         num_localities)
+                .c_str()));
+        resource_names_failed->insert(cluster_name);
+        continue;
+      }
+      size_t num_endpoints;
+      const auto* const* endpoints =
+          envoy_config_endpoint_v3_LocalityLbEndpoints_lb_endpoints(
+              localities[0], &num_endpoints);
+      if (num_endpoints != 1) {
+        errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+            absl::StrCat(cluster_name,
+                         ": locality for LOGICAL_DNS cluster must have "
+                         "exactly one endpoint, found ",
+                         num_endpoints)
+                .c_str()));
+        resource_names_failed->insert(cluster_name);
+        continue;
+      }
+      const auto* endpoint =
+          envoy_config_endpoint_v3_LbEndpoint_endpoint(endpoints[0]);
+      if (endpoint == nullptr) {
+        errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+            absl::StrCat(cluster_name, ": LbEndpoint endpoint field not set")
+                .c_str()));
+        resource_names_failed->insert(cluster_name);
+        continue;
+      }
+      const auto* address = envoy_config_endpoint_v3_Endpoint_address(endpoint);
+      if (address == nullptr) {
+        errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+            absl::StrCat(cluster_name, ": Endpoint address field not set")
+                .c_str()));
+        resource_names_failed->insert(cluster_name);
+        continue;
+      }
+      const auto* socket_address =
+          envoy_config_core_v3_Address_socket_address(address);
+      if (socket_address == nullptr) {
+        errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+            absl::StrCat(cluster_name, ": Address socket_address field not set")
+                .c_str()));
+        resource_names_failed->insert(cluster_name);
+        continue;
+      }
+      if (envoy_config_core_v3_SocketAddress_resolver_name(socket_address)
+              .size != 0) {
+        errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+            absl::StrCat(
+                cluster_name,
+                ": LOGICAL_DNS clusters must NOT have a custom resolver "
+                "name set")
+                .c_str()));
+        resource_names_failed->insert(cluster_name);
+        continue;
+      }
+      absl::string_view address_str = UpbStringToAbsl(
+          envoy_config_core_v3_SocketAddress_address(socket_address));
+      if (address_str.empty()) {
+        errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+            absl::StrCat(cluster_name, ": SocketAddress address field not set")
+                .c_str()));
+        resource_names_failed->insert(cluster_name);
+        continue;
+      }
+      if (!envoy_config_core_v3_SocketAddress_has_port_value(socket_address)) {
+        errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+            absl::StrCat(cluster_name,
+                         ": SocketAddress port_value field not set")
+                .c_str()));
+        resource_names_failed->insert(cluster_name);
+        continue;
+      }
+      cds_update.dns_hostname = JoinHostPort(
+          address_str,
+          envoy_config_core_v3_SocketAddress_port_value(socket_address));
     } else {
       if (envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) {
         const envoy_config_cluster_v3_Cluster_CustomClusterType*
@@ -2815,75 +2953,61 @@ grpc_error_handle CdsResponseParse(
       // Record ring hash lb config
       auto* ring_hash_config =
           envoy_config_cluster_v3_Cluster_ring_hash_lb_config(cluster);
-      if (ring_hash_config == nullptr) {
-        errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
-            absl::StrCat(cluster_name,
-                         ": ring hash lb config required but not present.")
-                .c_str()));
-        resource_names_failed->insert(cluster_name);
-        continue;
-      }
-      const google_protobuf_UInt64Value* max_ring_size =
-          envoy_config_cluster_v3_Cluster_RingHashLbConfig_maximum_ring_size(
-              ring_hash_config);
-      if (max_ring_size != nullptr) {
-        cds_update.max_ring_size =
-            google_protobuf_UInt64Value_value(max_ring_size);
-        if (cds_update.max_ring_size > 8388608 ||
-            cds_update.max_ring_size == 0) {
-          errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
-              absl::StrCat(
-                  cluster_name,
-                  ": max_ring_size is not in the range of 1 to 8388608.")
-                  .c_str()));
-          resource_names_failed->insert(cluster_name);
-          continue;
+      if (ring_hash_config != nullptr) {
+        const google_protobuf_UInt64Value* max_ring_size =
+            envoy_config_cluster_v3_Cluster_RingHashLbConfig_maximum_ring_size(
+                ring_hash_config);
+        if (max_ring_size != nullptr) {
+          cds_update.max_ring_size =
+              google_protobuf_UInt64Value_value(max_ring_size);
+          if (cds_update.max_ring_size > 8388608 ||
+              cds_update.max_ring_size == 0) {
+            errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+                absl::StrCat(
+                    cluster_name,
+                    ": max_ring_size is not in the range of 1 to 8388608.")
+                    .c_str()));
+            resource_names_failed->insert(cluster_name);
+            continue;
+          }
         }
-      }
-      const google_protobuf_UInt64Value* min_ring_size =
-          envoy_config_cluster_v3_Cluster_RingHashLbConfig_minimum_ring_size(
-              ring_hash_config);
-      if (min_ring_size != nullptr) {
-        cds_update.min_ring_size =
-            google_protobuf_UInt64Value_value(min_ring_size);
-        if (cds_update.min_ring_size > 8388608 ||
-            cds_update.min_ring_size == 0) {
-          errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
-              absl::StrCat(
-                  cluster_name,
-                  ": min_ring_size is not in the range of 1 to 8388608.")
-                  .c_str()));
-          resource_names_failed->insert(cluster_name);
-          continue;
+        const google_protobuf_UInt64Value* min_ring_size =
+            envoy_config_cluster_v3_Cluster_RingHashLbConfig_minimum_ring_size(
+                ring_hash_config);
+        if (min_ring_size != nullptr) {
+          cds_update.min_ring_size =
+              google_protobuf_UInt64Value_value(min_ring_size);
+          if (cds_update.min_ring_size > 8388608 ||
+              cds_update.min_ring_size == 0) {
+            errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+                absl::StrCat(
+                    cluster_name,
+                    ": min_ring_size is not in the range of 1 to 8388608.")
+                    .c_str()));
+            resource_names_failed->insert(cluster_name);
+            continue;
+          }
+          if (cds_update.min_ring_size > cds_update.max_ring_size) {
+            errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
+                absl::StrCat(
+                    cluster_name,
+                    ": min_ring_size cannot be greater than max_ring_size.")
+                    .c_str()));
+            resource_names_failed->insert(cluster_name);
+            continue;
+          }
         }
-        if (cds_update.min_ring_size > cds_update.max_ring_size) {
+        if (envoy_config_cluster_v3_Cluster_RingHashLbConfig_hash_function(
+                ring_hash_config) !=
+            envoy_config_cluster_v3_Cluster_RingHashLbConfig_XX_HASH) {
           errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
-              absl::StrCat(
-                  cluster_name,
-                  ": min_ring_size cannot be greater than max_ring_size.")
+              absl::StrCat(cluster_name,
+                           ": ring hash lb config has invalid hash function.")
                   .c_str()));
           resource_names_failed->insert(cluster_name);
           continue;
         }
       }
-      if (envoy_config_cluster_v3_Cluster_RingHashLbConfig_hash_function(
-              ring_hash_config) ==
-          envoy_config_cluster_v3_Cluster_RingHashLbConfig_XX_HASH) {
-        cds_update.hash_function = XdsApi::CdsUpdate::HashFunction::XX_HASH;
-      } else if (
-          envoy_config_cluster_v3_Cluster_RingHashLbConfig_hash_function(
-              ring_hash_config) ==
-          envoy_config_cluster_v3_Cluster_RingHashLbConfig_MURMUR_HASH_2) {
-        cds_update.hash_function =
-            XdsApi::CdsUpdate::HashFunction::MURMUR_HASH_2;
-      } else {
-        errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
-            absl::StrCat(cluster_name,
-                         ": ring hash lb config has invalid hash function.")
-                .c_str()));
-        resource_names_failed->insert(cluster_name);
-        continue;
-      }
     } else {
       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
           absl::StrCat(cluster_name, ": LB policy is not supported.").c_str()));
@@ -3014,13 +3138,28 @@ grpc_error_handle ServerAddressParseAndAppend(
   if (GPR_UNLIKELY(port >> 16) != 0) {
     return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid port.");
   }
+  // Find load_balancing_weight for the endpoint.
+  const google_protobuf_UInt32Value* load_balancing_weight =
+      envoy_config_endpoint_v3_LbEndpoint_load_balancing_weight(lb_endpoint);
+  const int32_t weight =
+      load_balancing_weight != nullptr
+          ? google_protobuf_UInt32Value_value(load_balancing_weight)
+          : 500;
+  if (weight == 0) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Invalid endpoint weight of 0.");
+  }
   // Populate grpc_resolved_address.
   grpc_resolved_address addr;
   grpc_error_handle error =
       grpc_string_to_sockaddr(&addr, address_str.c_str(), port);
   if (error != GRPC_ERROR_NONE) return error;
   // Append the address to the list.
-  list->emplace_back(addr, nullptr);
+  std::map<const char*, std::unique_ptr<ServerAddress::AttributeInterface>>
+      attributes;
+  attributes[ServerAddressWeightAttribute::kServerAddressWeightAttributeKey] =
+      absl::make_unique<ServerAddressWeightAttribute>(weight);
+  list->emplace_back(addr, nullptr, std::move(attributes));
   return GRPC_ERROR_NONE;
 }
 
@@ -3362,7 +3501,8 @@ grpc_slice XdsApi::CreateLrsInitialRequest(
   envoy_config_core_v3_Node* node_msg =
       envoy_service_load_stats_v3_LoadStatsRequest_mutable_node(request,
                                                                 arena.ptr());
-  PopulateNode(context, node_, build_version_, user_agent_name_, node_msg);
+  PopulateNode(context, node_, build_version_, user_agent_name_,
+               user_agent_version_, node_msg);
   envoy_config_core_v3_Node_add_client_features(
       node_msg, upb_strview_makez("envoy.lrs.supports_send_all_clusters"),
       arena.ptr());
@@ -3759,7 +3899,8 @@ std::string XdsApi::AssembleClientConfig(
                                                                  arena.ptr());
   const EncodingContext context = {client_, tracer_, symtab_.ptr(), arena.ptr(),
                                    true};
-  PopulateNode(context, node_, build_version_, user_agent_name_, node);
+  PopulateNode(context, node_, build_version_, user_agent_name_,
+               user_agent_version_, node);
   // Dump each xDS-type config into PerXdsConfig
   for (auto& p : resource_type_metadata_map) {
     absl::string_view type_url = p.first;
index e7bf1cd..4913216 100644 (file)
@@ -399,34 +399,42 @@ class XdsApi {
     // The name to use in the EDS request.
     // If empty, the cluster name will be used.
     std::string eds_service_name;
+    // For cluster type LOGICAL_DNS.
+    // The hostname to lookup in DNS.
+    std::string dns_hostname;
+    // For cluster type AGGREGATE.
+    // The prioritized list of cluster names.
+    std::vector<std::string> prioritized_cluster_names;
+
     // Tls Context used by clients
     CommonTlsContext common_tls_context;
+
     // The LRS server to use for load reporting.
     // If not set, load reporting will be disabled.
     // If set to the empty string, will use the same server we obtained the CDS
     // data from.
     absl::optional<std::string> lrs_load_reporting_server_name;
+
     // The LB policy to use (e.g., "ROUND_ROBIN" or "RING_HASH").
     std::string lb_policy;
     // Used for RING_HASH LB policy only.
     uint64_t min_ring_size = 1024;
     uint64_t max_ring_size = 8388608;
-    enum HashFunction { XX_HASH, MURMUR_HASH_2 };
-    HashFunction hash_function;
     // Maximum number of outstanding requests can be made to the upstream
     // cluster.
     uint32_t max_concurrent_requests = 1024;
-    // For cluster type AGGREGATE.
-    // The prioritized list of cluster names.
-    std::vector<std::string> prioritized_cluster_names;
 
     bool operator==(const CdsUpdate& other) const {
       return cluster_type == other.cluster_type &&
              eds_service_name == other.eds_service_name &&
+             dns_hostname == other.dns_hostname &&
+             prioritized_cluster_names == other.prioritized_cluster_names &&
              common_tls_context == other.common_tls_context &&
              lrs_load_reporting_server_name ==
                  other.lrs_load_reporting_server_name &&
-             prioritized_cluster_names == other.prioritized_cluster_names &&
+             lb_policy == other.lb_policy &&
+             min_ring_size == other.min_ring_size &&
+             max_ring_size == other.max_ring_size &&
              max_concurrent_requests == other.max_concurrent_requests;
     }
 
@@ -664,6 +672,7 @@ class XdsApi {
   upb::SymbolTable symtab_;
   const std::string build_version_;
   const std::string user_agent_name_;
+  const std::string user_agent_version_;
 };
 
 }  // namespace grpc_core
index 888cd1a..8bda081 100644 (file)
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_format.h"
 
+#include <grpc/event_engine/event_engine.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/host_port.h"
+#include "src/core/lib/iomgr/event_engine/resolved_address_internal.h"
 #include "src/core/lib/iomgr/sockaddr.h"
 #include "src/core/lib/iomgr/socket_utils.h"
 #include "src/core/lib/iomgr/unix_sockets_posix.h"
@@ -397,3 +399,14 @@ bool grpc_sockaddr_match_subnet(const grpc_resolved_address* address,
   }
   return false;
 }
+
+namespace grpc_event_engine {
+namespace experimental {
+
+std::string ResolvedAddressToURI(const EventEngine::ResolvedAddress& addr) {
+  auto gra = CreateGRPCResolvedAddress(addr);
+  return grpc_sockaddr_to_uri(&gra);
+}
+
+}  // namespace experimental
+}  // namespace grpc_event_engine
index e8bf79f..cf29fe7 100644 (file)
@@ -23,6 +23,8 @@
 
 #include <string>
 
+#include <grpc/event_engine/event_engine.h>
+
 #include "src/core/lib/iomgr/resolve_address.h"
 
 /* Returns true if addr is an IPv4-mapped IPv6 address within the
@@ -97,4 +99,12 @@ bool grpc_sockaddr_match_subnet(const grpc_resolved_address* address,
                                 const grpc_resolved_address* subnet_address,
                                 uint32_t mask_bits);
 
+namespace grpc_event_engine {
+namespace experimental {
+
+std::string ResolvedAddressToURI(const EventEngine::ResolvedAddress& addr);
+
+}  // namespace experimental
+}  // namespace grpc_event_engine
+
 #endif /* GRPC_CORE_LIB_ADDRESS_UTILS_SOCKADDR_UTILS_H */
index a0a449e..b01fe43 100644 (file)
@@ -276,6 +276,9 @@ class SocketNode : public BaseNode {
  public:
   struct Security : public RefCounted<Security> {
     struct Tls {
+      // This is a workaround for https://bugs.llvm.org/show_bug.cgi?id=50346
+      Tls() {}
+
       enum class NameType { kUnset = 0, kStandardName = 1, kOtherName = 2 };
       NameType type = NameType::kUnset;
       // Holds the value of standard_name or other_names if type is not kUnset.
diff --git a/src/core/lib/event_engine/endpoint_config.cc b/src/core/lib/event_engine/endpoint_config.cc
new file mode 100644 (file)
index 0000000..78d28ec
--- /dev/null
@@ -0,0 +1,46 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#include <grpc/event_engine/endpoint_config.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/impl/codegen/log.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/event_engine/endpoint_config_internal.h"
+#include "src/core/lib/gpr/useful.h"
+
+namespace grpc_event_engine {
+namespace experimental {
+
+EndpointConfig::Setting ChannelArgsEndpointConfig::Get(
+    absl::string_view key) const {
+  const grpc_arg* arg = grpc_channel_args_find(args_, std::string(key).c_str());
+  if (arg == nullptr) {
+    return absl::monostate();
+  }
+  switch (arg->type) {
+    case GRPC_ARG_STRING:
+      return absl::string_view(arg->value.string);
+    case GRPC_ARG_INTEGER:
+      return arg->value.integer;
+    case GRPC_ARG_POINTER:
+      return arg->value.pointer.p;
+  }
+  GPR_UNREACHABLE_CODE(return absl::monostate());
+}
+
+}  // namespace experimental
+}  // namespace grpc_event_engine
diff --git a/src/core/lib/event_engine/endpoint_config_internal.h b/src/core/lib/event_engine/endpoint_config_internal.h
new file mode 100644 (file)
index 0000000..1f39ac4
--- /dev/null
@@ -0,0 +1,42 @@
+// Copyright 2021 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_CORE_LIB_EVENT_ENGINE_ENDPOINT_CONFIG_INTERNAL_H
+#define GRPC_CORE_LIB_EVENT_ENGINE_ENDPOINT_CONFIG_INTERNAL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/event_engine/endpoint_config.h>
+
+#include "src/core/lib/channel/channel_args.h"
+
+namespace grpc_event_engine {
+namespace experimental {
+
+/// A readonly \a EndpointConfig based on grpc_channel_args. This class does not
+/// take ownership of the grpc_endpoint_args*, and instances of this class
+/// should not be used after the underlying args are destroyed.
+class ChannelArgsEndpointConfig : public EndpointConfig {
+ public:
+  explicit ChannelArgsEndpointConfig(const grpc_channel_args* args)
+      : args_(args) {}
+  Setting Get(absl::string_view key) const override;
+
+ private:
+  const grpc_channel_args* args_;
+};
+
+}  // namespace experimental
+}  // namespace grpc_event_engine
+
+#endif  // GRPC_CORE_LIB_EVENT_ENGINE_ENDPOINT_CONFIG_INTERNAL_H
diff --git a/src/core/lib/event_engine/event_engine.cc b/src/core/lib/event_engine/event_engine.cc
new file mode 100644 (file)
index 0000000..cfc270c
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#include <grpc/event_engine/endpoint_config.h>
+#include <grpc/event_engine/event_engine.h>
+#include <grpc/event_engine/port.h>
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/log.h>
+
+#include "absl/strings/str_format.h"
+#include "absl/strings/str_join.h"
+
+#include "src/core/lib/event_engine/sockaddr.h"
+
+namespace grpc_event_engine {
+namespace experimental {
+
+EventEngine::ResolvedAddress::ResolvedAddress(const sockaddr* address,
+                                              socklen_t size)
+    : size_(size) {
+  GPR_ASSERT(size <= sizeof(address_));
+  memcpy(&address_, address, size);
+}
+
+const struct sockaddr* EventEngine::ResolvedAddress::address() const {
+  return reinterpret_cast<const struct sockaddr*>(address_);
+}
+
+socklen_t EventEngine::ResolvedAddress::size() const { return size_; }
+
+std::shared_ptr<grpc_event_engine::experimental::EventEngine>
+DefaultEventEngineFactory() {
+  // TODO(nnoble): delete when uv-ee is merged
+  abort();
+}
+
+}  // namespace experimental
+}  // namespace grpc_event_engine
index 3ea615a..ccf9fac 100644 (file)
@@ -29,11 +29,26 @@ SliceAllocator::SliceAllocator(grpc_resource_user* user)
   grpc_resource_user_ref(resource_user_);
 };
 
-SliceAllocator::~SliceAllocator() { grpc_resource_user_unref(resource_user_); };
+SliceAllocator::~SliceAllocator() {
+  if (resource_user_ != nullptr) {
+    grpc_resource_user_unref(resource_user_);
+  }
+};
+
+SliceAllocator::SliceAllocator(SliceAllocator&& other) noexcept
+    : resource_user_(other.resource_user_) {
+  other.resource_user_ = nullptr;
+}
+
+SliceAllocator& SliceAllocator::operator=(SliceAllocator&& other) noexcept {
+  resource_user_ = other.resource_user_;
+  other.resource_user_ = nullptr;
+  return *this;
+}
 
 absl::Status SliceAllocator::Allocate(size_t size, SliceBuffer* dest,
                                       SliceAllocator::AllocateCallback cb) {
-  // TODO(hork): implement
+  // TODO(hork): merge the implementation from the uv-ee branch.
   (void)size;
   (void)dest;
   (void)cb;
@@ -46,7 +61,22 @@ SliceAllocatorFactory::SliceAllocatorFactory(grpc_resource_quota* quota)
 };
 
 SliceAllocatorFactory::~SliceAllocatorFactory() {
-  grpc_resource_quota_unref_internal(resource_quota_);
+  if (resource_quota_ != nullptr) {
+    grpc_resource_quota_unref_internal(resource_quota_);
+  }
+}
+
+SliceAllocatorFactory::SliceAllocatorFactory(
+    SliceAllocatorFactory&& other) noexcept
+    : resource_quota_(other.resource_quota_) {
+  other.resource_quota_ = nullptr;
+}
+
+SliceAllocatorFactory& SliceAllocatorFactory::operator=(
+    SliceAllocatorFactory&& other) noexcept {
+  resource_quota_ = other.resource_quota_;
+  other.resource_quota_ = nullptr;
+  return *this;
 }
 
 SliceAllocator SliceAllocatorFactory::CreateSliceAllocator(
index 811d351..9a784bd 100644 (file)
 // limitations under the License.
 #include <grpc/support/port_platform.h>
 
+#ifdef GRPC_USE_EVENT_ENGINE
 #include <string.h>
 
 #include "grpc/event_engine/event_engine.h"
 #include "grpc/event_engine/port.h"
 #include "grpc/support/log.h"
 
-namespace grpc_event_engine {
-namespace experimental {
+uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); }
 
-EventEngine::ResolvedAddress::ResolvedAddress(const sockaddr* address,
-                                              socklen_t size) {
-  GPR_ASSERT(size <= sizeof(address_));
-  memcpy(&address_, address, size);
-}
+uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); }
+
+uint32_t grpc_htonl(uint32_t hostlong) { return htonl(hostlong); }
 
-const struct sockaddr* EventEngine::ResolvedAddress::address() const {
-  return reinterpret_cast<const struct sockaddr*>(address_);
+uint32_t grpc_ntohl(uint32_t netlong) { return ntohl(netlong); }
+
+int grpc_inet_pton(int af, const char* src, void* dst) {
+  return inet_pton(af, src, dst);
 }
 
-socklen_t EventEngine::ResolvedAddress::size() const { return size_; }
+const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) {
+  inet_ntop(af, src, dst, size);
+  return dst;
+}
 
-}  // namespace experimental
-}  // namespace grpc_event_engine
+#endif  // GRPC_USE_EVENT_ENGINE
diff --git a/src/core/lib/event_engine/sockaddr.h b/src/core/lib/event_engine/sockaddr.h
new file mode 100644 (file)
index 0000000..23b83dd
--- /dev/null
@@ -0,0 +1,44 @@
+// Copyright 2021 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_CORE_LIB_EVENT_ENGINE_SOCKADDR_H
+#define GRPC_CORE_LIB_EVENT_ENGINE_SOCKADDR_H
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GRPC_USE_EVENT_ENGINE
+#include <grpc/event_engine/port.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+typedef struct sockaddr grpc_sockaddr;
+typedef struct sockaddr_in grpc_sockaddr_in;
+typedef struct sockaddr_in6 grpc_sockaddr_in6;
+typedef struct in_addr grpc_in_addr;
+typedef struct in6_addr grpc_in6_addr;
+
+#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN
+#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN
+
+#define GRPC_SOCK_STREAM SOCK_STREAM
+#define GRPC_SOCK_DGRAM SOCK_DGRAM
+
+#define GRPC_AF_UNSPEC AF_UNSPEC
+#define GRPC_AF_UNIX AF_UNIX
+#define GRPC_AF_INET AF_INET
+#define GRPC_AF_INET6 AF_INET6
+
+#define GRPC_AI_PASSIVE AI_PASSIVE
+
+#endif
+#endif  // GRPC_CORE_LIB_EVENT_ENGINE_SOCKADDR_H
index 9b8608e..51efc93 100644 (file)
@@ -28,7 +28,8 @@
 
 extern "C" {
 #ifdef __linux__
-#if defined(__x86_64__) && !defined(GPR_MUSL_LIBC_COMPAT)
+#if defined(__x86_64__) && !defined(GPR_MUSL_LIBC_COMPAT) && \
+    !defined(__ANDROID__)
 __asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
 void* __wrap_memcpy(void* destination, const void* source, size_t num) {
   return memcpy(destination, source, num);
index 50bcdff..d06f32c 100644 (file)
 
 #include <grpc/support/port_platform.h>
 
+#include <vector>
+
 #include "absl/status/status.h"
 #include "absl/time/time.h"
+#include "absl/types/optional.h"
 
 #include "src/core/lib/gprpp/debug_location.h"
 
diff --git a/src/core/lib/iomgr/endpoint_pair_event_engine.cc b/src/core/lib/iomgr/endpoint_pair_event_engine.cc
new file mode 100644 (file)
index 0000000..a0062e1
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_USE_EVENT_ENGINE
+#include "src/core/lib/iomgr/port.h"
+
+#include <stdlib.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/endpoint_pair.h"
+
+grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(
+    const char* /* name */, grpc_channel_args* /* args */) {
+  // TODO(hork): determine what's needed here in the long run
+  GPR_ASSERT(
+      false &&
+      "grpc_iomgr_create_endpoint_pair is not suppoted with event_engine");
+}
+
+#endif  // GRPC_USE_EVENT_ENGINE
index 378bdf7..c47c792 100644 (file)
@@ -59,7 +59,7 @@ absl::Status grpc_status_create(absl::StatusCode code, absl::string_view msg,
   absl::Status s = StatusCreate(code, msg, location, {});
   for (size_t i = 0; i < children_count; ++i) {
     if (!children[i].ok()) {
-      StatusAddChild(&s, children[i]);
+      grpc_core::StatusAddChild(&s, children[i]);
     }
   }
   return s;
@@ -73,10 +73,11 @@ absl::Status grpc_os_error(const grpc_core::DebugLocation& location, int err,
                            const char* call_name) {
   absl::Status s =
       StatusCreate(absl::StatusCode::kUnknown, "OS Error", location, {});
-  grpc_core::StatusSetInt(&s, grpc_core::StatusIntProperty::ERRNO, err);
-  grpc_core::StatusSetStr(&s, grpc_core::StatusStrProperty::OS_ERROR,
+  grpc_core::StatusSetInt(&s, grpc_core::StatusIntProperty::kErrorNo, err);
+  grpc_core::StatusSetStr(&s, grpc_core::StatusStrProperty::kOsError,
                           strerror(err));
-  grpc_core::StatusSetStr(&s, grpc_core::StatusStrProperty::SYSCALL, call_name);
+  grpc_core::StatusSetStr(&s, grpc_core::StatusStrProperty::kSyscall,
+                          call_name);
   return s;
 }
 
index aecd1e9..e0492b8 100644 (file)
@@ -166,7 +166,7 @@ void grpc_enable_error_creation();
 #define GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc) \
   StatusCreate(absl::StatusCode::kUnknown, desc, DEBUG_LOCATION, {})
 #define GRPC_ERROR_CREATE_FROM_STRING_VIEW(desc) \
-  StatusCreate(ababsl::StatusCode::kUnknown, desc, DEBUG_LOCATION, {})
+  StatusCreate(absl::StatusCode::kUnknown, desc, DEBUG_LOCATION, {})
 
 absl::Status grpc_status_create(absl::StatusCode code, absl::string_view msg,
                                 const grpc_core::DebugLocation& location,
diff --git a/src/core/lib/iomgr/event_engine/closure.cc b/src/core/lib/iomgr/event_engine/closure.cc
new file mode 100644 (file)
index 0000000..d9d0394
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_USE_EVENT_ENGINE
+#include <grpc/event_engine/event_engine.h>
+
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/event_engine/pollset.h"
+#include "src/core/lib/transport/error_utils.h"
+
+namespace grpc_event_engine {
+namespace experimental {
+
+EventEngine::Callback GrpcClosureToCallback(grpc_closure* closure,
+                                            grpc_error_handle error) {
+  return [closure, error](absl::Status status) {
+    grpc_error_handle new_error =
+        grpc_error_add_child(error, absl_status_to_grpc_error(status));
+#ifndef NDEBUG
+    closure->scheduled = false;
+    if (grpc_trace_closure.enabled()) {
+      gpr_log(GPR_DEBUG,
+              "EventEngine: running closure %p: created [%s:%d]: %s [%s:%d]",
+              closure, closure->file_created, closure->line_created,
+              closure->run ? "run" : "scheduled", closure->file_initiated,
+              closure->line_initiated);
+    }
+#endif
+    closure->cb(closure->cb_arg, new_error);
+#ifndef NDEBUG
+    if (grpc_trace_closure.enabled()) {
+      gpr_log(GPR_DEBUG, "EventEngine: closure %p finished", closure);
+    }
+#endif
+    GRPC_ERROR_UNREF(error);
+    grpc_pollset_ee_broadcast_event();
+  };
+}
+
+}  // namespace experimental
+}  // namespace grpc_event_engine
+#endif  // GRPC_USE_EVENT_ENGINE
diff --git a/src/core/lib/iomgr/event_engine/closure.h b/src/core/lib/iomgr/event_engine/closure.h
new file mode 100644 (file)
index 0000000..f1b4f76
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2021 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_CORE_LIB_IOMGR_EVENT_ENGINE_CLOSURE_H
+#define GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_CLOSURE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/event_engine/event_engine.h>
+
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/error.h"
+
+namespace grpc_event_engine {
+namespace experimental {
+
+EventEngine::Callback GrpcClosureToCallback(grpc_closure* closure,
+                                            grpc_error_handle error);
+
+}  // namespace experimental
+}  // namespace grpc_event_engine
+
+#endif  // GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_CLOSURE_H
diff --git a/src/core/lib/iomgr/event_engine/endpoint.cc b/src/core/lib/iomgr/event_engine/endpoint.cc
new file mode 100644 (file)
index 0000000..ddd9566
--- /dev/null
@@ -0,0 +1,194 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_USE_EVENT_ENGINE
+#include "src/core/lib/iomgr/event_engine/endpoint.h"
+
+#include <grpc/event_engine/event_engine.h>
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+#include <grpc/support/time.h>
+#include "absl/strings/string_view.h"
+
+#include "src/core/lib/address_utils/sockaddr_utils.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/event_engine/closure.h"
+#include "src/core/lib/iomgr/event_engine/pollset.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/iomgr/resource_quota.h"
+#include "src/core/lib/transport/error_utils.h"
+
+extern grpc_core::TraceFlag grpc_tcp_trace;
+
+namespace {
+
+using ::grpc_event_engine::experimental::EventEngine;
+using ::grpc_event_engine::experimental::ResolvedAddressToURI;
+using ::grpc_event_engine::experimental::SliceBuffer;
+
+void endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* slices,
+                   grpc_closure* cb, bool /* urgent */) {
+  auto* eeep = reinterpret_cast<grpc_event_engine_endpoint*>(ep);
+  if (eeep->endpoint == nullptr) {
+    grpc_core::ExecCtx::Run(DEBUG_LOCATION, cb, GRPC_ERROR_CANCELLED);
+    return;
+  }
+  SliceBuffer* read_buffer = new (&eeep->read_buffer) SliceBuffer(slices);
+  eeep->endpoint->Read(
+      [eeep, cb](absl::Status status) {
+        auto* read_buffer = reinterpret_cast<SliceBuffer*>(&eeep->read_buffer);
+        read_buffer->~SliceBuffer();
+        grpc_core::ExecCtx exec_ctx;
+        grpc_core::Closure::Run(DEBUG_LOCATION, cb,
+                                absl_status_to_grpc_error(status));
+        exec_ctx.Flush();
+        grpc_pollset_ee_broadcast_event();
+      },
+      read_buffer, absl::InfiniteFuture());
+}
+
+void endpoint_write(grpc_endpoint* ep, grpc_slice_buffer* slices,
+                    grpc_closure* cb, void* arg) {
+  // TODO(hork): adapt arg to some metrics collection mechanism.
+  (void)arg;
+  auto* eeep = reinterpret_cast<grpc_event_engine_endpoint*>(ep);
+  if (eeep->endpoint == nullptr) {
+    grpc_core::ExecCtx::Run(DEBUG_LOCATION, cb, GRPC_ERROR_CANCELLED);
+    return;
+  }
+  SliceBuffer* write_buffer = new (&eeep->write_buffer) SliceBuffer(slices);
+  eeep->endpoint->Write(
+      [eeep, cb](absl::Status status) {
+        auto* write_buffer =
+            reinterpret_cast<SliceBuffer*>(&eeep->write_buffer);
+        write_buffer->~SliceBuffer();
+        grpc_core::ExecCtx exec_ctx;
+        grpc_core::Closure::Run(DEBUG_LOCATION, cb,
+                                absl_status_to_grpc_error(status));
+        exec_ctx.Flush();
+        grpc_pollset_ee_broadcast_event();
+      },
+      write_buffer, absl::InfiniteFuture());
+}
+void endpoint_add_to_pollset(grpc_endpoint* /* ep */,
+                             grpc_pollset* /* pollset */) {}
+void endpoint_add_to_pollset_set(grpc_endpoint* /* ep */,
+                                 grpc_pollset_set* /* pollset */) {}
+void endpoint_delete_from_pollset_set(grpc_endpoint* /* ep */,
+                                      grpc_pollset_set* /* pollset */) {}
+/// After shutdown, all endpoint operations except destroy are no-op,
+/// and will return some kind of sane default (empty strings, nullptrs, etc). It
+/// is the caller's responsibility to ensure that calls to endpoint_shutdown are
+/// synchronized.
+void endpoint_shutdown(grpc_endpoint* ep, grpc_error* why) {
+  auto* eeep = reinterpret_cast<grpc_event_engine_endpoint*>(ep);
+  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
+    const char* str = grpc_error_string(why);
+    gpr_log(GPR_INFO, "TCP Endpoint %p shutdown why=%s", eeep->endpoint.get(),
+            str);
+  }
+  grpc_resource_user_shutdown(eeep->ru);
+  eeep->endpoint.reset();
+}
+
+void endpoint_destroy(grpc_endpoint* ep) {
+  auto* eeep = reinterpret_cast<grpc_event_engine_endpoint*>(ep);
+  grpc_resource_user_unref(eeep->ru);
+  delete eeep;
+}
+
+grpc_resource_user* endpoint_get_resource_user(grpc_endpoint* ep) {
+  auto* eeep = reinterpret_cast<grpc_event_engine_endpoint*>(ep);
+  return eeep->ru;
+}
+
+absl::string_view endpoint_get_peer(grpc_endpoint* ep) {
+  auto* eeep = reinterpret_cast<grpc_event_engine_endpoint*>(ep);
+  if (eeep->endpoint == nullptr) {
+    return "";
+  }
+  if (eeep->peer_address.empty()) {
+    const EventEngine::ResolvedAddress* addr = eeep->endpoint->GetPeerAddress();
+    GPR_ASSERT(addr != nullptr);
+    eeep->peer_address = ResolvedAddressToURI(*addr);
+  }
+  return eeep->peer_address;
+}
+
+absl::string_view endpoint_get_local_address(grpc_endpoint* ep) {
+  auto* eeep = reinterpret_cast<grpc_event_engine_endpoint*>(ep);
+  if (eeep->endpoint == nullptr) {
+    return "";
+  }
+  if (eeep->local_address.empty()) {
+    const EventEngine::ResolvedAddress* addr =
+        eeep->endpoint->GetLocalAddress();
+    GPR_ASSERT(addr != nullptr);
+    eeep->local_address = ResolvedAddressToURI(*addr);
+  }
+  return eeep->local_address;
+}
+
+int endpoint_get_fd(grpc_endpoint* /* ep */) { return -1; }
+
+bool endpoint_can_track_err(grpc_endpoint* /* ep */) { return false; }
+
+grpc_endpoint_vtable grpc_event_engine_endpoint_vtable = {
+    endpoint_read,
+    endpoint_write,
+    endpoint_add_to_pollset,
+    endpoint_add_to_pollset_set,
+    endpoint_delete_from_pollset_set,
+    endpoint_shutdown,
+    endpoint_destroy,
+    endpoint_get_resource_user,
+    endpoint_get_peer,
+    endpoint_get_local_address,
+    endpoint_get_fd,
+    endpoint_can_track_err};
+
+}  // namespace
+
+grpc_event_engine_endpoint* grpc_tcp_server_endpoint_create(
+    std::unique_ptr<EventEngine::Endpoint> ee_endpoint) {
+  auto endpoint = new grpc_event_engine_endpoint;
+  endpoint->base.vtable = &grpc_event_engine_endpoint_vtable;
+  // TODO(hork): populate endpoint->ru from the uvEngine's subclass
+  endpoint->endpoint = std::move(ee_endpoint);
+  return endpoint;
+}
+
+grpc_endpoint* grpc_tcp_create(const grpc_channel_args* channel_args,
+                               absl::string_view peer_address) {
+  auto endpoint = new grpc_event_engine_endpoint;
+  endpoint->base.vtable = &grpc_event_engine_endpoint_vtable;
+  grpc_resource_quota* resource_quota =
+      grpc_channel_args_find_pointer<grpc_resource_quota>(
+          channel_args, GRPC_ARG_RESOURCE_QUOTA);
+  if (resource_quota != nullptr) {
+    grpc_resource_quota_ref_internal(resource_quota);
+  } else {
+    resource_quota = grpc_resource_quota_create(nullptr);
+  }
+  endpoint->ru = grpc_resource_user_create(resource_quota,
+                                           std::string(peer_address).c_str());
+  grpc_resource_quota_unref_internal(resource_quota);
+  return &endpoint->base;
+}
+
+#endif  // GRPC_USE_EVENT_ENGINE
diff --git a/src/core/lib/iomgr/event_engine/endpoint.h b/src/core/lib/iomgr/event_engine/endpoint.h
new file mode 100644 (file)
index 0000000..e4a729d
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 2021 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_CORE_LIB_IOMGR_EVENT_ENGINE_ENDPOINT_H
+#define GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_ENDPOINT_H
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GRPC_USE_EVENT_ENGINE
+#include <grpc/event_engine/event_engine.h>
+
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/resource_quota.h"
+
+struct grpc_event_engine_endpoint {
+  grpc_endpoint base;
+  std::unique_ptr<grpc_event_engine::experimental::EventEngine::Endpoint>
+      endpoint;
+  std::string peer_address;
+  std::string local_address;
+  grpc_resource_user* ru = nullptr;
+  std::aligned_storage<
+      sizeof(grpc_event_engine::experimental::SliceBuffer),
+      alignof(grpc_event_engine::experimental::SliceBuffer)>::type read_buffer;
+  std::aligned_storage<
+      sizeof(grpc_event_engine::experimental::SliceBuffer),
+      alignof(grpc_event_engine::experimental::SliceBuffer)>::type write_buffer;
+};
+
+/// Creates an internal grpc_endpoint struct from an EventEngine Endpoint.
+/// Server code needs to create grpc_endpoints after the EventEngine has made
+/// connections.
+grpc_event_engine_endpoint* grpc_tcp_server_endpoint_create(
+    std::unique_ptr<grpc_event_engine::experimental::EventEngine::Endpoint> ee);
+
+/// Creates a new internal grpc_endpoint struct, when no EventEngine Endpoint
+/// has yet been created. This is used in client code before connections are
+/// established.
+grpc_endpoint* grpc_tcp_create(const grpc_channel_args* channel_args,
+                               absl::string_view peer_address);
+
+#endif
+#endif  // GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_ENDPOINT_H
diff --git a/src/core/lib/iomgr/event_engine/iomgr.cc b/src/core/lib/iomgr/event_engine/iomgr.cc
new file mode 100644 (file)
index 0000000..eb067b1
--- /dev/null
@@ -0,0 +1,105 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_USE_EVENT_ENGINE
+#include "src/core/lib/iomgr/event_engine/iomgr.h"
+
+#include <grpc/event_engine/event_engine.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/event_engine/promise.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/surface/init.h"
+
+extern grpc_tcp_client_vtable grpc_event_engine_tcp_client_vtable;
+extern grpc_tcp_server_vtable grpc_event_engine_tcp_server_vtable;
+extern grpc_timer_vtable grpc_event_engine_timer_vtable;
+extern grpc_pollset_vtable grpc_event_engine_pollset_vtable;
+extern grpc_pollset_set_vtable grpc_event_engine_pollset_set_vtable;
+extern grpc_address_resolver_vtable grpc_event_engine_resolver_vtable;
+
+// Disabled by default. grpc_polling_trace must be defined in all iomgr
+// implementations due to its usage in lockfree_event.
+grpc_core::DebugOnlyTraceFlag grpc_polling_trace(false, "polling");
+
+namespace {
+
+using ::grpc_event_engine::experimental::DefaultEventEngineFactory;
+using ::grpc_event_engine::experimental::EventEngine;
+using ::grpc_event_engine::experimental::Promise;
+
+// Note: This is a pointer to a shared_ptr, so it's trivially destructible.
+std::shared_ptr<EventEngine>* g_event_engine;
+
+void iomgr_platform_init(void) {
+  g_event_engine =
+      new std::shared_ptr<EventEngine>(DefaultEventEngineFactory());
+}
+
+void iomgr_platform_flush(void) {}
+
+void iomgr_platform_shutdown(void) {
+  Promise<absl::Status> shutdown_status_promise;
+  (*g_event_engine)->Shutdown([&shutdown_status_promise](absl::Status status) {
+    shutdown_status_promise.Set(std::move(status));
+  });
+  auto shutdown_status = shutdown_status_promise.Get();
+  GPR_ASSERT(shutdown_status.ok());
+  delete g_event_engine;
+  g_event_engine = nullptr;
+}
+
+void iomgr_platform_shutdown_background_closure(void) {}
+
+bool iomgr_platform_is_any_background_poller_thread(void) {
+  return (*g_event_engine)->IsWorkerThread();
+}
+
+bool iomgr_platform_add_closure_to_background_poller(
+    grpc_closure* /* closure */, grpc_error* /* error */) {
+  return false;
+}
+
+grpc_iomgr_platform_vtable vtable = {
+    iomgr_platform_init,
+    iomgr_platform_flush,
+    iomgr_platform_shutdown,
+    iomgr_platform_shutdown_background_closure,
+    iomgr_platform_is_any_background_poller_thread,
+    iomgr_platform_add_closure_to_background_poller};
+
+}  // namespace
+
+void grpc_set_default_iomgr_platform() {
+  grpc_set_tcp_client_impl(&grpc_event_engine_tcp_client_vtable);
+  grpc_set_tcp_server_impl(&grpc_event_engine_tcp_server_vtable);
+  grpc_set_timer_impl(&grpc_event_engine_timer_vtable);
+  grpc_set_pollset_vtable(&grpc_event_engine_pollset_vtable);
+  grpc_set_pollset_set_vtable(&grpc_event_engine_pollset_set_vtable);
+  grpc_set_resolver_impl(&grpc_event_engine_resolver_vtable);
+  grpc_set_iomgr_platform_vtable(&vtable);
+}
+
+bool grpc_iomgr_run_in_background() { return false; }
+
+grpc_event_engine::experimental::EventEngine* grpc_iomgr_event_engine() {
+  return g_event_engine->get();
+}
+
+#endif  // GRPC_USE_EVENT_ENGINE
diff --git a/src/core/lib/iomgr/event_engine/iomgr.h b/src/core/lib/iomgr/event_engine/iomgr.h
new file mode 100644 (file)
index 0000000..96e97e5
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright 2021 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_CORE_LIB_IOMGR_EVENT_ENGINE_IOMGR_H
+#define GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_IOMGR_H
+#include <grpc/support/port_platform.h>
+
+#include <grpc/event_engine/event_engine.h>
+
+// This can be called anywhere in the EE-based iomgr impl where we need to
+// access the global EE instance.
+grpc_event_engine::experimental::EventEngine* grpc_iomgr_event_engine();
+
+#endif  // GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_IOMGR_H
diff --git a/src/core/lib/iomgr/event_engine/pollset.cc b/src/core/lib/iomgr/event_engine/pollset.cc
new file mode 100644 (file)
index 0000000..e635539
--- /dev/null
@@ -0,0 +1,87 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_USE_EVENT_ENGINE
+#include <grpc/event_engine/event_engine.h>
+
+#include "src/core/lib/iomgr/event_engine/pollset.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+
+namespace {
+
+static gpr_mu g_mu;
+static gpr_cv g_cv;
+
+// --- pollset vtable API ---
+void pollset_global_init(void) {
+  gpr_mu_init(&g_mu);
+  gpr_cv_init(&g_cv);
+}
+void pollset_global_shutdown(void) {
+  gpr_cv_destroy(&g_cv);
+  gpr_mu_destroy(&g_mu);
+}
+void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { *mu = &g_mu; }
+void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) {
+  grpc_core::ExecCtx::Run(DEBUG_LOCATION, closure, GRPC_ERROR_NONE);
+}
+void pollset_destroy(grpc_pollset* pollset) {}
+grpc_error* pollset_work(grpc_pollset* pollset, grpc_pollset_worker** worker,
+                         grpc_millis deadline) {
+  (void)worker;
+  gpr_cv_wait(&g_cv, &g_mu,
+              grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME));
+  return GRPC_ERROR_NONE;
+}
+grpc_error* pollset_kick(grpc_pollset* pollset,
+                         grpc_pollset_worker* specific_worker) {
+  (void)pollset;
+  (void)specific_worker;
+  return GRPC_ERROR_NONE;
+}
+size_t pollset_size(void) { return 1; }
+
+// --- pollset_set vtable API ---
+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) {}
+
+}  // namespace
+
+void grpc_pollset_ee_broadcast_event() { gpr_cv_signal(&g_cv); }
+
+// --- vtables ---
+grpc_pollset_vtable grpc_event_engine_pollset_vtable = {
+    pollset_global_init, pollset_global_shutdown,
+    pollset_init,        pollset_shutdown,
+    pollset_destroy,     pollset_work,
+    pollset_kick,        pollset_size};
+
+grpc_pollset_set_vtable grpc_event_engine_pollset_set_vtable = {
+    pollset_set_create,          pollset_set_destroy,
+    pollset_set_add_pollset,     pollset_set_del_pollset,
+    pollset_set_add_pollset_set, pollset_set_del_pollset_set};
+
+#endif  // GRPC_USE_EVENT_ENGINE
similarity index 68%
rename from include/grpc/event_engine/channel_args.h
rename to src/core/lib/iomgr/event_engine/pollset.h
index d809b1f..94a46c0 100644 (file)
 // 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_EVENT_ENGINE_CHANNEL_ARGS_H
-#define GRPC_EVENT_ENGINE_CHANNEL_ARGS_H
+#ifndef GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_POLLSET_H
+#define GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_POLLSET_H
 
 #include <grpc/support/port_platform.h>
 
-namespace grpc_event_engine {
-namespace experimental {
+#ifdef GRPC_USE_EVENT_ENGINE
 
-// TODO(hork): define
-class ChannelArgs;
+/// Signals pollset_work that some work has been done.
+void grpc_pollset_ee_broadcast_event();
 
-}  // namespace experimental
-}  // namespace grpc_event_engine
-
-#endif  // GRPC_EVENT_ENGINE_CHANNEL_ARGS_H
+#endif
+#endif  // GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_POLLSET_H
diff --git a/src/core/lib/iomgr/event_engine/promise.h b/src/core/lib/iomgr/event_engine/promise.h
new file mode 100644 (file)
index 0000000..155534b
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright 2021 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_CORE_LIB_IOMGR_EVENT_ENGINE_PROMISE_H
+#define GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_PROMISE_H
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/sync.h"
+
+namespace grpc_event_engine {
+namespace experimental {
+
+/// A minimal promise implementation.
+///
+/// This is light-duty, syntactical sugar around cv wait & signal, which is
+/// useful in some cases. A more robust implementation is being worked on
+/// separately.
+template <typename T>
+class Promise {
+ public:
+  T& Get() {
+    absl::MutexLock lock(&mu_);
+    cv_.Wait(&mu_);
+    return val_;
+  }
+  void Set(T&& val) {
+    absl::MutexLock lock(&mu_);
+    val_ = std::move(val);
+    cv_.Signal();
+  }
+
+ private:
+  absl::Mutex mu_;
+  absl::CondVar cv_;
+  T val_;
+};
+
+}  // namespace experimental
+}  // namespace grpc_event_engine
+
+#endif  // GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_PROMISE_H
diff --git a/src/core/lib/iomgr/event_engine/resolved_address_internal.cc b/src/core/lib/iomgr/event_engine/resolved_address_internal.cc
new file mode 100644 (file)
index 0000000..561d91e
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/event_engine/resolved_address_internal.h"
+
+#include <grpc/event_engine/event_engine.h>
+
+#include "src/core/lib/address_utils/sockaddr_utils.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+
+namespace grpc_event_engine {
+namespace experimental {
+
+EventEngine::ResolvedAddress CreateResolvedAddress(
+    const grpc_resolved_address& addr) {
+  return EventEngine::ResolvedAddress(
+      reinterpret_cast<const sockaddr*>(addr.addr), addr.len);
+}
+
+grpc_resolved_address CreateGRPCResolvedAddress(
+    const EventEngine::ResolvedAddress& ra) {
+  grpc_resolved_address grpc_addr;
+  memcpy(grpc_addr.addr, ra.address(), ra.size());
+  grpc_addr.len = ra.size();
+  return grpc_addr;
+}
+
+}  // namespace experimental
+}  // namespace grpc_event_engine
diff --git a/src/core/lib/iomgr/event_engine/resolved_address_internal.h b/src/core/lib/iomgr/event_engine/resolved_address_internal.h
new file mode 100644 (file)
index 0000000..ae79480
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2021 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_CORE_LIB_IOMGR_EVENT_ENGINE_RESOLVED_ADDRESS_INTERNAL_H
+#define GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_RESOLVED_ADDRESS_INTERNAL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/event_engine/event_engine.h>
+
+#include "src/core/lib/iomgr/resolve_address.h"
+
+namespace grpc_event_engine {
+namespace experimental {
+
+EventEngine::ResolvedAddress CreateResolvedAddress(
+    const grpc_resolved_address& addr);
+
+grpc_resolved_address CreateGRPCResolvedAddress(
+    const EventEngine::ResolvedAddress& ra);
+
+}  // namespace experimental
+}  // namespace grpc_event_engine
+
+#endif  // GRPC_CORE_LIB_IOMGR_EVENT_ENGINE_RESOLVED_ADDRESS_INTERNAL_H
diff --git a/src/core/lib/iomgr/event_engine/resolver.cc b/src/core/lib/iomgr/event_engine/resolver.cc
new file mode 100644 (file)
index 0000000..3671302
--- /dev/null
@@ -0,0 +1,110 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_USE_EVENT_ENGINE
+#include <grpc/event_engine/event_engine.h>
+#include "absl/functional/bind_front.h"
+
+#include "src/core/lib/address_utils/sockaddr_utils.h"
+#include "src/core/lib/gprpp/sync.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/event_engine/iomgr.h"
+#include "src/core/lib/iomgr/event_engine/promise.h"
+#include "src/core/lib/iomgr/event_engine/resolved_address_internal.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/work_serializer.h"
+#include "src/core/lib/surface/init.h"
+#include "src/core/lib/transport/error_utils.h"
+
+namespace {
+using ::grpc_event_engine::experimental::CreateGRPCResolvedAddress;
+using ::grpc_event_engine::experimental::EventEngine;
+using ::grpc_event_engine::experimental::Promise;
+
+/// A fire-and-forget class representing an individual DNS request.
+///
+/// This provides a place to store the ownership of the DNSResolver object until
+/// the request is complete.
+class DnsRequest {
+ public:
+  DnsRequest(std::unique_ptr<EventEngine::DNSResolver> dns_resolver,
+             absl::string_view address, absl::string_view default_port,
+             grpc_closure* on_done, grpc_resolved_addresses** addresses)
+      : dns_resolver_(std::move(dns_resolver)),
+        cb_(on_done),
+        addresses_(addresses) {
+    dns_resolver_->LookupHostname(
+        absl::bind_front(&DnsRequest::OnLookupComplete, this), address,
+        default_port, absl::InfiniteFuture());
+  }
+
+ private:
+  void OnLookupComplete(
+      absl::StatusOr<std::vector<EventEngine::ResolvedAddress>> addresses) {
+    grpc_core::ExecCtx exec_ctx;
+    // Convert addresses to iomgr form.
+    *addresses_ = static_cast<grpc_resolved_addresses*>(
+        gpr_malloc(sizeof(grpc_resolved_addresses)));
+    (*addresses_)->naddrs = addresses->size();
+    (*addresses_)->addrs = static_cast<grpc_resolved_address*>(
+        gpr_malloc(sizeof(grpc_resolved_address) * addresses->size()));
+    for (size_t i = 0; i < addresses->size(); ++i) {
+      (*addresses_)->addrs[i] = CreateGRPCResolvedAddress((*addresses)[i]);
+    }
+    grpc_closure* cb = cb_;
+    delete this;
+    grpc_core::Closure::Run(DEBUG_LOCATION, cb,
+                            absl_status_to_grpc_error(addresses.status()));
+  }
+
+  std::unique_ptr<EventEngine::DNSResolver> dns_resolver_;
+  grpc_closure* cb_;
+  grpc_resolved_addresses** addresses_;
+};
+
+void resolve_address(const char* addr, const char* default_port,
+                     grpc_pollset_set* /* interested_parties */,
+                     grpc_closure* on_done,
+                     grpc_resolved_addresses** addresses) {
+  auto dns_resolver = grpc_iomgr_event_engine()->GetDNSResolver();
+  if (!dns_resolver.ok()) {
+    grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_done,
+                            absl_status_to_grpc_error(dns_resolver.status()));
+    return;
+  }
+  new DnsRequest(std::move(*dns_resolver), addr, default_port, on_done,
+                 addresses);
+}
+
+void blocking_handle_async_resolve_done(void* arg, grpc_error_handle error) {
+  static_cast<Promise<grpc_error_handle>*>(arg)->Set(std::move(error));
+}
+
+grpc_error* blocking_resolve_address(const char* name, const char* default_port,
+                                     grpc_resolved_addresses** addresses) {
+  grpc_closure on_done;
+  Promise<grpc_error_handle> evt;
+  GRPC_CLOSURE_INIT(&on_done, blocking_handle_async_resolve_done, &evt,
+                    grpc_schedule_on_exec_ctx);
+  resolve_address(name, default_port, nullptr, &on_done, addresses);
+  return evt.Get();
+}
+
+}  // namespace
+
+grpc_address_resolver_vtable grpc_event_engine_resolver_vtable{
+    resolve_address, blocking_resolve_address};
+
+#endif  // GRPC_USE_EVENT_ENGINE
diff --git a/src/core/lib/iomgr/event_engine/tcp.cc b/src/core/lib/iomgr/event_engine/tcp.cc
new file mode 100644 (file)
index 0000000..2815251
--- /dev/null
@@ -0,0 +1,243 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_USE_EVENT_ENGINE
+#include <grpc/event_engine/event_engine.h>
+
+#include "src/core/lib/address_utils/sockaddr_utils.h"
+#include "src/core/lib/event_engine/endpoint_config_internal.h"
+#include "src/core/lib/event_engine/sockaddr.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/iomgr/event_engine/closure.h"
+#include "src/core/lib/iomgr/event_engine/endpoint.h"
+#include "src/core/lib/iomgr/event_engine/iomgr.h"
+#include "src/core/lib/iomgr/event_engine/pollset.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/surface/init.h"
+#include "src/core/lib/transport/error_utils.h"
+
+extern grpc_core::TraceFlag grpc_tcp_trace;
+
+namespace {
+using ::grpc_event_engine::experimental::ChannelArgsEndpointConfig;
+using ::grpc_event_engine::experimental::EventEngine;
+using ::grpc_event_engine::experimental::GrpcClosureToCallback;
+using ::grpc_event_engine::experimental::SliceAllocator;
+using ::grpc_event_engine::experimental::SliceAllocatorFactory;
+}  // namespace
+
+struct grpc_tcp_server {
+  grpc_tcp_server(std::unique_ptr<EventEngine::Listener> listener,
+                  grpc_resource_quota* rq)
+      : refcount(1, GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace) ? "tcp" : nullptr),
+        listener(std::move(listener)),
+        resource_quota(rq) {
+    shutdown_starting.head = nullptr;
+    shutdown_starting.tail = nullptr;
+  };
+  ~grpc_tcp_server() {
+    // TODO(nnoble): see if we can handle this in ~SliceAllocatorFactory
+    grpc_resource_quota_unref_internal(resource_quota);
+    grpc_core::ExecCtx::RunList(DEBUG_LOCATION, &shutdown_starting);
+    grpc_core::ExecCtx::Get()->Flush();
+  }
+  grpc_core::RefCount refcount;
+  grpc_core::Mutex mu;
+  std::unique_ptr<EventEngine::Listener> listener;
+  grpc_closure_list shutdown_starting ABSL_GUARDED_BY(mu);
+  grpc_resource_quota* resource_quota;
+  grpc_tcp_server_cb on_accept_internal;
+  void* on_accept_internal_arg;
+};
+
+namespace {
+
+/// Converts a grpc_closure to an EventEngine Callback. The closure is expected
+/// to already be initialized.
+EventEngine::OnConnectCallback GrpcClosureToOnConnectCallback(
+    grpc_closure* closure, grpc_endpoint** endpoint_ptr) {
+  return [closure, endpoint_ptr](
+             absl::StatusOr<std::unique_ptr<EventEngine::Endpoint>> endpoint) {
+    grpc_core::ExecCtx exec_ctx;
+    if (endpoint.ok()) {
+      auto* grpc_endpoint_out =
+          reinterpret_cast<grpc_event_engine_endpoint*>(*endpoint_ptr);
+      grpc_endpoint_out->endpoint = std::move(*endpoint);
+    } else {
+      grpc_endpoint_destroy(*endpoint_ptr);
+      *endpoint_ptr = nullptr;
+    }
+    grpc_core::Closure::Run(DEBUG_LOCATION, closure,
+                            absl_status_to_grpc_error(endpoint.status()));
+    exec_ctx.Flush();
+    grpc_pollset_ee_broadcast_event();
+  };
+}
+
+/// Usage note: this method does not take ownership of any pointer arguments.
+void tcp_connect(grpc_closure* on_connect, grpc_endpoint** endpoint,
+                 grpc_pollset_set* /* interested_parties */,
+                 const grpc_channel_args* channel_args,
+                 const grpc_resolved_address* addr, grpc_millis deadline) {
+  grpc_event_engine_endpoint* ee_endpoint =
+      reinterpret_cast<grpc_event_engine_endpoint*>(
+          grpc_tcp_create(channel_args, grpc_sockaddr_to_uri(addr)));
+  *endpoint = &ee_endpoint->base;
+  EventEngine::OnConnectCallback ee_on_connect =
+      GrpcClosureToOnConnectCallback(on_connect, endpoint);
+  SliceAllocator sa(ee_endpoint->ru);
+  EventEngine::ResolvedAddress ra(reinterpret_cast<const sockaddr*>(addr->addr),
+                                  addr->len);
+  absl::Time ee_deadline = grpc_core::ToAbslTime(
+      grpc_millis_to_timespec(deadline, GPR_CLOCK_MONOTONIC));
+  ChannelArgsEndpointConfig endpoint_config(channel_args);
+  absl::Status connected = grpc_iomgr_event_engine()->Connect(
+      ee_on_connect, ra, endpoint_config, std::move(sa), ee_deadline);
+  if (!connected.ok()) {
+    // EventEngine failed to start an asynchronous connect.
+    grpc_endpoint_destroy(*endpoint);
+    *endpoint = nullptr;
+    grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_connect,
+                            absl_status_to_grpc_error(connected));
+  }
+}
+
+grpc_error* tcp_server_create(grpc_closure* shutdown_complete,
+                              const grpc_channel_args* args,
+                              grpc_tcp_server** server) {
+  ChannelArgsEndpointConfig endpoint_config(args);
+  grpc_resource_quota* rq = grpc_resource_quota_from_channel_args(args);
+  if (rq == nullptr) {
+    rq = grpc_resource_quota_create(nullptr);
+  }
+  EventEngine* event_engine = grpc_iomgr_event_engine();
+  absl::StatusOr<std::unique_ptr<EventEngine::Listener>> listener =
+      event_engine->CreateListener(
+          [server](std::unique_ptr<EventEngine::Endpoint> ee_endpoint) {
+            grpc_core::ExecCtx exec_ctx;
+            GPR_ASSERT((*server)->on_accept_internal != nullptr);
+            grpc_event_engine_endpoint* iomgr_endpoint =
+                grpc_tcp_server_endpoint_create(std::move(ee_endpoint));
+            grpc_tcp_server_acceptor* acceptor =
+                static_cast<grpc_tcp_server_acceptor*>(
+                    gpr_zalloc(sizeof(*acceptor)));
+            acceptor->from_server = *server;
+            acceptor->external_connection = false;
+            (*server)->on_accept_internal((*server)->on_accept_internal_arg,
+                                          &iomgr_endpoint->base, nullptr,
+                                          acceptor);
+            exec_ctx.Flush();
+            grpc_pollset_ee_broadcast_event();
+          },
+          GrpcClosureToCallback(shutdown_complete, GRPC_ERROR_NONE),
+          endpoint_config, SliceAllocatorFactory(rq));
+  if (!listener.ok()) {
+    return absl_status_to_grpc_error(listener.status());
+  }
+  *server = new grpc_tcp_server(std::move(*listener), rq);
+  return GRPC_ERROR_NONE;
+}
+
+void tcp_server_start(grpc_tcp_server* server,
+                      const std::vector<grpc_pollset*>* /* pollsets */,
+                      grpc_tcp_server_cb on_accept_cb, void* cb_arg) {
+  server->on_accept_internal = on_accept_cb;
+  server->on_accept_internal_arg = cb_arg;
+  // The iomgr API does not handle situations where the server cannot start, so
+  // a crash may be preferable for now.
+  GPR_ASSERT(server->listener->Start().ok());
+}
+
+grpc_error* tcp_server_add_port(grpc_tcp_server* s,
+                                const grpc_resolved_address* addr,
+                                int* out_port) {
+  EventEngine::ResolvedAddress ra(reinterpret_cast<const sockaddr*>(addr->addr),
+                                  addr->len);
+  auto port = s->listener->Bind(ra);
+  if (!port.ok()) {
+    return absl_status_to_grpc_error(port.status());
+  }
+  *out_port = *port;
+  return GRPC_ERROR_NONE;
+}
+
+grpc_core::TcpServerFdHandler* tcp_server_create_fd_handler(
+    grpc_tcp_server* /* s */) {
+  // EventEngine-iomgr does not support fds.
+  return nullptr;
+}
+
+unsigned tcp_server_port_fd_count(grpc_tcp_server* /* s */,
+                                  unsigned /* port_index */) {
+  return 0;
+}
+
+int tcp_server_port_fd(grpc_tcp_server* /* s */, unsigned /* port_index */,
+                       unsigned /* fd_index */) {
+  // Note: only used internally
+  return -1;
+}
+
+grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) {
+  s->refcount.Ref(DEBUG_LOCATION, "server ref");
+  return s;
+}
+
+void tcp_server_shutdown_starting_add(grpc_tcp_server* s,
+                                      grpc_closure* shutdown_starting) {
+  grpc_core::MutexLock lock(&s->mu);
+  grpc_closure_list_append(&s->shutdown_starting, shutdown_starting,
+                           GRPC_ERROR_NONE);
+}
+
+void tcp_server_unref(grpc_tcp_server* s) {
+  if (GPR_UNLIKELY(s->refcount.Unref(DEBUG_LOCATION, "server unref"))) {
+    delete s;
+  }
+}
+
+// No-op, all are handled on listener unref
+void tcp_server_shutdown_listeners(grpc_tcp_server* /* s */) {}
+
+}  // namespace
+
+grpc_tcp_client_vtable grpc_event_engine_tcp_client_vtable = {tcp_connect};
+grpc_tcp_server_vtable grpc_event_engine_tcp_server_vtable = {
+    tcp_server_create,        tcp_server_start,
+    tcp_server_add_port,      tcp_server_create_fd_handler,
+    tcp_server_port_fd_count, tcp_server_port_fd,
+    tcp_server_ref,           tcp_server_shutdown_starting_add,
+    tcp_server_unref,         tcp_server_shutdown_listeners};
+
+// Methods that are expected to exist elsewhere in the codebase.
+
+struct grpc_fd {
+  int fd;
+};
+
+grpc_fd* grpc_fd_create(int /* fd */, const char* /* name */,
+                        bool /* track_err */) {
+  return nullptr;
+}
+
+grpc_endpoint* grpc_tcp_client_create_from_fd(
+    grpc_fd* /* fd */, const grpc_channel_args* /* channel_args */,
+    const char* /* addr_str */) {
+  return nullptr;
+}
+
+#endif  // GRPC_USE_EVENT_ENGINE
diff --git a/src/core/lib/iomgr/event_engine/timer.cc b/src/core/lib/iomgr/event_engine/timer.cc
new file mode 100644 (file)
index 0000000..b0a0047
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_USE_EVENT_ENGINE
+#include <grpc/event_engine/event_engine.h>
+
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/event_engine/closure.h"
+#include "src/core/lib/iomgr/event_engine/iomgr.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/surface/init.h"
+#include "src/core/lib/transport/error_utils.h"
+
+namespace {
+using ::grpc_event_engine::experimental::EventEngine;
+using ::grpc_event_engine::experimental::GrpcClosureToCallback;
+
+void timer_init(grpc_timer* timer, grpc_millis deadline,
+                grpc_closure* closure) {
+  timer->ee_task_handle = grpc_iomgr_event_engine()->RunAt(
+      grpc_core::ToAbslTime(
+          grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME)),
+      GrpcClosureToCallback(closure, GRPC_ERROR_NONE), {});
+}
+
+void timer_cancel(grpc_timer* timer) {
+  auto handle = timer->ee_task_handle;
+  grpc_iomgr_event_engine()->TryCancel(handle);
+}
+
+/* Internal API */
+grpc_timer_check_result timer_check(grpc_millis* /* next */) {
+  return GRPC_TIMERS_NOT_CHECKED;
+}
+void timer_list_init() {}
+void timer_list_shutdown(void) {}
+void timer_consume_kick(void) {}
+
+}  // namespace
+
+grpc_timer_vtable grpc_event_engine_timer_vtable = {
+    timer_init,      timer_cancel,        timer_check,
+    timer_list_init, timer_list_shutdown, timer_consume_kick};
+
+#endif  // GRPC_USE_EVENT_ENGINE
index 54ab9fd..b1ae781 100644 (file)
 
 #include "src/core/lib/iomgr/exec_ctx.h"
 
+#include <grpc/event_engine/event_engine.h>
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 
 #include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/event_engine/closure.h"
+#include "src/core/lib/iomgr/event_engine/iomgr.h"
 #include "src/core/lib/profiling/timers.h"
 
 static void exec_ctx_run(grpc_closure* closure, grpc_error_handle error) {
@@ -47,8 +50,13 @@ static void exec_ctx_run(grpc_closure* closure, grpc_error_handle error) {
 }
 
 static void exec_ctx_sched(grpc_closure* closure, grpc_error_handle error) {
+#if defined(GRPC_USE_EVENT_ENGINE) && \
+    defined(GRPC_EVENT_ENGINE_REPLACE_EXEC_CTX)
+  grpc_iomgr_event_engine()->Run(GrpcClosureToCallback(closure, error), {});
+#else
   grpc_closure_list_append(grpc_core::ExecCtx::Get()->closure_list(), closure,
                            error);
+#endif
 }
 
 static gpr_timespec g_start_time;
index 9cb5257..4123b3c 100644 (file)
@@ -349,8 +349,7 @@ class ApplicationCallbackExecCtx {
     }
   }
 
-  static void Enqueue(grpc_experimental_completion_queue_functor* functor,
-                      int is_success) {
+  static void Enqueue(grpc_completion_queue_functor* functor, int is_success) {
     functor->internal_success = is_success;
     functor->internal_next = nullptr;
 
@@ -375,8 +374,8 @@ class ApplicationCallbackExecCtx {
 
  private:
   uintptr_t flags_{0u};
-  grpc_experimental_completion_queue_functor* head_{nullptr};
-  grpc_experimental_completion_queue_functor* tail_{nullptr};
+  grpc_completion_queue_functor* head_{nullptr};
+  grpc_completion_queue_functor* tail_{nullptr};
   GPR_TLS_CLASS_DECL(callback_exec_ctx_);
 };
 }  // namespace grpc_core
index 859c49a..a0dd871 100644 (file)
@@ -41,8 +41,7 @@ void ThreadPoolWorker::Run() {
       break;
     }
     // Runs closure
-    auto* closure =
-        static_cast<grpc_experimental_completion_queue_functor*>(elem);
+    auto* closure = static_cast<grpc_completion_queue_functor*>(elem);
     closure->functor_run(closure, closure->internal_success);
   }
 }
@@ -120,7 +119,7 @@ ThreadPool::~ThreadPool() {
   delete queue_;
 }
 
-void ThreadPool::Add(grpc_experimental_completion_queue_functor* closure) {
+void ThreadPool::Add(grpc_completion_queue_functor* closure) {
   AssertHasNotBeenShutDown();
   queue_->Put(static_cast<void*>(closure));
 }
index 66218b5..55bad8b 100644 (file)
@@ -43,7 +43,7 @@ class ThreadPoolInterface {
   // current thread to be blocked (in case of unable to schedule).
   // Closure should contain a function pointer and arguments it will take, more
   // details for closure struct at /grpc/include/grpc/impl/codegen/grpc_types.h
-  virtual void Add(grpc_experimental_completion_queue_functor* closure) = 0;
+  virtual void Add(grpc_completion_queue_functor* closure) = 0;
 
   // Returns the current number of pending closures
   virtual int num_pending_closures() const = 0;
@@ -120,7 +120,7 @@ class ThreadPool : public ThreadPoolInterface {
 
   // Adds given closure into pending queue immediately. Since closure queue has
   // infinite length, this routine will not block.
-  void Add(grpc_experimental_completion_queue_functor* closure) override;
+  void Add(grpc_completion_queue_functor* closure) override;
 
   int num_pending_closures() const override;
   int pool_capacity() const override;
index 1210bd7..01d3a53 100644 (file)
@@ -94,7 +94,6 @@ void grpc_iomgr_shutdown() {
   {
     grpc_timer_manager_shutdown();
     grpc_iomgr_platform_flush();
-    grpc_core::Executor::ShutdownAll();
 
     gpr_mu_lock(&g_mu);
     g_shutdown = 1;
@@ -149,6 +148,7 @@ void grpc_iomgr_shutdown() {
     gpr_mu_unlock(&g_mu);
     grpc_timer_list_shutdown();
     grpc_core::ExecCtx::Get()->Flush();
+    grpc_core::Executor::ShutdownAll();
   }
 
   /* ensure all threads have left g_mu */
index 0407e30..2450ef0 100644 (file)
@@ -41,11 +41,13 @@ extern grpc_address_resolver_vtable grpc_posix_resolver_vtable;
 static void iomgr_platform_init(void) {
   grpc_wakeup_fd_global_init();
   grpc_event_engine_init();
+  grpc_tcp_posix_init();
 }
 
 static void iomgr_platform_flush(void) {}
 
 static void iomgr_platform_shutdown(void) {
+  grpc_tcp_posix_shutdown();
   grpc_event_engine_shutdown();
   grpc_wakeup_fd_global_destroy();
 }
index 78b2985..27af38e 100644 (file)
@@ -84,7 +84,43 @@ static grpc_iomgr_platform_vtable apple_vtable = {
     apple_iomgr_platform_is_any_background_poller_thread,
     apple_iomgr_platform_add_closure_to_background_poller};
 
+namespace {
+struct CFStreamEnv {
+  bool enable_cfstream;
+  bool enable_cfstream_run_loop;
+};
+
+// Parses environment variables for CFStream specific settings
+CFStreamEnv ParseEnvForCFStream() {
+  CFStreamEnv env;
+  char* enable_cfstream_str = getenv(grpc_cfstream_env_var);
+  env.enable_cfstream =
+      enable_cfstream_str == nullptr || enable_cfstream_str[0] != '0';
+  char* enable_cfstream_run_loop_str = getenv(grpc_cfstream_run_loop_env_var);
+  // CFStream run-loop is disabled by default. The user has to enable it
+  // explicitly with environment variable.
+  env.enable_cfstream_run_loop = enable_cfstream_run_loop_str != nullptr &&
+                                 enable_cfstream_run_loop_str[0] == '1';
+  return env;
+}
+
+void MaybeInitializeTcpPosix(void) {
+  CFStreamEnv env = ParseEnvForCFStream();
+  if (!env.enable_cfstream || !env.enable_cfstream_run_loop) {
+    grpc_tcp_posix_init();
+  }
+}
+
+void MaybeShutdownTcpPosix(void) {
+  CFStreamEnv env = ParseEnvForCFStream();
+  if (!env.enable_cfstream || !env.enable_cfstream_run_loop) {
+    grpc_tcp_posix_shutdown();
+  }
+}
+}  // namespace
+
 static void iomgr_platform_init(void) {
+  MaybeInitializeTcpPosix();
   grpc_wakeup_fd_global_init();
   grpc_event_engine_init();
 }
@@ -94,6 +130,7 @@ static void iomgr_platform_flush(void) {}
 static void iomgr_platform_shutdown(void) {
   grpc_event_engine_shutdown();
   grpc_wakeup_fd_global_destroy();
+  MaybeShutdownTcpPosix();
 }
 
 static void iomgr_platform_shutdown_background_closure(void) {
@@ -118,22 +155,15 @@ static grpc_iomgr_platform_vtable vtable = {
     iomgr_platform_add_closure_to_background_poller};
 
 void grpc_set_default_iomgr_platform() {
-  char* enable_cfstream_str = getenv(grpc_cfstream_env_var);
-  bool enable_cfstream =
-      enable_cfstream_str == nullptr || enable_cfstream_str[0] != '0';
-  char* enable_cfstream_run_loop_str = getenv(grpc_cfstream_run_loop_env_var);
-  // CFStream run-loop is disabled by default. The user has to enable it
-  // explicitly with environment variable.
-  bool enable_cfstream_run_loop = enable_cfstream_run_loop_str != nullptr &&
-                                  enable_cfstream_run_loop_str[0] == '1';
-  if (!enable_cfstream) {
+  CFStreamEnv env = ParseEnvForCFStream();
+  if (!env.enable_cfstream) {
     // Use POSIX sockets for both client and server
     grpc_set_tcp_client_impl(&grpc_posix_tcp_client_vtable);
     grpc_set_tcp_server_impl(&grpc_posix_tcp_server_vtable);
     grpc_set_pollset_vtable(&grpc_posix_pollset_vtable);
     grpc_set_pollset_set_vtable(&grpc_posix_pollset_set_vtable);
     grpc_set_iomgr_platform_vtable(&vtable);
-  } else if (enable_cfstream && !enable_cfstream_run_loop) {
+  } else if (env.enable_cfstream && !env.enable_cfstream_run_loop) {
     // Use CFStream with dispatch queue for client; use POSIX sockets for server
     grpc_set_tcp_client_impl(&grpc_cfstream_client_vtable);
     grpc_set_tcp_server_impl(&grpc_posix_tcp_server_vtable);
index 62c4b39..1165ea8 100644 (file)
@@ -77,14 +77,14 @@ static grpc_error_handle pollset_work(grpc_pollset* pollset,
   // control back to the application
   grpc_core::ExecCtx* curr = grpc_core::ExecCtx::Get();
   grpc_core::ExecCtx::Set(nullptr);
-  poller_vtable->poll(static_cast<size_t>(timeout));
+  grpc_error* err = poller_vtable->poll(static_cast<size_t>(timeout));
   grpc_core::ExecCtx::Set(curr);
   grpc_core::ExecCtx::Get()->InvalidateNow();
   if (grpc_core::ExecCtx::Get()->HasWork()) {
     grpc_core::ExecCtx::Get()->Flush();
   }
   gpr_mu_lock(&pollset->mu);
-  return GRPC_ERROR_NONE;
+  return err;
 }
 
 static grpc_error_handle pollset_kick(
index 9e2027f..de3067f 100644 (file)
 
 #include <grpc/support/port_platform.h>
 
+#include "src/core/lib/iomgr/error.h"
+
 #include <stddef.h>
 
 typedef struct grpc_custom_poller_vtable {
   void (*init)();
-  void (*poll)(size_t timeout_ms);
+  grpc_error* (*poll)(size_t timeout_ms);
   void (*kick)();
   void (*shutdown)();
 } grpc_custom_poller_vtable;
index bade6ea..ef76870 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include "src/core/lib/iomgr/error.h"
 #include "src/core/lib/iomgr/pollset_custom.h"
 
 #include <uv.h>
@@ -54,7 +55,7 @@ static void empty_timer_cb(uv_timer_t* handle) {}
 
 static void kick_timer_cb(uv_timer_t* handle) { g_kicked = false; }
 
-static void run_loop(size_t timeout) {
+static grpc_error* run_loop(size_t timeout) {
   if (grpc_pollset_work_run_loop) {
     if (timeout == 0) {
       uv_run(uv_default_loop(), UV_RUN_NOWAIT);
@@ -64,6 +65,7 @@ static void run_loop(size_t timeout) {
       uv_timer_stop(&g_handle->poll_timer);
     }
   }
+  return GRPC_ERROR_NONE;
 }
 
 static void kick() {
index de82bcc..84fdefc 100644 (file)
 #ifndef GRPC_CORE_LIB_IOMGR_POLLSET_UV_H
 #define GRPC_CORE_LIB_IOMGR_POLLSET_UV_H
 
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/error.h"
+
 extern int grpc_pollset_work_run_loop;
 
 typedef struct grpc_custom_poller_vtable {
   void (*init)(void);
-  void (*run_loop)(int blocking);
+  grpc_error* (*run_loop)(int blocking);
 } grpc_custom_poller_vtable;
 
 void grpc_custom_pollset_global_init(grpc_custom_poller_vtable* vtable);
index 8d3cd98..5f14cd6 100644 (file)
  * limitations under the License.
  *
  */
-
-#include <grpc/support/port_platform.h>
-
 #ifndef GRPC_CORE_LIB_IOMGR_PORT_H
 #define GRPC_CORE_LIB_IOMGR_PORT_H
 
+#include <grpc/support/port_platform.h>
+
 #ifdef GRPC_UV
 #ifndef GRPC_CUSTOM_SOCKET
 #define GRPC_CUSTOM_SOCKET
@@ -33,6 +32,8 @@
 #endif
 #if defined(GRPC_CUSTOM_SOCKET)
 // Do Nothing
+#elif defined(GRPC_USE_EVENT_ENGINE)
+// Do Nothing
 #elif defined(GPR_WINDOWS)
 #define GRPC_WINSOCK_SOCKET 1
 #define GRPC_WINDOWS_SOCKETUTILS 1
 #endif
 
 #if defined(GRPC_POSIX_SOCKET) + defined(GRPC_WINSOCK_SOCKET) + \
-        defined(GRPC_CUSTOM_SOCKET) + defined(GRPC_CFSTREAM) != \
+        defined(GRPC_CUSTOM_SOCKET) + defined(GRPC_CFSTREAM) +  \
+        defined(GRPC_USE_EVENT_ENGINE) !=                       \
     1
 #error \
-    "Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GRPC_CUSTOM_SOCKET, GRPC_CFSTREAM"
+    "Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GRPC_CUSTOM_SOCKET, GRPC_CFSTREAM, GRPC_USE_EVENT_ENGINE"
 #endif
 
 #ifdef GRPC_POSIX_SOCKET
index 83c6aac..30c0db1 100644 (file)
  * limitations under the License.
  *
  */
-
 #include <grpc/support/port_platform.h>
 
+#include <grpc/event_engine/event_engine.h>
 #include <grpc/support/alloc.h>
 #include "src/core/lib/iomgr/resolve_address.h"
 
+namespace grpc_core {
+const char* kDefaultSecurePort = "https";
+}  // namespace grpc_core
+
 grpc_address_resolver_vtable* grpc_resolve_address_impl;
 
 void grpc_set_resolver_impl(grpc_address_resolver_vtable* vtable) {
index 112d0ac..bcb3666 100644 (file)
@@ -49,6 +49,12 @@ struct grpc_resolved_addresses {
   size_t naddrs;
   grpc_resolved_address* addrs;
 };
+
+namespace grpc_core {
+extern const char* kDefaultSecurePort;
+constexpr int kDefaultSecurePortInt = 443;
+}  // namespace grpc_core
+
 typedef struct grpc_address_resolver_vtable {
   void (*resolve_address)(const char* addr, const char* default_port,
                           grpc_pollset_set* interested_parties,
index 5edf735..96a8f77 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <grpc/support/port_platform.h>
 
+#include "src/core/lib/event_engine/sockaddr.h"
 #include "src/core/lib/iomgr/sockaddr_custom.h"
 #include "src/core/lib/iomgr/sockaddr_posix.h"
 #include "src/core/lib/iomgr/sockaddr_windows.h"
index 918754a..b11d204 100644 (file)
@@ -21,6 +21,7 @@
 #include "src/core/lib/iomgr/socket_mutator.h"
 
 #include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 
 #include "src/core/lib/channel/channel_args.h"
@@ -37,8 +38,20 @@ grpc_socket_mutator* grpc_socket_mutator_ref(grpc_socket_mutator* mutator) {
   return mutator;
 }
 
-bool grpc_socket_mutator_mutate_fd(grpc_socket_mutator* mutator, int fd) {
-  return mutator->vtable->mutate_fd(fd, mutator);
+bool grpc_socket_mutator_mutate_fd(grpc_socket_mutator* mutator, int fd,
+                                   grpc_fd_usage usage) {
+  if (mutator->vtable->mutate_fd_2 != nullptr) {
+    grpc_mutate_socket_info info{fd, usage};
+    return mutator->vtable->mutate_fd_2(&info, mutator);
+  }
+  switch (usage) {
+    case GRPC_FD_SERVER_CONNECTION_USAGE:
+      return true;
+    case GRPC_FD_CLIENT_CONNECTION_USAGE:
+    case GRPC_FD_SERVER_LISTENER_USAGE:
+      return mutator->vtable->mutate_fd(fd, mutator);
+  }
+  GPR_UNREACHABLE_CODE(return false);
 }
 
 int grpc_socket_mutator_compare(grpc_socket_mutator* a,
index 9f77a3b..55e69e3 100644 (file)
 
 #include <stdbool.h>
 
+/** How is an fd to be used? */
+typedef enum {
+  /** Used for client connection */
+  GRPC_FD_CLIENT_CONNECTION_USAGE,
+  /** Used for server listening */
+  GRPC_FD_SERVER_LISTENER_USAGE,
+  /** Used for server connection */
+  GRPC_FD_SERVER_CONNECTION_USAGE,
+} grpc_fd_usage;
+
+/** Information about an fd to mutate */
+typedef struct {
+  /** File descriptor to mutate */
+  int fd;
+  /** How the fd will be used */
+  grpc_fd_usage usage;
+} grpc_mutate_socket_info;
+
 /** The virtual table of grpc_socket_mutator */
 struct grpc_socket_mutator_vtable {
-  /** Mutates the socket options of \a fd */
+  /** Mutates the socket options of \a fd -- deprecated, prefer mutate_fd_2 */
   bool (*mutate_fd)(int fd, grpc_socket_mutator* mutator);
   /** Compare socket mutator \a a and \a b */
   int (*compare)(grpc_socket_mutator* a, grpc_socket_mutator* b);
   /** Destroys the socket mutator instance */
   void (*destroy)(grpc_socket_mutator* mutator);
+  /** Mutates the socket options of the fd in \a info - if set takes preference
+   * to mutate_fd */
+  bool (*mutate_fd_2)(const grpc_mutate_socket_info* info,
+                      grpc_socket_mutator* mutator);
 };
+
 /** The Socket Mutator interface allows changes on socket options */
 struct grpc_socket_mutator {
   const grpc_socket_mutator_vtable* vtable;
@@ -49,7 +72,8 @@ void grpc_socket_mutator_init(grpc_socket_mutator* mutator,
 grpc_arg grpc_socket_mutator_to_arg(grpc_socket_mutator* mutator);
 
 /** Perform the file descriptor mutation operation of \a mutator on \a fd */
-bool grpc_socket_mutator_mutate_fd(grpc_socket_mutator* mutator, int fd);
+bool grpc_socket_mutator_mutate_fd(grpc_socket_mutator* mutator, int fd,
+                                   grpc_fd_usage usage);
 
 /** Compare if \a a and \a b are the same mutator or have same settings */
 int grpc_socket_mutator_compare(grpc_socket_mutator* a, grpc_socket_mutator* b);
index e8c9ee1..a9e45ef 100644 (file)
@@ -388,17 +388,17 @@ grpc_error_handle grpc_set_socket_tcp_user_timeout(
 }
 
 /* set a socket using a grpc_socket_mutator */
-grpc_error_handle grpc_set_socket_with_mutator(int fd,
+grpc_error_handle grpc_set_socket_with_mutator(int fd, grpc_fd_usage usage,
                                                grpc_socket_mutator* mutator) {
   GPR_ASSERT(mutator);
-  if (!grpc_socket_mutator_mutate_fd(mutator, fd)) {
+  if (!grpc_socket_mutator_mutate_fd(mutator, fd, usage)) {
     return GRPC_ERROR_CREATE_FROM_STATIC_STRING("grpc_socket_mutator failed.");
   }
   return GRPC_ERROR_NONE;
 }
 
 grpc_error_handle grpc_apply_socket_mutator_in_args(
-    int fd, const grpc_channel_args* args) {
+    int fd, grpc_fd_usage usage, const grpc_channel_args* args) {
   const grpc_arg* socket_mutator_arg =
       grpc_channel_args_find(args, GRPC_ARG_SOCKET_MUTATOR);
   if (socket_mutator_arg == nullptr) {
@@ -407,7 +407,7 @@ grpc_error_handle grpc_apply_socket_mutator_in_args(
   GPR_DEBUG_ASSERT(socket_mutator_arg->type == GRPC_ARG_POINTER);
   grpc_socket_mutator* mutator =
       static_cast<grpc_socket_mutator*>(socket_mutator_arg->value.pointer.p);
-  return grpc_set_socket_with_mutator(fd, mutator);
+  return grpc_set_socket_with_mutator(fd, usage, mutator);
 }
 
 static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT;
index 13c22bc..d1a52a2 100644 (file)
@@ -101,12 +101,12 @@ grpc_error_handle grpc_set_socket_sndbuf(int fd, int buffer_size_bytes);
 grpc_error_handle grpc_set_socket_rcvbuf(int fd, int buffer_size_bytes);
 
 /* Tries to set the socket using a grpc_socket_mutator */
-grpc_error_handle grpc_set_socket_with_mutator(int fd,
+grpc_error_handle grpc_set_socket_with_mutator(int fd, grpc_fd_usage usage,
                                                grpc_socket_mutator* mutator);
 
 /* Extracts the first socket mutator from args if any and applies on the fd. */
 grpc_error_handle grpc_apply_socket_mutator_in_args(
-    int fd, const grpc_channel_args* args);
+    int fd, grpc_fd_usage usage, const grpc_channel_args* args);
 
 /* An enum to keep track of IPv4/IPv6 socket modes.
 
index 741cc6a..54c1511 100644 (file)
@@ -39,6 +39,7 @@
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/iomgr/sockaddr.h"
 #include "src/core/lib/iomgr/socket_mutator.h"
@@ -87,7 +88,8 @@ static grpc_error_handle prepare_socket(const grpc_resolved_address* addr,
   err = grpc_set_socket_no_sigpipe_if_possible(fd);
   if (err != GRPC_ERROR_NONE) goto error;
 
-  err = grpc_apply_socket_mutator_in_args(fd, channel_args);
+  err = grpc_apply_socket_mutator_in_args(fd, GRPC_FD_CLIENT_CONNECTION_USAGE,
+                                          channel_args);
   if (err != GRPC_ERROR_NONE) goto error;
 
   goto done;
@@ -238,7 +240,10 @@ finish:
     grpc_channel_args_destroy(ac->channel_args);
     delete ac;
   }
-  grpc_core::ExecCtx::Run(DEBUG_LOCATION, closure, error);
+  // Push async connect closure to the executor since this may actually be
+  // called during the shutdown process, in which case a deadlock could form
+  // between the core shutdown mu and the connector mu (b/188239051)
+  grpc_core::Executor::Run(closure, error);
 }
 
 grpc_error_handle grpc_tcp_client_prepare_fd(
index 1f28b77..ad09713 100644 (file)
@@ -432,8 +432,10 @@ static void ZerocopyDisableAndWaitForRemaining(grpc_tcp* tcp);
 
 #define BACKUP_POLLER_POLLSET(b) ((grpc_pollset*)((b) + 1))
 
-static gpr_atm g_uncovered_notifications_pending;
-static gpr_atm g_backup_poller; /* backup_poller* */
+static grpc_core::Mutex* g_backup_poller_mu = nullptr;
+static int g_uncovered_notifications_pending
+    ABSL_GUARDED_BY(g_backup_poller_mu);
+static backup_poller* g_backup_poller ABSL_GUARDED_BY(g_backup_poller_mu);
 
 static void tcp_handle_read(void* arg /* grpc_tcp */, grpc_error_handle error);
 static void tcp_handle_write(void* arg /* grpc_tcp */, grpc_error_handle error);
@@ -461,17 +463,13 @@ static void run_poller(void* bp, grpc_error_handle /*error_ignored*/) {
       "backup_poller:pollset_work",
       grpc_pollset_work(BACKUP_POLLER_POLLSET(p), nullptr, deadline));
   gpr_mu_unlock(p->pollset_mu);
-  /* last "uncovered" notification is the ref that keeps us polling, if we get
-   * there try a cas to release it */
-  if (gpr_atm_no_barrier_load(&g_uncovered_notifications_pending) == 1 &&
-      gpr_atm_full_cas(&g_uncovered_notifications_pending, 1, 0)) {
-    gpr_mu_lock(p->pollset_mu);
-    bool cas_ok =
-        gpr_atm_full_cas(&g_backup_poller, reinterpret_cast<gpr_atm>(p), 0);
-    if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
-      gpr_log(GPR_INFO, "BACKUP_POLLER:%p done cas_ok=%d", p, cas_ok);
-    }
-    gpr_mu_unlock(p->pollset_mu);
+  g_backup_poller_mu->Lock();
+  /* last "uncovered" notification is the ref that keeps us polling */
+  if (g_uncovered_notifications_pending == 1) {
+    GPR_ASSERT(g_backup_poller == p);
+    g_backup_poller = nullptr;
+    g_uncovered_notifications_pending = 0;
+    g_backup_poller_mu->Unlock();
     if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "BACKUP_POLLER:%p shutdown", p);
     }
@@ -479,6 +477,7 @@ static void run_poller(void* bp, grpc_error_handle /*error_ignored*/) {
                           GRPC_CLOSURE_INIT(&p->run_poller, done_poller, p,
                                             grpc_schedule_on_exec_ctx));
   } else {
+    g_backup_poller_mu->Unlock();
     if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "BACKUP_POLLER:%p reschedule", p);
     }
@@ -489,15 +488,17 @@ static void run_poller(void* bp, grpc_error_handle /*error_ignored*/) {
 }
 
 static void drop_uncovered(grpc_tcp* /*tcp*/) {
-  backup_poller* p =
-      reinterpret_cast<backup_poller*>(gpr_atm_acq_load(&g_backup_poller));
-  gpr_atm old_count =
-      gpr_atm_full_fetch_add(&g_uncovered_notifications_pending, -1);
+  int old_count;
+  backup_poller* p;
+  g_backup_poller_mu->Lock();
+  p = g_backup_poller;
+  old_count = g_uncovered_notifications_pending--;
+  g_backup_poller_mu->Unlock();
+  GPR_ASSERT(old_count > 1);
   if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
-    gpr_log(GPR_INFO, "BACKUP_POLLER:%p uncover cnt %d->%d", p,
-            static_cast<int>(old_count), static_cast<int>(old_count) - 1);
+    gpr_log(GPR_INFO, "BACKUP_POLLER:%p uncover cnt %d->%d", p, old_count,
+            old_count - 1);
   }
-  GPR_ASSERT(old_count != 1);
 }
 
 // gRPC API considers a Write operation to be done the moment it clears â€˜flow
@@ -509,38 +510,33 @@ static void drop_uncovered(grpc_tcp* /*tcp*/) {
 // polling thread and progress is made) and hence add it to a backup poller here
 static void cover_self(grpc_tcp* tcp) {
   backup_poller* p;
-  gpr_atm old_count =
-      gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, 2);
-  if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
-    gpr_log(GPR_INFO, "BACKUP_POLLER: cover cnt %d->%d",
-            static_cast<int>(old_count), 2 + static_cast<int>(old_count));
-  }
-  if (old_count == 0) {
-    GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED();
+  g_backup_poller_mu->Lock();
+  int old_count = 0;
+  if (g_uncovered_notifications_pending == 0) {
+    g_uncovered_notifications_pending = 2;
     p = static_cast<backup_poller*>(
         gpr_zalloc(sizeof(*p) + grpc_pollset_size()));
+    g_backup_poller = p;
+    grpc_pollset_init(BACKUP_POLLER_POLLSET(p), &p->pollset_mu);
+    g_backup_poller_mu->Unlock();
+    GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED();
     if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "BACKUP_POLLER:%p create", p);
     }
-    grpc_pollset_init(BACKUP_POLLER_POLLSET(p), &p->pollset_mu);
-    gpr_atm_rel_store(&g_backup_poller, (gpr_atm)p);
     grpc_core::Executor::Run(
         GRPC_CLOSURE_INIT(&p->run_poller, run_poller, p, nullptr),
         GRPC_ERROR_NONE, grpc_core::ExecutorType::DEFAULT,
         grpc_core::ExecutorJobType::LONG);
   } else {
-    while ((p = reinterpret_cast<backup_poller*>(
-                gpr_atm_acq_load(&g_backup_poller))) == nullptr) {
-      // spin waiting for backup poller
-    }
+    old_count = g_uncovered_notifications_pending++;
+    p = g_backup_poller;
+    g_backup_poller_mu->Unlock();
   }
   if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
-    gpr_log(GPR_INFO, "BACKUP_POLLER:%p add %p", p, tcp);
+    gpr_log(GPR_INFO, "BACKUP_POLLER:%p add %p cnt %d->%d", p, tcp,
+            old_count - 1, old_count);
   }
   grpc_pollset_add_fd(BACKUP_POLLER_POLLSET(p), tcp->em_fd);
-  if (old_count != 0) {
-    drop_uncovered(tcp);
-  }
 }
 
 static void notify_on_read(grpc_tcp* tcp) {
@@ -1164,9 +1160,9 @@ static bool process_errors(grpc_tcp* tcp) {
     struct cmsghdr align;
   } aligned_buf;
   msg.msg_control = aligned_buf.rbuf;
-  msg.msg_controllen = sizeof(aligned_buf.rbuf);
   int r, saved_errno;
   while (true) {
+    msg.msg_controllen = sizeof(aligned_buf.rbuf);
     do {
       r = recvmsg(tcp->fd, &msg, MSG_ERRQUEUE);
       saved_errno = errno;
@@ -1873,4 +1869,11 @@ void grpc_tcp_destroy_and_release_fd(grpc_endpoint* ep, int* fd,
   TCP_UNREF(tcp, "destroy");
 }
 
+void grpc_tcp_posix_init() { g_backup_poller_mu = new grpc_core::Mutex; }
+
+void grpc_tcp_posix_shutdown() {
+  delete g_backup_poller_mu;
+  g_backup_poller_mu = nullptr;
+}
+
 #endif /* GRPC_POSIX_SOCKET_TCP */
index eff825c..f9ab134 100644 (file)
@@ -57,4 +57,12 @@ int grpc_tcp_fd(grpc_endpoint* ep);
 void grpc_tcp_destroy_and_release_fd(grpc_endpoint* ep, int* fd,
                                      grpc_closure* done);
 
+#ifdef GRPC_POSIX_SOCKET_TCP
+
+void grpc_tcp_posix_init();
+
+void grpc_tcp_posix_shutdown();
+
+#endif /* GRPC_POSIX_SOCKET_TCP */
+
 #endif /* GRPC_CORE_LIB_IOMGR_TCP_POSIX_H */
index fff7545..8ffd728 100644 (file)
@@ -362,10 +362,9 @@ static grpc_error_handle tcp_server_add_port(grpc_tcp_server* s,
     for (sp = s->head; sp; sp = sp->next) {
       socket = sp->socket;
       sockname_temp.len = GRPC_MAX_SOCKADDR_SIZE;
-      if (nullptr == grpc_custom_socket_vtable->getsockname(
-                         socket,
-                         reinterpret_cast<grpc_sockaddr*>(&sockname_temp.addr),
-                         reinterpret_cast<int*>(&sockname_temp.len))) {
+      if (grpc_custom_socket_vtable->getsockname(
+              socket, reinterpret_cast<grpc_sockaddr*>(&sockname_temp.addr),
+              reinterpret_cast<int*>(&sockname_temp.len)) == GRPC_ERROR_NONE) {
         *port = grpc_sockaddr_get_port(&sockname_temp);
         if (*port > 0) {
           allocated_addr = static_cast<grpc_resolved_address*>(
index 5662aec..43290ec 100644 (file)
@@ -238,6 +238,12 @@ static void on_read(void* arg, grpc_error_handle err) {
 
     grpc_set_socket_no_sigpipe_if_possible(fd);
 
+    err = grpc_apply_socket_mutator_in_args(fd, GRPC_FD_SERVER_CONNECTION_USAGE,
+                                            sp->server->channel_args);
+    if (err != GRPC_ERROR_NONE) {
+      goto error;
+    }
+
     std::string addr_str = grpc_sockaddr_to_uri(&addr);
     if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
       gpr_log(GPR_INFO, "SERVER_CONNECT: incoming connection: %s",
index 6fdf070..2804cac 100644 (file)
@@ -182,7 +182,8 @@ grpc_error_handle grpc_tcp_server_prepare_socket(
   err = grpc_set_socket_no_sigpipe_if_possible(fd);
   if (err != GRPC_ERROR_NONE) goto error;
 
-  err = grpc_apply_socket_mutator_in_args(fd, s->channel_args);
+  err = grpc_apply_socket_mutator_in_args(fd, GRPC_FD_SERVER_LISTENER_USAGE,
+                                          s->channel_args);
   if (err != GRPC_ERROR_NONE) goto error;
 
   if (bind(fd, reinterpret_cast<grpc_sockaddr*>(const_cast<char*>(addr->addr)),
index 11da149..5b55950 100644 (file)
@@ -23,7 +23,9 @@
 
 #include "src/core/lib/iomgr/port.h"
 
+#include <grpc/event_engine/event_engine.h>
 #include <grpc/support/time.h>
+
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/iomgr.h"
 
@@ -40,7 +42,10 @@ typedef struct grpc_timer {
 #endif
 
   // Optional field used by custom timers
-  void* custom_timer;
+  union {
+    void* custom_timer;
+    grpc_event_engine::experimental::EventEngine::TaskHandle ee_task_handle;
+  };
 } grpc_timer;
 
 typedef enum {
index 7889e62..997ffbe 100644 (file)
 
 #include <string>
 
+#include "src/core/lib/gprpp/ref_counted.h"
 #include "src/core/lib/security/authorization/evaluate_args.h"
 
 namespace grpc_core {
 
 // Interface for gRPC Authorization Engine.
-class AuthorizationEngine {
+class AuthorizationEngine : public RefCounted<AuthorizationEngine> {
  public:
   struct Decision {
     enum class Type {
@@ -35,7 +36,6 @@ class AuthorizationEngine {
     std::string matching_policy_name;
   };
 
-  virtual ~AuthorizationEngine() = default;
   virtual Decision Evaluate(const EvaluateArgs& args) const = 0;
 };
 
diff --git a/src/core/lib/security/authorization/authorization_policy_provider.h b/src/core/lib/security/authorization/authorization_policy_provider.h
new file mode 100644 (file)
index 0000000..1c9116a
--- /dev/null
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef GRPC_CORE_LIB_SECURITY_AUTHORIZATION_AUTHORIZATION_POLICY_PROVIDER_H
+#define GRPC_CORE_LIB_SECURITY_AUTHORIZATION_AUTHORIZATION_POLICY_PROVIDER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/dual_ref_counted.h"
+#include "src/core/lib/security/authorization/authorization_engine.h"
+
+struct grpc_authorization_policy_provider
+    : public grpc_core::DualRefCounted<grpc_authorization_policy_provider> {
+ public:
+  virtual grpc_core::RefCountedPtr<grpc_core::AuthorizationEngine>
+  allow_engine() const = 0;
+  virtual grpc_core::RefCountedPtr<grpc_core::AuthorizationEngine> deny_engine()
+      const = 0;
+};
+
+#endif  // GRPC_CORE_LIB_SECURITY_AUTHORIZATION_AUTHORIZATION_POLICY_PROVIDER_H
diff --git a/src/core/lib/security/authorization/authorization_policy_provider_null_vtable.cc b/src/core/lib/security/authorization/authorization_policy_provider_null_vtable.cc
new file mode 100644 (file)
index 0000000..1f1ccc9
--- /dev/null
@@ -0,0 +1,24 @@
+// 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+// Wrapper API declared in grpc.h
+
+// Required only for insecure build targets.
+const grpc_arg_pointer_vtable* grpc_authorization_policy_provider_arg_vtable() {
+  return nullptr;
+}
diff --git a/src/core/lib/security/authorization/authorization_policy_provider_vtable.cc b/src/core/lib/security/authorization/authorization_policy_provider_vtable.cc
new file mode 100644 (file)
index 0000000..ed040ed
--- /dev/null
@@ -0,0 +1,46 @@
+// 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/lib/security/authorization/authorization_policy_provider.h"
+
+namespace {
+
+void* ProviderArgCopy(void* p) {
+  grpc_authorization_policy_provider* provider =
+      static_cast<grpc_authorization_policy_provider*>(p);
+  provider->Ref().release();
+  return provider;
+}
+
+void ProviderArgDestroy(void* p) {
+  grpc_authorization_policy_provider* provider =
+      static_cast<grpc_authorization_policy_provider*>(p);
+  provider->Unref();
+}
+
+int ProviderArgCmp(void* p, void* q) { return GPR_ICMP(p, q); }
+
+}  // namespace
+
+// Wrapper API declared in grpc.h
+
+const grpc_arg_pointer_vtable* grpc_authorization_policy_provider_arg_vtable() {
+  static const grpc_arg_pointer_vtable vtable = {
+      ProviderArgCopy, ProviderArgDestroy, ProviderArgCmp};
+  return &vtable;
+}
index 844d9fb..fae5cc4 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "absl/memory/memory.h"
 
+#include "src/core/lib/address_utils/sockaddr_utils.h"
 #include "src/core/lib/security/authorization/cel_authorization_engine.h"
 
 namespace grpc_core {
@@ -132,7 +133,7 @@ std::unique_ptr<mock_cel::Activation> CelAuthorizationEngine::CreateActivation(
       activation->InsertValue(kHeaders,
                               mock_cel::CelValue::CreateMap(headers_.get()));
     } else if (elem == kSourceAddress) {
-      absl::string_view source_address(args.GetPeerAddress());
+      absl::string_view source_address(args.GetPeerAddressString());
       if (!source_address.empty()) {
         activation->InsertValue(
             kSourceAddress,
@@ -142,7 +143,7 @@ std::unique_ptr<mock_cel::Activation> CelAuthorizationEngine::CreateActivation(
       activation->InsertValue(
           kSourcePort, mock_cel::CelValue::CreateInt64(args.GetPeerPort()));
     } else if (elem == kDestinationAddress) {
-      absl::string_view destination_address(args.GetLocalAddress());
+      absl::string_view destination_address(args.GetLocalAddressString());
       if (!destination_address.empty()) {
         activation->InsertValue(
             kDestinationAddress,
index 12144df..2a8e0a6 100644 (file)
 #include "src/core/lib/security/authorization/evaluate_args.h"
 
 #include "src/core/lib/address_utils/parse_address.h"
+#include "src/core/lib/address_utils/sockaddr_utils.h"
 #include "src/core/lib/gprpp/host_port.h"
+#include "src/core/lib/security/credentials/tls/tls_utils.h"
 #include "src/core/lib/slice/slice_utils.h"
 
 namespace grpc_core {
 
 namespace {
 
-absl::string_view GetAuthPropertyValue(grpc_auth_context* context,
-                                       const char* property_name) {
-  grpc_auth_property_iterator it =
-      grpc_auth_context_find_properties_by_name(context, property_name);
-  const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it);
-  if (prop == nullptr) {
-    gpr_log(GPR_DEBUG, "No value found for %s property.", property_name);
-    return "";
-  }
-  if (grpc_auth_property_iterator_next(&it) != nullptr) {
-    gpr_log(GPR_DEBUG, "Multiple values found for %s property.", property_name);
-    return "";
-  }
-  return absl::string_view(prop->value, prop->value_length);
-}
-
-void ParseEndpointUri(absl::string_view uri_text, std::string* address,
-                      int* port) {
+EvaluateArgs::PerChannelArgs::Address ParseEndpointUri(
+    absl::string_view uri_text) {
+  EvaluateArgs::PerChannelArgs::Address address;
   absl::StatusOr<URI> uri = URI::Parse(uri_text);
   if (!uri.ok()) {
     gpr_log(GPR_DEBUG, "Failed to parse uri.");
-    return;
+    return address;
   }
   absl::string_view host_view;
   absl::string_view port_view;
   if (!SplitHostPort(uri->path(), &host_view, &port_view)) {
     gpr_log(GPR_DEBUG, "Failed to split %s into host and port.",
             uri->path().c_str());
-    return;
+    return address;
   }
-  *address = std::string(host_view);
-  if (!absl::SimpleAtoi(port_view, port)) {
+  if (!absl::SimpleAtoi(port_view, &address.port)) {
     gpr_log(GPR_DEBUG, "Port %s is out of range or null.",
             std::string(port_view).c_str());
   }
+  address.address_str = std::string(host_view);
+  grpc_error_handle error = grpc_string_to_sockaddr(
+      &address.address, address.address_str.c_str(), address.port);
+  if (error != GRPC_ERROR_NONE) {
+    gpr_log(GPR_DEBUG, "Address %s is not IPv4/IPv6. Error: %s",
+            address.address_str.c_str(), grpc_error_std_string(error).c_str());
+  }
+  GRPC_ERROR_UNREF(error);
+  return address;
 }
 
 }  // namespace
@@ -70,14 +65,14 @@ EvaluateArgs::PerChannelArgs::PerChannelArgs(grpc_auth_context* auth_context,
         auth_context, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME);
     spiffe_id =
         GetAuthPropertyValue(auth_context, GRPC_PEER_SPIFFE_ID_PROPERTY_NAME);
+    uri_sans = GetAuthPropertyArray(auth_context, GRPC_PEER_URI_PROPERTY_NAME);
+    dns_sans = GetAuthPropertyArray(auth_context, GRPC_PEER_DNS_PROPERTY_NAME);
     common_name =
         GetAuthPropertyValue(auth_context, GRPC_X509_CN_PROPERTY_NAME);
   }
   if (endpoint != nullptr) {
-    ParseEndpointUri(grpc_endpoint_get_local_address(endpoint), &local_address,
-                     &local_port);
-    ParseEndpointUri(grpc_endpoint_get_peer(endpoint), &peer_address,
-                     &peer_port);
+    local_address = ParseEndpointUri(grpc_endpoint_get_local_address(endpoint));
+    peer_address = ParseEndpointUri(grpc_endpoint_get_peer(endpoint));
   }
 }
 
@@ -134,32 +129,46 @@ absl::optional<absl::string_view> EvaluateArgs::GetHeaderValue(
   return grpc_metadata_batch_get_value(metadata_, key, concatenated_value);
 }
 
-absl::string_view EvaluateArgs::GetLocalAddress() const {
+grpc_resolved_address EvaluateArgs::GetLocalAddress() const {
+  if (channel_args_ == nullptr) {
+    return {};
+  }
+  return channel_args_->local_address.address;
+}
+
+absl::string_view EvaluateArgs::GetLocalAddressString() const {
   if (channel_args_ == nullptr) {
     return "";
   }
-  return channel_args_->local_address;
+  return channel_args_->local_address.address_str;
 }
 
 int EvaluateArgs::GetLocalPort() const {
   if (channel_args_ == nullptr) {
     return 0;
   }
-  return channel_args_->local_port;
+  return channel_args_->local_address.port;
 }
 
-absl::string_view EvaluateArgs::GetPeerAddress() const {
+grpc_resolved_address EvaluateArgs::GetPeerAddress() const {
+  if (channel_args_ == nullptr) {
+    return {};
+  }
+  return channel_args_->peer_address.address;
+}
+
+absl::string_view EvaluateArgs::GetPeerAddressString() const {
   if (channel_args_ == nullptr) {
     return "";
   }
-  return channel_args_->peer_address;
+  return channel_args_->peer_address.address_str;
 }
 
 int EvaluateArgs::GetPeerPort() const {
   if (channel_args_ == nullptr) {
     return 0;
   }
-  return channel_args_->peer_port;
+  return channel_args_->peer_address.port;
 }
 
 absl::string_view EvaluateArgs::GetTransportSecurityType() const {
@@ -176,6 +185,20 @@ absl::string_view EvaluateArgs::GetSpiffeId() const {
   return channel_args_->spiffe_id;
 }
 
+std::vector<absl::string_view> EvaluateArgs::GetUriSans() const {
+  if (channel_args_ == nullptr) {
+    return {};
+  }
+  return channel_args_->uri_sans;
+}
+
+std::vector<absl::string_view> EvaluateArgs::GetDnsSans() const {
+  if (channel_args_ == nullptr) {
+    return {};
+  }
+  return channel_args_->dns_sans;
+}
+
 absl::string_view EvaluateArgs::GetCommonName() const {
   if (channel_args_ == nullptr) {
     return "";
index b3ac93c..3c345b3 100644 (file)
@@ -22,6 +22,7 @@
 #include "absl/types/optional.h"
 
 #include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/transport/metadata_batch.h"
 
@@ -32,15 +33,23 @@ class EvaluateArgs {
   // Caller is responsible for ensuring auth_context outlives PerChannelArgs
   // struct.
   struct PerChannelArgs {
+    struct Address {
+      // The address in sockaddr form.
+      grpc_resolved_address address;
+      // The same address with only the host part.
+      std::string address_str;
+      int port = 0;
+    };
+
     PerChannelArgs(grpc_auth_context* auth_context, grpc_endpoint* endpoint);
 
     absl::string_view transport_security_type;
     absl::string_view spiffe_id;
+    std::vector<absl::string_view> uri_sans;
+    std::vector<absl::string_view> dns_sans;
     absl::string_view common_name;
-    std::string local_address;
-    int local_port = 0;
-    std::string peer_address;
-    int peer_port = 0;
+    Address local_address;
+    Address peer_address;
   };
 
   EvaluateArgs(grpc_metadata_batch* metadata, PerChannelArgs* channel_args)
@@ -60,12 +69,16 @@ class EvaluateArgs {
   absl::optional<absl::string_view> GetHeaderValue(
       absl::string_view key, std::string* concatenated_value) const;
 
-  absl::string_view GetLocalAddress() const;
+  grpc_resolved_address GetLocalAddress() const;
+  absl::string_view GetLocalAddressString() const;
   int GetLocalPort() const;
-  absl::string_view GetPeerAddress() const;
+  grpc_resolved_address GetPeerAddress() const;
+  absl::string_view GetPeerAddressString() const;
   int GetPeerPort() const;
   absl::string_view GetTransportSecurityType() const;
   absl::string_view GetSpiffeId() const;
+  std::vector<absl::string_view> GetUriSans() const;
+  std::vector<absl::string_view> GetDnsSans() const;
   absl::string_view GetCommonName() const;
 
  private:
index 5b7791a..c482f79 100644 (file)
@@ -36,6 +36,8 @@ class GrpcAuthorizationEngine : public AuthorizationEngine {
   // Builds GrpcAuthorizationEngine with allow/deny RBAC policy.
   explicit GrpcAuthorizationEngine(Rbac policy);
 
+  Rbac::Action action() { return action_; }
+
   // Evaluates incoming request against RBAC policy and makes a decision to
   // whether allow/deny this request.
   Decision Evaluate(const EvaluateArgs& args) const override;
diff --git a/src/core/lib/security/authorization/grpc_authorization_policy_provider.cc b/src/core/lib/security/authorization/grpc_authorization_policy_provider.cc
new file mode 100644 (file)
index 0000000..a2e706c
--- /dev/null
@@ -0,0 +1,67 @@
+// 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc_security.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/security/authorization/grpc_authorization_engine.h"
+#include "src/core/lib/security/authorization/grpc_authorization_policy_provider.h"
+
+namespace grpc_core {
+
+absl::StatusOr<RefCountedPtr<grpc_authorization_policy_provider>>
+StaticDataAuthorizationPolicyProvider::Create(absl::string_view authz_policy) {
+  auto policies_or = GenerateRbacPolicies(authz_policy);
+  if (!policies_or.ok()) {
+    return policies_or.status();
+  }
+  return MakeRefCounted<StaticDataAuthorizationPolicyProvider>(
+      std::move(*policies_or));
+}
+
+StaticDataAuthorizationPolicyProvider::StaticDataAuthorizationPolicyProvider(
+    RbacPolicies policies)
+    : allow_engine_(MakeRefCounted<GrpcAuthorizationEngine>(
+          std::move(policies.allow_policy))),
+      deny_engine_(MakeRefCounted<GrpcAuthorizationEngine>(
+          std::move(policies.deny_policy))) {}
+
+}  // namespace grpc_core
+
+// Wrapper APIs declared in grpc_security.h
+
+grpc_authorization_policy_provider*
+grpc_authorization_policy_provider_static_data_create(
+    const char* authz_policy, grpc_status_code* code,
+    const char** error_details) {
+  GPR_ASSERT(authz_policy != nullptr);
+  auto provider_or =
+      grpc_core::StaticDataAuthorizationPolicyProvider::Create(authz_policy);
+  if (!provider_or.ok()) {
+    *code = static_cast<grpc_status_code>(provider_or.status().code());
+    *error_details =
+        gpr_strdup(std::string(provider_or.status().message()).c_str());
+    return nullptr;
+  }
+  *code = GRPC_STATUS_OK;
+  *error_details = nullptr;
+  return provider_or->release();
+}
+
+void grpc_authorization_policy_provider_release(
+    grpc_authorization_policy_provider* provider) {
+  if (provider != nullptr) provider->Unref();
+}
diff --git a/src/core/lib/security/authorization/grpc_authorization_policy_provider.h b/src/core/lib/security/authorization/grpc_authorization_policy_provider.h
new file mode 100644 (file)
index 0000000..97c9d85
--- /dev/null
@@ -0,0 +1,60 @@
+// 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.
+
+#ifndef GRPC_CORE_LIB_SECURITY_AUTHORIZATION_GRPC_AUTHORIZATION_POLICY_PROVIDER_H
+#define GRPC_CORE_LIB_SECURITY_AUTHORIZATION_GRPC_AUTHORIZATION_POLICY_PROVIDER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <memory>
+
+#include "absl/status/statusor.h"
+
+#include "src/core/lib/security/authorization/authorization_policy_provider.h"
+#include "src/core/lib/security/authorization/rbac_translator.h"
+
+namespace grpc_core {
+
+// Provider class will get SDK Authorization policy from string during
+// initialization. This policy will be translated to Envoy RBAC policies and
+// used to initialize allow and deny AuthorizationEngine objects. This provider
+// will return the same authorization engines everytime.
+class StaticDataAuthorizationPolicyProvider
+    : public grpc_authorization_policy_provider {
+ public:
+  static absl::StatusOr<RefCountedPtr<grpc_authorization_policy_provider>>
+  Create(absl::string_view authz_policy);
+
+  explicit StaticDataAuthorizationPolicyProvider(RbacPolicies policies);
+
+  RefCountedPtr<AuthorizationEngine> allow_engine() const override {
+    return allow_engine_;
+  }
+  RefCountedPtr<AuthorizationEngine> deny_engine() const override {
+    return deny_engine_;
+  }
+
+  void Orphan() override {}
+
+ private:
+  RefCountedPtr<AuthorizationEngine> allow_engine_;
+  RefCountedPtr<AuthorizationEngine> deny_engine_;
+};
+
+// TODO(ashithasantosh): Add implementation for file watcher authorization
+// policy provider.
+
+}  // namespace grpc_core
+
+#endif  // GRPC_CORE_LIB_SECURITY_AUTHORIZATION_GRPC_AUTHORIZATION_POLICY_PROVIDER_H
index 5613b74..ddff2bb 100644 (file)
 
 #include <grpc/support/port_platform.h>
 
+#include <grpc/grpc_security_constants.h>
+
+#include "src/core/lib/address_utils/sockaddr_utils.h"
 #include "src/core/lib/security/authorization/matchers.h"
 
 namespace grpc_core {
 
-namespace {
-
-bool AuthenticatedMatchesHelper(const EvaluateArgs& args,
-                                const StringMatcher& matcher) {
-  if (args.GetTransportSecurityType() != GRPC_SSL_TRANSPORT_SECURITY_TYPE) {
-    // Connection is not authenticated.
-    return false;
-  }
-  if (matcher.string_matcher().empty()) {
-    // Allows any authenticated user.
-    return true;
-  }
-  absl::string_view spiffe_id = args.GetSpiffeId();
-  if (!spiffe_id.empty()) {
-    return matcher.Match(spiffe_id);
-  }
-  // TODO(ashithasantosh): Check principal matches DNS SAN, followed by Subject
-  // field from certificate. This requires updating tsi_peer to expose these
-  // fields.
-  return false;
-}
-
-}  // namespace
-
 std::unique_ptr<AuthorizationMatcher> AuthorizationMatcher::Create(
     Rbac::Permission permission) {
   switch (permission.type) {
-    case Rbac::Permission::RuleType::kAnd:
-      return absl::make_unique<AndAuthorizationMatcher>(
-          std::move(permission.permissions), permission.not_rule);
-    case Rbac::Permission::RuleType::kOr:
-      return absl::make_unique<OrAuthorizationMatcher>(
-          std::move(permission.permissions), permission.not_rule);
+    case Rbac::Permission::RuleType::kAnd: {
+      std::vector<std::unique_ptr<AuthorizationMatcher>> matchers;
+      for (const auto& rule : permission.permissions) {
+        matchers.push_back(AuthorizationMatcher::Create(std::move(*rule)));
+      }
+      return absl::make_unique<AndAuthorizationMatcher>(std::move(matchers));
+    }
+    case Rbac::Permission::RuleType::kOr: {
+      std::vector<std::unique_ptr<AuthorizationMatcher>> matchers;
+      for (const auto& rule : permission.permissions) {
+        matchers.push_back(AuthorizationMatcher::Create(std::move(*rule)));
+      }
+      return absl::make_unique<OrAuthorizationMatcher>(std::move(matchers));
+    }
+    case Rbac::Permission::RuleType::kNot:
+      return absl::make_unique<NotAuthorizationMatcher>(
+          AuthorizationMatcher::Create(std::move(*permission.permissions[0])));
     case Rbac::Permission::RuleType::kAny:
-      return absl::make_unique<AlwaysAuthorizationMatcher>(permission.not_rule);
+      return absl::make_unique<AlwaysAuthorizationMatcher>();
     case Rbac::Permission::RuleType::kHeader:
       return absl::make_unique<HeaderAuthorizationMatcher>(
-          std::move(permission.header_matcher), permission.not_rule);
+          std::move(permission.header_matcher));
     case Rbac::Permission::RuleType::kPath:
       return absl::make_unique<PathAuthorizationMatcher>(
-          std::move(permission.string_matcher), permission.not_rule);
+          std::move(permission.string_matcher));
     case Rbac::Permission::RuleType::kDestIp:
-      return absl::make_unique<IpAuthorizationMatcher>(std::move(permission.ip),
-                                                       permission.not_rule);
+      return absl::make_unique<IpAuthorizationMatcher>(
+          IpAuthorizationMatcher::Type::kDestIp, std::move(permission.ip));
     case Rbac::Permission::RuleType::kDestPort:
-      return absl::make_unique<PortAuthorizationMatcher>(permission.port,
-                                                         permission.not_rule);
+      return absl::make_unique<PortAuthorizationMatcher>(permission.port);
     case Rbac::Permission::RuleType::kReqServerName:
       return absl::make_unique<ReqServerNameAuthorizationMatcher>(
-          std::move(permission.string_matcher), permission.not_rule);
+          std::move(permission.string_matcher));
   }
   return nullptr;
 }
@@ -75,124 +64,156 @@ std::unique_ptr<AuthorizationMatcher> AuthorizationMatcher::Create(
 std::unique_ptr<AuthorizationMatcher> AuthorizationMatcher::Create(
     Rbac::Principal principal) {
   switch (principal.type) {
-    case Rbac::Principal::RuleType::kAnd:
-      return absl::make_unique<AndAuthorizationMatcher>(
-          std::move(principal.principals), principal.not_rule);
-    case Rbac::Principal::RuleType::kOr:
-      return absl::make_unique<OrAuthorizationMatcher>(
-          std::move(principal.principals), principal.not_rule);
+    case Rbac::Principal::RuleType::kAnd: {
+      std::vector<std::unique_ptr<AuthorizationMatcher>> matchers;
+      for (const auto& id : principal.principals) {
+        matchers.push_back(AuthorizationMatcher::Create(std::move(*id)));
+      }
+      return absl::make_unique<AndAuthorizationMatcher>(std::move(matchers));
+    }
+    case Rbac::Principal::RuleType::kOr: {
+      std::vector<std::unique_ptr<AuthorizationMatcher>> matchers;
+      for (const auto& id : principal.principals) {
+        matchers.push_back(AuthorizationMatcher::Create(std::move(*id)));
+      }
+      return absl::make_unique<OrAuthorizationMatcher>(std::move(matchers));
+    }
+    case Rbac::Principal::RuleType::kNot:
+      return absl::make_unique<NotAuthorizationMatcher>(
+          AuthorizationMatcher::Create(std::move(*principal.principals[0])));
     case Rbac::Principal::RuleType::kAny:
-      return absl::make_unique<AlwaysAuthorizationMatcher>(principal.not_rule);
+      return absl::make_unique<AlwaysAuthorizationMatcher>();
     case Rbac::Principal::RuleType::kPrincipalName:
       return absl::make_unique<AuthenticatedAuthorizationMatcher>(
-          std::move(principal.string_matcher), principal.not_rule);
+          std::move(principal.string_matcher));
     case Rbac::Principal::RuleType::kSourceIp:
+      return absl::make_unique<IpAuthorizationMatcher>(
+          IpAuthorizationMatcher::Type::kSourceIp, std::move(principal.ip));
     case Rbac::Principal::RuleType::kDirectRemoteIp:
+      return absl::make_unique<IpAuthorizationMatcher>(
+          IpAuthorizationMatcher::Type::kDirectRemoteIp,
+          std::move(principal.ip));
     case Rbac::Principal::RuleType::kRemoteIp:
-      return absl::make_unique<IpAuthorizationMatcher>(std::move(principal.ip),
-                                                       principal.not_rule);
+      return absl::make_unique<IpAuthorizationMatcher>(
+          IpAuthorizationMatcher::Type::kRemoteIp, std::move(principal.ip));
     case Rbac::Principal::RuleType::kHeader:
       return absl::make_unique<HeaderAuthorizationMatcher>(
-          std::move(principal.header_matcher), principal.not_rule);
+          std::move(principal.header_matcher));
     case Rbac::Principal::RuleType::kPath:
       return absl::make_unique<PathAuthorizationMatcher>(
-          std::move(principal.string_matcher), principal.not_rule);
+          std::move(principal.string_matcher));
   }
   return nullptr;
 }
 
-AndAuthorizationMatcher::AndAuthorizationMatcher(
-    std::vector<std::unique_ptr<Rbac::Permission>> rules, bool not_rule)
-    : not_rule_(not_rule) {
-  for (auto& rule : rules) {
-    matchers_.push_back(AuthorizationMatcher::Create(std::move(*rule)));
-  }
-}
-
-AndAuthorizationMatcher::AndAuthorizationMatcher(
-    std::vector<std::unique_ptr<Rbac::Principal>> ids, bool not_rule)
-    : not_rule_(not_rule) {
-  for (const auto& id : ids) {
-    matchers_.push_back(AuthorizationMatcher::Create(std::move(*id)));
-  }
-}
-
 bool AndAuthorizationMatcher::Matches(const EvaluateArgs& args) const {
-  bool matches = true;
   for (const auto& matcher : matchers_) {
     if (!matcher->Matches(args)) {
-      matches = false;
-      break;
+      return false;
     }
   }
-  return matches != not_rule_;
-}
-
-OrAuthorizationMatcher::OrAuthorizationMatcher(
-    std::vector<std::unique_ptr<Rbac::Permission>> rules, bool not_rule)
-    : not_rule_(not_rule) {
-  for (const auto& rule : rules) {
-    matchers_.push_back(AuthorizationMatcher::Create(std::move(*rule)));
-  }
-}
-
-OrAuthorizationMatcher::OrAuthorizationMatcher(
-    std::vector<std::unique_ptr<Rbac::Principal>> ids, bool not_rule)
-    : not_rule_(not_rule) {
-  for (const auto& id : ids) {
-    matchers_.push_back(AuthorizationMatcher::Create(std::move(*id)));
-  }
+  return true;
 }
 
 bool OrAuthorizationMatcher::Matches(const EvaluateArgs& args) const {
-  bool matches = false;
   for (const auto& matcher : matchers_) {
     if (matcher->Matches(args)) {
-      matches = true;
-      break;
+      return true;
     }
   }
-  return matches != not_rule_;
+  return false;
+}
+
+bool NotAuthorizationMatcher::Matches(const EvaluateArgs& args) const {
+  return !matcher_->Matches(args);
 }
 
 bool HeaderAuthorizationMatcher::Matches(const EvaluateArgs& args) const {
   std::string concatenated_value;
-  bool matches =
-      matcher_.Match(args.GetHeaderValue(matcher_.name(), &concatenated_value));
-  return matches != not_rule_;
+  return matcher_.Match(
+      args.GetHeaderValue(matcher_.name(), &concatenated_value));
+}
+
+IpAuthorizationMatcher::IpAuthorizationMatcher(Type type, Rbac::CidrRange range)
+    : type_(type), prefix_len_(range.prefix_len) {
+  grpc_error_handle error =
+      grpc_string_to_sockaddr(&subnet_address_, range.address_prefix.c_str(),
+                              /*port does not matter here*/ 0);
+  if (error == GRPC_ERROR_NONE) {
+    grpc_sockaddr_mask_bits(&subnet_address_, prefix_len_);
+  } else {
+    gpr_log(GPR_DEBUG, "CidrRange address %s is not IPv4/IPv6. Error: %s",
+            range.address_prefix.c_str(), grpc_error_std_string(error).c_str());
+  }
+  GRPC_ERROR_UNREF(error);
 }
 
-// TODO(ashithasantosh): Implement IpAuthorizationMatcher::Matches.
-bool IpAuthorizationMatcher::Matches(const EvaluateArgs&) const {
-  bool matches = false;
-  return matches != not_rule_;
+bool IpAuthorizationMatcher::Matches(const EvaluateArgs& args) const {
+  grpc_resolved_address address;
+  switch (type_) {
+    case Type::kDestIp: {
+      address = args.GetLocalAddress();
+      break;
+    }
+    case Type::kSourceIp:
+    case Type::kDirectRemoteIp: {
+      address = args.GetPeerAddress();
+      break;
+    }
+    default: {
+      // Currently we do not support matching rules containing "remote_ip".
+      return false;
+    }
+  }
+  return grpc_sockaddr_match_subnet(&address, &subnet_address_, prefix_len_);
 }
 
 bool PortAuthorizationMatcher::Matches(const EvaluateArgs& args) const {
-  bool matches = (port_ == args.GetLocalPort());
-  return matches != not_rule_;
+  return port_ == args.GetLocalPort();
 }
 
 bool AuthenticatedAuthorizationMatcher::Matches(
     const EvaluateArgs& args) const {
-  bool matches = AuthenticatedMatchesHelper(args, matcher_);
-  return matches != not_rule_;
+  if (args.GetTransportSecurityType() != GRPC_SSL_TRANSPORT_SECURITY_TYPE) {
+    // Connection is not authenticated.
+    return false;
+  }
+  if (matcher_.string_matcher().empty()) {
+    // Allows any authenticated user.
+    return true;
+  }
+  std::vector<absl::string_view> uri_sans = args.GetUriSans();
+  if (!uri_sans.empty()) {
+    for (const auto& uri : uri_sans) {
+      if (matcher_.Match(uri)) {
+        return true;
+      }
+    }
+  }
+  std::vector<absl::string_view> dns_sans = args.GetDnsSans();
+  if (!dns_sans.empty()) {
+    for (const auto& dns : dns_sans) {
+      if (matcher_.Match(dns)) {
+        return true;
+      }
+    }
+  }
+  // TODO(ashithasantosh): Check Subject field from certificate.
+  return false;
 }
 
 bool ReqServerNameAuthorizationMatcher::Matches(const EvaluateArgs&) const {
   // Currently we do not support matching rules containing
   // "requested_server_name".
-  bool matches = false;
-  return matches != not_rule_;
+  return false;
 }
 
 bool PathAuthorizationMatcher::Matches(const EvaluateArgs& args) const {
-  bool matches = false;
   absl::string_view path = args.GetPath();
   if (!path.empty()) {
-    matches = matcher_.Match(path);
+    return matcher_.Match(path);
   }
-  return matches != not_rule_;
+  return false;
 }
 
 bool PolicyAuthorizationMatcher::Matches(const EvaluateArgs& args) const {
index 86b40ee..95ebd51 100644 (file)
@@ -47,46 +47,46 @@ class AuthorizationMatcher {
 
 class AlwaysAuthorizationMatcher : public AuthorizationMatcher {
  public:
-  explicit AlwaysAuthorizationMatcher(bool not_rule = false)
-      : not_rule_(not_rule) {}
+  explicit AlwaysAuthorizationMatcher() = default;
 
-  bool Matches(const EvaluateArgs&) const override { return !not_rule_; }
-
- private:
-  // Negates matching the provided permission/principal.
-  const bool not_rule_;
+  bool Matches(const EvaluateArgs&) const override { return true; }
 };
 
 class AndAuthorizationMatcher : public AuthorizationMatcher {
  public:
   explicit AndAuthorizationMatcher(
-      std::vector<std::unique_ptr<Rbac::Permission>> rules,
-      bool not_rule = false);
-  explicit AndAuthorizationMatcher(
-      std::vector<std::unique_ptr<Rbac::Principal>> ids, bool not_rule = false);
+      std::vector<std::unique_ptr<AuthorizationMatcher>> matchers)
+      : matchers_(std::move(matchers)) {}
 
   bool Matches(const EvaluateArgs& args) const override;
 
  private:
   std::vector<std::unique_ptr<AuthorizationMatcher>> matchers_;
-  // Negates matching the provided permission/principal.
-  const bool not_rule_;
 };
 
 class OrAuthorizationMatcher : public AuthorizationMatcher {
  public:
   explicit OrAuthorizationMatcher(
-      std::vector<std::unique_ptr<Rbac::Permission>> rules,
-      bool not_rule = false);
-  explicit OrAuthorizationMatcher(
-      std::vector<std::unique_ptr<Rbac::Principal>> ids, bool not_rule = false);
+      std::vector<std::unique_ptr<AuthorizationMatcher>> matchers)
+      : matchers_(std::move(matchers)) {}
 
   bool Matches(const EvaluateArgs& args) const override;
 
  private:
   std::vector<std::unique_ptr<AuthorizationMatcher>> matchers_;
-  // Negates matching the provided permission/principal.
-  const bool not_rule_;
+};
+
+// Negates matching the provided permission/principal.
+class NotAuthorizationMatcher : public AuthorizationMatcher {
+ public:
+  explicit NotAuthorizationMatcher(
+      std::unique_ptr<AuthorizationMatcher> matcher)
+      : matcher_(std::move(matcher)) {}
+
+  bool Matches(const EvaluateArgs& args) const override;
+
+ private:
+  std::unique_ptr<AuthorizationMatcher> matcher_;
 };
 
 // TODO(ashithasantosh): Add matcher implementation for metadata field.
@@ -94,63 +94,58 @@ class OrAuthorizationMatcher : public AuthorizationMatcher {
 // Perform a match against HTTP headers.
 class HeaderAuthorizationMatcher : public AuthorizationMatcher {
  public:
-  explicit HeaderAuthorizationMatcher(HeaderMatcher matcher,
-                                      bool not_rule = false)
-      : matcher_(std::move(matcher)), not_rule_(not_rule) {}
+  explicit HeaderAuthorizationMatcher(HeaderMatcher matcher)
+      : matcher_(std::move(matcher)) {}
 
   bool Matches(const EvaluateArgs& args) const override;
 
  private:
   const HeaderMatcher matcher_;
-  // Negates matching the provided permission/principal.
-  const bool not_rule_;
 };
 
 // Perform a match against IP Cidr Range.
-// TODO(ashithasantosh): Handle type of Ip or use seperate matchers for each
-// type. Implement Match functionality, this would require updating EvaluateArgs
-// getters, to return format of IP as well.
 class IpAuthorizationMatcher : public AuthorizationMatcher {
  public:
-  explicit IpAuthorizationMatcher(Rbac::CidrRange range, bool not_rule = false)
-      : range_(std::move(range)), not_rule_(not_rule) {}
+  enum class Type {
+    kDestIp,
+    kSourceIp,
+    kDirectRemoteIp,
+    kRemoteIp,
+  };
 
-  bool Matches(const EvaluateArgs&) const override;
+  IpAuthorizationMatcher(Type type, Rbac::CidrRange range);
+
+  bool Matches(const EvaluateArgs& args) const override;
 
  private:
-  const Rbac::CidrRange range_;
-  // Negates matching the provided permission/principal.
-  const bool not_rule_;
+  const Type type_;
+  // Subnet masked address.
+  grpc_resolved_address subnet_address_;
+  const uint32_t prefix_len_;
 };
 
 // Perform a match against port number of the destination (local) address.
 class PortAuthorizationMatcher : public AuthorizationMatcher {
  public:
-  explicit PortAuthorizationMatcher(int port, bool not_rule = false)
-      : port_(port), not_rule_(not_rule) {}
+  explicit PortAuthorizationMatcher(int port) : port_(port) {}
 
   bool Matches(const EvaluateArgs& args) const override;
 
  private:
   const int port_;
-  // Negates matching the provided permission/principal.
-  const bool not_rule_;
 };
 
 // Matches the principal name as described in the peer certificate. Uses URI SAN
 // or DNS SAN in that order, otherwise uses subject field.
 class AuthenticatedAuthorizationMatcher : public AuthorizationMatcher {
  public:
-  explicit AuthenticatedAuthorizationMatcher(StringMatcher auth,
-                                             bool not_rule = false)
-      : matcher_(std::move(auth)), not_rule_(not_rule) {}
+  explicit AuthenticatedAuthorizationMatcher(StringMatcher auth)
+      : matcher_(std::move(auth)) {}
 
   bool Matches(const EvaluateArgs& args) const override;
 
  private:
   const StringMatcher matcher_;
-  // Negates matching the provided permission/principal.
-  const bool not_rule_;
 };
 
 // Perform a match against the request server from the client's connection
@@ -158,29 +153,25 @@ class AuthenticatedAuthorizationMatcher : public AuthorizationMatcher {
 class ReqServerNameAuthorizationMatcher : public AuthorizationMatcher {
  public:
   explicit ReqServerNameAuthorizationMatcher(
-      StringMatcher requested_server_name, bool not_rule = false)
-      : matcher_(std::move(requested_server_name)), not_rule_(not_rule) {}
+      StringMatcher requested_server_name)
+      : matcher_(std::move(requested_server_name)) {}
 
   bool Matches(const EvaluateArgs&) const override;
 
  private:
   const StringMatcher matcher_;
-  // Negates matching the provided permission/principal.
-  const bool not_rule_;
 };
 
 // Perform a match against the path header of HTTP request.
 class PathAuthorizationMatcher : public AuthorizationMatcher {
  public:
-  explicit PathAuthorizationMatcher(StringMatcher path, bool not_rule = false)
-      : matcher_(std::move(path)), not_rule_(not_rule) {}
+  explicit PathAuthorizationMatcher(StringMatcher path)
+      : matcher_(std::move(path)) {}
 
   bool Matches(const EvaluateArgs& args) const override;
 
  private:
   const StringMatcher matcher_;
-  // Negates matching the provided permission/principal.
-  const bool not_rule_;
 };
 
 // Performs a match for policy field in RBAC, which is a collection of
index a081c51..a6f7ff2 100644 (file)
@@ -77,31 +77,31 @@ std::string Rbac::CidrRange::ToString() const {
 
 Rbac::Permission::Permission(
     Permission::RuleType type,
-    std::vector<std::unique_ptr<Permission>> permissions, bool not_rule)
-    : type(type), permissions(std::move(permissions)), not_rule(not_rule) {}
-Rbac::Permission::Permission(Permission::RuleType type, bool not_rule)
-    : type(type), not_rule(not_rule) {}
+    std::vector<std::unique_ptr<Permission>> permissions)
+    : type(type), permissions(std::move(permissions)) {}
+Rbac::Permission::Permission(Permission::RuleType type, Permission permission)
+    : type(type) {
+  permissions.push_back(
+      absl::make_unique<Rbac::Permission>(std::move(permission)));
+}
+Rbac::Permission::Permission(Permission::RuleType type) : type(type) {}
 Rbac::Permission::Permission(Permission::RuleType type,
-                             HeaderMatcher header_matcher, bool not_rule)
-    : type(type),
-      header_matcher(std::move(header_matcher)),
-      not_rule(not_rule) {}
+                             HeaderMatcher header_matcher)
+    : type(type), header_matcher(std::move(header_matcher)) {}
 Rbac::Permission::Permission(Permission::RuleType type,
-                             StringMatcher string_matcher, bool not_rule)
-    : type(type),
-      string_matcher(std::move(string_matcher)),
-      not_rule(not_rule) {}
-Rbac::Permission::Permission(Permission::RuleType type, CidrRange ip,
-                             bool not_rule)
-    : type(type), ip(std::move(ip)), not_rule(not_rule) {}
-Rbac::Permission::Permission(Permission::RuleType type, int port, bool not_rule)
-    : type(type), port(port), not_rule(not_rule) {}
+                             StringMatcher string_matcher)
+    : type(type), string_matcher(std::move(string_matcher)) {}
+Rbac::Permission::Permission(Permission::RuleType type, CidrRange ip)
+    : type(type), ip(std::move(ip)) {}
+Rbac::Permission::Permission(Permission::RuleType type, int port)
+    : type(type), port(port) {}
 
 Rbac::Permission::Permission(Rbac::Permission&& other) noexcept
-    : type(other.type), not_rule(other.not_rule) {
+    : type(other.type) {
   switch (type) {
     case RuleType::kAnd:
     case RuleType::kOr:
+    case RuleType::kNot:
       permissions = std::move(other.permissions);
       break;
     case RuleType::kAny:
@@ -124,10 +124,10 @@ Rbac::Permission::Permission(Rbac::Permission&& other) noexcept
 Rbac::Permission& Rbac::Permission::operator=(
     Rbac::Permission&& other) noexcept {
   type = other.type;
-  not_rule = other.not_rule;
   switch (type) {
     case RuleType::kAnd:
     case RuleType::kOr:
+    case RuleType::kNot:
       permissions = std::move(other.permissions);
       break;
     case RuleType::kAny:
@@ -156,8 +156,7 @@ std::string Rbac::Permission::ToString() const {
       for (const auto& permission : permissions) {
         contents.push_back(permission->ToString());
       }
-      return absl::StrFormat("%sand=[%s]", not_rule ? "not " : "",
-                             absl::StrJoin(contents, ","));
+      return absl::StrFormat("and=[%s]", absl::StrJoin(contents, ","));
     }
     case RuleType::kOr: {
       std::vector<std::string> contents;
@@ -165,25 +164,23 @@ std::string Rbac::Permission::ToString() const {
       for (const auto& permission : permissions) {
         contents.push_back(permission->ToString());
       }
-      return absl::StrFormat("%sor=[%s]", not_rule ? "not " : "",
-                             absl::StrJoin(contents, ","));
+      return absl::StrFormat("or=[%s]", absl::StrJoin(contents, ","));
     }
+    case RuleType::kNot:
+      return absl::StrFormat("not %s", permissions[0]->ToString());
     case RuleType::kAny:
-      return absl::StrFormat("%sany", not_rule ? "not " : "");
+      return "any";
     case RuleType::kHeader:
-      return absl::StrFormat("%sheader=%s", not_rule ? "not " : "",
-                             header_matcher.ToString());
+      return absl::StrFormat("header=%s", header_matcher.ToString());
     case RuleType::kPath:
-      return absl::StrFormat("%spath=%s", not_rule ? "not " : "",
-                             string_matcher.ToString());
+      return absl::StrFormat("path=%s", string_matcher.ToString());
     case RuleType::kDestIp:
-      return absl::StrFormat("%sdest_ip=%s", not_rule ? "not " : "",
-                             ip.ToString());
+      return absl::StrFormat("dest_ip=%s", ip.ToString());
     case RuleType::kDestPort:
-      return absl::StrFormat("%sdest_port=%d", not_rule ? "not " : "", port);
+      return absl::StrFormat("dest_port=%d", port);
     case RuleType::kReqServerName:
-      return absl::StrFormat("%srequested_server_name=%s",
-                             not_rule ? "not " : "", string_matcher.ToString());
+      return absl::StrFormat("requested_server_name=%s",
+                             string_matcher.ToString());
     default:
       return "";
   }
@@ -194,30 +191,29 @@ std::string Rbac::Permission::ToString() const {
 //
 
 Rbac::Principal::Principal(Principal::RuleType type,
-                           std::vector<std::unique_ptr<Principal>> principals,
-                           bool not_rule)
-    : type(type), principals(std::move(principals)), not_rule(not_rule) {}
-Rbac::Principal::Principal(Principal::RuleType type, bool not_rule)
-    : type(type), not_rule(not_rule) {}
+                           std::vector<std::unique_ptr<Principal>> principals)
+    : type(type), principals(std::move(principals)) {}
+Rbac::Principal::Principal(Principal::RuleType type, Principal principal)
+    : type(type) {
+  principals.push_back(
+      absl::make_unique<Rbac::Principal>(std::move(principal)));
+}
+Rbac::Principal::Principal(Principal::RuleType type) : type(type) {}
 Rbac::Principal::Principal(Principal::RuleType type,
-                           StringMatcher string_matcher, bool not_rule)
-    : type(type),
-      string_matcher(std::move(string_matcher)),
-      not_rule(not_rule) {}
-Rbac::Principal::Principal(Principal::RuleType type, CidrRange ip,
-                           bool not_rule)
-    : type(type), ip(std::move(ip)), not_rule(not_rule) {}
+                           StringMatcher string_matcher)
+    : type(type), string_matcher(std::move(string_matcher)) {}
+Rbac::Principal::Principal(Principal::RuleType type, CidrRange ip)
+    : type(type), ip(std::move(ip)) {}
 Rbac::Principal::Principal(Principal::RuleType type,
-                           HeaderMatcher header_matcher, bool not_rule)
-    : type(type),
-      header_matcher(std::move(header_matcher)),
-      not_rule(not_rule) {}
+                           HeaderMatcher header_matcher)
+    : type(type), header_matcher(std::move(header_matcher)) {}
 
 Rbac::Principal::Principal(Rbac::Principal&& other) noexcept
-    : type(other.type), not_rule(other.not_rule) {
+    : type(other.type) {
   switch (type) {
     case RuleType::kAnd:
     case RuleType::kOr:
+    case RuleType::kNot:
       principals = std::move(other.principals);
       break;
     case RuleType::kAny:
@@ -236,10 +232,10 @@ Rbac::Principal::Principal(Rbac::Principal&& other) noexcept
 
 Rbac::Principal& Rbac::Principal::operator=(Rbac::Principal&& other) noexcept {
   type = other.type;
-  not_rule = other.not_rule;
   switch (type) {
     case RuleType::kAnd:
     case RuleType::kOr:
+    case RuleType::kNot:
       principals = std::move(other.principals);
       break;
     case RuleType::kAny:
@@ -265,8 +261,7 @@ std::string Rbac::Principal::ToString() const {
       for (const auto& principal : principals) {
         contents.push_back(principal->ToString());
       }
-      return absl::StrFormat("%sand=[%s]", not_rule ? "not " : "",
-                             absl::StrJoin(contents, ","));
+      return absl::StrFormat("and=[%s]", absl::StrJoin(contents, ","));
     }
     case RuleType::kOr: {
       std::vector<std::string> contents;
@@ -274,29 +269,24 @@ std::string Rbac::Principal::ToString() const {
       for (const auto& principal : principals) {
         contents.push_back(principal->ToString());
       }
-      return absl::StrFormat("%sor=[%s]", not_rule ? "not " : "",
-                             absl::StrJoin(contents, ","));
+      return absl::StrFormat("or=[%s]", absl::StrJoin(contents, ","));
     }
+    case RuleType::kNot:
+      return absl::StrFormat("not %s", principals[0]->ToString());
     case RuleType::kAny:
-      return absl::StrFormat("%sany", not_rule ? "not " : "");
+      return "any";
     case RuleType::kPrincipalName:
-      return absl::StrFormat("%sprincipal_name=%s", not_rule ? "not " : "",
-                             string_matcher.ToString());
+      return absl::StrFormat("principal_name=%s", string_matcher.ToString());
     case RuleType::kSourceIp:
-      return absl::StrFormat("%ssource_ip=%s", not_rule ? "not " : "",
-                             ip.ToString());
+      return absl::StrFormat("source_ip=%s", ip.ToString());
     case RuleType::kDirectRemoteIp:
-      return absl::StrFormat("%sdirect_remote_ip=%s", not_rule ? "not " : "",
-                             ip.ToString());
+      return absl::StrFormat("direct_remote_ip=%s", ip.ToString());
     case RuleType::kRemoteIp:
-      return absl::StrFormat("%sremote_ip=%s", not_rule ? "not " : "",
-                             ip.ToString());
+      return absl::StrFormat("remote_ip=%s", ip.ToString());
     case RuleType::kHeader:
-      return absl::StrFormat("%sheader=%s", not_rule ? "not " : "",
-                             header_matcher.ToString());
+      return absl::StrFormat("header=%s", header_matcher.ToString());
     case RuleType::kPath:
-      return absl::StrFormat("%spath=%s", not_rule ? "not " : "",
-                             string_matcher.ToString());
+      return absl::StrFormat("path=%s", string_matcher.ToString());
     default:
       return "";
   }
index 027bf92..3963eb8 100644 (file)
@@ -49,6 +49,7 @@ struct Rbac {
     enum class RuleType {
       kAnd,
       kOr,
+      kNot,
       kAny,
       kHeader,
       kPath,
@@ -58,22 +59,21 @@ struct Rbac {
     };
 
     Permission() = default;
-    // For AND/OR RuleType.
+    // For kAnd/kOr RuleType.
     Permission(Permission::RuleType type,
-               std::vector<std::unique_ptr<Permission>> permissions,
-               bool not_rule = false);
-    // For ANY RuleType.
-    explicit Permission(Permission::RuleType type, bool not_rule = false);
-    // For HEADER RuleType.
-    Permission(Permission::RuleType type, HeaderMatcher header_matcher,
-               bool not_rule = false);
-    // For PATH/REQ_SERVER_NAME RuleType.
-    Permission(Permission::RuleType type, StringMatcher string_matcher,
-               bool not_rule = false);
-    // For DEST_IP RuleType.
-    Permission(Permission::RuleType type, CidrRange ip, bool not_rule = false);
-    // For DEST_PORT RuleType.
-    Permission(Permission::RuleType type, int port, bool not_rule = false);
+               std::vector<std::unique_ptr<Permission>> permissions);
+    // For kNot RuleType.
+    Permission(Permission::RuleType type, Permission permission);
+    // For kAny RuleType.
+    explicit Permission(Permission::RuleType type);
+    // For kHeader RuleType.
+    Permission(Permission::RuleType type, HeaderMatcher header_matcher);
+    // For kPath/kReqServerName RuleType.
+    Permission(Permission::RuleType type, StringMatcher string_matcher);
+    // For kDestIp RuleType.
+    Permission(Permission::RuleType type, CidrRange ip);
+    // For kDestPort RuleType.
+    Permission(Permission::RuleType type, int port);
 
     Permission(Permission&& other) noexcept;
     Permission& operator=(Permission&& other) noexcept;
@@ -85,15 +85,16 @@ struct Rbac {
     StringMatcher string_matcher;
     CidrRange ip;
     int port;
-    // For type AND/OR.
+    // For type kAnd/kOr/kNot. For kNot type, the vector will have only one
+    // element.
     std::vector<std::unique_ptr<Permission>> permissions;
-    bool not_rule = false;
   };
 
   struct Principal {
     enum class RuleType {
       kAnd,
       kOr,
+      kNot,
       kAny,
       kPrincipalName,
       kSourceIp,
@@ -104,20 +105,19 @@ struct Rbac {
     };
 
     Principal() = default;
-    // For AND/OR RuleType.
+    // For kAnd/kOr RuleType.
     Principal(Principal::RuleType type,
-              std::vector<std::unique_ptr<Principal>> principals,
-              bool not_rule = false);
-    // For ANY RuleType.
-    explicit Principal(Principal::RuleType type, bool not_rule = false);
-    // For PRINCIPAL_NAME/PATH RuleType.
-    Principal(Principal::RuleType type, StringMatcher string_matcher,
-              bool not_rule = false);
-    // For SOURCE_IP/DIRECT_REMOTE_IP/REMOTE_IP RuleType.
-    Principal(Principal::RuleType type, CidrRange ip, bool not_rule = false);
-    // For HEADER RuleType.
-    Principal(Principal::RuleType type, HeaderMatcher header_matcher,
-              bool not_rule = false);
+              std::vector<std::unique_ptr<Principal>> principals);
+    // For kNot RuleType.
+    Principal(Principal::RuleType type, Principal principal);
+    // For kAny RuleType.
+    explicit Principal(Principal::RuleType type);
+    // For kPrincipalName/kPath RuleType.
+    Principal(Principal::RuleType type, StringMatcher string_matcher);
+    // For kSourceIp/kDirectRemoteIp/kRemoteIp RuleType.
+    Principal(Principal::RuleType type, CidrRange ip);
+    // For kHeader RuleType.
+    Principal(Principal::RuleType type, HeaderMatcher header_matcher);
 
     Principal(Principal&& other) noexcept;
     Principal& operator=(Principal&& other) noexcept;
@@ -128,9 +128,9 @@ struct Rbac {
     HeaderMatcher header_matcher;
     StringMatcher string_matcher;
     CidrRange ip;
-    // For type AND/OR.
+    // For type kAnd/kOr/kNot. For kNot type, the vector will have only one
+    // element.
     std::vector<std::unique_ptr<Principal>> principals;
-    bool not_rule = false;
   };
 
   struct Policy {
index 0e1a2a1..93fdd53 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <string.h>
 
+#include "absl/strings/match.h"
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
@@ -91,7 +93,7 @@ grpc_google_default_channel_credentials::create_security_connector(
   const char* xds_cluster =
       grpc_channel_args_find_string(args, GRPC_ARG_XDS_CLUSTER_NAME);
   const bool is_xds_non_cfe_cluster =
-      xds_cluster != nullptr && strcmp(xds_cluster, "google_cfe") != 0;
+      xds_cluster != nullptr && !absl::StartsWith(xds_cluster, "google_cfe_");
   const bool use_alts = is_grpclb_load_balancer ||
                         is_backend_from_grpclb_load_balancer ||
                         is_xds_non_cfe_cluster;
index b94c2ee..2dcc197 100644 (file)
@@ -88,4 +88,36 @@ bool VerifySubjectAlternativeName(absl::string_view subject_alternative_name,
              std::string::npos;
 }
 
+absl::string_view GetAuthPropertyValue(grpc_auth_context* context,
+                                       const char* property_name) {
+  grpc_auth_property_iterator it =
+      grpc_auth_context_find_properties_by_name(context, property_name);
+  const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it);
+  if (prop == nullptr) {
+    gpr_log(GPR_DEBUG, "No value found for %s property.", property_name);
+    return "";
+  }
+  if (grpc_auth_property_iterator_next(&it) != nullptr) {
+    gpr_log(GPR_DEBUG, "Multiple values found for %s property.", property_name);
+    return "";
+  }
+  return absl::string_view(prop->value, prop->value_length);
+}
+
+std::vector<absl::string_view> GetAuthPropertyArray(grpc_auth_context* context,
+                                                    const char* property_name) {
+  std::vector<absl::string_view> values;
+  grpc_auth_property_iterator it =
+      grpc_auth_context_find_properties_by_name(context, property_name);
+  const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it);
+  while (prop != nullptr) {
+    values.emplace_back(prop->value, prop->value_length);
+    prop = grpc_auth_property_iterator_next(&it);
+  }
+  if (values.empty()) {
+    gpr_log(GPR_DEBUG, "No value found for %s property.", property_name);
+  }
+  return values;
+}
+
 }  // namespace grpc_core
index 6fe9bb4..31a7f32 100644 (file)
@@ -26,6 +26,8 @@
 
 #include "absl/strings/string_view.h"
 
+#include "src/core/lib/security/context/security_context.h"
+
 namespace grpc_core {
 
 // Matches \a subject_alternative_name with \a matcher. Returns true if there
@@ -33,6 +35,17 @@ namespace grpc_core {
 bool VerifySubjectAlternativeName(absl::string_view subject_alternative_name,
                                   const std::string& matcher);
 
+// Returns value for the specified property_name from auth context. Here the
+// property is expected to have a single value. Returns empty if multiple values
+// are found.
+absl::string_view GetAuthPropertyValue(grpc_auth_context* context,
+                                       const char* property_name);
+
+// Returns values for the specified property_name from auth context. Here the
+// property can have any number of values.
+std::vector<absl::string_view> GetAuthPropertyArray(grpc_auth_context* context,
+                                                    const char* property_name);
+
 }  // namespace grpc_core
 
 #endif  // GRPC_CORE_LIB_SECURITY_CREDENTIALS_TLS_TLS_UTILS_H
index 1c1cfe9..aec493c 100644 (file)
@@ -29,6 +29,7 @@
 #include <grpc/support/string_util.h>
 
 #include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/lib/address_utils/parse_address.h"
 #include "src/core/lib/address_utils/sockaddr_utils.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
@@ -39,6 +40,7 @@
 #include "src/core/lib/iomgr/unix_sockets_posix.h"
 #include "src/core/lib/security/credentials/local/local_credentials.h"
 #include "src/core/lib/security/transport/security_handshaker.h"
+#include "src/core/lib/uri/uri_parser.h"
 #include "src/core/tsi/local_transport_security.h"
 
 #define GRPC_UDS_URI_PATTERN "unix:"
@@ -70,13 +72,14 @@ void local_check_peer(tsi_peer peer, grpc_endpoint* ep,
                       grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
                       grpc_closure* on_peer_checked,
                       grpc_local_connect_type type) {
-  int fd = grpc_endpoint_get_fd(ep);
   grpc_resolved_address resolved_addr;
-  memset(&resolved_addr, 0, sizeof(resolved_addr));
-  resolved_addr.len = GRPC_MAX_SOCKADDR_SIZE;
   bool is_endpoint_local = false;
-  if (getsockname(fd, reinterpret_cast<grpc_sockaddr*>(resolved_addr.addr),
-                  &resolved_addr.len) == 0) {
+  absl::string_view local_addr = grpc_endpoint_get_local_address(ep);
+  absl::StatusOr<grpc_core::URI> uri = grpc_core::URI::Parse(local_addr);
+  if (!uri.ok() || !grpc_parse_uri(*uri, &resolved_addr)) {
+    gpr_log(GPR_ERROR, "Could not parse endpoint address: %s",
+            std::string(local_addr.data(), local_addr.size()).c_str());
+  } else {
     grpc_resolved_address addr_normalized;
     grpc_resolved_address* addr =
         grpc_sockaddr_is_v4mapped(&resolved_addr, &addr_normalized)
@@ -103,7 +106,7 @@ void local_check_peer(tsi_peer peer, grpc_endpoint* ep,
       }
     }
   }
-  grpc_error_handle error = GRPC_ERROR_NONE;
+  grpc_error_handle error;
   if (!is_endpoint_local) {
     error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
         "Endpoint is neither UDS or TCP loopback address.");
index f445be9..803d31d 100644 (file)
@@ -309,6 +309,8 @@ grpc_core::RefCountedPtr<grpc_auth_context> grpc_ssl_peer_to_auth_context(
       grpc_auth_context_add_property(ctx.get(), GRPC_PEER_DNS_PROPERTY_NAME,
                                      prop->value.data, prop->value.length);
     } else if (strcmp(prop->name, TSI_X509_URI_PEER_PROPERTY) == 0) {
+      grpc_auth_context_add_property(ctx.get(), GRPC_PEER_URI_PROPERTY_NAME,
+                                     prop->value.data, prop->value.length);
       uri_count++;
       absl::string_view spiffe_id(prop->value.data, prop->value.length);
       if (IsSpiffeId(spiffe_id)) {
@@ -388,6 +390,9 @@ tsi_peer grpc_shallow_peer_from_ssl_auth_context(
       } else if (strcmp(prop->name, GRPC_PEER_DNS_PROPERTY_NAME) == 0) {
         add_shallow_auth_property_to_peer(&peer, prop,
                                           TSI_X509_DNS_PEER_PROPERTY);
+      } else if (strcmp(prop->name, GRPC_PEER_URI_PROPERTY_NAME) == 0) {
+        add_shallow_auth_property_to_peer(&peer, prop,
+                                          TSI_X509_URI_PEER_PROPERTY);
       } else if (strcmp(prop->name, GRPC_PEER_SPIFFE_ID_PROPERTY_NAME) == 0) {
         add_shallow_auth_property_to_peer(&peer, prop,
                                           TSI_X509_URI_PEER_PROPERTY);
index ac0854c..fd2542e 100644 (file)
@@ -170,6 +170,8 @@ struct grpc_call {
   bool destroy_called = false;
   /** flag indicating that cancellation is inherited */
   bool cancellation_is_inherited = false;
+  // Trailers-only response status
+  bool is_trailers_only = false;
   /** which ops are in-flight */
   bool sent_initial_metadata = false;
   bool sending_message = false;
@@ -223,6 +225,7 @@ struct grpc_call {
   grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> sending_stream;
 
   grpc_core::OrphanablePtr<grpc_core::ByteStream> receiving_stream;
+  bool call_failed_before_recv_message = false;
   grpc_byte_buffer** receiving_buffer = nullptr;
   grpc_slice receiving_slice = grpc_empty_slice();
   grpc_closure receiving_slice_ready;
@@ -1824,7 +1827,10 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
             &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
         stream_op_payload->recv_initial_metadata.recv_initial_metadata_ready =
             &call->receiving_initial_metadata_ready;
-        if (!call->is_client) {
+        if (call->is_client) {
+          stream_op_payload->recv_initial_metadata.trailing_metadata_available =
+              &call->is_trailers_only;
+        } else {
           stream_op_payload->recv_initial_metadata.peer_string =
               &call->peer_string;
         }
@@ -1845,6 +1851,8 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
         stream_op->recv_message = true;
         call->receiving_buffer = op->data.recv_message.recv_message;
         stream_op_payload->recv_message.recv_message = &call->receiving_stream;
+        stream_op_payload->recv_message.call_failed_before_recv_message =
+            &call->call_failed_before_recv_message;
         GRPC_CLOSURE_INIT(&call->receiving_stream_ready,
                           receiving_stream_ready_in_call_combiner, bctl,
                           grpc_schedule_on_exec_ctx);
@@ -2014,6 +2022,18 @@ grpc_compression_algorithm grpc_call_compression_for_level(
   return algo;
 }
 
+bool grpc_call_is_trailers_only(const grpc_call* call) {
+  bool result = call->is_trailers_only;
+  GPR_DEBUG_ASSERT(
+      !result || call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]
+                         .list.count == 0);
+  return result;
+}
+
+bool grpc_call_failed_before_recv_message(const grpc_call* c) {
+  return c->call_failed_before_recv_message;
+}
+
 const char* grpc_call_error_to_string(grpc_call_error error) {
   switch (error) {
     case GRPC_CALL_ERROR:
index a6ad3ca..d682760 100644 (file)
@@ -120,6 +120,17 @@ size_t grpc_call_get_initial_size_estimate();
 grpc_compression_algorithm grpc_call_compression_for_level(
     grpc_call* call, grpc_compression_level level);
 
+/* Did this client call receive a trailers-only response */
+/* TODO(markdroth): This is currently available only to the C++ API.
+                    Move to surface API if requested by other languages. */
+bool grpc_call_is_trailers_only(const grpc_call* call);
+
+/* Returns whether or not the call's receive message operation failed because of
+ * an error (as opposed to a graceful end-of-stream) */
+/* TODO(markdroth): This is currently available only to the C++ API.
+                    Move to surface API if requested by other languages. */
+bool grpc_call_failed_before_recv_message(const grpc_call* c);
+
 extern grpc_core::TraceFlag grpc_call_error_trace;
 extern grpc_core::TraceFlag grpc_compression_trace;
 
index 2b889e0..1d210d2 100644 (file)
@@ -197,8 +197,7 @@ const cq_poller_vtable g_poller_vtable_by_poller_type[] = {
 struct cq_vtable {
   grpc_cq_completion_type cq_completion_type;
   size_t data_size;
-  void (*init)(void* data,
-               grpc_experimental_completion_queue_functor* shutdown_callback);
+  void (*init)(void* data, grpc_completion_queue_functor* shutdown_callback);
   void (*shutdown)(grpc_completion_queue* cq);
   void (*destroy)(void* data);
   bool (*begin_op)(grpc_completion_queue* cq, void* tag);
@@ -310,8 +309,7 @@ struct cq_pluck_data {
 };
 
 struct cq_callback_data {
-  explicit cq_callback_data(
-      grpc_experimental_completion_queue_functor* shutdown_callback)
+  explicit cq_callback_data(grpc_completion_queue_functor* shutdown_callback)
       : shutdown_callback(shutdown_callback) {}
 
   ~cq_callback_data() {
@@ -332,7 +330,7 @@ struct cq_callback_data {
   bool shutdown_called = false;
 
   /** A callback that gets invoked when the CQ completes shutdown */
-  grpc_experimental_completion_queue_functor* shutdown_callback;
+  grpc_completion_queue_functor* shutdown_callback;
 };
 
 }  // namespace
@@ -397,12 +395,12 @@ static grpc_event cq_pluck(grpc_completion_queue* cq, void* tag,
                            gpr_timespec deadline, void* reserved);
 
 // Note that cq_init_next and cq_init_pluck do not use the shutdown_callback
-static void cq_init_next(
-    void* data, grpc_experimental_completion_queue_functor* shutdown_callback);
-static void cq_init_pluck(
-    void* data, grpc_experimental_completion_queue_functor* shutdown_callback);
-static void cq_init_callback(
-    void* data, grpc_experimental_completion_queue_functor* shutdown_callback);
+static void cq_init_next(void* data,
+                         grpc_completion_queue_functor* shutdown_callback);
+static void cq_init_pluck(void* data,
+                          grpc_completion_queue_functor* shutdown_callback);
+static void cq_init_callback(void* data,
+                             grpc_completion_queue_functor* shutdown_callback);
 static void cq_destroy_next(void* data);
 static void cq_destroy_pluck(void* data);
 static void cq_destroy_callback(void* data);
@@ -513,7 +511,7 @@ grpc_cq_completion* CqEventQueue::Pop() {
 
 grpc_completion_queue* grpc_completion_queue_create_internal(
     grpc_cq_completion_type completion_type, grpc_cq_polling_type polling_type,
-    grpc_experimental_completion_queue_functor* shutdown_callback) {
+    grpc_completion_queue_functor* shutdown_callback) {
   GPR_TIMER_SCOPE("grpc_completion_queue_create_internal", 0);
 
   grpc_completion_queue* cq;
@@ -548,9 +546,8 @@ grpc_completion_queue* grpc_completion_queue_create_internal(
   return cq;
 }
 
-static void cq_init_next(
-    void* data,
-    grpc_experimental_completion_queue_functor* /*shutdown_callback*/) {
+static void cq_init_next(void* data,
+                         grpc_completion_queue_functor* /*shutdown_callback*/) {
   new (data) cq_next_data();
 }
 
@@ -560,8 +557,7 @@ static void cq_destroy_next(void* data) {
 }
 
 static void cq_init_pluck(
-    void* data,
-    grpc_experimental_completion_queue_functor* /*shutdown_callback*/) {
+    void* data, grpc_completion_queue_functor* /*shutdown_callback*/) {
   new (data) cq_pluck_data();
 }
 
@@ -570,8 +566,8 @@ static void cq_destroy_pluck(void* data) {
   cqd->~cq_pluck_data();
 }
 
-static void cq_init_callback(
-    void* data, grpc_experimental_completion_queue_functor* shutdown_callback) {
+static void cq_init_callback(void* data,
+                             grpc_completion_queue_functor* shutdown_callback) {
   new (data) cq_callback_data(shutdown_callback);
 }
 
@@ -836,7 +832,7 @@ static void cq_end_op_for_pluck(
 }
 
 static void functor_callback(void* arg, grpc_error_handle error) {
-  auto* functor = static_cast<grpc_experimental_completion_queue_functor*>(arg);
+  auto* functor = static_cast<grpc_completion_queue_functor*>(arg);
   functor->functor_run(functor, error == GRPC_ERROR_NONE);
 }
 
@@ -880,7 +876,7 @@ static void cq_end_op_for_callback(
   // 2. The callback is marked inlineable and there is an ACEC available
   // 3. We are already running in a background poller thread (which always has
   //    an ACEC available at the base of the stack).
-  auto* functor = static_cast<grpc_experimental_completion_queue_functor*>(tag);
+  auto* functor = static_cast<grpc_completion_queue_functor*>(tag);
   if (((internal || functor->inlineable) &&
        grpc_core::ApplicationCallbackExecCtx::Available()) ||
       grpc_iomgr_is_any_background_poller_thread()) {
@@ -1065,7 +1061,11 @@ static grpc_event cq_next(grpc_completion_queue* cq, gpr_timespec deadline,
       gpr_log(GPR_ERROR, "Completion queue next failed: %s",
               grpc_error_std_string(err).c_str());
       GRPC_ERROR_UNREF(err);
-      ret.type = GRPC_QUEUE_TIMEOUT;
+      if (err == GRPC_ERROR_CANCELLED) {
+        ret.type = GRPC_QUEUE_SHUTDOWN;
+      } else {
+        ret.type = GRPC_QUEUE_TIMEOUT;
+      }
       ret.success = 0;
       dump_pending_tags(cq);
       break;
index 2bf91fa..b2cf19e 100644 (file)
@@ -93,6 +93,6 @@ int grpc_get_cq_poll_num(grpc_completion_queue* cq);
 
 grpc_completion_queue* grpc_completion_queue_create_internal(
     grpc_cq_completion_type completion_type, grpc_cq_polling_type polling_type,
-    grpc_experimental_completion_queue_functor* shutdown_callback);
+    grpc_completion_queue_functor* shutdown_callback);
 
 #endif /* GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_H */
index 72aa03a..d719f3b 100644 (file)
@@ -72,8 +72,7 @@ grpc_completion_queue* grpc_completion_queue_create_for_pluck(void* reserved) {
 }
 
 grpc_completion_queue* grpc_completion_queue_create_for_callback(
-    grpc_experimental_completion_queue_functor* shutdown_callback,
-    void* reserved) {
+    grpc_completion_queue_functor* shutdown_callback, void* reserved) {
   GPR_ASSERT(!reserved);
   grpc_completion_queue_attributes attr = {
       2, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING, shutdown_callback};
index 41866f1..19d353f 100644 (file)
@@ -126,7 +126,6 @@ void grpc_register_plugin(void (*init)(void), void (*destroy)(void)) {
 }
 
 void grpc_init(void) {
-  int i;
   gpr_once_init(&g_basic_init, do_basic_init);
 
   grpc_core::MutexLock lock(g_init_mu);
@@ -150,7 +149,7 @@ void grpc_init(void) {
     gpr_timers_global_init();
     grpc_core::HandshakerRegistry::Init();
     grpc_security_init();
-    for (i = 0; i < g_number_of_plugins; i++) {
+    for (int i = 0; i < g_number_of_plugins; i++) {
       if (g_all_of_the_plugins[i].init != nullptr) {
         g_all_of_the_plugins[i].init();
       }
@@ -175,7 +174,6 @@ void grpc_shutdown_internal_locked(void) {
     grpc_iomgr_shutdown_background_closure();
     {
       grpc_timer_manager_set_threading(false);  // shutdown timer_manager thread
-      grpc_core::Executor::ShutdownAll();
       for (i = g_number_of_plugins; i >= 0; i--) {
         if (g_all_of_the_plugins[i].destroy != nullptr) {
           g_all_of_the_plugins[i].destroy();
index 6eaa488..9e8bedc 100644 (file)
  * limitations under the License.
  *
  */
-
 #ifndef GRPC_CORE_LIB_SURFACE_INIT_H
 #define GRPC_CORE_LIB_SURFACE_INIT_H
 
+#include <grpc/support/port_platform.h>
+
+#include <memory>
+
+namespace grpc_event_engine {
+namespace experimental {
+class EventEngine;
+}
+}  // namespace grpc_event_engine
+
 void grpc_register_security_filters(void);
 void grpc_security_pre_init(void);
 void grpc_security_init(void);
index 8061df0..e0c0ac7 100644 (file)
@@ -23,7 +23,7 @@
 
 #include <grpc/grpc.h>
 
-const char* grpc_version_string(void) { return "16.0.0"; }
+const char* grpc_version_string(void) { return "18.0.0"; }
 
 const char* grpc_g_stands_for(void) {
   return "guadalupe_river_park_conservancy";
index e8f0555..2d75a56 100644 (file)
@@ -74,7 +74,7 @@ void grpc_error_get_status(grpc_error_handle error, grpc_millis deadline,
   // until we find the first one that has a status code.
   grpc_error_handle found_error =
       recursively_find_error_with_field(error, GRPC_ERROR_INT_GRPC_STATUS);
-  if (found_error == nullptr) {
+  if (found_error == GRPC_ERROR_NONE) {
     /// If no grpc-status exists, retry through the tree to find a http2 error
     /// code
     found_error =
@@ -83,7 +83,7 @@ void grpc_error_get_status(grpc_error_handle error, grpc_millis deadline,
 
   // If we found an error with a status code above, use that; otherwise,
   // fall back to using the parent error.
-  if (found_error == nullptr) found_error = error;
+  if (found_error == GRPC_ERROR_NONE) found_error = error;
 
   grpc_status_code status = GRPC_STATUS_UNKNOWN;
   intptr_t integer;
index 7372b38..5d223ee 100644 (file)
@@ -295,6 +295,8 @@ struct grpc_transport_stream_op_batch_payload {
     // containing a received message.
     // Will be NULL if trailing metadata is received instead of a message.
     grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message = nullptr;
+    // Was this recv_message failed for reasons other than a clean end-of-stream
+    bool* call_failed_before_recv_message = nullptr;
     /** Should be enqueued when one message is ready to be processed. */
     grpc_closure* recv_message_ready = nullptr;
   } recv_message;
index 49dec38..0285ac0 100644 (file)
@@ -135,7 +135,7 @@ std::string grpc_transport_op_string(grpc_transport_op* op) {
         " DISCONNECT:", grpc_error_std_string(op->disconnect_with_error)));
   }
 
-  if (op->goaway_error) {
+  if (op->goaway_error != GRPC_ERROR_NONE) {
     out.push_back(absl::StrCat(" SEND_GOAWAY:%s",
                                grpc_error_std_string(op->goaway_error)));
   }
index d3def27..271b010 100644 (file)
@@ -63,6 +63,8 @@ void grpc_workaround_cronet_compression_filter_shutdown(void);
 namespace grpc_core {
 void FaultInjectionFilterInit(void);
 void FaultInjectionFilterShutdown(void);
+void GrpcLbPolicyRingHashInit(void);
+void GrpcLbPolicyRingHashShutdown(void);
 }  // namespace grpc_core
 
 #ifndef GRPC_NO_XDS
@@ -115,6 +117,8 @@ void grpc_register_built_in_plugins(void) {
                        grpc_lb_policy_pick_first_shutdown);
   grpc_register_plugin(grpc_lb_policy_round_robin_init,
                        grpc_lb_policy_round_robin_shutdown);
+  grpc_register_plugin(grpc_core::GrpcLbPolicyRingHashInit,
+                       grpc_core::GrpcLbPolicyRingHashShutdown);
   grpc_register_plugin(grpc_resolver_dns_ares_init,
                        grpc_resolver_dns_ares_shutdown);
   grpc_register_plugin(grpc_resolver_dns_native_init,
index 5e74529..b58ab32 100644 (file)
@@ -57,6 +57,8 @@ void grpc_message_size_filter_shutdown(void);
 namespace grpc_core {
 void FaultInjectionFilterInit(void);
 void FaultInjectionFilterShutdown(void);
+void GrpcLbPolicyRingHashInit(void);
+void GrpcLbPolicyRingHashShutdown(void);
 }  // namespace grpc_core
 void grpc_service_config_channel_arg_filter_init(void);
 void grpc_service_config_channel_arg_filter_shutdown(void);
@@ -94,6 +96,8 @@ void grpc_register_built_in_plugins(void) {
                        grpc_lb_policy_pick_first_shutdown);
   grpc_register_plugin(grpc_lb_policy_round_robin_init,
                        grpc_lb_policy_round_robin_shutdown);
+  grpc_register_plugin(grpc_core::GrpcLbPolicyRingHashInit,
+                       grpc_core::GrpcLbPolicyRingHashShutdown);
   grpc_register_plugin(grpc_client_idle_filter_init,
                        grpc_client_idle_filter_shutdown);
   grpc_register_plugin(grpc_max_age_filter_init,
index c330ca6..f7d1cda 100644 (file)
 
 #include <grpc/grpc.h>
 
+#ifndef _STRUCT_IOVEC
 #ifndef GRPC_EVENT_ENGINE_POSIX
 struct iovec {
   void* iov_base;
   size_t iov_len;
 };
 #endif  // GRPC_EVENT_ENGINE_POSIX
+#endif  // _STRUCT_IOVEC
 
 /**
  * A gsec interface for AEAD encryption schemes. The API is thread-compatible.
index 134f4db..bb825eb 100755 (executable)
@@ -147,11 +147,16 @@ gRPC is available using the [vcpkg](https://github.com/Microsoft/vcpkg) dependen
 # install vcpkg package manager on your system using the official instructions
 git clone https://github.com/Microsoft/vcpkg.git
 cd vcpkg
+
+# Bootstrap on Linux:
 ./bootstrap-vcpkg.sh
+# Bootstrap on Windows instead:
+# ./bootstrap-vcpkg.bat
+
 ./vcpkg integrate install
 
 # install gRPC using vcpkg package manager
-vcpkg install grpc
+./vcpkg install grpc
 ```
 
 The gRPC port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
index a9f2083..a96e4d1 100644 (file)
@@ -214,7 +214,7 @@ bool Channel::WaitForStateChangeImpl(grpc_connectivity_state last_observed,
 }
 
 namespace {
-class ShutdownCallback : public grpc_experimental_completion_queue_functor {
+class ShutdownCallback : public grpc_completion_queue_functor {
  public:
   ShutdownCallback() {
     functor_run = &ShutdownCallback::Run;
@@ -230,7 +230,7 @@ class ShutdownCallback : public grpc_experimental_completion_queue_functor {
 
   // The Run function will get invoked by the completion queue library
   // when the shutdown is actually complete
-  static void Run(grpc_experimental_completion_queue_functor* cb, int) {
+  static void Run(grpc_completion_queue_functor* cb, int) {
     auto* callback = static_cast<ShutdownCallback*>(cb);
     delete callback->cq_;
     delete callback;
index a10e1b5..9e4ebbb 100644 (file)
@@ -20,6 +20,7 @@
 #include "src/core/lib/iomgr/closure.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/surface/call.h"
 
 namespace grpc {
 namespace internal {
@@ -48,5 +49,9 @@ void ClientReactor::InternalScheduleOnDone(grpc::Status s) {
   grpc_core::Executor::Run(&arg->closure, GRPC_ERROR_NONE);
 }
 
+bool ClientReactor::InternalTrailersOnly(const grpc_call* call) const {
+  return grpc_call_is_trailers_only(call);
+}
+
 }  // namespace internal
 }  // namespace grpc
index 386b731..4d35cce 100644 (file)
@@ -98,7 +98,7 @@ std::unique_ptr<ClientContext> ClientContext::FromInternalServerContext(
 }
 
 std::unique_ptr<ClientContext> ClientContext::FromServerContext(
-    const grpc::ServerContext& server_context, PropagationOptions options) {
+    const grpc::ServerContextBase& server_context, PropagationOptions options) {
   return FromInternalServerContext(server_context, options);
 }
 
index 71398bd..466d524 100644 (file)
  */
 
 #include <memory>
+#include <string>
+#include <utility>
+#include <vector>
 
 #include <grpcpp/channel.h>
 
+#include "src/cpp/client/create_channel_internal.h"
+
 struct grpc_channel;
 
 namespace grpc {
index 82b6979..f10bb05 100644 (file)
@@ -20,6 +20,8 @@
 #define GRPC_INTERNAL_CPP_CLIENT_CREATE_CHANNEL_INTERNAL_H
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include <grpcpp/channel.h>
 #include <grpcpp/impl/codegen/client_interceptor.h>
index e342e8d..2eb2809 100644 (file)
@@ -66,9 +66,9 @@ ChannelArguments::ChannelArguments(const ChannelArguments& other)
 }
 
 ChannelArguments::~ChannelArguments() {
-  grpc_core::ExecCtx exec_ctx;
   for (auto& arg : args_) {
     if (arg.type == GRPC_ARG_POINTER) {
+      grpc_core::ExecCtx exec_ctx;
       arg.value.pointer.vtable->destroy(arg.value.pointer.p);
     }
   }
index 015b5c1..cdf36b8 100644 (file)
@@ -89,8 +89,7 @@ struct CallbackAlternativeCQ {
                 // hold any application locks before executing the callback,
                 // and cannot be entered recursively.
                 auto* functor =
-                    static_cast<grpc_experimental_completion_queue_functor*>(
-                        ev.tag);
+                    static_cast<grpc_completion_queue_functor*>(ev.tag);
                 functor->functor_run(functor, ev.success);
               }
             },
index 8239eff..2b4d47b 100644 (file)
@@ -22,5 +22,5 @@
 #include <grpcpp/grpcpp.h>
 
 namespace grpc {
-std::string Version() { return "1.38.1"; }
+std::string Version() { return "1.39.0"; }
 }  // namespace grpc
diff --git a/src/cpp/server/authorization_policy_provider.cc b/src/cpp/server/authorization_policy_provider.cc
new file mode 100644 (file)
index 0000000..e5b51a5
--- /dev/null
@@ -0,0 +1,45 @@
+// 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 <grpc/grpc_security.h>
+#include <grpc/support/alloc.h>
+#include <grpcpp/security/authorization_policy_provider.h>
+
+namespace grpc {
+namespace experimental {
+
+std::shared_ptr<StaticDataAuthorizationPolicyProvider>
+StaticDataAuthorizationPolicyProvider::Create(const std::string& authz_policy,
+                                              grpc::Status* status) {
+  grpc_status_code code;
+  const char* error_details;
+  grpc_authorization_policy_provider* provider =
+      grpc_authorization_policy_provider_static_data_create(
+          authz_policy.c_str(), &code, &error_details);
+  if (code != GRPC_STATUS_OK) {
+    *status = grpc::Status(static_cast<grpc::StatusCode>(code), error_details);
+    gpr_free(const_cast<char*>(error_details));
+    return nullptr;
+  }
+  *status = grpc::Status();
+  return std::make_shared<StaticDataAuthorizationPolicyProvider>(provider);
+}
+
+StaticDataAuthorizationPolicyProvider::
+    ~StaticDataAuthorizationPolicyProvider() {
+  grpc_authorization_policy_provider_release(c_provider_);
+}
+
+}  // namespace experimental
+}  // namespace grpc
index 695aa54..af30879 100644 (file)
@@ -102,38 +102,23 @@ ServerBuilder& ServerBuilder::RegisterAsyncGenericService(
   return *this;
 }
 
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
 ServerBuilder& ServerBuilder::RegisterCallbackGenericService(
     CallbackGenericService* service) {
   if (generic_service_ || callback_generic_service_) {
     gpr_log(GPR_ERROR,
             "Adding multiple generic services is unsupported for now. "
             "Dropping the service %p",
-            (void*)service);
+            service);
   } else {
     callback_generic_service_ = service;
   }
   return *this;
 }
-#else
-ServerBuilder& ServerBuilder::experimental_type::RegisterCallbackGenericService(
-    experimental::CallbackGenericService* service) {
-  if (builder_->generic_service_ || builder_->callback_generic_service_) {
-    gpr_log(GPR_ERROR,
-            "Adding multiple generic services is unsupported for now. "
-            "Dropping the service %p",
-            service);
-  } else {
-    builder_->callback_generic_service_ = service;
-  }
-  return *builder_;
-}
-#endif
 
-ServerBuilder& ServerBuilder::experimental_type::SetContextAllocator(
+ServerBuilder& ServerBuilder::SetContextAllocator(
     std::unique_ptr<grpc::ContextAllocator> context_allocator) {
-  builder_->context_allocator_ = std::move(context_allocator);
-  return *builder_;
+  context_allocator_ = std::move(context_allocator);
+  return *this;
 }
 
 std::unique_ptr<grpc::experimental::ExternalConnectionAcceptor>
@@ -149,6 +134,12 @@ ServerBuilder::experimental_type::AddExternalConnectionAcceptor(
   return builder_->acceptors_.back()->GetAcceptor();
 }
 
+void ServerBuilder::experimental_type::SetAuthorizationPolicyProvider(
+    std::shared_ptr<experimental::AuthorizationPolicyProviderInterface>
+        provider) {
+  builder_->authorization_provider_ = std::move(provider);
+}
+
 ServerBuilder& ServerBuilder::SetOption(
     std::unique_ptr<ServerBuilderOption> option) {
   options_.push_back(std::move(option));
@@ -253,6 +244,11 @@ ChannelArguments ServerBuilder::BuildChannelArgs() {
     plugin->UpdateServerBuilder(this);
     plugin->UpdateChannelArguments(&args);
   }
+  if (authorization_provider_ != nullptr) {
+    args.SetPointerWithVtable(GRPC_ARG_AUTHORIZATION_POLICY_PROVIDER,
+                              authorization_provider_->c_provider(),
+                              grpc_authorization_policy_provider_arg_vtable());
+  }
   return args;
 }
 
@@ -382,12 +378,7 @@ std::unique_ptr<grpc::Server> ServerBuilder::BuildAndStart() {
     return nullptr;
   }
 
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
   server->RegisterContextAllocator(std::move(context_allocator_));
-#else
-  server->experimental_registration()->RegisterContextAllocator(
-      std::move(context_allocator_));
-#endif
 
   for (const auto& value : services_) {
     if (!server->RegisterService(value->host.get(), value->service)) {
index a334cdd..c82c83c 100644 (file)
@@ -114,15 +114,6 @@ class UnimplementedAsyncRequestContext {
   GenericServerAsyncReaderWriter generic_stream_;
 };
 
-// TODO(vjpai): Just for this file, use some contents of the experimental
-// namespace here to make the code easier to read below. Remove this when
-// de-experimentalized fully.
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-using ::grpc::experimental::CallbackGenericService;
-using ::grpc::experimental::CallbackServerContext;
-using ::grpc::experimental::GenericCallbackServerContext;
-#endif
-
 }  // namespace
 
 ServerInterface::BaseAsyncRequest::BaseAsyncRequest(
@@ -273,7 +264,7 @@ bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag,
 }
 
 namespace {
-class ShutdownCallback : public grpc_experimental_completion_queue_functor {
+class ShutdownCallback : public grpc_completion_queue_functor {
  public:
   ShutdownCallback() {
     functor_run = &ShutdownCallback::Run;
@@ -289,7 +280,7 @@ class ShutdownCallback : public grpc_experimental_completion_queue_functor {
 
   // The Run function will get invoked by the completion queue library
   // when the shutdown is actually complete
-  static void Run(grpc_experimental_completion_queue_functor* cb, int) {
+  static void Run(grpc_completion_queue_functor* cb, int) {
     auto* callback = static_cast<ShutdownCallback*>(cb);
     delete callback->cq_;
     delete callback;
@@ -417,7 +408,9 @@ class Server::SyncRequest final : public grpc::internal::CompletionQueueTag {
                                  : server_->resource_exhausted_handler_.get();
       deserialized_request_ = handler->Deserialize(call_, request_payload_,
                                                    &request_status_, nullptr);
-
+      if (!request_status_.ok()) {
+        gpr_log(GPR_DEBUG, "Failed to deserialize message.");
+      }
       request_payload_ = nullptr;
       interceptor_methods_.AddInterceptionHookPoint(
           grpc::experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
@@ -583,7 +576,7 @@ class Server::CallbackRequest final
   // method_name needs to be specialized between named method and generic
   const char* method_name() const;
 
-  class CallbackCallTag : public grpc_experimental_completion_queue_functor {
+  class CallbackCallTag : public grpc_completion_queue_functor {
    public:
     explicit CallbackCallTag(Server::CallbackRequest<ServerContextType>* req)
         : req_(req) {
@@ -606,8 +599,7 @@ class Server::CallbackRequest final
     Server::CallbackRequest<ServerContextType>* req_;
     grpc::internal::Call* call_;
 
-    static void StaticRun(grpc_experimental_completion_queue_functor* cb,
-                          int ok) {
+    static void StaticRun(grpc_completion_queue_functor* cb, int ok) {
       static_cast<CallbackCallTag*>(cb)->Run(static_cast<bool>(ok));
     }
     void Run(bool ok) {
@@ -657,6 +649,9 @@ class Server::CallbackRequest final
         req_->request_ = req_->method_->handler()->Deserialize(
             req_->call_, req_->request_payload_, &req_->request_status_,
             &req_->handler_data_);
+        if (!(req_->request_status_.ok())) {
+          gpr_log(GPR_DEBUG, "Failed to deserialize message.");
+        }
         req_->request_payload_ = nullptr;
         req_->interceptor_methods_.AddInterceptionHookPoint(
             grpc::experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
index 2cb014b..4df3ea5 100644 (file)
@@ -19,6 +19,7 @@
 #include <grpcpp/impl/codegen/server_context.h>
 
 #include <algorithm>
+#include <atomic>
 #include <utility>
 
 #include <grpc/compression.h>
@@ -324,10 +325,17 @@ void ServerContextBase::TryCancel() const {
   }
 }
 
+void ServerContextBase::MaybeMarkCancelledOnRead() {
+  if (grpc_call_failed_before_recv_message(call_.call)) {
+    marked_cancelled_.store(true, std::memory_order_release);
+  }
+}
+
 bool ServerContextBase::IsCancelled() const {
   if (completion_tag_) {
     // When using callback API, this result is always valid.
-    return completion_op_->CheckCancelledAsync();
+    return marked_cancelled_.load(std::memory_order_acquire) ||
+           completion_op_->CheckCancelledAsync();
   } else if (has_notify_when_done_tag_) {
     // When using async API, the result is only valid
     // if the tag has already been delivered at the completion queue
index fb70590..5c6f22b 100644 (file)
@@ -25,6 +25,37 @@ namespace grpc {
 
 static internal::GrpcLibraryInitializer g_gli_initializer;
 
+Status ByteBuffer::TrySingleSlice(Slice* slice) const {
+  if (!buffer_) {
+    return Status(StatusCode::FAILED_PRECONDITION, "Buffer not initialized");
+  }
+  if ((buffer_->type == GRPC_BB_RAW) &&
+      (buffer_->data.raw.compression == GRPC_COMPRESS_NONE) &&
+      (buffer_->data.raw.slice_buffer.count == 1)) {
+    grpc_slice internal_slice = buffer_->data.raw.slice_buffer.slices[0];
+    *slice = Slice(internal_slice, Slice::ADD_REF);
+    return Status::OK;
+  } else {
+    return Status(StatusCode::FAILED_PRECONDITION,
+                  "Buffer isn't made up of a single uncompressed slice.");
+  }
+}
+
+Status ByteBuffer::DumpToSingleSlice(Slice* slice) const {
+  if (!buffer_) {
+    return Status(StatusCode::FAILED_PRECONDITION, "Buffer not initialized");
+  }
+  grpc_byte_buffer_reader reader;
+  if (!grpc_byte_buffer_reader_init(&reader, buffer_)) {
+    return Status(StatusCode::INTERNAL,
+                  "Couldn't initialize byte buffer reader");
+  }
+  grpc_slice s = grpc_byte_buffer_reader_readall(&reader);
+  *slice = Slice(s, Slice::STEAL_REF);
+  grpc_byte_buffer_reader_destroy(&reader);
+  return Status::OK;
+}
+
 Status ByteBuffer::Dump(std::vector<Slice>* slices) const {
   slices->clear();
   if (!buffer_) {
index 19b8080..4d6ede3 100644 (file)
@@ -31,6 +31,7 @@ namespace Grpc.Core
     public abstract class ChannelCredentials
     {
         static readonly ChannelCredentials InsecureInstance = new InsecureCredentials();
+        static readonly ChannelCredentials SecureSslInstance = new SslCredentials();
 
         /// <summary>
         /// Creates a new instance of channel credentials
@@ -52,6 +53,22 @@ namespace Grpc.Core
         }
 
         /// <summary>
+        /// Returns instance of credentials that provides SSL security.
+        /// <para>
+        /// These credentials are the same as creating <see cref="SslCredentials"/> without parameters.
+        /// Apps that are using Grpc.Core can create <see cref="SslCredentials"/> directly to customize
+        /// the secure SSL credentials.
+        /// </para>
+        /// </summary>
+        public static ChannelCredentials SecureSsl
+        {
+            get
+            {
+                return SecureSslInstance;
+            }
+        }
+
+        /// <summary>
         /// Creates a new instance of <c>ChannelCredentials</c> class by composing
         /// given channel credentials with call credentials.
         /// </summary>
index 40de47e..21fc9ad 100644 (file)
@@ -33,11 +33,11 @@ namespace Grpc.Core
         /// <summary>
         /// Current <c>AssemblyFileVersion</c> of gRPC C# assemblies
         /// </summary>
-        public const string CurrentAssemblyFileVersion = "2.38.1.0";
+        public const string CurrentAssemblyFileVersion = "2.39.0.0";
 
         /// <summary>
         /// Current version of gRPC C#
         /// </summary>
-        public const string CurrentVersion = "2.38.1";
+        public const string CurrentVersion = "2.39.0";
     }
 }
index 4be6a06..f5d06ad 100644 (file)
@@ -31,6 +31,12 @@ namespace Grpc.Core.Tests
         }
 
         [Test]
+        public void SecureCredentials_IsComposable()
+        {
+            Assert.IsTrue(ChannelCredentials.SecureSsl.IsComposable);
+        }
+
+        [Test]
         public void ChannelCredentials_CreateComposite()
         {
             var composite = ChannelCredentials.Create(new FakeChannelCredentials(true), new FakeCallCredentials());
@@ -52,6 +58,10 @@ namespace Grpc.Core.Tests
             var nativeCreds1 = creds.ToNativeCredentials();
             var nativeCreds2 = creds.ToNativeCredentials();
             Assert.AreSame(nativeCreds1, nativeCreds2);
+
+            var nativeCreds3 = ChannelCredentials.SecureSsl.ToNativeCredentials();
+            var nativeCreds4 = ChannelCredentials.SecureSsl.ToNativeCredentials();
+            Assert.AreSame(nativeCreds3, nativeCreds4);
         }
     }
 }
index af66006..ccb26b8 100644 (file)
@@ -5,5 +5,11 @@
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <Pack>false</Pack>
     </Content>
+    <Content Include="..\..\..\cmake\build\libgrpc_csharp_ext.so">
+      <!-- make sure the developer build works when on arm64 linux as well -->
+      <Link>libgrpc_csharp_ext.arm64.so</Link>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+      <Pack>false</Pack>
+    </Content>
   </ItemGroup>
 </Project>
\ No newline at end of file
index cb7cb08..50657c2 100644 (file)
@@ -27,6 +27,7 @@ namespace Math {
   {
     static readonly string __ServiceName = "math.Math";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -41,11 +42,13 @@ namespace Math {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -57,11 +60,16 @@ namespace Math {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Math.DivArgs> __Marshaller_math_DivArgs = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Math.DivArgs.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Math.DivReply> __Marshaller_math_DivReply = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Math.DivReply.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Math.FibArgs> __Marshaller_math_FibArgs = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Math.FibArgs.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Math.Num> __Marshaller_math_Num = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Math.Num.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Math.DivArgs, global::Math.DivReply> __Method_Div = new grpc::Method<global::Math.DivArgs, global::Math.DivReply>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -69,6 +77,7 @@ namespace Math {
         __Marshaller_math_DivArgs,
         __Marshaller_math_DivReply);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Math.DivArgs, global::Math.DivReply> __Method_DivMany = new grpc::Method<global::Math.DivArgs, global::Math.DivReply>(
         grpc::MethodType.DuplexStreaming,
         __ServiceName,
@@ -76,6 +85,7 @@ namespace Math {
         __Marshaller_math_DivArgs,
         __Marshaller_math_DivReply);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Math.FibArgs, global::Math.Num> __Method_Fib = new grpc::Method<global::Math.FibArgs, global::Math.Num>(
         grpc::MethodType.ServerStreaming,
         __ServiceName,
@@ -83,6 +93,7 @@ namespace Math {
         __Marshaller_math_FibArgs,
         __Marshaller_math_Num);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Math.Num, global::Math.Num> __Method_Sum = new grpc::Method<global::Math.Num, global::Math.Num>(
         grpc::MethodType.ClientStreaming,
         __ServiceName,
@@ -107,6 +118,7 @@ namespace Math {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Math.DivReply> Div(global::Math.DivArgs request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -122,6 +134,7 @@ namespace Math {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task DivMany(grpc::IAsyncStreamReader<global::Math.DivArgs> requestStream, grpc::IServerStreamWriter<global::Math.DivReply> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -136,6 +149,7 @@ namespace Math {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task Fib(global::Math.FibArgs request, grpc::IServerStreamWriter<global::Math.Num> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -148,6 +162,7 @@ namespace Math {
       /// <param name="requestStream">Used for reading requests from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Math.Num> Sum(grpc::IAsyncStreamReader<global::Math.Num> requestStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -160,20 +175,24 @@ namespace Math {
     {
       /// <summary>Creates a new client for Math</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public MathClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for Math that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public MathClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected MathClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected MathClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
@@ -187,6 +206,7 @@ namespace Math {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Math.DivReply Div(global::Math.DivArgs request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Div(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -198,6 +218,7 @@ namespace Math {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Math.DivReply Div(global::Math.DivArgs request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_Div, null, options, request);
@@ -211,6 +232,7 @@ namespace Math {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Math.DivReply> DivAsync(global::Math.DivArgs request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return DivAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -222,6 +244,7 @@ namespace Math {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Math.DivReply> DivAsync(global::Math.DivArgs request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_Div, null, options, request);
@@ -236,6 +259,7 @@ namespace Math {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Math.DivArgs, global::Math.DivReply> DivMany(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return DivMany(new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -248,6 +272,7 @@ namespace Math {
       /// </summary>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Math.DivArgs, global::Math.DivReply> DivMany(grpc::CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_DivMany, null, options);
@@ -262,6 +287,7 @@ namespace Math {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncServerStreamingCall<global::Math.Num> Fib(global::Math.FibArgs request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Fib(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -274,6 +300,7 @@ namespace Math {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncServerStreamingCall<global::Math.Num> Fib(global::Math.FibArgs request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncServerStreamingCall(__Method_Fib, null, options, request);
@@ -286,6 +313,7 @@ namespace Math {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncClientStreamingCall<global::Math.Num, global::Math.Num> Sum(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Sum(new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -296,11 +324,13 @@ namespace Math {
       /// </summary>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncClientStreamingCall<global::Math.Num, global::Math.Num> Sum(grpc::CallOptions options)
       {
         return CallInvoker.AsyncClientStreamingCall(__Method_Sum, null, options);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override MathClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new MathClient(configuration);
@@ -309,6 +339,7 @@ namespace Math {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(MathBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -322,6 +353,7 @@ namespace Math {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, MathBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_Div, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Math.DivArgs, global::Math.DivReply>(serviceImpl.Div));
index ffa666c..523f112 100644 (file)
@@ -30,6 +30,7 @@ namespace Grpc.Health.V1 {
   {
     static readonly string __ServiceName = "grpc.health.v1.Health";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -44,11 +45,13 @@ namespace Grpc.Health.V1 {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -60,9 +63,12 @@ namespace Grpc.Health.V1 {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Health.V1.HealthCheckRequest> __Marshaller_grpc_health_v1_HealthCheckRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Health.V1.HealthCheckRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Health.V1.HealthCheckResponse> __Marshaller_grpc_health_v1_HealthCheckResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Health.V1.HealthCheckResponse.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Health.V1.HealthCheckRequest, global::Grpc.Health.V1.HealthCheckResponse> __Method_Check = new grpc::Method<global::Grpc.Health.V1.HealthCheckRequest, global::Grpc.Health.V1.HealthCheckResponse>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -70,6 +76,7 @@ namespace Grpc.Health.V1 {
         __Marshaller_grpc_health_v1_HealthCheckRequest,
         __Marshaller_grpc_health_v1_HealthCheckResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Health.V1.HealthCheckRequest, global::Grpc.Health.V1.HealthCheckResponse> __Method_Watch = new grpc::Method<global::Grpc.Health.V1.HealthCheckRequest, global::Grpc.Health.V1.HealthCheckResponse>(
         grpc::MethodType.ServerStreaming,
         __ServiceName,
@@ -94,6 +101,7 @@ namespace Grpc.Health.V1 {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Health.V1.HealthCheckResponse> Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -120,6 +128,7 @@ namespace Grpc.Health.V1 {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task Watch(global::Grpc.Health.V1.HealthCheckRequest request, grpc::IServerStreamWriter<global::Grpc.Health.V1.HealthCheckResponse> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -132,20 +141,24 @@ namespace Grpc.Health.V1 {
     {
       /// <summary>Creates a new client for Health</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public HealthClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for Health that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public HealthClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected HealthClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected HealthClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
@@ -159,6 +172,7 @@ namespace Grpc.Health.V1 {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Health.V1.HealthCheckResponse Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Check(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -170,6 +184,7 @@ namespace Grpc.Health.V1 {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Health.V1.HealthCheckResponse Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_Check, null, options, request);
@@ -183,6 +198,7 @@ namespace Grpc.Health.V1 {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Health.V1.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return CheckAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -194,6 +210,7 @@ namespace Grpc.Health.V1 {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Health.V1.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1.HealthCheckRequest request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_Check, null, options, request);
@@ -220,6 +237,7 @@ namespace Grpc.Health.V1 {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncServerStreamingCall<global::Grpc.Health.V1.HealthCheckResponse> Watch(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Watch(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -244,11 +262,13 @@ namespace Grpc.Health.V1 {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncServerStreamingCall<global::Grpc.Health.V1.HealthCheckResponse> Watch(global::Grpc.Health.V1.HealthCheckRequest request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncServerStreamingCall(__Method_Watch, null, options, request);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override HealthClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new HealthClient(configuration);
@@ -257,6 +277,7 @@ namespace Grpc.Health.V1 {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(HealthBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -268,6 +289,7 @@ namespace Grpc.Health.V1 {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, HealthBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_Check, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Grpc.Health.V1.HealthCheckRequest, global::Grpc.Health.V1.HealthCheckResponse>(serviceImpl.Check));
index 7c65cda..d39cb5b 100644 (file)
@@ -29,6 +29,7 @@ namespace Grpc.Testing {
   {
     static readonly string __ServiceName = "grpc.testing.BenchmarkService";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -43,11 +44,13 @@ namespace Grpc.Testing {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -59,9 +62,12 @@ namespace Grpc.Testing {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.SimpleRequest> __Marshaller_grpc_testing_SimpleRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.SimpleRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.SimpleResponse> __Marshaller_grpc_testing_SimpleResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.SimpleResponse.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> __Method_UnaryCall = new grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -69,6 +75,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_SimpleRequest,
         __Marshaller_grpc_testing_SimpleResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> __Method_StreamingCall = new grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse>(
         grpc::MethodType.DuplexStreaming,
         __ServiceName,
@@ -76,6 +83,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_SimpleRequest,
         __Marshaller_grpc_testing_SimpleResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> __Method_StreamingFromClient = new grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse>(
         grpc::MethodType.ClientStreaming,
         __ServiceName,
@@ -83,6 +91,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_SimpleRequest,
         __Marshaller_grpc_testing_SimpleResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> __Method_StreamingFromServer = new grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse>(
         grpc::MethodType.ServerStreaming,
         __ServiceName,
@@ -90,6 +99,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_SimpleRequest,
         __Marshaller_grpc_testing_SimpleResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> __Method_StreamingBothWays = new grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse>(
         grpc::MethodType.DuplexStreaming,
         __ServiceName,
@@ -114,6 +124,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.SimpleResponse> UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -128,6 +139,7 @@ namespace Grpc.Testing {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task StreamingCall(grpc::IAsyncStreamReader<global::Grpc.Testing.SimpleRequest> requestStream, grpc::IServerStreamWriter<global::Grpc.Testing.SimpleResponse> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -140,6 +152,7 @@ namespace Grpc.Testing {
       /// <param name="requestStream">Used for reading requests from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.SimpleResponse> StreamingFromClient(grpc::IAsyncStreamReader<global::Grpc.Testing.SimpleRequest> requestStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -153,6 +166,7 @@ namespace Grpc.Testing {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task StreamingFromServer(global::Grpc.Testing.SimpleRequest request, grpc::IServerStreamWriter<global::Grpc.Testing.SimpleResponse> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -166,6 +180,7 @@ namespace Grpc.Testing {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task StreamingBothWays(grpc::IAsyncStreamReader<global::Grpc.Testing.SimpleRequest> requestStream, grpc::IServerStreamWriter<global::Grpc.Testing.SimpleResponse> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -178,20 +193,24 @@ namespace Grpc.Testing {
     {
       /// <summary>Creates a new client for BenchmarkService</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public BenchmarkServiceClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for BenchmarkService that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public BenchmarkServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected BenchmarkServiceClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected BenchmarkServiceClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
@@ -205,6 +224,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnaryCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -216,6 +236,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_UnaryCall, null, options, request);
@@ -229,6 +250,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnaryCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -240,6 +262,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_UnaryCall, null, options, request);
@@ -253,6 +276,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StreamingCall(new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -264,6 +288,7 @@ namespace Grpc.Testing {
       /// </summary>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(grpc::CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_StreamingCall, null, options);
@@ -276,6 +301,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncClientStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingFromClient(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StreamingFromClient(new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -286,6 +312,7 @@ namespace Grpc.Testing {
       /// </summary>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncClientStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingFromClient(grpc::CallOptions options)
       {
         return CallInvoker.AsyncClientStreamingCall(__Method_StreamingFromClient, null, options);
@@ -299,6 +326,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.SimpleResponse> StreamingFromServer(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StreamingFromServer(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -310,6 +338,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.SimpleResponse> StreamingFromServer(global::Grpc.Testing.SimpleRequest request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncServerStreamingCall(__Method_StreamingFromServer, null, options, request);
@@ -322,6 +351,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingBothWays(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StreamingBothWays(new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -332,11 +362,13 @@ namespace Grpc.Testing {
       /// </summary>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingBothWays(grpc::CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_StreamingBothWays, null, options);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override BenchmarkServiceClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new BenchmarkServiceClient(configuration);
@@ -345,6 +377,7 @@ namespace Grpc.Testing {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(BenchmarkServiceBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -359,6 +392,7 @@ namespace Grpc.Testing {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, BenchmarkServiceBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_UnaryCall, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse>(serviceImpl.UnaryCall));
index 30fd6de..c5e9d43 100644 (file)
@@ -49,25 +49,30 @@ namespace Grpc.Testing {
     {
       /// <summary>Creates a new client for EmptyService</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public EmptyServiceClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for EmptyService that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public EmptyServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected EmptyServiceClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected EmptyServiceClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
 
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override EmptyServiceClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new EmptyServiceClient(configuration);
@@ -76,6 +81,7 @@ namespace Grpc.Testing {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(EmptyServiceBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder().Build();
@@ -85,6 +91,7 @@ namespace Grpc.Testing {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, EmptyServiceBase serviceImpl)
     {
     }
index 0b364cd..994546c 100644 (file)
@@ -33,6 +33,7 @@ namespace Grpc.Testing {
   {
     static readonly string __ServiceName = "grpc.testing.MetricsService";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -47,11 +48,13 @@ namespace Grpc.Testing {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -63,10 +66,14 @@ namespace Grpc.Testing {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.EmptyMessage> __Marshaller_grpc_testing_EmptyMessage = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.EmptyMessage.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.GaugeResponse> __Marshaller_grpc_testing_GaugeResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.GaugeResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.GaugeRequest> __Marshaller_grpc_testing_GaugeRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.GaugeRequest.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.EmptyMessage, global::Grpc.Testing.GaugeResponse> __Method_GetAllGauges = new grpc::Method<global::Grpc.Testing.EmptyMessage, global::Grpc.Testing.GaugeResponse>(
         grpc::MethodType.ServerStreaming,
         __ServiceName,
@@ -74,6 +81,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_EmptyMessage,
         __Marshaller_grpc_testing_GaugeResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.GaugeRequest, global::Grpc.Testing.GaugeResponse> __Method_GetGauge = new grpc::Method<global::Grpc.Testing.GaugeRequest, global::Grpc.Testing.GaugeResponse>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -99,6 +107,7 @@ namespace Grpc.Testing {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task GetAllGauges(global::Grpc.Testing.EmptyMessage request, grpc::IServerStreamWriter<global::Grpc.Testing.GaugeResponse> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -110,6 +119,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.GaugeResponse> GetGauge(global::Grpc.Testing.GaugeRequest request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -122,20 +132,24 @@ namespace Grpc.Testing {
     {
       /// <summary>Creates a new client for MetricsService</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public MetricsServiceClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for MetricsService that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public MetricsServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected MetricsServiceClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected MetricsServiceClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
@@ -149,6 +163,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.GaugeResponse> GetAllGauges(global::Grpc.Testing.EmptyMessage request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return GetAllGauges(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -160,6 +175,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.GaugeResponse> GetAllGauges(global::Grpc.Testing.EmptyMessage request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncServerStreamingCall(__Method_GetAllGauges, null, options, request);
@@ -172,6 +188,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.GaugeResponse GetGauge(global::Grpc.Testing.GaugeRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return GetGauge(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -182,6 +199,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.GaugeResponse GetGauge(global::Grpc.Testing.GaugeRequest request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_GetGauge, null, options, request);
@@ -194,6 +212,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.GaugeResponse> GetGaugeAsync(global::Grpc.Testing.GaugeRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return GetGaugeAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -204,11 +223,13 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.GaugeResponse> GetGaugeAsync(global::Grpc.Testing.GaugeRequest request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_GetGauge, null, options, request);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override MetricsServiceClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new MetricsServiceClient(configuration);
@@ -217,6 +238,7 @@ namespace Grpc.Testing {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(MetricsServiceBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -228,6 +250,7 @@ namespace Grpc.Testing {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, MetricsServiceBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_GetAllGauges, serviceImpl == null ? null : new grpc::ServerStreamingServerMethod<global::Grpc.Testing.EmptyMessage, global::Grpc.Testing.GaugeResponse>(serviceImpl.GetAllGauges));
index 98f03db..36ba70d 100644 (file)
@@ -29,6 +29,7 @@ namespace Grpc.Testing {
   {
     static readonly string __ServiceName = "grpc.testing.ReportQpsScenarioService";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -43,11 +44,13 @@ namespace Grpc.Testing {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -59,9 +62,12 @@ namespace Grpc.Testing {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.ScenarioResult> __Marshaller_grpc_testing_ScenarioResult = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.ScenarioResult.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.Void> __Marshaller_grpc_testing_Void = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.Void.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.ScenarioResult, global::Grpc.Testing.Void> __Method_ReportScenario = new grpc::Method<global::Grpc.Testing.ScenarioResult, global::Grpc.Testing.Void>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -85,6 +91,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Void> ReportScenario(global::Grpc.Testing.ScenarioResult request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -97,20 +104,24 @@ namespace Grpc.Testing {
     {
       /// <summary>Creates a new client for ReportQpsScenarioService</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public ReportQpsScenarioServiceClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for ReportQpsScenarioService that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public ReportQpsScenarioServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected ReportQpsScenarioServiceClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected ReportQpsScenarioServiceClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
@@ -123,6 +134,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Void ReportScenario(global::Grpc.Testing.ScenarioResult request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return ReportScenario(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -133,6 +145,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Void ReportScenario(global::Grpc.Testing.ScenarioResult request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_ReportScenario, null, options, request);
@@ -145,6 +158,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Void> ReportScenarioAsync(global::Grpc.Testing.ScenarioResult request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return ReportScenarioAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -155,11 +169,13 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Void> ReportScenarioAsync(global::Grpc.Testing.ScenarioResult request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_ReportScenario, null, options, request);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override ReportQpsScenarioServiceClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new ReportQpsScenarioServiceClient(configuration);
@@ -168,6 +184,7 @@ namespace Grpc.Testing {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(ReportQpsScenarioServiceBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -178,6 +195,7 @@ namespace Grpc.Testing {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, ReportQpsScenarioServiceBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_ReportScenario, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Grpc.Testing.ScenarioResult, global::Grpc.Testing.Void>(serviceImpl.ReportScenario));
index 8c1e012..4887c67 100644 (file)
@@ -34,6 +34,7 @@ namespace Grpc.Testing {
   {
     static readonly string __ServiceName = "grpc.testing.TestService";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -48,11 +49,13 @@ namespace Grpc.Testing {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -64,14 +67,22 @@ namespace Grpc.Testing {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.Empty> __Marshaller_grpc_testing_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.Empty.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.SimpleRequest> __Marshaller_grpc_testing_SimpleRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.SimpleRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.SimpleResponse> __Marshaller_grpc_testing_SimpleResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.SimpleResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.StreamingOutputCallRequest> __Marshaller_grpc_testing_StreamingOutputCallRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.StreamingOutputCallRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.StreamingOutputCallResponse> __Marshaller_grpc_testing_StreamingOutputCallResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.StreamingOutputCallResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.StreamingInputCallRequest> __Marshaller_grpc_testing_StreamingInputCallRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.StreamingInputCallRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.StreamingInputCallResponse> __Marshaller_grpc_testing_StreamingInputCallResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.StreamingInputCallResponse.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty> __Method_EmptyCall = new grpc::Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -79,6 +90,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_Empty,
         __Marshaller_grpc_testing_Empty);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> __Method_UnaryCall = new grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -86,6 +98,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_SimpleRequest,
         __Marshaller_grpc_testing_SimpleResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> __Method_CacheableUnaryCall = new grpc::Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -93,6 +106,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_SimpleRequest,
         __Marshaller_grpc_testing_SimpleResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> __Method_StreamingOutputCall = new grpc::Method<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse>(
         grpc::MethodType.ServerStreaming,
         __ServiceName,
@@ -100,6 +114,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_StreamingOutputCallRequest,
         __Marshaller_grpc_testing_StreamingOutputCallResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> __Method_StreamingInputCall = new grpc::Method<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse>(
         grpc::MethodType.ClientStreaming,
         __ServiceName,
@@ -107,6 +122,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_StreamingInputCallRequest,
         __Marshaller_grpc_testing_StreamingInputCallResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> __Method_FullDuplexCall = new grpc::Method<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse>(
         grpc::MethodType.DuplexStreaming,
         __ServiceName,
@@ -114,6 +130,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_StreamingOutputCallRequest,
         __Marshaller_grpc_testing_StreamingOutputCallResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> __Method_HalfDuplexCall = new grpc::Method<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse>(
         grpc::MethodType.DuplexStreaming,
         __ServiceName,
@@ -121,6 +138,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_StreamingOutputCallRequest,
         __Marshaller_grpc_testing_StreamingOutputCallResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty> __Method_UnimplementedCall = new grpc::Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -144,6 +162,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Empty> EmptyCall(global::Grpc.Testing.Empty request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -155,6 +174,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.SimpleResponse> UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -168,6 +188,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.SimpleResponse> CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -181,6 +202,7 @@ namespace Grpc.Testing {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, grpc::IServerStreamWriter<global::Grpc.Testing.StreamingOutputCallResponse> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -193,6 +215,7 @@ namespace Grpc.Testing {
       /// <param name="requestStream">Used for reading requests from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(grpc::IAsyncStreamReader<global::Grpc.Testing.StreamingInputCallRequest> requestStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -207,6 +230,7 @@ namespace Grpc.Testing {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task FullDuplexCall(grpc::IAsyncStreamReader<global::Grpc.Testing.StreamingOutputCallRequest> requestStream, grpc::IServerStreamWriter<global::Grpc.Testing.StreamingOutputCallResponse> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -222,6 +246,7 @@ namespace Grpc.Testing {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task HalfDuplexCall(grpc::IAsyncStreamReader<global::Grpc.Testing.StreamingOutputCallRequest> requestStream, grpc::IServerStreamWriter<global::Grpc.Testing.StreamingOutputCallResponse> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -234,6 +259,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Empty> UnimplementedCall(global::Grpc.Testing.Empty request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -246,20 +272,24 @@ namespace Grpc.Testing {
     {
       /// <summary>Creates a new client for TestService</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public TestServiceClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for TestService that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public TestServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected TestServiceClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected TestServiceClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
@@ -272,6 +302,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return EmptyCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -282,6 +313,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_EmptyCall, null, options, request);
@@ -294,6 +326,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> EmptyCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return EmptyCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -304,6 +337,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> EmptyCallAsync(global::Grpc.Testing.Empty request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_EmptyCall, null, options, request);
@@ -316,6 +350,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnaryCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -326,6 +361,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_UnaryCall, null, options, request);
@@ -338,6 +374,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnaryCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -348,6 +385,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_UnaryCall, null, options, request);
@@ -362,6 +400,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.SimpleResponse CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return CacheableUnaryCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -374,6 +413,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.SimpleResponse CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_CacheableUnaryCall, null, options, request);
@@ -388,6 +428,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> CacheableUnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return CacheableUnaryCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -400,6 +441,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> CacheableUnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_CacheableUnaryCall, null, options, request);
@@ -413,6 +455,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.StreamingOutputCallResponse> StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StreamingOutputCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -424,6 +467,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.StreamingOutputCallResponse> StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncServerStreamingCall(__Method_StreamingOutputCall, null, options, request);
@@ -436,6 +480,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncClientStreamingCall<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StreamingInputCall(new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -446,6 +491,7 @@ namespace Grpc.Testing {
       /// </summary>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncClientStreamingCall<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(grpc::CallOptions options)
       {
         return CallInvoker.AsyncClientStreamingCall(__Method_StreamingInputCall, null, options);
@@ -459,6 +505,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> FullDuplexCall(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return FullDuplexCall(new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -470,6 +517,7 @@ namespace Grpc.Testing {
       /// </summary>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> FullDuplexCall(grpc::CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_FullDuplexCall, null, options);
@@ -484,6 +532,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> HalfDuplexCall(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return HalfDuplexCall(new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -496,6 +545,7 @@ namespace Grpc.Testing {
       /// </summary>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> HalfDuplexCall(grpc::CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_HalfDuplexCall, null, options);
@@ -509,6 +559,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnimplementedCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -520,6 +571,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_UnimplementedCall, null, options, request);
@@ -533,6 +585,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnimplementedCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -544,11 +597,13 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_UnimplementedCall, null, options, request);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override TestServiceClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new TestServiceClient(configuration);
@@ -557,6 +612,7 @@ namespace Grpc.Testing {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(TestServiceBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -574,6 +630,7 @@ namespace Grpc.Testing {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, TestServiceBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_EmptyCall, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>(serviceImpl.EmptyCall));
@@ -595,6 +652,7 @@ namespace Grpc.Testing {
   {
     static readonly string __ServiceName = "grpc.testing.UnimplementedService";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -609,11 +667,13 @@ namespace Grpc.Testing {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -625,8 +685,10 @@ namespace Grpc.Testing {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.Empty> __Marshaller_grpc_testing_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.Empty.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty> __Method_UnimplementedCall = new grpc::Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -650,6 +712,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Empty> UnimplementedCall(global::Grpc.Testing.Empty request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -662,20 +725,24 @@ namespace Grpc.Testing {
     {
       /// <summary>Creates a new client for UnimplementedService</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public UnimplementedServiceClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for UnimplementedService that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public UnimplementedServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected UnimplementedServiceClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected UnimplementedServiceClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
@@ -688,6 +755,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnimplementedCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -698,6 +766,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_UnimplementedCall, null, options, request);
@@ -710,6 +779,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnimplementedCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -720,11 +790,13 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_UnimplementedCall, null, options, request);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override UnimplementedServiceClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new UnimplementedServiceClient(configuration);
@@ -733,6 +805,7 @@ namespace Grpc.Testing {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(UnimplementedServiceBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -743,6 +816,7 @@ namespace Grpc.Testing {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, UnimplementedServiceBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_UnimplementedCall, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>(serviceImpl.UnimplementedCall));
@@ -756,6 +830,7 @@ namespace Grpc.Testing {
   {
     static readonly string __ServiceName = "grpc.testing.ReconnectService";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -770,11 +845,13 @@ namespace Grpc.Testing {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -786,10 +863,14 @@ namespace Grpc.Testing {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.ReconnectParams> __Marshaller_grpc_testing_ReconnectParams = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.ReconnectParams.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.Empty> __Marshaller_grpc_testing_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.Empty.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.ReconnectInfo> __Marshaller_grpc_testing_ReconnectInfo = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.ReconnectInfo.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.ReconnectParams, global::Grpc.Testing.Empty> __Method_Start = new grpc::Method<global::Grpc.Testing.ReconnectParams, global::Grpc.Testing.Empty>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -797,6 +878,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_ReconnectParams,
         __Marshaller_grpc_testing_Empty);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.Empty, global::Grpc.Testing.ReconnectInfo> __Method_Stop = new grpc::Method<global::Grpc.Testing.Empty, global::Grpc.Testing.ReconnectInfo>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -814,11 +896,13 @@ namespace Grpc.Testing {
     [grpc::BindServiceMethod(typeof(ReconnectService), "BindService")]
     public abstract partial class ReconnectServiceBase
     {
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Empty> Start(global::Grpc.Testing.ReconnectParams request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
       }
 
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.ReconnectInfo> Stop(global::Grpc.Testing.Empty request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -831,57 +915,70 @@ namespace Grpc.Testing {
     {
       /// <summary>Creates a new client for ReconnectService</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public ReconnectServiceClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for ReconnectService that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public ReconnectServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected ReconnectServiceClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected ReconnectServiceClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
 
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Empty Start(global::Grpc.Testing.ReconnectParams request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Start(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Empty Start(global::Grpc.Testing.ReconnectParams request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_Start, null, options, request);
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.ReconnectParams request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StartAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.ReconnectParams request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_Start, null, options, request);
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Stop(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_Stop, null, options, request);
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StopAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_Stop, null, options, request);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override ReconnectServiceClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new ReconnectServiceClient(configuration);
@@ -890,6 +987,7 @@ namespace Grpc.Testing {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(ReconnectServiceBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -901,6 +999,7 @@ namespace Grpc.Testing {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, ReconnectServiceBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_Start, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Grpc.Testing.ReconnectParams, global::Grpc.Testing.Empty>(serviceImpl.Start));
@@ -915,6 +1014,7 @@ namespace Grpc.Testing {
   {
     static readonly string __ServiceName = "grpc.testing.LoadBalancerStatsService";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -929,11 +1029,13 @@ namespace Grpc.Testing {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -945,11 +1047,16 @@ namespace Grpc.Testing {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.LoadBalancerStatsRequest> __Marshaller_grpc_testing_LoadBalancerStatsRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.LoadBalancerStatsRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.LoadBalancerStatsResponse> __Marshaller_grpc_testing_LoadBalancerStatsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.LoadBalancerStatsResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.LoadBalancerAccumulatedStatsRequest> __Marshaller_grpc_testing_LoadBalancerAccumulatedStatsRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.LoadBalancerAccumulatedStatsRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.LoadBalancerAccumulatedStatsResponse> __Marshaller_grpc_testing_LoadBalancerAccumulatedStatsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.LoadBalancerAccumulatedStatsResponse.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.LoadBalancerStatsRequest, global::Grpc.Testing.LoadBalancerStatsResponse> __Method_GetClientStats = new grpc::Method<global::Grpc.Testing.LoadBalancerStatsRequest, global::Grpc.Testing.LoadBalancerStatsResponse>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -957,6 +1064,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_LoadBalancerStatsRequest,
         __Marshaller_grpc_testing_LoadBalancerStatsResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.LoadBalancerAccumulatedStatsRequest, global::Grpc.Testing.LoadBalancerAccumulatedStatsResponse> __Method_GetClientAccumulatedStats = new grpc::Method<global::Grpc.Testing.LoadBalancerAccumulatedStatsRequest, global::Grpc.Testing.LoadBalancerAccumulatedStatsResponse>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -980,6 +1088,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.LoadBalancerStatsResponse> GetClientStats(global::Grpc.Testing.LoadBalancerStatsRequest request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -991,6 +1100,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.LoadBalancerAccumulatedStatsResponse> GetClientAccumulatedStats(global::Grpc.Testing.LoadBalancerAccumulatedStatsRequest request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -1003,20 +1113,24 @@ namespace Grpc.Testing {
     {
       /// <summary>Creates a new client for LoadBalancerStatsService</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public LoadBalancerStatsServiceClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for LoadBalancerStatsService that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public LoadBalancerStatsServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected LoadBalancerStatsServiceClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected LoadBalancerStatsServiceClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
@@ -1029,6 +1143,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.LoadBalancerStatsResponse GetClientStats(global::Grpc.Testing.LoadBalancerStatsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return GetClientStats(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -1039,6 +1154,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.LoadBalancerStatsResponse GetClientStats(global::Grpc.Testing.LoadBalancerStatsRequest request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_GetClientStats, null, options, request);
@@ -1051,6 +1167,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.LoadBalancerStatsResponse> GetClientStatsAsync(global::Grpc.Testing.LoadBalancerStatsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return GetClientStatsAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -1061,6 +1178,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.LoadBalancerStatsResponse> GetClientStatsAsync(global::Grpc.Testing.LoadBalancerStatsRequest request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_GetClientStats, null, options, request);
@@ -1073,6 +1191,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.LoadBalancerAccumulatedStatsResponse GetClientAccumulatedStats(global::Grpc.Testing.LoadBalancerAccumulatedStatsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return GetClientAccumulatedStats(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -1083,6 +1202,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.LoadBalancerAccumulatedStatsResponse GetClientAccumulatedStats(global::Grpc.Testing.LoadBalancerAccumulatedStatsRequest request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_GetClientAccumulatedStats, null, options, request);
@@ -1095,6 +1215,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.LoadBalancerAccumulatedStatsResponse> GetClientAccumulatedStatsAsync(global::Grpc.Testing.LoadBalancerAccumulatedStatsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return GetClientAccumulatedStatsAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -1105,11 +1226,13 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.LoadBalancerAccumulatedStatsResponse> GetClientAccumulatedStatsAsync(global::Grpc.Testing.LoadBalancerAccumulatedStatsRequest request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_GetClientAccumulatedStats, null, options, request);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override LoadBalancerStatsServiceClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new LoadBalancerStatsServiceClient(configuration);
@@ -1118,6 +1241,7 @@ namespace Grpc.Testing {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(LoadBalancerStatsServiceBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -1129,6 +1253,7 @@ namespace Grpc.Testing {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, LoadBalancerStatsServiceBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_GetClientStats, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Grpc.Testing.LoadBalancerStatsRequest, global::Grpc.Testing.LoadBalancerStatsResponse>(serviceImpl.GetClientStats));
@@ -1143,6 +1268,7 @@ namespace Grpc.Testing {
   {
     static readonly string __ServiceName = "grpc.testing.XdsUpdateHealthService";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -1157,11 +1283,13 @@ namespace Grpc.Testing {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -1173,8 +1301,10 @@ namespace Grpc.Testing {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.Empty> __Marshaller_grpc_testing_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.Empty.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty> __Method_SetServing = new grpc::Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -1182,6 +1312,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_Empty,
         __Marshaller_grpc_testing_Empty);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty> __Method_SetNotServing = new grpc::Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -1199,11 +1330,13 @@ namespace Grpc.Testing {
     [grpc::BindServiceMethod(typeof(XdsUpdateHealthService), "BindService")]
     public abstract partial class XdsUpdateHealthServiceBase
     {
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Empty> SetServing(global::Grpc.Testing.Empty request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
       }
 
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Empty> SetNotServing(global::Grpc.Testing.Empty request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -1216,57 +1349,70 @@ namespace Grpc.Testing {
     {
       /// <summary>Creates a new client for XdsUpdateHealthService</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public XdsUpdateHealthServiceClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for XdsUpdateHealthService that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public XdsUpdateHealthServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected XdsUpdateHealthServiceClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected XdsUpdateHealthServiceClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
 
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Empty SetServing(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return SetServing(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Empty SetServing(global::Grpc.Testing.Empty request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_SetServing, null, options, request);
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> SetServingAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return SetServingAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> SetServingAsync(global::Grpc.Testing.Empty request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_SetServing, null, options, request);
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Empty SetNotServing(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return SetNotServing(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Empty SetNotServing(global::Grpc.Testing.Empty request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_SetNotServing, null, options, request);
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> SetNotServingAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return SetNotServingAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> SetNotServingAsync(global::Grpc.Testing.Empty request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_SetNotServing, null, options, request);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override XdsUpdateHealthServiceClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new XdsUpdateHealthServiceClient(configuration);
@@ -1275,6 +1421,7 @@ namespace Grpc.Testing {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(XdsUpdateHealthServiceBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -1286,6 +1433,7 @@ namespace Grpc.Testing {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, XdsUpdateHealthServiceBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_SetServing, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>(serviceImpl.SetServing));
@@ -1300,6 +1448,7 @@ namespace Grpc.Testing {
   {
     static readonly string __ServiceName = "grpc.testing.XdsUpdateClientConfigureService";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -1314,11 +1463,13 @@ namespace Grpc.Testing {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -1330,9 +1481,12 @@ namespace Grpc.Testing {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.ClientConfigureRequest> __Marshaller_grpc_testing_ClientConfigureRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.ClientConfigureRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.ClientConfigureResponse> __Marshaller_grpc_testing_ClientConfigureResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.ClientConfigureResponse.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.ClientConfigureRequest, global::Grpc.Testing.ClientConfigureResponse> __Method_Configure = new grpc::Method<global::Grpc.Testing.ClientConfigureRequest, global::Grpc.Testing.ClientConfigureResponse>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -1356,6 +1510,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.ClientConfigureResponse> Configure(global::Grpc.Testing.ClientConfigureRequest request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -1368,20 +1523,24 @@ namespace Grpc.Testing {
     {
       /// <summary>Creates a new client for XdsUpdateClientConfigureService</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public XdsUpdateClientConfigureServiceClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for XdsUpdateClientConfigureService that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public XdsUpdateClientConfigureServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected XdsUpdateClientConfigureServiceClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected XdsUpdateClientConfigureServiceClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
@@ -1394,6 +1553,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.ClientConfigureResponse Configure(global::Grpc.Testing.ClientConfigureRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Configure(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -1404,6 +1564,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.ClientConfigureResponse Configure(global::Grpc.Testing.ClientConfigureRequest request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_Configure, null, options, request);
@@ -1416,6 +1577,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.ClientConfigureResponse> ConfigureAsync(global::Grpc.Testing.ClientConfigureRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return ConfigureAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -1426,11 +1588,13 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.ClientConfigureResponse> ConfigureAsync(global::Grpc.Testing.ClientConfigureRequest request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_Configure, null, options, request);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override XdsUpdateClientConfigureServiceClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new XdsUpdateClientConfigureServiceClient(configuration);
@@ -1439,6 +1603,7 @@ namespace Grpc.Testing {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(XdsUpdateClientConfigureServiceBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -1449,6 +1614,7 @@ namespace Grpc.Testing {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, XdsUpdateClientConfigureServiceBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_Configure, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Grpc.Testing.ClientConfigureRequest, global::Grpc.Testing.ClientConfigureResponse>(serviceImpl.Configure));
index 31bab7c..9004793 100644 (file)
@@ -29,6 +29,7 @@ namespace Grpc.Testing {
   {
     static readonly string __ServiceName = "grpc.testing.WorkerService";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -43,11 +44,13 @@ namespace Grpc.Testing {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -59,14 +62,22 @@ namespace Grpc.Testing {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.ServerArgs> __Marshaller_grpc_testing_ServerArgs = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.ServerArgs.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.ServerStatus> __Marshaller_grpc_testing_ServerStatus = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.ServerStatus.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.ClientArgs> __Marshaller_grpc_testing_ClientArgs = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.ClientArgs.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.ClientStatus> __Marshaller_grpc_testing_ClientStatus = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.ClientStatus.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.CoreRequest> __Marshaller_grpc_testing_CoreRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.CoreRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.CoreResponse> __Marshaller_grpc_testing_CoreResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.CoreResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Testing.Void> __Marshaller_grpc_testing_Void = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Testing.Void.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> __Method_RunServer = new grpc::Method<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus>(
         grpc::MethodType.DuplexStreaming,
         __ServiceName,
@@ -74,6 +85,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_ServerArgs,
         __Marshaller_grpc_testing_ServerStatus);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> __Method_RunClient = new grpc::Method<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus>(
         grpc::MethodType.DuplexStreaming,
         __ServiceName,
@@ -81,6 +93,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_ClientArgs,
         __Marshaller_grpc_testing_ClientStatus);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.CoreRequest, global::Grpc.Testing.CoreResponse> __Method_CoreCount = new grpc::Method<global::Grpc.Testing.CoreRequest, global::Grpc.Testing.CoreResponse>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -88,6 +101,7 @@ namespace Grpc.Testing {
         __Marshaller_grpc_testing_CoreRequest,
         __Marshaller_grpc_testing_CoreResponse);
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Testing.Void, global::Grpc.Testing.Void> __Method_QuitWorker = new grpc::Method<global::Grpc.Testing.Void, global::Grpc.Testing.Void>(
         grpc::MethodType.Unary,
         __ServiceName,
@@ -117,6 +131,7 @@ namespace Grpc.Testing {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task RunServer(grpc::IAsyncStreamReader<global::Grpc.Testing.ServerArgs> requestStream, grpc::IServerStreamWriter<global::Grpc.Testing.ServerStatus> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -134,6 +149,7 @@ namespace Grpc.Testing {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task RunClient(grpc::IAsyncStreamReader<global::Grpc.Testing.ClientArgs> requestStream, grpc::IServerStreamWriter<global::Grpc.Testing.ClientStatus> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -145,6 +161,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.CoreResponse> CoreCount(global::Grpc.Testing.CoreRequest request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -156,6 +173,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request received from the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Void> QuitWorker(global::Grpc.Testing.Void request, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -168,20 +186,24 @@ namespace Grpc.Testing {
     {
       /// <summary>Creates a new client for WorkerService</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public WorkerServiceClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for WorkerService that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public WorkerServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected WorkerServiceClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected WorkerServiceClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
@@ -198,6 +220,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return RunServer(new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -212,6 +235,7 @@ namespace Grpc.Testing {
       /// </summary>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(grpc::CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_RunServer, null, options);
@@ -228,6 +252,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return RunClient(new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -242,6 +267,7 @@ namespace Grpc.Testing {
       /// </summary>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(grpc::CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_RunClient, null, options);
@@ -254,6 +280,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.CoreResponse CoreCount(global::Grpc.Testing.CoreRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return CoreCount(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -264,6 +291,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.CoreResponse CoreCount(global::Grpc.Testing.CoreRequest request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_CoreCount, null, options, request);
@@ -276,6 +304,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.CoreResponse> CoreCountAsync(global::Grpc.Testing.CoreRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return CoreCountAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -286,6 +315,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.CoreResponse> CoreCountAsync(global::Grpc.Testing.CoreRequest request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_CoreCount, null, options, request);
@@ -298,6 +328,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Void QuitWorker(global::Grpc.Testing.Void request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return QuitWorker(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -308,6 +339,7 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::Grpc.Testing.Void QuitWorker(global::Grpc.Testing.Void request, grpc::CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_QuitWorker, null, options, request);
@@ -320,6 +352,7 @@ namespace Grpc.Testing {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Void> QuitWorkerAsync(global::Grpc.Testing.Void request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return QuitWorkerAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -330,11 +363,13 @@ namespace Grpc.Testing {
       /// <param name="request">The request to send to the server.</param>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Void> QuitWorkerAsync(global::Grpc.Testing.Void request, grpc::CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_QuitWorker, null, options, request);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override WorkerServiceClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new WorkerServiceClient(configuration);
@@ -343,6 +378,7 @@ namespace Grpc.Testing {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(WorkerServiceBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -356,6 +392,7 @@ namespace Grpc.Testing {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, WorkerServiceBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_RunServer, serviceImpl == null ? null : new grpc::DuplexStreamingServerMethod<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus>(serviceImpl.RunServer));
index d5822be..2dd819e 100644 (file)
@@ -29,6 +29,7 @@ namespace Grpc.Reflection.V1Alpha {
   {
     static readonly string __ServiceName = "grpc.reflection.v1alpha.ServerReflection";
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -43,11 +44,13 @@ namespace Grpc.Reflection.V1Alpha {
       context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static class __Helper_MessageCache<T>
     {
       public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
     {
       #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
@@ -59,9 +62,12 @@ namespace Grpc.Reflection.V1Alpha {
       return parser.ParseFrom(context.PayloadAsNewBuffer());
     }
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest> __Marshaller_grpc_reflection_v1alpha_ServerReflectionRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Reflection.V1Alpha.ServerReflectionRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Marshaller<global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> __Marshaller_grpc_reflection_v1alpha_ServerReflectionResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse.Parser));
 
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     static readonly grpc::Method<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> __Method_ServerReflectionInfo = new grpc::Method<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse>(
         grpc::MethodType.DuplexStreaming,
         __ServiceName,
@@ -87,6 +93,7 @@ namespace Grpc.Reflection.V1Alpha {
       /// <param name="responseStream">Used for sending responses back to the client.</param>
       /// <param name="context">The context of the server-side call handler being invoked.</param>
       /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual global::System.Threading.Tasks.Task ServerReflectionInfo(grpc::IAsyncStreamReader<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest> requestStream, grpc::IServerStreamWriter<global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> responseStream, grpc::ServerCallContext context)
       {
         throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
@@ -99,20 +106,24 @@ namespace Grpc.Reflection.V1Alpha {
     {
       /// <summary>Creates a new client for ServerReflection</summary>
       /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public ServerReflectionClient(grpc::ChannelBase channel) : base(channel)
       {
       }
       /// <summary>Creates a new client for ServerReflection that uses a custom <c>CallInvoker</c>.</summary>
       /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public ServerReflectionClient(grpc::CallInvoker callInvoker) : base(callInvoker)
       {
       }
       /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected ServerReflectionClient() : base()
       {
       }
       /// <summary>Protected constructor to allow creation of configured clients.</summary>
       /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected ServerReflectionClient(ClientBaseConfiguration configuration) : base(configuration)
       {
       }
@@ -125,6 +136,7 @@ namespace Grpc.Reflection.V1Alpha {
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> ServerReflectionInfo(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return ServerReflectionInfo(new grpc::CallOptions(headers, deadline, cancellationToken));
@@ -135,11 +147,13 @@ namespace Grpc.Reflection.V1Alpha {
       /// </summary>
       /// <param name="options">The options for the call.</param>
       /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> ServerReflectionInfo(grpc::CallOptions options)
       {
         return CallInvoker.AsyncDuplexStreamingCall(__Method_ServerReflectionInfo, null, options);
       }
       /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
       protected override ServerReflectionClient NewInstance(ClientBaseConfiguration configuration)
       {
         return new ServerReflectionClient(configuration);
@@ -148,6 +162,7 @@ namespace Grpc.Reflection.V1Alpha {
 
     /// <summary>Creates service definition that can be registered with a server</summary>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static grpc::ServerServiceDefinition BindService(ServerReflectionBase serviceImpl)
     {
       return grpc::ServerServiceDefinition.CreateBuilder()
@@ -158,6 +173,7 @@ namespace Grpc.Reflection.V1Alpha {
     /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
     /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
     /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
     public static void BindService(grpc::ServiceBinderBase serviceBinder, ServerReflectionBase serviceImpl)
     {
       serviceBinder.AddMethod(__Method_ServerReflectionInfo, serviceImpl == null ? null : new grpc::DuplexStreamingServerMethod<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse>(serviceImpl.ServerReflectionInfo));
index 5f49393..12ac43b 100644 (file)
@@ -1,7 +1,7 @@
 <!-- This file is generated -->
 <Project>
   <PropertyGroup>
-    <GrpcCsharpVersion>2.38.1</GrpcCsharpVersion>
+    <GrpcCsharpVersion>2.39.0</GrpcCsharpVersion>
     <GoogleProtobufVersion>3.15.8</GoogleProtobufVersion>
   </PropertyGroup>
 </Project>
index a58fbdf..f0c9e5c 100644 (file)
@@ -16,7 +16,7 @@ What's currently supported:
 
 Xamarin.Android
 - supported API level: Kitkat 4.4+ (= API level 19)
-- supported ABIs: `armeabi-v7a` (vast majority of Android devices out there), 
+- supported ABIs: `armeabi-v7a` (vast majority of Android devices out there),
   `arm64-v8a` (some newer Android devices), `x86` (for emulator)
 
 Xamarin.iOS
@@ -39,7 +39,65 @@ How to test gRPC in a Unity project
 4. Unity IDE will pick up all the bundled files and add them to project automatically.
    You should be able to use gRPC and Protobuf in your scripts from now on.
 
+5. (optional) Extra steps for iOS, see below
+
 What's currently bundled in the `grpc_unity_package`
 -  Grpc.Core and its dependencies
 -  Google.Protobuf
 -  Precompiled native libraries for Linux, MacOS, Windows, Android and iOS.
+
+Please note that `Grpc.Core` is now in maintenance mode (see [The future of gRPC in C# belongs to grpc-dotnet](https://grpc.io/blog/grpc-csharp-future/)). There is a plan to support Unity in `Grpc.Net.Client`, which depends on Unity's .NET 5 or .NET 6 support. See [this issue](https://github.com/grpc/grpc-dotnet/issues/1309) for more information.
+
+## Building for iOS
+
+To build a Unity app on iOS, there are extra steps to do to make it work:
+
+1. Add a `Assets/link.xml` asset file to your Unity project with the following content:
+   ```xml
+   <linker>
+       <assembly fullname="UnityEngine">
+          <type fullname="UnityEngine.Application" preserve="fields">
+               <property name="platform"/>
+           </type>
+       </assembly>
+   </linker>
+   ```
+   If you don't, you might encounter the following error: `System.IO.FileNotFoundException: Error loading native library. Not found in any of the possible locations:` with a list of paths that point to the `libgrpc_csharp_ext.x64.dylib` file.
+2. Due to the growing build size, bitcode has been disabled for the gRPC library. You must disable it in your XCode project as well.
+3. Add the `libz` framework.
+
+Steps 2 and 3 can be automated by adding the following `Assets/Scripts/BuildIos.cs` script in your Unity project, and attaching it to a Unity game object:
+
+```cs
+#if UNITY_EDITOR && UNITY_IOS
+using System.IO;
+using UnityEngine;
+using UnityEditor;
+using UnityEditor.Callbacks;
+using UnityEditor.iOS.Xcode;
+
+public class BuildIos
+{
+  [PostProcessBuild]
+  public static void OnPostProcessBuild(BuildTarget target, string path)
+  {
+    var projectPath = PBXProject.GetPBXProjectPath(path);
+    var project = new PBXProject();
+    project.ReadFromString(File.ReadAllText(projectPath));
+#if UNITY_2019_3_OR_NEWER
+    var targetGuid = project.GetUnityFrameworkTargetGuid();
+#else
+    var targetGuid = project.TargetGuidByName(PBXProject.GetUnityTargetName());
+#endif
+
+    // libz.tbd for grpc ios build
+    project.AddFrameworkToProject(targetGuid, "libz.tbd", false);
+
+    // bitode is disabled for libgrpc_csharp_ext, so need to disable it for the whole project
+    project.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO");
+
+    File.WriteAllText(projectPath, project.WriteToString());
+  }
+}
+#endif
+```
index 6518e03..2749d4e 100755 (executable)
@@ -47,6 +47,7 @@ ${ANDROID_SDK_CMAKE} ../.. \
   -DCMAKE_BUILD_TYPE=Release \
   -DANDROID_PLATFORM=android-19 \
   -DANDROID_ABI="${ANDROID_ABI}" \
-  -DANDROID_NDK="${ANDROID_NDK_PATH}"
+  -DANDROID_NDK="${ANDROID_NDK_PATH}" \
+  -DgRPC_XDS_USER_AGENT_IS_CSHARP=ON
 
 make -j4 grpc_csharp_ext
index f296142..760442f 100755 (executable)
@@ -31,6 +31,11 @@ function build {
     CPPFLAGS="-O2 -Wframe-larger-than=16384 -arch $ARCH -isysroot $(xcrun --sdk $SDK --show-sdk-path) -mios-version-min=9.0 -DPB_NO_PACKED_STRUCTS=1"
     LDFLAGS="-arch $ARCH -isysroot $(xcrun --sdk $SDK --show-sdk-path) -Wl,ios_version_min=9.0"
 
+    # TODO(jtattermusch): Ideally we'd be setting build defines that correspond to using cmake's
+    # gRPC_XDS_USER_AGENT_IS_CSHARP option here, but since using XDS with C# on iOS is unlikely
+    # and gRPC C#'s support of iOS is only experimental, it's fair to skip that for now
+    # (which will result in XDS user agent language being "not specified" and that's ok
+    # since there are other circumstances in which it isn't set).
     # TODO(jtattermusch): revisit the build arguments
     make -j4 static_csharp \
         VALID_CONFIG_ios_$ARCH="1" \
index ea5b6f1..23c4c3e 100644 (file)
@@ -42,7 +42,7 @@ Pod::Spec.new do |s|
   # exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
   # before them.
   s.name     = '!ProtoCompiler-gRPCCppPlugin'
-  v = '1.38.1'
+  v = '1.39.0'
   s.version  = v
   s.summary  = 'The gRPC ProtoC plugin generates C++ files from .proto services.'
   s.description = <<-DESC
index 7ca9419..ad2bd0e 100644 (file)
@@ -42,7 +42,7 @@ Pod::Spec.new do |s|
   # exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
   # before them.
   s.name     = '!ProtoCompiler-gRPCPlugin'
-  v = '1.38.1'
+  v = '1.39.0'
   s.version  = v
   s.summary  = 'The gRPC ProtoC plugin generates Objective-C files from .proto services.'
   s.description = <<-DESC
index f2ef17d..aae630d 100644 (file)
@@ -39,7 +39,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'BoringSSL-GRPC'
-  version = '0.0.18'
+  version = '0.0.19'
   s.version  = version
   s.summary  = 'BoringSSL is a fork of OpenSSL that is designed to meet Google\'s needs.'
   # Adapted from the homepage:
@@ -76,7 +76,7 @@ Pod::Spec.new do |s|
 
   s.source = {
     :git => 'https://github.com/google/boringssl.git',
-    :commit => "688fc5cf5428868679d2ae1072cad81055752068",
+    :commit => "bcc01b6c66b1c6fa2816b108e50a544b757fbd7b",
   }
 
   s.ios.deployment_target = '9.0'
@@ -215,494 +215,496 @@ Pod::Spec.new do |s|
     base64 -D <<EOF | gunzip > src/include/openssl/boringssl_prefix_symbols.h
       H4sICAAAAAAC/2JvcmluZ3NzbF9wcmVmaXhfc3ltYm9scy5oAKydXXPbuJZo3+dXuO683Kk6NRM77bT7
       vim20tG0Y/tISk9nXliURNk8oUiFoOy4f/0FQErEx94g94arTs10LK21KQDEF0Hgv/7r7DErszptss3Z
-      6vX0j2RV1Xn5KESR7Otsm/9MnrJ0k9X/KZ7OqvLso/50sbg9W1e7Xd78v7MPV1fb9eV6e/nLxdXVh6sP
-      v/62uUiz83e/XqzTzdX5u8vLXy8v3n24+rd/+6//Oruu9q91/vjUnP3f9X+cXbw7v/rH2e9V9VhkZ7Ny
-      /Z/yK+pbD1m9y4XIZbymOjuI7B8y2v71H2e7apNv5f9Py81/VfXZJhdNna8OTXbWPOXiTFTb5iWts7Ot
-      /DAtX5Vrf6j3lcjOXvJG/oBa///q0Jxts+xMIk9ZnalfX6elTIh/nO3r6jnfyCRpntJG/p/sLF1Vz5ky
-      rU/XXlZNvs7UVbRx9/31Hj/a77O0PsvLs7QoFJln4vjrlp+nZ4v7T8v/mcynZ7PF2cP8/s/ZzfTm7P9M
-      FvLf/+dscnejvzT5uvx8Pz+7mS2ubyezL4uzye3tmaTmk7vlbLpQrv+ZLT+fzae/T+YSuZeU9PXuu+vb
-      rzezu981OPvycDuTUXrB2f0n5fgynV9/ln+ZfJzdzpbfdPhPs+XddLH4T+k4u7s/m/45vVueLT4rj3Fl
-      H6dnt7PJx9vp2Sf5r8ndN6VbPEyvZ5Pbf8jrnk+vl/+QiuN/yS9d398tpv/8KnXyO2c3ky+T39WFaPr4
-      T/3DPk+Wi3sZdy5/3uLr7VL9jE/z+y9nt/cLdeVnXxdTGWOynChapqG85MU/JDeVFzhX1z2R/7tezu7v
-      lE8CMvRyPlHXcTf9/Xb2+/TueqrYew0s7+fyu18XHfOPs8l8tlBB778uFX2vnLoI39/dTfV32tRX6SGv
-      RV/FdC4T4stEiz/ZufGfuvx/vJ9Lp7x9ksnNTfIwn36a/XW2T0WTibPmpTqTRa9s8m2e1UIWHln4qzKT
-      mdCoIiYL9U6oPyhR3qi7VZW4anu2S9d1dZb93KelLoTyf3kjztL68bCTPnG2yiSc6UDy7v3Pf/v3jbyz
-      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
-      pwvqGUNQy+0GGrjsUez33/4Hk9kXTTeXBAA=
+      6vX0j2RV1Xn5KESR7Otsm/9MnrJ0k9X/KZ7OqvLso/50sbg9W1e7Xd78v7PVev3ufPVh/eHD6nz9YZte
+      XJ3L/3p3lV2+Sy9/+WX16+Wv29Xm19W//dt//dfZdbV/rfPHp+bs/67/4+zi3fnVP85+r6rHIjublev/
+      lF9R33rI6l0uRC7jNdXZQWT/kNH2r/8421WbfCv/f1pu/quqzza5aOp8dWiys+YpF2ei2jYvaZ2dbeWH
+      afmqXPtDva9EdvaSN/IH1Pr/V4fmbJtlZxJ5yupM/fo6LWVC/ONsX1fP+UYmSfOUNvL/ZGfpqnrOlGl9
+      uvayavJ1pq6ijbvvr/f40X6fpfVZXp6lRaHIPBPHX7f8PD1b3H9a/s9kPj2bLc4e5vd/zm6mN2f/Z7KQ
+      //4/Z5O7G/2lydfl5/v52c1scX07mX1ZnE1ub88kNZ/cLWfThXL9z2z5+Ww+/X0yl8i9pKSvd99d3369
+      md39rsHZl4fbmYzSC87uPynHl+n8+rP8y+Tj7Ha2/KbDf5ot76aLxX9Kx9nd/dn0z+nd8mzxWXmMK/s4
+      PbudTT7eTs8+yX9N7r4p3eJhej2b3P5DXvd8er38h1Qc/0t+6fr+bjH951epk985u5l8mfyuLkTTx3/q
+      H/Z5slzcy7hz+fMWX2+X6md8mt9/Obu9X6grP/u6mMoYk+VE0TIN5SUv/iG5qbzAubruifzf9XJ2f6d8
+      EpChl/OJuo676e+3s9+nd9dTxd5rYHk/l9/9uuiYf5xN5rOFCnr/danoe+XURfj+7m6qv9OmvkoPeS36
+      KqZzmRBfJlr8yc6N/9Tl/+P9XDrl7ZNMbm6Sh/n00+yvs30qmkycNS/VmSx6ZZNv86wWsvDIwl+VmcyE
+      RhUxWah3Qv1BifJG3a2qxFXbs126rquz7Oc+LXUhlP/LG3GW1o+HnfSJs1Um4UwHknfvf/7bv2/knV1m
+      4OX83/QfZ6v/AD9KZvKnz9svBB3mF8/Ss3//97NE/R9ZB5yo2X2yTWQtA19D/8f2D//ogf+wHCJrqJYO
+      6T03y9tFsi5ymVTJLpPVw2aszicdK0MHekRWP2c1R2eRjlXVhcnqsN3K4sZxA7wd4fk8ueCnrE8DdqYW
+      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
+      lWuvnUE9Y/Bq6S/JgY6UYr//9j8R+yVO/ZwEAA==
     EOF
 
     # We are renaming openssl to openssl_grpc so that there is no conflict with openssl if it exists
index 1b113a1..f278a5d 100644 (file)
@@ -22,4 +22,4 @@
 // instead. This file can be regenerated from the template by running
 // `tools/buildgen/generate_projects.sh`.
 
-#define GRPC_OBJC_VERSION_STRING @"1.38.1"
+#define GRPC_OBJC_VERSION_STRING @"1.39.0"
index 6499745..48e8b95 100644 (file)
@@ -22,5 +22,5 @@
 // instead. This file can be regenerated from the template by running
 // `tools/buildgen/generate_projects.sh`.
 
-#define GRPC_OBJC_VERSION_STRING @"1.38.1"
-#define GRPC_C_VERSION_STRING @"16.0.0"
+#define GRPC_OBJC_VERSION_STRING @"1.39.0"
+#define GRPC_C_VERSION_STRING @"18.0.0"
index c904753..e501353 100755 (executable)
@@ -23,7 +23,7 @@ PLUGIN=protoc-gen-grpc=bazel-bin/src/compiler/grpc_php_plugin
 
 $PROTOC --proto_path=src/proto/math \
        --php_out=src/php/tests/generated_code \
-       --grpc_out=src/php/tests/generated_code \
+       --grpc_out=generate_server:src/php/tests/generated_code \
        --plugin=$PLUGIN \
        src/proto/math/math.proto
 
index 6a28593..e046727 100644 (file)
@@ -2,7 +2,7 @@
   "name": "grpc/grpc-dev",
   "description": "gRPC library for PHP - for Development use only",
   "license": "Apache-2.0",
-  "version": "1.38.1",
+  "version": "1.39.0",
   "require": {
     "php": ">=7.0.0",
     "google/protobuf": "^v3.3.0"
index 13c107d..62033ec 100644 (file)
@@ -22,6 +22,7 @@
 #include "channel.h"
 #include "server.h"
 #include "timeval.h"
+#include "version.h"
 #include "channel_credentials.h"
 #include "call_credentials.h"
 #include "server_credentials.h"
@@ -541,6 +542,10 @@ PHP_MINIT_FUNCTION(grpc) {
                          GRPC_CHANNEL_SHUTDOWN,
                          CONST_CS | CONST_PERSISTENT);
 
+  /** grpc version string */
+  REGISTER_STRING_CONSTANT("Grpc\\VERSION", PHP_GRPC_VERSION,
+                           CONST_CS | CONST_PERSISTENT);
+  
   grpc_init_call(TSRMLS_C);
   GRPC_STARTUP(channel);
   grpc_init_server(TSRMLS_C);
index e930491..6d1c475 100644 (file)
@@ -20,6 +20,6 @@
 #ifndef VERSION_H
 #define VERSION_H
 
-#define PHP_GRPC_VERSION "1.38.1"
+#define PHP_GRPC_VERSION "1.39.0"
 
 #endif /* VERSION_H */
index 2feccf8..9697c71 100644 (file)
@@ -86,18 +86,22 @@ class BaseStub
     }
 
     private static function updateOpts($opts) {
-        if (!file_exists($composerFile = __DIR__.'/../../composer.json')) {
-            // for grpc/grpc-php subpackage
-            $composerFile = __DIR__.'/../composer.json';
-        }
-        $package_config = json_decode(file_get_contents($composerFile), true);
         if (!empty($opts['grpc.primary_user_agent'])) {
             $opts['grpc.primary_user_agent'] .= ' ';
         } else {
             $opts['grpc.primary_user_agent'] = '';
         }
-        $opts['grpc.primary_user_agent'] .=
-            'grpc-php/'.$package_config['version'];
+        if (defined('\Grpc\VERSION')) {
+            $version_str = \Grpc\VERSION;
+        } else {
+            if (!file_exists($composerFile = __DIR__.'/../../composer.json')) {
+                // for grpc/grpc-php subpackage
+                $composerFile = __DIR__.'/../composer.json';
+            }
+            $package_config = json_decode(file_get_contents($composerFile), true);
+            $version_str = $package_config['version'];
+        }
+        $opts['grpc.primary_user_agent'] .= 'grpc-php/'.$version_str;
         if (!array_key_exists('credentials', $opts)) {
             throw new \Exception("The opts['credentials'] key is now ".
                 'required. Please see one of the '.
index 590152c..20477ab 100644 (file)
@@ -160,7 +160,7 @@ abstract class AbstractGeneratedCodeTest extends \PHPUnit\Framework\TestCase
             },
         ));
         list($response, $status) = $call->wait();
-        $this->assertSame(\Grpc\STATUS_UNAUTHENTICATED, $status->code);
+        $this->assertSame(\Grpc\STATUS_OK, $status->code);
     }
 
     public function testInvalidMethodName()
diff --git a/src/php/tests/generated_code/Math/MathStub.php b/src/php/tests/generated_code/Math/MathStub.php
new file mode 100644 (file)
index 0000000..babcafe
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+// GENERATED CODE -- DO NOT EDIT!
+
+// Original file comments:
+// 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.
+//
+namespace Math;
+
+/**
+ */
+class MathStub {
+
+    /**
+     * Div divides DivArgs.dividend by DivArgs.divisor and returns the quotient
+     * and remainder.
+     * @param \Math\DivArgs $request client request
+     * @param \Grpc\ServerContext $context server request context
+     * @return \Math\DivReply for response data, null if if error occured
+     *     initial metadata (if any) and status (if not ok) should be set to $context
+     */
+    public function Div(
+        \Math\DivArgs $request,
+        \Grpc\ServerContext $context
+    ): ?\Math\DivReply {
+        $context->setStatus(\Grpc\Status::unimplemented());
+        return null;
+    }
+
+    /**
+     * DivMany accepts an arbitrary number of division args from the client stream
+     * and sends back the results in the reply stream.  The stream continues until
+     * the client closes its end; the server does the same after sending all the
+     * replies.  The stream ends immediately if either end aborts.
+     * @param \Grpc\ServerCallReader $reader read client request data of \Math\DivArgs
+     * @param \Grpc\ServerCallWriter $writer write response data of \Math\DivReply
+     * @param \Grpc\ServerContext $context server request context
+     * @return void
+     */
+    public function DivMany(
+        \Grpc\ServerCallReader $reader,
+        \Grpc\ServerCallWriter $writer,
+        \Grpc\ServerContext $context
+    ): void {
+        $context->setStatus(\Grpc\Status::unimplemented());
+        $writer->finish();
+    }
+
+    /**
+     * Fib generates numbers in the Fibonacci sequence.  If FibArgs.limit > 0, Fib
+     * generates up to limit numbers; otherwise it continues until the call is
+     * canceled.  Unlike Fib above, Fib has no final FibReply.
+     * @param \Math\FibArgs $request client request
+     * @param \Grpc\ServerCallWriter $writer write response data of \Math\Num
+     * @param \Grpc\ServerContext $context server request context
+     * @return void
+     */
+    public function Fib(
+        \Math\FibArgs $request,
+        \Grpc\ServerCallWriter $writer,
+        \Grpc\ServerContext $context
+    ): void {
+        $context->setStatus(\Grpc\Status::unimplemented());
+        $writer->finish();
+    }
+
+    /**
+     * Sum sums a stream of numbers, returning the final result once the stream
+     * is closed.
+     * @param \Grpc\ServerCallReader $reader read client request data of \Math\Num
+     * @param \Grpc\ServerContext $context server request context
+     * @return \Math\Num for response data, null if if error occured
+     *     initial metadata (if any) and status (if not ok) should be set to $context
+     */
+    public function Sum(
+        \Grpc\ServerCallReader $reader,
+        \Grpc\ServerContext $context
+    ): ?\Math\Num {
+        $context->setStatus(\Grpc\Status::unimplemented());
+        return null;
+    }
+
+    /**
+     * Get the method descriptors of the service for server registration
+     *
+     * @return array of \Grpc\MethodDescriptor for the service methods
+     */
+    public final function getMethodDescriptors(): array
+    {
+        return [
+            '/math.Math/Div' => new \Grpc\MethodDescriptor(
+                $this,
+                'Div',
+                '\Math\DivArgs',
+                \Grpc\MethodDescriptor::UNARY_CALL
+            ),
+            '/math.Math/DivMany' => new \Grpc\MethodDescriptor(
+                $this,
+                'DivMany',
+                '\Math\DivArgs',
+                \Grpc\MethodDescriptor::BIDI_STREAMING_CALL
+            ),
+            '/math.Math/Fib' => new \Grpc\MethodDescriptor(
+                $this,
+                'Fib',
+                '\Math\FibArgs',
+                \Grpc\MethodDescriptor::SERVER_STREAMING_CALL
+            ),
+            '/math.Math/Sum' => new \Grpc\MethodDescriptor(
+                $this,
+                'Sum',
+                '\Math\Num',
+                \Grpc\MethodDescriptor::CLIENT_STREAMING_CALL
+            ),
+        ];
+    }
+
+}
diff --git a/src/php/tests/generated_code/math_server.php b/src/php/tests/generated_code/math_server.php
new file mode 100644 (file)
index 0000000..09beb88
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/*
+ *
+ * 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.
+ *
+ */
+
+require dirname(__FILE__) . '/../../lib/Grpc/MethodDescriptor.php';
+require dirname(__FILE__) . '/../../lib/Grpc/Status.php';
+require dirname(__FILE__) . '/../../lib/Grpc/ServerCallReader.php';
+require dirname(__FILE__) . '/../../lib/Grpc/ServerCallWriter.php';
+require dirname(__FILE__) . '/../../lib/Grpc/ServerContext.php';
+require dirname(__FILE__) . '/../../lib/Grpc/RpcServer.php';
+include 'vendor/autoload.php';
+
+class MathService extends Math\MathStub
+{
+    public function Div(
+        \Math\DivArgs $request,
+        \Grpc\ServerContext $context
+    ): ?\Math\DivReply {
+        $dividend = $request->getDividend();
+        $divisor = $request->getDivisor();
+        if ($divisor == 0) {
+            $context->setStatus(
+                \Grpc\Status::status(
+                    \Grpc\STATUS_INVALID_ARGUMENT,
+                    'Cannot divide by zero'
+                )
+            );
+            return null;
+        }
+        usleep(1000); // for GeneratedCodeTest::testTimeout
+        $quotient = intdiv($dividend, $divisor);
+        $remainder = $dividend % $divisor;
+        $reply = new \Math\DivReply([
+            'quotient' => $quotient,
+            'remainder' => $remainder,
+        ]);
+        return $reply;
+    }
+
+    public function DivMany(
+        \Grpc\ServerCallReader $reader,
+        \Grpc\ServerCallWriter $writter,
+        \Grpc\ServerContext $context
+    ): void {
+        while ($divArgs = $reader->read()) {
+            $dividend = $divArgs->getDividend();
+            $divisor = $divArgs->getDivisor();
+            if ($divisor == 0) {
+                $context->setStatus(\Grpc\Status::status(
+                    \Grpc\STATUS_INVALID_ARGUMENT,
+                    'Cannot divide by zero'
+                ));
+                $writter->finish();
+                return;
+            }
+            $quotient = intdiv($dividend, $divisor);
+            $remainder = $dividend % $divisor;
+            $reply = new \Math\DivReply([
+                'quotient' => $quotient,
+                'remainder' => $remainder,
+            ]);
+            $writter->write($reply);
+        }
+        $writter->finish();
+    }
+
+    public function Fib(
+        \Math\FibArgs $request,
+        \Grpc\ServerCallWriter $writter,
+        \Grpc\ServerContext $context
+    ): void {
+        $previous = 0;
+        $current = 1;
+        $limit = $request->getLimit();
+        for ($i = 0; $i < $limit; $i++) {
+            $num = new \Math\Num();
+            $num->setNum($current);
+            $writter->write($num);
+            $next = $previous + $current;
+            $previous = $current;
+            $current = $next;
+        }
+        $writter->finish();
+    }
+
+    /**
+     * Sum sums a stream of numbers, returning the final result once the stream
+     * is closed.
+     * @param \Grpc\ServerCallReader $reader read client request data of \Math\Num
+     * @param \Grpc\ServerContext $context server request context
+     * @return array with [
+     *     \Math\Num, // response data
+     *     optional array = [], // initial metadata
+     *     optional \Grpc\Status = \Grpc\Status::ok // Grpc status
+     * ]
+     */
+    public function Sum(
+        \Grpc\ServerCallReader $reader,
+        \Grpc\ServerContext $context
+    ): ?\Math\Num {
+        $sum = 0;
+        while ($num = $reader->read()) {
+            $sum += $num->getNum();
+        }
+        $reply = new \Math\Num();
+        $reply->setNum($sum);
+        return $reply;
+    }
+}
+
+$server = new \Grpc\RpcServer();
+$server->addHttp2Port('0.0.0.0:50052');
+$server_credentials = Grpc\ServerCredentials::createSsl(
+    null,
+    file_get_contents(dirname(__FILE__) . '/../data/server1.key'),
+    file_get_contents(dirname(__FILE__) . '/../data/server1.pem')
+);
+$server->addSecureHttp2Port('0.0.0.0:50051', $server_credentials);
+$server->handle(new MathService());
+$server->run();
diff --git a/src/proto/grpc/auth/v1/authz_policy.proto b/src/proto/grpc/auth/v1/authz_policy.proto
deleted file mode 100644 (file)
index 347386f..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2021 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.
-
-syntax = "proto3";
-
-package grpc.auth.v1;
-
-// Peer specifies attributes of a peer. Fields in the Peer are ANDed together, once
-// we support multiple fields in the future.
-message Peer {
-  // Optional. A list of peer identities to match for authorization. The principals
-  // are one of, i.e., it matches if one of the principals matches. The field
-  // supports Exact, Prefix, Suffix, and Presence matches.
-  // - Exact match: "abc" will match on value "abc".
-  // - Prefix match: "abc*" will match on value "abc" and "abcd".
-  // - Suffix match: "*abc" will match on value "abc" and "xabc".
-  // - Presence match: "*" will match when the value is not empty.
-  repeated string principals = 1;
-}
-
-// Specification of HTTP header match attributes.
-message Header {
-  // Required. The name of the HTTP header to match. The following headers are *not*
-  // supported: "hop-by-hop" headers (e.g., those listed in "Connection" header),
-  // HTTP/2 pseudo headers (":"-prefixed), the "Host" header, and headers prefixed
-  // with "grpc-".
-  string key = 1;
-
-  // Required. A list of header values to match. The header values are ORed together,
-  // i.e., it matches if one of the values matches. This field supports Exact,
-  // Prefix, Suffix, and Presence match. Multi-valued headers are considered a single
-  // value with commas added between values.
-  // - Exact match: "abc" will match on value "abc".
-  // - Prefix match: "abc*" will match on value "abc" and "abcd".
-  // - Suffix match: "*abc" will match on value "abc" and "xabc".
-  // - Presence match: "*" will match when the value is not empty.
-  repeated string values = 2;
-}
-
-// Request specifies attributes of a request. Fields in the Request are ANDed
-// together.
-message Request {
-  // Optional. A list of paths to match for authorization. This is the fully
-  // qualified name in the form of "/package.service/method". The paths are ORed
-  // together, i.e., it matches if one of the paths matches. This field supports
-  // Exact, Prefix, Suffix, and Presence matches.
-  // - Exact match: "abc" will match on value "abc".
-  // - Prefix match: "abc*" will match on value "abc" and "abcd".
-  // - Suffix match: "*abc" will match on value "abc" and "xabc".
-  // - Presence match: "*" will match when the value is not empty.
-  repeated string paths = 1;
-
-  // Optional. A list of HTTP header key/value pairs to match against, for
-  // potentially advanced use cases. The headers are ANDed together, i.e., it matches
-  // only if *all* the headers match.
-  repeated Header headers = 3;
-}
-
-// Specification of rules.
-message Rule {
-  // Required. The name of an authorization rule.
-  // It is mainly for monitoring and error message generation.
-  string name = 1;
-
-  // Optional. If not set, no checks will be performed against the source. An empty
-  // rule is always matched (i.e., both source and request are empty).
-  Peer source = 2;
-
-  // Optional. If not set, no checks will be performed against the request. An empty
-  // rule is always matched (i.e., both source and request are empty).
-  Request request = 3;
-}
-
-// AuthorizationPolicy defines which principals are permitted to access which
-// resource. Resources are RPC methods scoped by services.
-//
-// In the following yaml policy example, a peer identity from ["admin1", "admin2", "admin3"] 
-// is authorized to access any RPC methods in pkg.service, and peer identity "dev" is 
-// authorized to access the "foo" and "bar" RPC methods.
-//
-// name: example-policy
-// allow_rules:
-// - name: admin-access
-//   source:
-//     principals:
-//     - "spiffe://foo.com/sa/admin1"
-//     - "spiffe://foo.com/sa/admin2"
-//     - "spiffe://foo.com/sa/admin3"
-//   request:
-//     paths: ["/pkg.service/*"]
-// - name: dev-access
-//   source:
-//     principals: ["spiffe://foo.com/sa/dev"]
-//   request:
-//     paths: ["/pkg.service/foo", "/pkg.service/bar"]
-
-message AuthorizationPolicy {
-  // Required. The name of an authorization policy.
-  // It is mainly for monitoring and error message generation.
-  string name = 1;
-
-  // Optional. List of deny rules to match. If a request matches any of the deny
-  // rules, then it will be denied. If none of the deny rules matches or there are
-  // no deny rules, the allow rules will be evaluated.
-  repeated Rule deny_rules = 2;
-
-  // Required. List of allow rules to match. The allow rules will only be evaluated
-  // after the deny rules. If a request matches any of the allow rules, then it will
-  // allowed. If none of the allow rules matches, it will be denied. 
-  repeated Rule allow_rules = 3;
-}
index c423303..4c8f2c5 100644 (file)
@@ -33,6 +33,7 @@ service EchoTestService {
   rpc ResponseStream(EchoRequest) returns (stream EchoResponse);
   rpc BidiStream(stream EchoRequest) returns (stream EchoResponse);
   rpc Unimplemented(EchoRequest) returns (EchoResponse);
+  rpc UnimplementedBidi(stream EchoRequest) returns (stream EchoResponse);
 }
 
 service EchoTest1Service {
index a28c1b2..f38bc9b 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load("@rules_proto//proto:defs.bzl", "proto_library")
 load("@grpc_python_dependencies//:requirements.bzl", "requirement")
 
index be730de..0a6a2dc 100644 (file)
@@ -87,6 +87,7 @@ grpc_proto_library(
     deps = [
         "base_proto",
         "config_source_proto",
+        "endpoint_proto",
     ],
 )
 
index 38592cc..47efbed 100644 (file)
@@ -40,6 +40,13 @@ message SocketAddress {
   oneof port_specifier {
     uint32 port_value = 3;
   }
+
+  // The name of the custom resolver. This must have been registered with Envoy. If
+  // this is empty, a context dependent default applies. If the address is a concrete
+  // IP address, no resolution will occur. If address is a hostname this
+  // should be set for resolution other than DNS. Specifying a custom resolver with
+  // *STRICT_DNS* or *LOGICAL_DNS* will generate an error at runtime.
+  string resolver_name = 5;
 }
 
 // Addresses specify either a logical or physical address and port, which are
index c04fe20..1bc2d51 100644 (file)
@@ -20,6 +20,7 @@ package envoy.config.cluster.v3;
 
 import "src/proto/grpc/testing/xds/v3/base.proto";
 import "src/proto/grpc/testing/xds/v3/config_source.proto";
+import "src/proto/grpc/testing/xds/v3/endpoint.proto";
 
 import "google/protobuf/any.proto";
 import "google/protobuf/wrappers.proto";
@@ -153,12 +154,63 @@ message Cluster {
   // Configuration to use for EDS updates for the Cluster.
   EdsClusterConfig eds_cluster_config = 3;
 
+  // Specific configuration for the :ref:`RingHash<arch_overview_load_balancing_types_ring_hash>`
+  // load balancing policy.
+  message RingHashLbConfig {
+    // The hash function used to hash hosts onto the ketama ring.
+    enum HashFunction {
+      // Use `xxHash <https://github.com/Cyan4973/xxHash>`_, this is the default hash function.
+      XX_HASH = 0;
+      MURMUR_HASH_2 = 1;
+    }
+
+    reserved 2;
+
+    // Minimum hash ring size. The larger the ring is (that is, the more hashes there are for each
+    // provided host) the better the request distribution will reflect the desired weights. Defaults
+    // to 1024 entries, and limited to 8M entries. See also
+    // :ref:`maximum_ring_size<envoy_api_field_config.cluster.v3.Cluster.RingHashLbConfig.maximum_ring_size>`.
+    google.protobuf.UInt64Value minimum_ring_size = 1;
+
+    // The hash function used to hash hosts onto the ketama ring. The value defaults to
+    // :ref:`XX_HASH<envoy_api_enum_value_config.cluster.v3.Cluster.RingHashLbConfig.HashFunction.XX_HASH>`.
+    HashFunction hash_function = 3;
+
+    // Maximum hash ring size. Defaults to 8M entries, and limited to 8M entries, but can be lowered
+    // to further constrain resource use. See also
+    // :ref:`minimum_ring_size<envoy_api_field_config.cluster.v3.Cluster.RingHashLbConfig.minimum_ring_size>`.
+    google.protobuf.UInt64Value maximum_ring_size = 4;
+  }
+
   // The :ref:`load balancer type <arch_overview_load_balancing_types>` to use
   // when picking a host in the cluster.
   LbPolicy lb_policy = 6;
 
+  // Setting this is required for specifying members of
+  // :ref:`STATIC<envoy_api_enum_value_config.cluster.v3.Cluster.DiscoveryType.STATIC>`,
+  // :ref:`STRICT_DNS<envoy_api_enum_value_config.cluster.v3.Cluster.DiscoveryType.STRICT_DNS>`
+  // or :ref:`LOGICAL_DNS<envoy_api_enum_value_config.cluster.v3.Cluster.DiscoveryType.LOGICAL_DNS>` clusters.
+  // This field supersedes the *hosts* field in the v2 API.
+  //
+  // .. attention::
+  //
+  //   Setting this allows non-EDS cluster types to contain embedded EDS equivalent
+  //   :ref:`endpoint assignments<envoy_api_msg_config.endpoint.v3.ClusterLoadAssignment>`.
+  //
+  endpoint.v3.ClusterLoadAssignment load_assignment = 33;
+
   CircuitBreakers circuit_breakers = 10;
 
+  // Optional configuration for the load balancing algorithm selected by
+  // LbPolicy. Currently only
+  // :ref:`RING_HASH<envoy_api_enum_value_config.cluster.v3.Cluster.LbPolicy.RING_HASH>`,
+  // Specifying ring_hash_lb_config without setting the corresponding
+  // LbPolicy will generate an error at runtime.
+  oneof lb_config {
+    // Optional configuration for the Ring Hash load balancing policy.
+    RingHashLbConfig ring_hash_lb_config = 23;
+  }
+
   // Optional custom transport socket implementation to use for upstream connections.
   // To setup TLS, set a transport socket with name `tls` and
   // :ref:`UpstreamTlsContexts <envoy_api_msg_extensions.transport_sockets.tls.v3.UpstreamTlsContext>` in the `typed_config`.
index 7cc1d40..7cbea7f 100644 (file)
@@ -76,6 +76,17 @@ message LbEndpoint {
 
   // Optional health status when known and supplied by EDS server.
   HealthStatus health_status = 2;
+
+  // The optional load balancing weight of the upstream host; at least 1.
+  // Envoy uses the load balancing weight in some of the built in load
+  // balancers. The load balancing weight for an endpoint is divided by the sum
+  // of the weights of all endpoints in the endpoint's locality to produce a
+  // percentage of traffic for the endpoint. This percentage is then further
+  // weighted by the endpoint's locality's load balancing weight from
+  // LocalityLbEndpoints. If unspecified, each host is presumed to have equal
+  // weight in a locality. The sum of the weights of all endpoints in the
+  // endpoint's locality must not exceed uint32_t maximal value (4294967295).
+  google.protobuf.UInt32Value load_balancing_weight = 4;
 }
 
 // A group of endpoints belonging to a Locality.
index af90457..9039ed4 100644 (file)
@@ -36,3 +36,8 @@ message RegexMatcher {
   // The regex match string. The string must be supported by the configured engine.
   string regex = 2;
 }
+
+message RegexMatchAndSubstitute {
+  RegexMatcher pattern = 1;
+  string substitution = 2;
+}
index baeaaf6..89260f6 100644 (file)
@@ -246,6 +246,78 @@ message RouteAction {
     // for additional documentation.
     WeightedCluster weighted_clusters = 3;
   }
+
+  message HashPolicy {
+    message Header {
+      // The name of the request header that will be used to obtain the hash
+      // key. If the request header is not present, no hash will be produced.
+      string header_name = 1;
+
+      // If specified, the request header value will be rewritten and used
+      // to produce the hash key.
+      type.matcher.v3.RegexMatchAndSubstitute regex_rewrite = 2;
+    }
+    
+    message Cookie {
+      string name = 1;
+    }
+
+    message ConnectionProperties {
+      bool source_ip = 1;
+    }
+
+    message QueryParameter {
+      string name = 1;
+    }
+
+    message FilterState {
+      // The name of the Object in the per-request filterState, which is an
+      // Envoy::Http::Hashable object. If there is no data associated with the key,
+      // or the stored object is not Envoy::Http::Hashable, no hash will be produced.
+      string key = 1;
+    }
+
+    oneof policy_specifier {
+      // Header hash policy.
+      Header header = 1;
+
+      // Cookie hash policy.
+      Cookie cookie = 2;
+
+      // Connection properties hash policy.
+      ConnectionProperties connection_properties = 3;
+
+      // Query parameter hash policy.
+      QueryParameter query_parameter = 5;
+
+      // Filter state hash policy.
+      FilterState filter_state = 6;
+    }
+
+    // The flag that short-circuits the hash computing. This field provides a
+    // 'fallback' style of configuration: "if a terminal policy doesn't work,
+    // fallback to rest of the policy list", it saves time when the terminal
+    // policy works.
+    //
+    // If true, and there is already a hash computed, ignore rest of the
+    // list of hash polices.
+    // For example, if the following hash methods are configured:
+    //
+    //  ========= ========
+    //  specifier terminal
+    //  ========= ========
+    //  Header A  true
+    //  Header B  false
+    //  Header C  false
+    //  ========= ========
+    //
+    // The generateHash process ends if policy "header A" generates a hash, as
+    // it's a terminal policy.
+    bool terminal = 4;
+  }
+
+  repeated HashPolicy hash_policy = 15;
+
   // Specifies the maximum stream duration for this route.
   MaxStreamDuration max_stream_duration = 36;
 }
index 9d3a027..dd4a5fb 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 py_library(
index 919d7ce..96e6d9d 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 load("//bazel:cython_library.bzl", "pyx_library")
index e5620cd..e54e510 100644 (file)
@@ -25,7 +25,7 @@ cdef struct CallbackContext:
     # C struct to store callback context in the form of pointers.
     #    
     #   Attributes:
-    #     functor: A grpc_experimental_completion_queue_functor represents the
+    #     functor: A grpc_completion_queue_functor represents the
     #       callback function in the only way Core understands.
     #     waiter: An asyncio.Future object that fulfills when the callback is
     #       invoked by Core.
@@ -33,7 +33,7 @@ cdef struct CallbackContext:
     #       returns 'success == 0' state.
     #     wrapper: A self-reference to the CallbackWrapper to help life cycle
     #       management.
-    grpc_experimental_completion_queue_functor functor
+    grpc_completion_queue_functor functor
     cpython.PyObject *waiter
     cpython.PyObject *loop
     cpython.PyObject *failure_handler
@@ -47,10 +47,10 @@ cdef class CallbackWrapper:
 
     @staticmethod
     cdef void functor_run(
-            grpc_experimental_completion_queue_functor* functor,
+            grpc_completion_queue_functor* functor,
             int succeed)
 
-    cdef grpc_experimental_completion_queue_functor *c_functor(self)
+    cdef grpc_completion_queue_functor *c_functor(self)
 
 
 cdef class GrpcCallWrapper:
index bc25c2e..f2d94a9 100644 (file)
@@ -49,7 +49,7 @@ cdef class CallbackWrapper:
 
     @staticmethod
     cdef void functor_run(
-            grpc_experimental_completion_queue_functor* functor,
+            grpc_completion_queue_functor* functor,
             int success):
         cdef CallbackContext *context = <CallbackContext *>functor
         cdef object waiter = <object>context.waiter
@@ -60,7 +60,7 @@ cdef class CallbackWrapper:
                 waiter.set_result(None)
         cpython.Py_DECREF(<object>context.callback_wrapper)
 
-    cdef grpc_experimental_completion_queue_functor *c_functor(self):
+    cdef grpc_completion_queue_functor *c_functor(self):
         return &self.context.functor
 
 
index 1bfac7f..e3c3188 100644 (file)
@@ -163,7 +163,7 @@ cdef class PollerCompletionQueue(BaseCompletionQueue):
             if loop is context_loop:
                 # Executes callbacks: complete the future
                 CallbackWrapper.functor_run(
-                    <grpc_experimental_completion_queue_functor *>event.tag,
+                    <grpc_completion_queue_functor *>event.tag,
                     event.success
                 )
             else:
index 86171de..0923276 100644 (file)
@@ -208,8 +208,8 @@ cdef void asyncio_kick_loop() with gil:
     pass
 
 
-cdef void asyncio_run_loop(size_t timeout_ms) with gil:
-    pass
+cdef grpc_error* asyncio_run_loop(size_t timeout_ms) with gil:
+    return grpc_error_none()
 
 
 def _auth_plugin_callback_wrapper(object cb,
index 54c55f8..cb5fad9 100644 (file)
@@ -31,7 +31,7 @@ cdef grpc_event _next(grpc_completion_queue *c_completion_queue, deadline) excep
       c_timeout = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c_increment)
       if gpr_time_cmp(c_timeout, c_deadline) > 0:
         c_timeout = c_deadline
-  
+
       c_event = grpc_completion_queue_next(c_completion_queue, c_timeout, NULL)
   
       if (c_event.type != GRPC_QUEUE_TIMEOUT or
index b2ca84f..23de3a0 100644 (file)
@@ -57,7 +57,11 @@ cdef int _get_metadata(void *state,
       with nogil:
         cb(user_data, NULL, 0, status, c_error_details)
   args = context.service_url, context.method_name, callback,
-  _spawn_callback_async(<object>state, args)
+  plugin = <object>state
+  if plugin._stored_ctx is not None:
+    plugin._stored_ctx.copy().run(_spawn_callback_async, plugin, args)
+  else:
+    _spawn_callback_async(<object>state, args)
   return 0  # Asynchronous return
 
 
index fb8ceae..6698dd6 100644 (file)
@@ -42,8 +42,8 @@ cdef extern from "grpc/byte_buffer_reader.h":
 
 
 cdef extern from "grpc/impl/codegen/grpc_types.h":
-    ctypedef struct grpc_experimental_completion_queue_functor:
-        void (*functor_run)(grpc_experimental_completion_queue_functor*, int);
+    ctypedef struct grpc_completion_queue_functor:
+        void (*functor_run)(grpc_completion_queue_functor*, int);
 
 
 cdef extern from "grpc/grpc.h":
@@ -358,7 +358,7 @@ cdef extern from "grpc/grpc.h":
   void grpc_completion_queue_destroy(grpc_completion_queue *cq) nogil
 
   grpc_completion_queue *grpc_completion_queue_create_for_callback(
-    grpc_experimental_completion_queue_functor* shutdown_callback,
+    grpc_completion_queue_functor* shutdown_callback,
     void *reserved) nogil
 
   grpc_call_error grpc_call_start_batch(
@@ -699,3 +699,7 @@ cdef extern from "grpc/grpc_security_constants.h":
   ctypedef enum grpc_local_connect_type:
     UDS
     LOCAL_TCP
+
+
+cdef extern from "src/core/lib/iomgr/error.h":
+  const grpc_error* GRPC_ERROR_CANCELLED
index c86ff2f..22b4766 100644 (file)
@@ -15,6 +15,7 @@
 
 from libc cimport string
 import errno
+import sys
 gevent_g = None
 gevent_socket = None
 gevent_hub = None
@@ -348,12 +349,24 @@ cdef void destroy_loop() with gil:
 cdef void kick_loop() with gil:
   g_event.set()
 
-cdef void run_loop(size_t timeout_ms) with gil:
-    timeout = timeout_ms / 1000.0
-    if timeout_ms > 0:
+def _run_loop(timeout_ms):
+  timeout = timeout_ms / 1000.0
+  if timeout_ms > 0:
+    try:
       g_event.wait(timeout)
+    finally:
       g_event.clear()
 
+cdef grpc_error* run_loop(size_t timeout_ms) with gil:
+  try:
+    _run_loop(timeout_ms)
+    return grpc_error_none()
+  except BaseException:
+    exc_info = sys.exc_info()
+    # Avoid running any Python code after setting the exception
+    cpython.PyErr_SetObject(exc_info[0], exc_info[1])
+    return GRPC_ERROR_CANCELLED
+
 ###############################
 ### Initializer ###############
 ###############################
index 9cf3113..b427554 100644 (file)
@@ -110,7 +110,7 @@ cdef extern from "src/core/lib/iomgr/timer_custom.h":
 cdef extern from "src/core/lib/iomgr/pollset_custom.h":
   struct grpc_custom_poller_vtable:
     void (*init)()
-    void (*poll)(size_t timeout_ms)
+    grpc_error* (*poll)(size_t timeout_ms)
     void (*kick)()
     void (*shutdown)()
 
index a13c474..fbf63de 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc/_grpcio_metadata.py.template`!!!
 
-__version__ = """1.38.1"""
+__version__ = """1.39.0"""
index e3bfa90..15ef131 100644 (file)
@@ -68,6 +68,17 @@ class _Plugin(object):
 
     def __init__(self, metadata_plugin):
         self._metadata_plugin = metadata_plugin
+        self._stored_ctx = None
+
+        try:
+            import contextvars  # pylint: disable=wrong-import-position
+            # The plugin may be invoked on a thread created by Core, which will not
+            # have the context propagated. This context is stored and installed in
+            # the thread invoking the plugin.
+            self._stored_ctx = contextvars.copy_context()
+        except ImportError:
+            # Support versions predating contextvars.
+            pass
 
     def __call__(self, service_url, method_name, callback):
         context = _AuthMetadataContext(_common.decode(service_url),
index f62685c..b40aa96 100644 (file)
@@ -169,7 +169,7 @@ class StreamUnaryClientInterceptor(ClientInterceptor, metaclass=ABCMeta):
     async def intercept_stream_unary(
         self,
         continuation: Callable[[ClientCallDetails, RequestType],
-                               UnaryStreamCall],
+                               StreamUnaryCall],
         client_call_details: ClientCallDetails,
         request_iterator: RequestIterableType,
     ) -> StreamUnaryCall:
@@ -210,7 +210,7 @@ class StreamStreamClientInterceptor(ClientInterceptor, metaclass=ABCMeta):
     async def intercept_stream_stream(
         self,
         continuation: Callable[[ClientCallDetails, RequestType],
-                               UnaryStreamCall],
+                               StreamStreamCall],
         client_call_details: ClientCallDetails,
         request_iterator: RequestIterableType,
     ) -> Union[ResponseIterableType, StreamStreamCall]:
index cdca2f4..48a9f53 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 py_library(
index c7880ba..ad34d06 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 py_library(
index 1b9d3a2..9eeb580 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 py_library(
index 4a71183..c90a225 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 py_library(
index 0cb21f5..647662a 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 py_library(
index fab7e1a..98a1c5f 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 py_library(
index 7255ca0..19f764f 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 py_library(
index c8c4468..dff7fb1 100644 (file)
@@ -52,10 +52,12 @@ CORE_SOURCE_FILES = [
     'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
     'src/core/ext/filters/client_channel/resolver.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc',
+    'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc',
+    'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc',
     'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc',
@@ -349,6 +351,8 @@ CORE_SOURCE_FILES = [
     'src/core/lib/debug/stats.cc',
     'src/core/lib/debug/stats_data.cc',
     'src/core/lib/debug/trace.cc',
+    'src/core/lib/event_engine/endpoint_config.cc',
+    'src/core/lib/event_engine/event_engine.cc',
     'src/core/lib/event_engine/slice_allocator.cc',
     'src/core/lib/event_engine/sockaddr.cc',
     'src/core/lib/gpr/alloc.cc',
@@ -406,6 +410,7 @@ CORE_SOURCE_FILES = [
     'src/core/lib/iomgr/dualstack_socket_posix.cc',
     'src/core/lib/iomgr/endpoint.cc',
     'src/core/lib/iomgr/endpoint_cfstream.cc',
+    'src/core/lib/iomgr/endpoint_pair_event_engine.cc',
     'src/core/lib/iomgr/endpoint_pair_posix.cc',
     'src/core/lib/iomgr/endpoint_pair_uv.cc',
     'src/core/lib/iomgr/endpoint_pair_windows.cc',
@@ -417,6 +422,14 @@ CORE_SOURCE_FILES = [
     'src/core/lib/iomgr/ev_poll_posix.cc',
     'src/core/lib/iomgr/ev_posix.cc',
     'src/core/lib/iomgr/ev_windows.cc',
+    'src/core/lib/iomgr/event_engine/closure.cc',
+    'src/core/lib/iomgr/event_engine/endpoint.cc',
+    'src/core/lib/iomgr/event_engine/iomgr.cc',
+    'src/core/lib/iomgr/event_engine/pollset.cc',
+    'src/core/lib/iomgr/event_engine/resolved_address_internal.cc',
+    'src/core/lib/iomgr/event_engine/resolver.cc',
+    'src/core/lib/iomgr/event_engine/tcp.cc',
+    'src/core/lib/iomgr/event_engine/timer.cc',
     'src/core/lib/iomgr/exec_ctx.cc',
     'src/core/lib/iomgr/executor.cc',
     'src/core/lib/iomgr/executor/mpmcqueue.cc',
@@ -498,6 +511,8 @@ CORE_SOURCE_FILES = [
     'src/core/lib/matchers/matchers.cc',
     'src/core/lib/profiling/basic_timers.cc',
     'src/core/lib/profiling/stap_timers.cc',
+    'src/core/lib/security/authorization/authorization_policy_provider_vtable.cc',
+    'src/core/lib/security/authorization/evaluate_args.cc',
     'src/core/lib/security/context/security_context.cc',
     'src/core/lib/security/credentials/alts/alts_credentials.cc',
     'src/core/lib/security/credentials/alts/check_gcp_environment.cc',
index 2c21b7c..b0fa119 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!!
 
-VERSION = '1.38.1'
+VERSION = '1.39.0'
index 0aec347..6f2e843 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_admin/grpc_version.py.template`!!!
 
-VERSION = '1.38.1'
+VERSION = '1.39.0'
index d2393b0..07b5873 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load("//bazel:python_rules.bzl", "py_grpc_library", "py_proto_library")
 
 package(default_visibility = ["//visibility:public"])
index 482df68..2dbd52d 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_channelz/grpc_version.py.template`!!!
 
-VERSION = '1.38.1'
+VERSION = '1.39.0'
index f2f757f..b3dcd35 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_csds/grpc_version.py.template`!!!
 
-VERSION = '1.38.1'
+VERSION = '1.39.0'
index fe4aaed..a9ad3f4 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load("//bazel:python_rules.bzl", "py_grpc_library", "py_proto_library")
 
 package(default_visibility = ["//visibility:public"])
index 9ce60fc..a166944 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_health_checking/grpc_version.py.template`!!!
 
-VERSION = '1.38.1'
+VERSION = '1.39.0'
index 6e3d8e0..b23f577 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load("//bazel:python_rules.bzl", "py_grpc_library", "py_proto_library")
 load("@grpc_python_dependencies//:requirements.bzl", "requirement")
 
index cc06b6c..a082c46 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_reflection/grpc_version.py.template`!!!
 
-VERSION = '1.38.1'
+VERSION = '1.39.0'
index a6abdd3..6a7ef2d 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load("@grpc_python_dependencies//:requirements.bzl", "requirement")
 
 package(default_visibility = ["//visibility:public"])
index f28ed5b..8088e18 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_status/grpc_version.py.template`!!!
 
-VERSION = '1.38.1'
+VERSION = '1.39.0'
index 9652362..057313e 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_testing/grpc_version.py.template`!!!
 
-VERSION = '1.38.1'
+VERSION = '1.39.0'
index f5f3849..ab7dbc5 100644 (file)
@@ -257,7 +257,7 @@ class TestGevent(setuptools.Command):
 
         import tests
         loader = tests.Loader()
-        loader.loadTestsFromNames(['tests'])
+        loader.loadTestsFromNames(['tests', 'tests_gevent'])
         runner = tests.Runner()
         if sys.platform == 'win32':
             runner.skip_tests(self.BANNED_TESTS + self.BANNED_WINDOWS_TESTS)
index 098cd3f..ebecce2 100644 (file)
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_tests/grpc_version.py.template`!!!
 
-VERSION = '1.38.1'
+VERSION = '1.39.0'
index c177c77..21b5306 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 py_library(
     name = "bazel_namespace_package_hack",
     srcs = ["bazel_namespace_package_hack.py"],
index 6f80c1a..d16df1c 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 load("//bazel:python_rules.bzl", "py2and3_test")
index 03380d3..9c588b9 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 load("//bazel:python_rules.bzl", "py2and3_test")
index 1b30060..1180a95 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load("@grpc_python_dependencies//:requirements.bzl", "requirement")
 load("//bazel:python_rules.bzl", "py2and3_test")
 
index 91858cd..e59783f 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 filegroup(
index 6d37618..bdce2b7 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load("@grpc_python_dependencies//:requirements.bzl", "requirement")
 load("//bazel:python_rules.bzl", "py2and3_test")
 
index 798032d..e96ceb3 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load("@grpc_python_dependencies//:requirements.bzl", "requirement")
 load("//bazel:python_rules.bzl", "py2and3_test")
 
index 01c1509..a675929 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load("@rules_proto//proto:defs.bzl", "proto_library")
 load("@com_github_grpc_grpc//bazel:python_rules.bzl", "py_grpc_library", "py_proto_library")
 
index 78a072b..1521270 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 py_library(
index d933886..67d7a90 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load("//bazel:python_rules.bzl", "py2and3_test")
 
 package(default_visibility = ["//visibility:public"])
index fec0fbd..d97990a 100644 (file)
@@ -17,11 +17,13 @@ import contextlib
 import logging
 import os
 import sys
+import threading
 import unittest
 
 import grpc
 
 from tests.unit import test_common
+from six.moves import queue
 
 _UNARY_UNARY = "/test/UnaryUnary"
 _REQUEST = b"0000"
@@ -112,6 +114,47 @@ class ContextVarsPropagationTest(unittest.TestCase):
                 response = stub(_REQUEST, wait_for_ready=True)
                 self.assertEqual(_REQUEST, response)
 
+    def test_concurrent_propagation(self):
+        _THREAD_COUNT = 32
+        _RPC_COUNT = 32
+
+        set_up_expected_context()
+        with _server() as port:
+            target = "localhost:{}".format(port)
+            local_credentials = grpc.local_channel_credentials()
+            test_call_credentials = TestCallCredentials()
+            call_credentials = grpc.metadata_call_credentials(
+                test_call_credentials, "test call credentials")
+            composite_credentials = grpc.composite_channel_credentials(
+                local_credentials, call_credentials)
+            wait_group = test_common.WaitGroup(_THREAD_COUNT)
+
+            def _run_on_thread(exception_queue):
+                try:
+                    with grpc.secure_channel(target,
+                                             composite_credentials) as channel:
+                        stub = channel.unary_unary(_UNARY_UNARY)
+                        wait_group.done()
+                        wait_group.wait()
+                        for i in range(_RPC_COUNT):
+                            response = stub(_REQUEST, wait_for_ready=True)
+                            self.assertEqual(_REQUEST, response)
+                except Exception as e:  # pylint: disable=broad-except
+                    exception_queue.put(e)
+
+            threads = []
+            for _ in range(_RPC_COUNT):
+                q = queue.Queue()
+                thread = threading.Thread(target=_run_on_thread, args=(q,))
+                thread.setDaemon(True)
+                thread.start()
+                threads.append((thread, q))
+
+            for thread, q in threads:
+                thread.join()
+                if not q.empty():
+                    raise q.get()
+
 
 if __name__ == '__main__':
     logging.basicConfig()
index 33376b8..5e4a358 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 load("@grpc_python_dependencies//:requirements.bzl", "requirement")
 load("//bazel:python_rules.bzl", "py2and3_test")
 
index 91858cd..e59783f 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 filegroup(
index a5ddcf3..809d90d 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 py_library(
index 4584a86..3e6a48c 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 package(default_visibility = ["//visibility:public"])
 
 load("//bazel:python_rules.bzl", "py2and3_test")
index 8bf2dc8..e80d593 100644 (file)
@@ -97,6 +97,8 @@ class TestChannelArgument(AioTestBase):
 
     @unittest.skipIf(platform.system() == 'Windows',
                      'SO_REUSEPORT only available in Linux-like OS.')
+    @unittest.skipIf('aarch64' in platform.machine(),
+                     'SO_REUSEPORT needs to be enabled in Core\'s port.h.')
     async def test_server_so_reuse_port_is_set_properly(self):
 
         async def test_body():
index 7f98329..8ed2689 100644 (file)
@@ -15,6 +15,7 @@
 
 import asyncio
 import logging
+import platform
 import threading
 import time
 import unittest
@@ -37,6 +38,8 @@ class TestConnectivityState(AioTestBase):
     async def tearDown(self):
         await self._server.stop(None)
 
+    @unittest.skipIf('aarch64' in platform.machine(),
+                     'The transient failure propagation is slower on aarch64')
     async def test_unavailable_backend(self):
         async with aio.insecure_channel(UNREACHABLE_TARGET) as channel:
             self.assertEqual(grpc.ChannelConnectivity.IDLE,
diff --git a/src/python/grpcio_tests/tests_gevent/__init__.py b/src/python/grpcio_tests/tests_gevent/__init__.py
new file mode 100644 (file)
index 0000000..712a2e1
--- /dev/null
@@ -0,0 +1,13 @@
+# Copyright 2021 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.
diff --git a/src/python/grpcio_tests/tests_gevent/tests.json b/src/python/grpcio_tests/tests_gevent/tests.json
new file mode 100644 (file)
index 0000000..11307dc
--- /dev/null
@@ -0,0 +1 @@
+["unit.close_channel_test.CloseChannelTest"]
diff --git a/src/python/grpcio_tests/tests_gevent/unit/__init__.py b/src/python/grpcio_tests/tests_gevent/unit/__init__.py
new file mode 100644 (file)
index 0000000..712a2e1
--- /dev/null
@@ -0,0 +1,13 @@
+# Copyright 2021 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.
diff --git a/src/python/grpcio_tests/tests_gevent/unit/_test_server.py b/src/python/grpcio_tests/tests_gevent/unit/_test_server.py
new file mode 100644 (file)
index 0000000..f1ba23e
--- /dev/null
@@ -0,0 +1,58 @@
+# Copyright 2021 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.
+
+from concurrent import futures
+from src.proto.grpc.testing import messages_pb2, test_pb2_grpc
+import grpc
+import gevent
+from typing import Any, Tuple
+
+LONG_UNARY_CALL_WITH_SLEEP_VALUE = 1
+
+
+class TestServiceServicer(test_pb2_grpc.TestServiceServicer):
+
+    def UnaryCall(self, request, context):
+        return messages_pb2.SimpleResponse()
+
+    def UnaryCallWithSleep(self, unused_request, unused_context):
+        gevent.sleep(LONG_UNARY_CALL_WITH_SLEEP_VALUE)
+        return messages_pb2.SimpleResponse()
+
+
+def start_test_server(port: int = 0) -> Tuple[str, Any]:
+    server = grpc.server(futures.ThreadPoolExecutor())
+    servicer = TestServiceServicer()
+    test_pb2_grpc.add_TestServiceServicer_to_server(TestServiceServicer(),
+                                                    server)
+
+    server.add_generic_rpc_handlers((_create_extra_generic_handler(servicer),))
+    port = server.add_insecure_port('[::]:%d' % port)
+    server.start()
+    return 'localhost:%d' % port, server
+
+
+def _create_extra_generic_handler(servicer: TestServiceServicer) -> Any:
+    # Add programatically extra methods not provided by the proto file
+    # that are used during the tests
+    rpc_method_handlers = {
+        'UnaryCallWithSleep':
+            grpc.unary_unary_rpc_method_handler(
+                servicer.UnaryCallWithSleep,
+                request_deserializer=messages_pb2.SimpleRequest.FromString,
+                response_serializer=messages_pb2.SimpleResponse.
+                SerializeToString)
+    }
+    return grpc.method_handlers_generic_handler('grpc.testing.TestService',
+                                                rpc_method_handlers)
diff --git a/src/python/grpcio_tests/tests_gevent/unit/close_channel_test.py b/src/python/grpcio_tests/tests_gevent/unit/close_channel_test.py
new file mode 100644 (file)
index 0000000..54f1361
--- /dev/null
@@ -0,0 +1,102 @@
+# Copyright 2021 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.
+
+import unittest
+from src.proto.grpc.testing import messages_pb2, test_pb2_grpc
+import grpc
+import gevent
+import sys
+from gevent.pool import Group
+from tests_gevent.unit._test_server import start_test_server
+
+_UNARY_CALL_METHOD_WITH_SLEEP = '/grpc.testing.TestService/UnaryCallWithSleep'
+
+
+class CloseChannelTest(unittest.TestCase):
+
+    def setUp(self):
+        self._server_target, self._server = start_test_server()
+        self._channel = grpc.insecure_channel(self._server_target)
+        self._unhandled_exception = False
+        sys.excepthook = self._global_exception_handler
+
+    def tearDown(self):
+        self._channel.close()
+        self._server.stop(None)
+
+    def test_graceful_close(self):
+        stub = test_pb2_grpc.TestServiceStub(self._channel)
+        _, response = stub.UnaryCall.with_call(messages_pb2.SimpleRequest())
+
+        self._channel.close()
+
+        self.assertEqual(grpc.StatusCode.OK, response.code())
+
+    def test_graceful_close_in_greenlet(self):
+        group = Group()
+        stub = test_pb2_grpc.TestServiceStub(self._channel)
+        greenlet = group.spawn(self._run_client, stub.UnaryCall)
+        # release loop so that greenlet can take control
+        gevent.sleep()
+        self._channel.close()
+        group.killone(greenlet)
+        self.assertFalse(self._unhandled_exception, "Unhandled GreenletExit")
+        try:
+            greenlet.get()
+        except Exception as e:  # pylint: disable=broad-except
+            self.fail(f"Unexpected exception in greenlet: {e}")
+
+    def test_ungraceful_close_in_greenlet(self):
+        group = Group()
+        UnaryCallWithSleep = self._channel.unary_unary(
+            _UNARY_CALL_METHOD_WITH_SLEEP,
+            request_serializer=messages_pb2.SimpleRequest.SerializeToString,
+            response_deserializer=messages_pb2.SimpleResponse.FromString,
+        )
+        greenlet = group.spawn(self._run_client, UnaryCallWithSleep)
+        # release loop so that greenlet can take control
+        gevent.sleep()
+        group.killone(greenlet)
+        self.assertFalse(self._unhandled_exception, "Unhandled GreenletExit")
+
+    def test_kill_greenlet_with_generic_exception(self):
+        group = Group()
+        UnaryCallWithSleep = self._channel.unary_unary(
+            _UNARY_CALL_METHOD_WITH_SLEEP,
+            request_serializer=messages_pb2.SimpleRequest.SerializeToString,
+            response_deserializer=messages_pb2.SimpleResponse.FromString,
+        )
+        greenlet = group.spawn(self._run_client, UnaryCallWithSleep)
+        # release loop so that greenlet can take control
+        gevent.sleep()
+        group.killone(greenlet, exception=Exception)
+        self.assertFalse(self._unhandled_exception, "Unhandled exception")
+        self.assertRaises(Exception, greenlet.get)
+
+    def _run_client(self, call):
+        try:
+            call.with_call(messages_pb2.SimpleRequest())
+        except grpc.RpcError as e:
+            if e.code() != grpc.StatusCode.CANCELLED:
+                raise
+
+    def _global_exception_handler(self, exctype, value, tb):
+        if exctype == gevent.GreenletExit:
+            self._unhandled_exception = True
+            return
+        sys.__excepthook__(exctype, value, tb)
+
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)
index 506d744..2551c31 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 py_binary(
     name = "xds_interop_client",
     srcs = ["xds_interop_client.py"],
@@ -8,6 +21,7 @@ py_binary(
         "//src/proto/grpc/testing:py_test_proto",
         "//src/proto/grpc/testing:test_py_pb2_grpc",
         "//src/python/grpcio/grpc:grpcio",
+        "//src/python/grpcio_admin/grpc_admin",
         "//src/python/grpcio_channelz/grpc_channelz/v1:grpc_channelz",
     ],
 )
index e8184e7..82e1ea1 100644 (file)
@@ -19,6 +19,9 @@ RUN cp -rL /workdir/bazel-bin/src/python/grpcio_tests/tests_py3_only/interop/xds
 FROM phusion/baseimage:master@sha256:65ea10d5f757e5e86272625f8675d437dd83d8db64bdb429e2354d58f5462750
 COPY --from=0 /artifacts ./
 
+ENV GRPC_VERBOSITY="DEBUG"
+ENV GRPC_TRACE="xds_client,xds_resolver,xds_cluster_manager_lb,cds_lb,xds_cluster_resolver_lb,priority_lb,xds_cluster_impl_lb,weighted_target_lb"
+
 RUN apt-get update -y && apt-get install -y python3
 RUN ln -s /usr/bin/python3 /usr/bin/python
 
index b7259bf..d9fa7b3 100644 (file)
@@ -19,6 +19,9 @@ RUN cp -rL /workdir/bazel-bin/src/python/grpcio_tests/tests_py3_only/interop/xds
 FROM phusion/baseimage:master@sha256:65ea10d5f757e5e86272625f8675d437dd83d8db64bdb429e2354d58f5462750
 COPY --from=0 /artifacts ./
 
+ENV GRPC_VERBOSITY="DEBUG"
+ENV GRPC_TRACE="xds_client,xds_resolver,xds_cluster_manager_lb,cds_lb,xds_cluster_resolver_lb,priority_lb,xds_cluster_impl_lb,weighted_target_lb"
+
 RUN apt-get update -y && apt-get install -y python3
 RUN ln -s /usr/bin/python3 /usr/bin/python
 
index e71be52..dd9d504 100644 (file)
@@ -28,6 +28,7 @@ from concurrent import futures
 
 import grpc
 from grpc_channelz.v1 import channelz
+import grpc_admin
 
 from src.proto.grpc.testing import test_pb2
 from src.proto.grpc.testing import test_pb2_grpc
@@ -402,6 +403,7 @@ def _run(args: argparse.Namespace, methods: Sequence[str],
         _XdsUpdateClientConfigureServicer(channel_configs, args.qps),
         _global_server)
     channelz.add_channelz_servicer(_global_server)
+    grpc_admin.add_admin_servicers(_global_server)
     _global_server.start()
     _global_server.wait_for_termination()
     for method_handle in method_handles:
index b0901b2..bc94dcb 100644 (file)
@@ -41,7 +41,7 @@ from src.proto.grpc.testing import empty_pb2
 #  It currently only implements enough functionality to pass the xDS security
 #  tests.
 
-_LISTEN_HOST = "[::]"
+_LISTEN_HOST = "0.0.0.0"
 
 _THREAD_POOL_SIZE = 256
 
index bb05b53..8bec5a9 100644 (file)
@@ -56,6 +56,8 @@ if RUBY_PLATFORM =~ /darwin/
 end
 
 ENV['CPPFLAGS'] = '-DGPR_BACKWARDS_COMPATIBILITY_MODE'
+ENV['CPPFLAGS'] += ' -DGRPC_XDS_USER_AGENT_NAME_SUFFIX="\"RUBY\"" '
+ENV['CPPFLAGS'] += ' -DGRPC_XDS_USER_AGENT_VERSION_SUFFIX="\"1.39.0\"" '
 
 output_dir = File.expand_path(RbConfig::CONFIG['topdir'])
 grpc_lib_dir = File.join(output_dir, 'libs', grpc_config)
index 8d799ba..f05555a 100644 (file)
@@ -107,6 +107,7 @@ grpc_channelz_get_server_sockets_type grpc_channelz_get_server_sockets_import;
 grpc_channelz_get_channel_type grpc_channelz_get_channel_import;
 grpc_channelz_get_subchannel_type grpc_channelz_get_subchannel_import;
 grpc_channelz_get_socket_type grpc_channelz_get_socket_import;
+grpc_authorization_policy_provider_arg_vtable_type grpc_authorization_policy_provider_arg_vtable_import;
 grpc_insecure_channel_create_from_fd_type grpc_insecure_channel_create_from_fd_import;
 grpc_server_add_insecure_channel_from_fd_type grpc_server_add_insecure_channel_from_fd_import;
 grpc_auth_property_iterator_next_type grpc_auth_property_iterator_next_import;
@@ -182,6 +183,8 @@ grpc_tls_server_authorization_check_config_create_type grpc_tls_server_authoriza
 grpc_tls_server_authorization_check_config_release_type grpc_tls_server_authorization_check_config_release_import;
 grpc_xds_credentials_create_type grpc_xds_credentials_create_import;
 grpc_xds_server_credentials_create_type grpc_xds_server_credentials_create_import;
+grpc_authorization_policy_provider_static_data_create_type grpc_authorization_policy_provider_static_data_create_import;
+grpc_authorization_policy_provider_release_type grpc_authorization_policy_provider_release_import;
 grpc_raw_byte_buffer_create_type grpc_raw_byte_buffer_create_import;
 grpc_raw_compressed_byte_buffer_create_type grpc_raw_compressed_byte_buffer_create_import;
 grpc_byte_buffer_copy_type grpc_byte_buffer_copy_import;
@@ -392,6 +395,7 @@ void grpc_rb_load_imports(HMODULE library) {
   grpc_channelz_get_channel_import = (grpc_channelz_get_channel_type) GetProcAddress(library, "grpc_channelz_get_channel");
   grpc_channelz_get_subchannel_import = (grpc_channelz_get_subchannel_type) GetProcAddress(library, "grpc_channelz_get_subchannel");
   grpc_channelz_get_socket_import = (grpc_channelz_get_socket_type) GetProcAddress(library, "grpc_channelz_get_socket");
+  grpc_authorization_policy_provider_arg_vtable_import = (grpc_authorization_policy_provider_arg_vtable_type) GetProcAddress(library, "grpc_authorization_policy_provider_arg_vtable");
   grpc_insecure_channel_create_from_fd_import = (grpc_insecure_channel_create_from_fd_type) GetProcAddress(library, "grpc_insecure_channel_create_from_fd");
   grpc_server_add_insecure_channel_from_fd_import = (grpc_server_add_insecure_channel_from_fd_type) GetProcAddress(library, "grpc_server_add_insecure_channel_from_fd");
   grpc_auth_property_iterator_next_import = (grpc_auth_property_iterator_next_type) GetProcAddress(library, "grpc_auth_property_iterator_next");
@@ -467,6 +471,8 @@ void grpc_rb_load_imports(HMODULE library) {
   grpc_tls_server_authorization_check_config_release_import = (grpc_tls_server_authorization_check_config_release_type) GetProcAddress(library, "grpc_tls_server_authorization_check_config_release");
   grpc_xds_credentials_create_import = (grpc_xds_credentials_create_type) GetProcAddress(library, "grpc_xds_credentials_create");
   grpc_xds_server_credentials_create_import = (grpc_xds_server_credentials_create_type) GetProcAddress(library, "grpc_xds_server_credentials_create");
+  grpc_authorization_policy_provider_static_data_create_import = (grpc_authorization_policy_provider_static_data_create_type) GetProcAddress(library, "grpc_authorization_policy_provider_static_data_create");
+  grpc_authorization_policy_provider_release_import = (grpc_authorization_policy_provider_release_type) GetProcAddress(library, "grpc_authorization_policy_provider_release");
   grpc_raw_byte_buffer_create_import = (grpc_raw_byte_buffer_create_type) GetProcAddress(library, "grpc_raw_byte_buffer_create");
   grpc_raw_compressed_byte_buffer_create_import = (grpc_raw_compressed_byte_buffer_create_type) GetProcAddress(library, "grpc_raw_compressed_byte_buffer_create");
   grpc_byte_buffer_copy_import = (grpc_byte_buffer_copy_type) GetProcAddress(library, "grpc_byte_buffer_copy");
index 44033ce..33c636d 100644 (file)
@@ -110,7 +110,7 @@ extern grpc_completion_queue_create_for_next_type grpc_completion_queue_create_f
 typedef grpc_completion_queue*(*grpc_completion_queue_create_for_pluck_type)(void* reserved);
 extern grpc_completion_queue_create_for_pluck_type grpc_completion_queue_create_for_pluck_import;
 #define grpc_completion_queue_create_for_pluck grpc_completion_queue_create_for_pluck_import
-typedef grpc_completion_queue*(*grpc_completion_queue_create_for_callback_type)(grpc_experimental_completion_queue_functor* shutdown_callback, void* reserved);
+typedef grpc_completion_queue*(*grpc_completion_queue_create_for_callback_type)(grpc_completion_queue_functor* shutdown_callback, void* reserved);
 extern grpc_completion_queue_create_for_callback_type grpc_completion_queue_create_for_callback_import;
 #define grpc_completion_queue_create_for_callback grpc_completion_queue_create_for_callback_import
 typedef grpc_completion_queue*(*grpc_completion_queue_create_type)(const grpc_completion_queue_factory* factory, const grpc_completion_queue_attributes* attributes, void* reserved);
@@ -296,6 +296,9 @@ extern grpc_channelz_get_subchannel_type grpc_channelz_get_subchannel_import;
 typedef char*(*grpc_channelz_get_socket_type)(intptr_t socket_id);
 extern grpc_channelz_get_socket_type grpc_channelz_get_socket_import;
 #define grpc_channelz_get_socket grpc_channelz_get_socket_import
+typedef const grpc_arg_pointer_vtable*(*grpc_authorization_policy_provider_arg_vtable_type)(void);
+extern grpc_authorization_policy_provider_arg_vtable_type grpc_authorization_policy_provider_arg_vtable_import;
+#define grpc_authorization_policy_provider_arg_vtable grpc_authorization_policy_provider_arg_vtable_import
 typedef grpc_channel*(*grpc_insecure_channel_create_from_fd_type)(const char* target, int fd, const grpc_channel_args* args);
 extern grpc_insecure_channel_create_from_fd_type grpc_insecure_channel_create_from_fd_import;
 #define grpc_insecure_channel_create_from_fd grpc_insecure_channel_create_from_fd_import
@@ -521,6 +524,12 @@ extern grpc_xds_credentials_create_type grpc_xds_credentials_create_import;
 typedef grpc_server_credentials*(*grpc_xds_server_credentials_create_type)(grpc_server_credentials* fallback_credentials);
 extern grpc_xds_server_credentials_create_type grpc_xds_server_credentials_create_import;
 #define grpc_xds_server_credentials_create grpc_xds_server_credentials_create_import
+typedef grpc_authorization_policy_provider*(*grpc_authorization_policy_provider_static_data_create_type)(const char* authz_policy, grpc_status_code* code, const char** error_details);
+extern grpc_authorization_policy_provider_static_data_create_type grpc_authorization_policy_provider_static_data_create_import;
+#define grpc_authorization_policy_provider_static_data_create grpc_authorization_policy_provider_static_data_create_import
+typedef void(*grpc_authorization_policy_provider_release_type)(grpc_authorization_policy_provider* provider);
+extern grpc_authorization_policy_provider_release_type grpc_authorization_policy_provider_release_import;
+#define grpc_authorization_policy_provider_release grpc_authorization_policy_provider_release_import
 typedef grpc_byte_buffer*(*grpc_raw_byte_buffer_create_type)(grpc_slice* slices, size_t nslices);
 extern grpc_raw_byte_buffer_create_type grpc_raw_byte_buffer_create_import;
 #define grpc_raw_byte_buffer_create grpc_raw_byte_buffer_create_import
index 1087a52..7fe83e6 100644 (file)
@@ -14,5 +14,5 @@
 
 # GRPC contains the General RPC module.
 module GRPC
-  VERSION = '1.38.1'
+  VERSION = '1.39.0'
 end
index ed111b0..ec283b9 100755 (executable)
@@ -63,6 +63,8 @@ $shutdown = false
 $rpc_config = RpcConfig.new
 $rpc_config.init([:UNARY_CALL], {})
 # These stats are shared across threads
+$thread_results = Array.new
+$thread_results_mu = Mutex.new
 $accumulated_stats_mu = Mutex.new
 $num_rpcs_started_by_method = {}
 $num_rpcs_succeeded_by_method = {}
@@ -188,54 +190,28 @@ class TestTarget < Grpc::Testing::LoadBalancerStatsService::Service
   end
 end
 
-# execute 1 RPC and return remote hostname
-def execute_rpc(op, fail_on_failed_rpcs, rpc_stats_key)
-  remote_peer = ""
-  status_code = 0
-  begin
-    op.execute
-    if op.metadata.key?('hostname')
-      remote_peer = op.metadata['hostname']
-    end
-  rescue GRPC::BadStatus => e
-    if fail_on_failed_rpcs
-      raise e
-    end
-    status_code = e.code
-  end
-  $accumulated_stats_mu.synchronize do
-    $accumulated_method_stats[rpc_stats_key].add_result(status_code)
-    if remote_peer.empty?
-      $num_rpcs_failed_by_method[rpc_stats_key] += 1
-    else
-      $num_rpcs_succeeded_by_method[rpc_stats_key] += 1
-    end
-  end
-  remote_peer
-end
-
-def execute_rpc_in_thread(op, rpc_stats_key)
+def execute_rpc_in_thread(op, rpc)
   Thread.new {
+    rpc_stats_key = rpc.to_s
+    remote_peer = ""
     begin
       op.execute
-      # The following should _not_ happen with the current spec
-      # because we are only executing RPCs in a thread if we expect it
-      # to be kept open, or deadline_exceeded, or dropped by the load
-      # balancing policy. These RPCs should not complete successfully.
-      # Doing this for consistency
+      if op.metadata.key?('hostname')
+        remote_peer = op.metadata['hostname']
+      end
       $accumulated_stats_mu.synchronize do
         $num_rpcs_succeeded_by_method[rpc_stats_key] += 1
         $accumulated_method_stats[rpc_stats_key].add_result(0)
       end
     rescue GRPC::BadStatus => e
-      # Normal execution arrives here,
-      # either because of deadline_exceeded or "call dropped by load
-      # balancing policy"
       $accumulated_stats_mu.synchronize do
         $num_rpcs_failed_by_method[rpc_stats_key] += 1
         $accumulated_method_stats[rpc_stats_key].add_result(e.code)
       end
     end
+    $thread_results_mu.synchronize do
+      $thread_results << [rpc, remote_peer]
+    end
   }
 end
 
@@ -282,12 +258,15 @@ def run_test_loop(stub, target_seconds_between_rpcs, fail_on_failed_rpcs)
       else
         raise "Unsupported rpc #{rpc}"
       end
-      rpc_stats_key = rpc.to_s
-      if metadata.key?('rpc-behavior') or metadata.key?('fi_testcase')
-        keep_open_threads << execute_rpc_in_thread(op, rpc_stats_key)
-      else
-        results[rpc] = execute_rpc(op, fail_on_failed_rpcs, rpc_stats_key)
+      keep_open_threads << execute_rpc_in_thread(op, rpc)
+    end
+    # collect results from threads
+    $thread_results_mu.synchronize do
+      $thread_results.each do |r|
+        rpc_name, remote_peer = r
+        results[rpc_name] = remote_peer
       end
+      $thread_results = Array.new
     end
     $watchers_mutex.synchronize do
       $watchers.each do |watcher|
index 0c5540f..b624df3 100644 (file)
@@ -14,6 +14,6 @@
 
 module GRPC
   module Tools
-    VERSION = '1.38.1'
+    VERSION = '1.39.0'
   end
 end
index cef2534..0f0883e 100644 (file)
     set(_gRPC_CORE_NOSTDCXX_FLAGS "")
   endif()
 
+  if (gRPC_XDS_USER_AGENT_IS_CSHARP)
+    # The value of the defines needs to contain quotes.
+    # See https://github.com/grpc/grpc/blob/fbf32836a418cc84f58786700273b65cb9174e1d/src/core/ext/xds/xds_api.cc#L854
+    add_definitions("-DGRPC_XDS_USER_AGENT_NAME_SUFFIX=\"csharp\"" "-DGRPC_XDS_USER_AGENT_VERSION_SUFFIX=\"${settings.csharp_version}\"")
+  endif()
+
   include(cmake/abseil-cpp.cmake)
   include(cmake/address_sorting.cmake)
   include(cmake/benchmark.cmake)
              --plugin=protoc-gen-grpc=<%text>${_gRPC_CPP_PLUGIN}</%text>
              <%text>${_protobuf_include_path}</%text>
              <%text>${REL_FIL}</%text>
-        DEPENDS <%text>${ABS_FIL}</%text> <%text>${_gRPC_PROTOBUF_PROTOC}</%text> grpc_cpp_plugin
+        DEPENDS <%text>${ABS_FIL}</%text> <%text>${_gRPC_PROTOBUF_PROTOC}</%text> <%text>${_gRPC_CPP_PLUGIN}</%text>
         WORKING_DIRECTORY <%text>${CMAKE_CURRENT_SOURCE_DIR}</%text>
         COMMENT "Running gRPC C++ protocol buffer compiler on <%text>${FIL}</%text>"
         VERBATIM)
   # grpcpp_channelz doesn't build with protobuf-lite, so no install required
   # See https://github.com/grpc/grpc/issues/22826
   if(gRPC_INSTALL AND NOT gRPC_USE_PROTO_LITE)
+  % elif tgt.build == 'protoc':
+  if(gRPC_INSTALL AND NOT CMAKE_CROSSCOMPILING)
   % else:
   if(gRPC_INSTALL)
   % endif
diff --git a/templates/_metadata.py.template b/templates/_metadata.py.template
new file mode 100644 (file)
index 0000000..a0eafce
--- /dev/null
@@ -0,0 +1,19 @@
+%YAML 1.2
+--- |
+  # Copyright 2021 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.
+
+  # AUTO-GENERATED FROM `$REPO_ROOT/templates/_metadata.py.template`!!!
+
+  __version__ = """${settings.python_version.pep440()}"""
index edd103c..ad69451 100644 (file)
@@ -60,7 +60,9 @@
       , $ext_shared, , -fvisibility=hidden ${"\\"}
       -DOPENSSL_NO_ASM -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN ${"\\"}
       -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0 ${"\\"}
-      -DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1)
+      -DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1 ${"\\"}
+      -DGRPC_XDS_USER_AGENT_NAME_SUFFIX='"\"PHP\""' ${"\\"}
+      -DGRPC_XDS_USER_AGENT_VERSION_SUFFIX='"\"${settings.php_version.php()}\""')
   <%
     dirs = sorted(set(src[:src.rfind('/')] for src in srcs))
   %>
index 254ebf5..001a2e5 100644 (file)
         files.update(lib.get(field, []))
     return list(sorted(files))
 
-  # Wrapped languages don't need to access EventEngine APIs. `port.h` is a
-  # special case - it's needed in some security code.
-  event_engine_files = [
-      file
-      for file in list_lib_files("grpc", ("public_headers", "headers", "src"))
-      if '/event_engine/' in file
-      and not file.endswith('/port.h')
-  ]
-
   # ObjectiveC doesn't use c-ares so we don't need address_sorting files at all
   address_sorting_unwanted_files = list_lib_files("address_sorting", ("public_headers", "headers", "src"))
 
   # ObjectiveC needs to obtain re2 explicitly unlike other languages; TODO @donnadionne make ObjC more consistent with others
   grpc_private_files = list(
       sorted((set(list_lib_files("grpc", ("headers", "src"))) -
-              set(address_sorting_unwanted_files) - set(event_engine_files))
+              set(address_sorting_unwanted_files))
             | set(list_lib_files("re2", ("headers", "src")))))
   grpc_public_headers = list(
       sorted((set(list_lib_files("grpc", ("public_headers", ))) -
-              set(address_sorting_unwanted_files)) - set(event_engine_files)
+              set(address_sorting_unwanted_files))
              | set(list_lib_files("re2", ("public_headers", )))))
   grpc_private_headers = list(
       sorted((set(list_lib_files("grpc", ("headers", ))) -
-              set(address_sorting_unwanted_files)) - set(event_engine_files)
+              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)))
     set(list_lib_files("end2end_tests", ("src", "headers")))
     - set(grpc_private_files)
     - set(address_sorting_unwanted_files)
-    - set(event_engine_files)
     - set([
       # Subprocess is not supported in tvOS and not needed by our tests.
       "test/core/util/subprocess_posix.cc",
       ss.header_mappings_dir = '.'
       ss.libraries = 'z'
       ss.dependency "#{s.name}/Interface", version
-      ss.dependency 'BoringSSL-GRPC', '0.0.18'
+      ss.dependency 'BoringSSL-GRPC', '0.0.19'
       % for abseil_spec in grpc_abseil_specs:
       ss.dependency '${abseil_spec}', abseil_version
       % endfor
index c927fb9..16c28a0 100644 (file)
@@ -69,7 +69,7 @@
 
   Pod::Spec.new do |s|
     s.name     = 'BoringSSL-GRPC'
-    version = '0.0.18'
+    version = '0.0.19'
     s.version  = version
     s.summary  = 'BoringSSL is a fork of OpenSSL that is designed to meet Google\'s needs.'
     # Adapted from the homepage:
diff --git a/templates/src/ruby/ext/grpc/extconf.rb.template b/templates/src/ruby/ext/grpc/extconf.rb.template
new file mode 100644 (file)
index 0000000..1130ba8
--- /dev/null
@@ -0,0 +1,122 @@
+%YAML 1.2
+--- |
+  # 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.
+  
+  require 'etc'
+  require 'mkmf'
+  
+  windows = RUBY_PLATFORM =~ /mingw|mswin/
+  bsd = RUBY_PLATFORM =~ /bsd/
+  
+  grpc_root = File.expand_path(File.join(File.dirname(__FILE__), '../../../..'))
+  
+  grpc_config = ENV['GRPC_CONFIG'] || 'opt'
+  
+  ENV['MACOSX_DEPLOYMENT_TARGET'] = '10.10'
+  
+  if ENV['AR'].nil? || ENV['AR'].size == 0
+      ENV['AR'] = RbConfig::CONFIG['AR']
+  end
+  if ENV['CC'].nil? || ENV['CC'].size == 0
+      ENV['CC'] = RbConfig::CONFIG['CC']
+  end
+  if ENV['CXX'].nil? || ENV['CXX'].size == 0
+      ENV['CXX'] = RbConfig::CONFIG['CXX']
+  end
+  if ENV['LD'].nil? || ENV['LD'].size == 0
+      ENV['LD'] = ENV['CC']
+  end
+  
+  if RUBY_PLATFORM =~ /darwin/
+    ENV['AR'] = 'libtool'
+    ENV['ARFLAGS'] = '-o'
+   end
+  
+  ENV['EMBED_OPENSSL'] = 'true'
+  ENV['EMBED_ZLIB'] = 'true'
+  ENV['EMBED_CARES'] = 'true'
+  
+  ENV['ARCH_FLAGS'] = RbConfig::CONFIG['ARCH_FLAG']
+  if RUBY_PLATFORM =~ /darwin/
+    if RUBY_PLATFORM =~ /arm64/
+      ENV['ARCH_FLAGS'] = '-arch arm64'
+    else
+      ENV['ARCH_FLAGS'] = '-arch i386 -arch x86_64'
+    end
+  end
+  
+  ENV['CPPFLAGS'] = '-DGPR_BACKWARDS_COMPATIBILITY_MODE'
+  ENV['CPPFLAGS'] += ' -DGRPC_XDS_USER_AGENT_NAME_SUFFIX="\"RUBY\"" '
+  ENV['CPPFLAGS'] += ' -DGRPC_XDS_USER_AGENT_VERSION_SUFFIX="\"${settings.ruby_version.ruby()}\"" '
+  
+  output_dir = File.expand_path(RbConfig::CONFIG['topdir'])
+  grpc_lib_dir = File.join(output_dir, 'libs', grpc_config)
+  ENV['BUILDDIR'] = output_dir
+  
+  unless windows
+    puts 'Building internal gRPC into ' + grpc_lib_dir
+    nproc = 4
+    nproc = Etc.nprocessors * 2 if Etc.respond_to? :nprocessors
+    make = bsd ? 'gmake' : 'make'
+    system("#{make} -j#{nproc} -C #{grpc_root} #{grpc_lib_dir}/libgrpc.a CONFIG=#{grpc_config} Q=")
+    exit 1 unless $? == 0
+  end
+  
+  $CFLAGS << ' -I' + File.join(grpc_root, 'include')
+  
+  ext_export_file = File.join(grpc_root, 'src', 'ruby', 'ext', 'grpc', 'ext-export')
+  $LDFLAGS << ' -Wl,--version-script="' + ext_export_file + '.gcc"' if RUBY_PLATFORM =~ /linux/
+  $LDFLAGS << ' -Wl,-exported_symbols_list,"' + ext_export_file + '.clang"' if RUBY_PLATFORM =~ /darwin/
+  
+  $LDFLAGS << ' ' + File.join(grpc_lib_dir, 'libgrpc.a') unless windows
+  if grpc_config == 'gcov'
+    $CFLAGS << ' -O0 -fprofile-arcs -ftest-coverage'
+    $LDFLAGS << ' -fprofile-arcs -ftest-coverage -rdynamic'
+  end
+  
+  if grpc_config == 'dbg'
+    $CFLAGS << ' -O0 -ggdb3'
+  end
+  
+  $LDFLAGS << ' -Wl,-wrap,memcpy' if RUBY_PLATFORM =~ /linux/
+  $LDFLAGS << ' -static-libgcc -static-libstdc++' if RUBY_PLATFORM =~ /linux/
+  $LDFLAGS << ' -static' if windows
+  
+  $CFLAGS << ' -std=c99 '
+  $CFLAGS << ' -Wall '
+  $CFLAGS << ' -Wextra '
+  $CFLAGS << ' -pedantic '
+  
+  output = File.join('grpc', 'grpc_c')
+  puts 'Generating Makefile for ' + output
+  create_makefile(output)
+  
+  strip_tool = RbConfig::CONFIG['STRIP']
+  strip_tool = 'strip -x' if RUBY_PLATFORM =~ /darwin/
+  
+  if grpc_config == 'opt'
+    File.open('Makefile.new', 'w') do |o|
+      o.puts 'hijack: all strip'
+      o.puts
+      File.foreach('Makefile') do |i|
+        o.puts i
+      end
+      o.puts
+      o.puts 'strip: $(DLLIB)'
+      o.puts "\t$(ECHO) Stripping $(DLLIB)"
+      o.puts "\t$(Q) #{strip_tool} $(DLLIB)"
+    end
+    File.rename('Makefile.new', 'Makefile')
+  end
diff --git a/templates/tools/dockerfile/test/cxx_buster_openssl102_x64/Dockerfile.template b/templates/tools/dockerfile/test/cxx_buster_openssl102_x64/Dockerfile.template
new file mode 100644 (file)
index 0000000..c4c23c0
--- /dev/null
@@ -0,0 +1,39 @@
+%YAML 1.2
+--- |
+  # Copyright 2021 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.
+  
+  FROM debian:buster
+  
+  <%include file="../../apt_get_basic.include"/>
+  <%include file="../../python_deps.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
+  <%include file="../../cxx_deps.include"/>
+  <%include file="../../cmake.include"/>
+  <%include file="../../run_tests_addons.include"/>
+
+  # Install openssl 1.0.2 from source
+  RUN apt-get update && apt-get install -y build-essential zlib1g-dev
+  RUN cd /tmp && ${"\\"}
+      wget https://www.openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz && ${"\\"}
+      tar -xf openssl-1.0.2u.tar.gz && ${"\\"}
+      cd openssl-1.0.2u && ${"\\"}
+      ./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared zlib && ${"\\"}
+      make -j 4 && ${"\\"}
+      make install && ${"\\"}
+      rm -rf /tmp/openssl-1.0.2u*
+  ENV OPENSSL_ROOT_DIR=/usr/local/ssl
+
+  # Define the default command.
+  CMD ["bash"]
index fb5e554..fde73fc 100644 (file)
@@ -886,11 +886,88 @@ TEST_F(RetryParserTest, ValidRetryPolicy) {
   EXPECT_EQ(parsed_config->initial_backoff(), 1000);
   EXPECT_EQ(parsed_config->max_backoff(), 120000);
   EXPECT_EQ(parsed_config->backoff_multiplier(), 1.6f);
+  EXPECT_EQ(parsed_config->per_attempt_recv_timeout(), absl::nullopt);
   EXPECT_TRUE(
       parsed_config->retryable_status_codes().Contains(GRPC_STATUS_ABORTED));
 }
 
-TEST_F(RetryParserTest, InvalidRetryPolicyMaxAttempts) {
+TEST_F(RetryParserTest, InvalidRetryPolicyWrongType) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": 5\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  EXPECT_THAT(grpc_error_std_string(error),
+              ::testing::ContainsRegex(
+                  "Service config parsing error.*referenced_errors.*"
+                  "Method Params.*referenced_errors.*"
+                  "methodConfig.*referenced_errors.*"
+                  "field:retryPolicy error:should be of type object"));
+  GRPC_ERROR_UNREF(error);
+}
+
+TEST_F(RetryParserTest, InvalidRetryPolicyRequiredFieldsMissing) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  EXPECT_THAT(grpc_error_std_string(error),
+              ::testing::ContainsRegex(
+                  "Service config parsing error.*referenced_errors.*"
+                  "Method Params.*referenced_errors.*"
+                  "methodConfig.*referenced_errors.*"
+                  "retryPolicy.*referenced_errors.*"
+                  "field:maxAttempts error:required field missing.*"
+                  "field:initialBackoff error:does not exist.*"
+                  "field:maxBackoff error:does not exist.*"
+                  "field:backoffMultiplier error:required field missing"));
+  GRPC_ERROR_UNREF(error);
+}
+
+TEST_F(RetryParserTest, InvalidRetryPolicyMaxAttemptsWrongType) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": \"FOO\",\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  EXPECT_THAT(grpc_error_std_string(error),
+              ::testing::ContainsRegex(
+                  "Service config parsing error.*referenced_errors.*"
+                  "Method Params.*referenced_errors.*"
+                  "methodConfig.*referenced_errors.*"
+                  "retryPolicy.*referenced_errors.*"
+                  "field:maxAttempts error:should be of type number"));
+  GRPC_ERROR_UNREF(error);
+}
+
+TEST_F(RetryParserTest, InvalidRetryPolicyMaxAttemptsBadValue) {
   const char* test_json =
       "{\n"
       "  \"methodConfig\": [ {\n"
@@ -918,7 +995,7 @@ TEST_F(RetryParserTest, InvalidRetryPolicyMaxAttempts) {
   GRPC_ERROR_UNREF(error);
 }
 
-TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoff) {
+TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoffWrongType) {
   const char* test_json =
       "{\n"
       "  \"methodConfig\": [ {\n"
@@ -926,7 +1003,7 @@ TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoff) {
       "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
       "    ],\n"
       "    \"retryPolicy\": {\n"
-      "      \"maxAttempts\": 1,\n"
+      "      \"maxAttempts\": 2,\n"
       "      \"initialBackoff\": \"1sec\",\n"
       "      \"maxBackoff\": \"120s\",\n"
       "      \"backoffMultiplier\": 1.6,\n"
@@ -947,7 +1024,7 @@ TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoff) {
   GRPC_ERROR_UNREF(error);
 }
 
-TEST_F(RetryParserTest, InvalidRetryPolicyMaxBackoff) {
+TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoffBadValue) {
   const char* test_json =
       "{\n"
       "  \"methodConfig\": [ {\n"
@@ -955,7 +1032,35 @@ TEST_F(RetryParserTest, InvalidRetryPolicyMaxBackoff) {
       "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
       "    ],\n"
       "    \"retryPolicy\": {\n"
-      "      \"maxAttempts\": 1,\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"0s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  EXPECT_THAT(grpc_error_std_string(error),
+              ::testing::ContainsRegex(
+                  "Service config parsing error.*referenced_errors.*"
+                  "Method Params.*referenced_errors.*"
+                  "methodConfig.*referenced_errors.*"
+                  "retryPolicy.*referenced_errors.*"
+                  "field:initialBackoff error:must be greater than 0"));
+  GRPC_ERROR_UNREF(error);
+}
+
+TEST_F(RetryParserTest, InvalidRetryPolicyMaxBackoffWrongType) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
       "      \"initialBackoff\": \"1s\",\n"
       "      \"maxBackoff\": \"120sec\",\n"
       "      \"backoffMultiplier\": 1.6,\n"
@@ -976,7 +1081,7 @@ TEST_F(RetryParserTest, InvalidRetryPolicyMaxBackoff) {
   GRPC_ERROR_UNREF(error);
 }
 
-TEST_F(RetryParserTest, InvalidRetryPolicyBackoffMultiplier) {
+TEST_F(RetryParserTest, InvalidRetryPolicyMaxBackoffBadValue) {
   const char* test_json =
       "{\n"
       "  \"methodConfig\": [ {\n"
@@ -984,7 +1089,35 @@ TEST_F(RetryParserTest, InvalidRetryPolicyBackoffMultiplier) {
       "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
       "    ],\n"
       "    \"retryPolicy\": {\n"
-      "      \"maxAttempts\": 1,\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"0s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  EXPECT_THAT(grpc_error_std_string(error),
+              ::testing::ContainsRegex(
+                  "Service config parsing error.*referenced_errors.*"
+                  "Method Params.*referenced_errors.*"
+                  "methodConfig.*referenced_errors.*"
+                  "retryPolicy.*referenced_errors.*"
+                  "field:maxBackoff error:must be greater than 0"));
+  GRPC_ERROR_UNREF(error);
+}
+
+TEST_F(RetryParserTest, InvalidRetryPolicyBackoffMultiplierWrongType) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
       "      \"initialBackoff\": \"1s\",\n"
       "      \"maxBackoff\": \"120s\",\n"
       "      \"backoffMultiplier\": \"1.6\",\n"
@@ -1004,7 +1137,7 @@ TEST_F(RetryParserTest, InvalidRetryPolicyBackoffMultiplier) {
   GRPC_ERROR_UNREF(error);
 }
 
-TEST_F(RetryParserTest, InvalidRetryPolicyRetryableStatusCodes) {
+TEST_F(RetryParserTest, InvalidRetryPolicyBackoffMultiplierBadValue) {
   const char* test_json =
       "{\n"
       "  \"methodConfig\": [ {\n"
@@ -1012,7 +1145,35 @@ TEST_F(RetryParserTest, InvalidRetryPolicyRetryableStatusCodes) {
       "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
       "    ],\n"
       "    \"retryPolicy\": {\n"
-      "      \"maxAttempts\": 1,\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 0,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  EXPECT_THAT(grpc_error_std_string(error),
+              ::testing::ContainsRegex(
+                  "Service config parsing error.*referenced_errors.*"
+                  "Method Params.*referenced_errors.*"
+                  "methodConfig.*referenced_errors.*"
+                  "retryPolicy.*referenced_errors.*"
+                  "field:backoffMultiplier error:must be greater than 0"));
+  GRPC_ERROR_UNREF(error);
+}
+
+TEST_F(RetryParserTest, InvalidRetryPolicyEmptyRetryableStatusCodes) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
       "      \"initialBackoff\": \"1s\",\n"
       "      \"maxBackoff\": \"120s\",\n"
       "      \"backoffMultiplier\": \"1.6\",\n"
@@ -1028,7 +1189,227 @@ TEST_F(RetryParserTest, InvalidRetryPolicyRetryableStatusCodes) {
                   "Method Params.*referenced_errors.*"
                   "methodConfig.*referenced_errors.*"
                   "retryPolicy.*referenced_errors.*"
-                  "field:retryableStatusCodes error:should be non-empty"));
+                  "field:retryableStatusCodes error:must be non-empty if "
+                  "perAttemptRecvTimeout not present"));
+  GRPC_ERROR_UNREF(error);
+}
+
+TEST_F(RetryParserTest, InvalidRetryPolicyRetryableStatusCodesWrongType) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": \"1.6\",\n"
+      "      \"retryableStatusCodes\": 0\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  EXPECT_THAT(grpc_error_std_string(error),
+              ::testing::ContainsRegex(
+                  "Service config parsing error.*referenced_errors.*"
+                  "Method Params.*referenced_errors.*"
+                  "methodConfig.*referenced_errors.*"
+                  "retryPolicy.*referenced_errors.*"
+                  "field:retryableStatusCodes error:must be of type array"));
+  GRPC_ERROR_UNREF(error);
+}
+
+TEST_F(RetryParserTest, InvalidRetryPolicyUnparseableRetryableStatusCodes) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": \"1.6\",\n"
+      "      \"retryableStatusCodes\": [\"FOO\", 2]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  EXPECT_THAT(grpc_error_std_string(error),
+              ::testing::ContainsRegex(
+                  "Service config parsing error.*referenced_errors.*"
+                  "Method Params.*referenced_errors.*"
+                  "methodConfig.*referenced_errors.*"
+                  "retryPolicy.*referenced_errors.*"
+                  "field:retryableStatusCodes "
+                  "error:failed to parse status code.*"
+                  "field:retryableStatusCodes "
+                  "error:status codes should be of type string"));
+  GRPC_ERROR_UNREF(error);
+}
+
+TEST_F(RetryParserTest, ValidRetryPolicyWithPerAttemptRecvTimeout) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"perAttemptRecvTimeout\": \"1s\",\n"
+      "      \"retryableStatusCodes\": [\"ABORTED\"]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_std_string(error);
+  const auto* vector_ptr = svc_cfg->GetMethodParsedConfigVector(
+      grpc_slice_from_static_string("/TestServ/TestMethod"));
+  ASSERT_NE(vector_ptr, nullptr);
+  const auto* parsed_config =
+      static_cast<grpc_core::internal::RetryMethodConfig*>(
+          ((*vector_ptr)[0]).get());
+  ASSERT_NE(parsed_config, nullptr);
+  EXPECT_EQ(parsed_config->max_attempts(), 2);
+  EXPECT_EQ(parsed_config->initial_backoff(), 1000);
+  EXPECT_EQ(parsed_config->max_backoff(), 120000);
+  EXPECT_EQ(parsed_config->backoff_multiplier(), 1.6f);
+  EXPECT_EQ(parsed_config->per_attempt_recv_timeout(), 1000);
+  EXPECT_TRUE(
+      parsed_config->retryable_status_codes().Contains(GRPC_STATUS_ABORTED));
+}
+
+TEST_F(RetryParserTest,
+       ValidRetryPolicyWithPerAttemptRecvTimeoutAndUnsetRetryableStatusCodes) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"perAttemptRecvTimeout\": \"1s\"\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_std_string(error);
+  const auto* vector_ptr = svc_cfg->GetMethodParsedConfigVector(
+      grpc_slice_from_static_string("/TestServ/TestMethod"));
+  ASSERT_NE(vector_ptr, nullptr);
+  const auto* parsed_config =
+      static_cast<grpc_core::internal::RetryMethodConfig*>(
+          ((*vector_ptr)[0]).get());
+  ASSERT_NE(parsed_config, nullptr);
+  EXPECT_EQ(parsed_config->max_attempts(), 2);
+  EXPECT_EQ(parsed_config->initial_backoff(), 1000);
+  EXPECT_EQ(parsed_config->max_backoff(), 120000);
+  EXPECT_EQ(parsed_config->backoff_multiplier(), 1.6f);
+  EXPECT_EQ(parsed_config->per_attempt_recv_timeout(), 1000);
+  EXPECT_TRUE(parsed_config->retryable_status_codes().Empty());
+}
+
+TEST_F(RetryParserTest, InvalidRetryPolicyPerAttemptRecvTimeoutUnparseable) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": \"1.6\",\n"
+      "      \"perAttemptRecvTimeout\": \"1sec\",\n"
+      "      \"retryableStatusCodes\": [\"ABORTED\"]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  EXPECT_THAT(grpc_error_std_string(error),
+              ::testing::ContainsRegex(
+                  "Service config parsing error.*referenced_errors.*"
+                  "Method Params.*referenced_errors.*"
+                  "methodConfig.*referenced_errors.*"
+                  "retryPolicy.*referenced_errors.*"
+                  "field:perAttemptRecvTimeout error:type must be STRING "
+                  "of the form given by google.proto.Duration."));
+  GRPC_ERROR_UNREF(error);
+}
+
+TEST_F(RetryParserTest, InvalidRetryPolicyPerAttemptRecvTimeoutWrongType) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": \"1.6\",\n"
+      "      \"perAttemptRecvTimeout\": 1,\n"
+      "      \"retryableStatusCodes\": [\"ABORTED\"]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  EXPECT_THAT(grpc_error_std_string(error),
+              ::testing::ContainsRegex(
+                  "Service config parsing error.*referenced_errors.*"
+                  "Method Params.*referenced_errors.*"
+                  "methodConfig.*referenced_errors.*"
+                  "retryPolicy.*referenced_errors.*"
+                  "field:perAttemptRecvTimeout error:type must be STRING "
+                  "of the form given by google.proto.Duration."));
+  GRPC_ERROR_UNREF(error);
+}
+
+TEST_F(RetryParserTest, InvalidRetryPolicyPerAttemptRecvTimeoutBadValue) {
+  const char* test_json =
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": \"1.6\",\n"
+      "      \"perAttemptRecvTimeout\": \"0s\",\n"
+      "      \"retryableStatusCodes\": [\"ABORTED\"]\n"
+      "    }\n"
+      "  } ]\n"
+      "}";
+  grpc_error_handle error = GRPC_ERROR_NONE;
+  auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
+  EXPECT_THAT(grpc_error_std_string(error),
+              ::testing::ContainsRegex(
+                  "Service config parsing error.*referenced_errors.*"
+                  "Method Params.*referenced_errors.*"
+                  "methodConfig.*referenced_errors.*"
+                  "retryPolicy.*referenced_errors.*"
+                  "field:perAttemptRecvTimeout error:must be greater than 0"));
   GRPC_ERROR_UNREF(error);
 }
 
index 958b1b7..1b73857 100644 (file)
@@ -131,6 +131,8 @@ extern void retry_cancellation(grpc_end2end_test_config config);
 extern void retry_cancellation_pre_init(void);
 extern void retry_disabled(grpc_end2end_test_config config);
 extern void retry_disabled_pre_init(void);
+extern void retry_exceeds_buffer_size_in_delay(grpc_end2end_test_config config);
+extern void retry_exceeds_buffer_size_in_delay_pre_init(void);
 extern void retry_exceeds_buffer_size_in_initial_batch(grpc_end2end_test_config config);
 extern void retry_exceeds_buffer_size_in_initial_batch_pre_init(void);
 extern void retry_exceeds_buffer_size_in_subsequent_batch(grpc_end2end_test_config config);
@@ -141,10 +143,16 @@ extern void retry_non_retriable_status(grpc_end2end_test_config config);
 extern void retry_non_retriable_status_pre_init(void);
 extern void retry_non_retriable_status_before_recv_trailing_metadata_started(grpc_end2end_test_config config);
 extern void retry_non_retriable_status_before_recv_trailing_metadata_started_pre_init(void);
+extern void retry_per_attempt_recv_timeout(grpc_end2end_test_config config);
+extern void retry_per_attempt_recv_timeout_pre_init(void);
 extern void retry_recv_initial_metadata(grpc_end2end_test_config config);
 extern void retry_recv_initial_metadata_pre_init(void);
 extern void retry_recv_message(grpc_end2end_test_config config);
 extern void retry_recv_message_pre_init(void);
+extern void retry_recv_trailing_metadata_error(grpc_end2end_test_config config);
+extern void retry_recv_trailing_metadata_error_pre_init(void);
+extern void retry_send_op_fails(grpc_end2end_test_config config);
+extern void retry_send_op_fails_pre_init(void);
 extern void retry_server_pushback_delay(grpc_end2end_test_config config);
 extern void retry_server_pushback_delay_pre_init(void);
 extern void retry_server_pushback_disabled(grpc_end2end_test_config config);
@@ -246,13 +254,17 @@ void grpc_end2end_tests_pre_init(void) {
   retry_cancel_during_delay_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_non_retriable_status_pre_init();
   retry_non_retriable_status_before_recv_trailing_metadata_started_pre_init();
+  retry_per_attempt_recv_timeout_pre_init();
   retry_recv_initial_metadata_pre_init();
   retry_recv_message_pre_init();
+  retry_recv_trailing_metadata_error_pre_init();
+  retry_send_op_fails_pre_init();
   retry_server_pushback_delay_pre_init();
   retry_server_pushback_disabled_pre_init();
   retry_streaming_pre_init();
@@ -336,13 +348,17 @@ void grpc_end2end_tests(int argc, char **argv,
     retry_cancel_during_delay(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_non_retriable_status(config);
     retry_non_retriable_status_before_recv_trailing_metadata_started(config);
+    retry_per_attempt_recv_timeout(config);
     retry_recv_initial_metadata(config);
     retry_recv_message(config);
+    retry_recv_trailing_metadata_error(config);
+    retry_send_op_fails(config);
     retry_server_pushback_delay(config);
     retry_server_pushback_disabled(config);
     retry_streaming(config);
@@ -574,6 +590,10 @@ void grpc_end2end_tests(int argc, char **argv,
       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;
@@ -594,6 +614,10 @@ void grpc_end2end_tests(int argc, char **argv,
       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_recv_initial_metadata", argv[i])) {
       retry_recv_initial_metadata(config);
       continue;
@@ -602,6 +626,14 @@ void grpc_end2end_tests(int argc, char **argv,
       retry_recv_message(config);
       continue;
     }
+    if (0 == strcmp("retry_recv_trailing_metadata_error", argv[i])) {
+      retry_recv_trailing_metadata_error(config);
+      continue;
+    }
+    if (0 == strcmp("retry_send_op_fails", argv[i])) {
+      retry_send_op_fails(config);
+      continue;
+    }
     if (0 == strcmp("retry_server_pushback_delay", argv[i])) {
       retry_server_pushback_delay(config);
       continue;
index d3603b3..cd27d33 100644 (file)
@@ -133,6 +133,8 @@ extern void retry_cancellation(grpc_end2end_test_config config);
 extern void retry_cancellation_pre_init(void);
 extern void retry_disabled(grpc_end2end_test_config config);
 extern void retry_disabled_pre_init(void);
+extern void retry_exceeds_buffer_size_in_delay(grpc_end2end_test_config config);
+extern void retry_exceeds_buffer_size_in_delay_pre_init(void);
 extern void retry_exceeds_buffer_size_in_initial_batch(grpc_end2end_test_config config);
 extern void retry_exceeds_buffer_size_in_initial_batch_pre_init(void);
 extern void retry_exceeds_buffer_size_in_subsequent_batch(grpc_end2end_test_config config);
@@ -143,10 +145,16 @@ extern void retry_non_retriable_status(grpc_end2end_test_config config);
 extern void retry_non_retriable_status_pre_init(void);
 extern void retry_non_retriable_status_before_recv_trailing_metadata_started(grpc_end2end_test_config config);
 extern void retry_non_retriable_status_before_recv_trailing_metadata_started_pre_init(void);
+extern void retry_per_attempt_recv_timeout(grpc_end2end_test_config config);
+extern void retry_per_attempt_recv_timeout_pre_init(void);
 extern void retry_recv_initial_metadata(grpc_end2end_test_config config);
 extern void retry_recv_initial_metadata_pre_init(void);
 extern void retry_recv_message(grpc_end2end_test_config config);
 extern void retry_recv_message_pre_init(void);
+extern void retry_recv_trailing_metadata_error(grpc_end2end_test_config config);
+extern void retry_recv_trailing_metadata_error_pre_init(void);
+extern void retry_send_op_fails(grpc_end2end_test_config config);
+extern void retry_send_op_fails_pre_init(void);
 extern void retry_server_pushback_delay(grpc_end2end_test_config config);
 extern void retry_server_pushback_delay_pre_init(void);
 extern void retry_server_pushback_disabled(grpc_end2end_test_config config);
@@ -249,13 +257,17 @@ void grpc_end2end_tests_pre_init(void) {
   retry_cancel_during_delay_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_non_retriable_status_pre_init();
   retry_non_retriable_status_before_recv_trailing_metadata_started_pre_init();
+  retry_per_attempt_recv_timeout_pre_init();
   retry_recv_initial_metadata_pre_init();
   retry_recv_message_pre_init();
+  retry_recv_trailing_metadata_error_pre_init();
+  retry_send_op_fails_pre_init();
   retry_server_pushback_delay_pre_init();
   retry_server_pushback_disabled_pre_init();
   retry_streaming_pre_init();
@@ -340,13 +352,17 @@ void grpc_end2end_tests(int argc, char **argv,
     retry_cancel_during_delay(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_non_retriable_status(config);
     retry_non_retriable_status_before_recv_trailing_metadata_started(config);
+    retry_per_attempt_recv_timeout(config);
     retry_recv_initial_metadata(config);
     retry_recv_message(config);
+    retry_recv_trailing_metadata_error(config);
+    retry_send_op_fails(config);
     retry_server_pushback_delay(config);
     retry_server_pushback_disabled(config);
     retry_streaming(config);
@@ -582,6 +598,10 @@ void grpc_end2end_tests(int argc, char **argv,
       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;
@@ -602,6 +622,10 @@ void grpc_end2end_tests(int argc, char **argv,
       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_recv_initial_metadata", argv[i])) {
       retry_recv_initial_metadata(config);
       continue;
@@ -610,6 +634,14 @@ void grpc_end2end_tests(int argc, char **argv,
       retry_recv_message(config);
       continue;
     }
+    if (0 == strcmp("retry_recv_trailing_metadata_error", argv[i])) {
+      retry_recv_trailing_metadata_error(config);
+      continue;
+    }
+    if (0 == strcmp("retry_send_op_fails", argv[i])) {
+      retry_send_op_fails(config);
+      continue;
+    }
     if (0 == strcmp("retry_server_pushback_delay", argv[i])) {
       retry_server_pushback_delay(config);
       continue;
index abfefbc..9ef0375 100644 (file)
@@ -119,6 +119,6 @@ int main(int argc, char** argv) {
 
 #else /* GRPC_POSIX_SOCKET */
 
-int main(int argc, char** argv) { return 1; }
+int main(int /* argc */, char** /* argv */) { return 1; }
 
 #endif /* GRPC_POSIX_SOCKET */
index c6fd2ec..fcc508e 100644 (file)
@@ -47,14 +47,7 @@ static grpc_server* create_proxy_server(const char* port,
 
 static grpc_channel* create_proxy_client(const char* target,
                                          grpc_channel_args* client_args) {
-  // Disable retries in proxy client.
-  const char* args_to_remove = GRPC_ARG_ENABLE_RETRIES;
-  grpc_channel_args* new_args =
-      grpc_channel_args_copy_and_remove(client_args, &args_to_remove, 1);
-  grpc_channel* channel =
-      grpc_insecure_channel_create(target, new_args, nullptr);
-  grpc_channel_args_destroy(new_args);
-  return channel;
+  return grpc_insecure_channel_create(target, client_args, nullptr);
 }
 
 static const grpc_end2end_proxy_def proxy_def = {create_proxy_server,
index 919d632..851d55f 100644 (file)
@@ -28,6 +28,7 @@
 #include "src/core/lib/gprpp/host_port.h"
 #include "src/core/lib/gprpp/memory.h"
 #include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/surface/call.h"
 #include "test/core/util/port.h"
 
 struct grpc_end2end_proxy {
@@ -99,7 +100,16 @@ grpc_end2end_proxy* grpc_end2end_proxy_create(const grpc_end2end_proxy_def* def,
 
   proxy->cq = grpc_completion_queue_create_for_next(nullptr);
   proxy->server = def->create_server(proxy->proxy_port.c_str(), server_args);
-  proxy->client = def->create_client(proxy->server_port.c_str(), client_args);
+
+  const char* arg_to_remove = GRPC_ARG_ENABLE_RETRIES;
+  grpc_arg arg_to_add = grpc_channel_arg_integer_create(
+      const_cast<char*>(GRPC_ARG_ENABLE_RETRIES), 0);
+  grpc_channel_args* proxy_client_args =
+      grpc_channel_args_copy_and_add_and_remove(client_args, &arg_to_remove, 1,
+                                                &arg_to_add, 1);
+  proxy->client =
+      def->create_client(proxy->server_port.c_str(), proxy_client_args);
+  grpc_channel_args_destroy(proxy_client_args);
 
   grpc_server_register_completion_queue(proxy->server, proxy->cq, nullptr);
   grpc_server_start(proxy->server);
@@ -164,7 +174,7 @@ static void on_p2s_recv_initial_metadata(void* arg, int /*success*/) {
   grpc_call_error err;
 
   memset(&op, 0, sizeof(op));
-  if (!pc->proxy->shutdown) {
+  if (!pc->proxy->shutdown && !grpc_call_is_trailers_only(pc->p2s)) {
     op.op = GRPC_OP_SEND_INITIAL_METADATA;
     op.flags = 0;
     op.reserved = nullptr;
@@ -294,22 +304,33 @@ static void on_c2p_sent_status(void* arg, int /*success*/) {
 
 static void on_p2s_status(void* arg, int success) {
   proxy_call* pc = static_cast<proxy_call*>(arg);
-  grpc_op op;
+  grpc_op op[2];  // Possibly send empty initial metadata also if trailers-only
   grpc_call_error err;
 
+  memset(op, 0, sizeof(op));
+
   if (!pc->proxy->shutdown) {
     GPR_ASSERT(success);
-    op.op = GRPC_OP_SEND_STATUS_FROM_SERVER;
-    op.flags = 0;
-    op.reserved = nullptr;
-    op.data.send_status_from_server.trailing_metadata_count =
+
+    int op_count = 0;
+    if (grpc_call_is_trailers_only(pc->p2s)) {
+      op[op_count].op = GRPC_OP_SEND_INITIAL_METADATA;
+      op_count++;
+    }
+
+    op[op_count].op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+    op[op_count].flags = 0;
+    op[op_count].reserved = nullptr;
+    op[op_count].data.send_status_from_server.trailing_metadata_count =
         pc->p2s_trailing_metadata.count;
-    op.data.send_status_from_server.trailing_metadata =
+    op[op_count].data.send_status_from_server.trailing_metadata =
         pc->p2s_trailing_metadata.metadata;
-    op.data.send_status_from_server.status = pc->p2s_status;
-    op.data.send_status_from_server.status_details = &pc->p2s_status_details;
+    op[op_count].data.send_status_from_server.status = pc->p2s_status;
+    op[op_count].data.send_status_from_server.status_details =
+        &pc->p2s_status_details;
+    op_count++;
     refpc(pc, "on_c2p_sent_status");
-    err = grpc_call_start_batch(pc->c2p, &op, 1,
+    err = grpc_call_start_batch(pc->c2p, op, op_count,
                                 new_closure(on_c2p_sent_status, pc), nullptr);
     GPR_ASSERT(err == GRPC_CALL_OK);
   }
index 58aae8f..94a41e0 100755 (executable)
@@ -272,85 +272,54 @@ END2END_TESTS = {
     "registered_call": _test_options(),
     "request_with_flags": _test_options(proxyable = False),
     "request_with_payload": _test_options(),
-    # TODO(roth): Remove proxyable=False for all retry tests once we
-    # have a way for the proxy to propagate the fact that trailing
-    # metadata is available when initial metadata is returned.
-    # See https://github.com/grpc/grpc/issues/14467 for context.
-    "retry": _test_options(needs_client_channel = True, proxyable = False),
-    "retry_cancellation": _test_options(
+    "retry": _test_options(needs_client_channel = True),
+    "retry_cancellation": _test_options(needs_client_channel = True),
+    "retry_cancel_during_delay": _test_options(needs_client_channel = True),
+    "retry_disabled": _test_options(needs_client_channel = True),
+    "retry_exceeds_buffer_size_in_delay": _test_options(
         needs_client_channel = True,
-        proxyable = False,
-    ),
-    "retry_cancel_during_delay": _test_options(
-        needs_client_channel = True,
-        proxyable = False,
     ),
-    "retry_disabled": _test_options(needs_client_channel = True, proxyable = False),
     "retry_exceeds_buffer_size_in_initial_batch": _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_exceeds_buffer_size_in_init",
     ),
     "retry_exceeds_buffer_size_in_subsequent_batch": _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_exceeds_buffer_size_in_subseq",
     ),
-    "retry_lb_drop": _test_options(
-        needs_client_channel = True,
-        proxyable = False,
-    ),
-    "retry_non_retriable_status": _test_options(
-        needs_client_channel = True,
-        proxyable = False,
-    ),
+    "retry_lb_drop": _test_options(needs_client_channel = True),
+    "retry_non_retriable_status": _test_options(needs_client_channel = True),
     "retry_non_retriable_status_before_recv_trailing_metadata_started": _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_non_retriable_status2",
     ),
-    "retry_recv_initial_metadata": _test_options(
+    "retry_per_attempt_recv_timeout": _test_options(
         needs_client_channel = True,
-        proxyable = False,
     ),
-    "retry_recv_message": _test_options(
+    "retry_recv_initial_metadata": _test_options(needs_client_channel = True),
+    "retry_recv_message": _test_options(needs_client_channel = True),
+    "retry_recv_trailing_metadata_error": _test_options(
         needs_client_channel = True,
-        proxyable = False,
-    ),
-    "retry_server_pushback_delay": _test_options(
-        needs_client_channel = True,
-        proxyable = False,
-    ),
-    "retry_server_pushback_disabled": _test_options(
-        needs_client_channel = True,
-        proxyable = False,
-    ),
-    "retry_streaming": _test_options(needs_client_channel = True, proxyable = False),
-    "retry_streaming_after_commit": _test_options(
-        needs_client_channel = True,
-        proxyable = False,
     ),
+    "retry_send_op_fails": _test_options(needs_client_channel = True),
+    "retry_server_pushback_delay": _test_options(needs_client_channel = True),
+    "retry_server_pushback_disabled": _test_options(needs_client_channel = True),
+    "retry_streaming": _test_options(needs_client_channel = True),
+    "retry_streaming_after_commit": _test_options(needs_client_channel = True),
     "retry_streaming_succeeds_before_replay_finished": _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_streaming2",
     ),
-    "retry_throttled": _test_options(
-        needs_client_channel = True,
-        proxyable = False,
-    ),
-    "retry_too_many_attempts": _test_options(
-        needs_client_channel = True,
-        proxyable = False,
-    ),
+    "retry_throttled": _test_options(needs_client_channel = True),
+    "retry_too_many_attempts": _test_options(needs_client_channel = True),
     "server_finishes_request": _test_options(),
     "server_streaming": _test_options(needs_http2 = True),
     "shutdown_finishes_calls": _test_options(),
index caf4e37..327affa 100644 (file)
@@ -37,14 +37,14 @@ typedef struct inproc_fixture_data {
 
 namespace {
 template <typename F>
-class CQDeletingCallback : public grpc_experimental_completion_queue_functor {
+class CQDeletingCallback : public grpc_completion_queue_functor {
  public:
   explicit CQDeletingCallback(F f) : func_(f) {
     functor_run = &CQDeletingCallback::Run;
     inlineable = false;
   }
   ~CQDeletingCallback() {}
-  static void Run(grpc_experimental_completion_queue_functor* cb, int ok) {
+  static void Run(grpc_completion_queue_functor* cb, int ok) {
     auto* callback = static_cast<CQDeletingCallback*>(cb);
     callback->func_(static_cast<bool>(ok));
     delete callback;
@@ -55,11 +55,11 @@ class CQDeletingCallback : public grpc_experimental_completion_queue_functor {
 };
 
 template <typename F>
-grpc_experimental_completion_queue_functor* NewDeletingCallback(F f) {
+grpc_completion_queue_functor* NewDeletingCallback(F f) {
   return new CQDeletingCallback<F>(f);
 }
 
-class ShutdownCallback : public grpc_experimental_completion_queue_functor {
+class ShutdownCallback : public grpc_completion_queue_functor {
  public:
   ShutdownCallback() : done_(false) {
     functor_run = &ShutdownCallback::StaticRun;
@@ -71,8 +71,7 @@ class ShutdownCallback : public grpc_experimental_completion_queue_functor {
     gpr_mu_destroy(&mu_);
     gpr_cv_destroy(&cv_);
   }
-  static void StaticRun(grpc_experimental_completion_queue_functor* cb,
-                        int ok) {
+  static void StaticRun(grpc_completion_queue_functor* cb, int ok) {
     auto* callback = static_cast<ShutdownCallback*>(cb);
     callback->Run(static_cast<bool>(ok));
   }
@@ -184,7 +183,7 @@ static void verify_tags(gpr_timespec deadline) {
 
 // This function creates a callback functor that emits the
 // desired tag into the global tag set
-static grpc_experimental_completion_queue_functor* tag(intptr_t t) {
+static grpc_completion_queue_functor* tag(intptr_t t) {
   auto func = [t](bool ok) {
     gpr_mu_lock(&tags_mu);
     gpr_log(GPR_DEBUG, "Completing operation %" PRIdPTR, t);
index 208672e..4175eae 100644 (file)
@@ -34,10 +34,10 @@ typedef struct {
 } child_events;
 
 struct CallbackContext {
-  grpc_experimental_completion_queue_functor functor;
+  grpc_completion_queue_functor functor;
   gpr_event finished;
-  explicit CallbackContext(void (*cb)(
-      grpc_experimental_completion_queue_functor* functor, int success)) {
+  explicit CallbackContext(void (*cb)(grpc_completion_queue_functor* functor,
+                                      int success)) {
     functor.functor_run = cb;
     functor.inlineable = false;
     gpr_event_init(&finished);
@@ -174,8 +174,8 @@ static void test_connectivity(grpc_end2end_test_config config) {
   cq_verifier_destroy(cqv);
 }
 
-static void cb_watch_connectivity(
-    grpc_experimental_completion_queue_functor* functor, int success) {
+static void cb_watch_connectivity(grpc_completion_queue_functor* functor,
+                                  int success) {
   CallbackContext* cb_ctx = reinterpret_cast<CallbackContext*>(functor);
 
   gpr_log(GPR_DEBUG, "cb_watch_connectivity called, verifying");
@@ -186,7 +186,7 @@ static void cb_watch_connectivity(
   gpr_event_set(&cb_ctx->finished, reinterpret_cast<void*>(1));
 }
 
-static void cb_shutdown(grpc_experimental_completion_queue_functor* functor,
+static void cb_shutdown(grpc_completion_queue_functor* functor,
                         int /*success*/) {
   CallbackContext* cb_ctx = reinterpret_cast<CallbackContext*>(functor);
 
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
new file mode 100644 (file)
index 0000000..79b03dd
--- /dev/null
@@ -0,0 +1,319 @@
+//
+// 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return reinterpret_cast<void*>(t); }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests the case where the retry buffer size is exceeded during backoff.
+// - 1 retry allowed for ABORTED status
+// - buffer size set to 100 KiB (larger than initial metadata)
+// - client initially sends initial metadata (smaller than buffer size)
+// - 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(
+    grpc_end2end_test_config 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<char*>(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_integer_create(
+          const_cast<char*>(GRPC_ARG_ENABLE_RETRIES), 1),
+      grpc_channel_arg_string_create(
+          const_cast<char*>(GRPC_ARG_SERVICE_CONFIG),
+          const_cast<char*>(
+              "{\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<char*>(GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE), 102400),
+  };
+  grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args};
+  grpc_end2end_test_fixture f = begin_test(
+      config, "retry_exceeds_buffer_size_in_delay", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(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_details = grpc_slice_from_static_string("xyz");
+
+  // 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<size_t>(op - ops), tag(1),
+                                nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  // Server gets a call.
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  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);
+
+  // 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_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<size_t>(op - ops), tag(102),
+                                nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  cq_verify(cqv);
+
+  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);
+
+  // 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.)
+  cq_verify_empty(cqv);
+
+  // 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<size_t>(op - ops), tag(2),
+                                nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(2), true);
+  cq_verify(cqv);
+
+  // Server gets another call.
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(201));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(201), true);
+  cq_verify(cqv);
+
+  // 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_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<size_t>(op - ops), tag(202),
+                                nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(202), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  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(0 == call_details.flags);
+  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);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+  gpr_free(buf);
+}
+
+void retry_exceeds_buffer_size_in_delay(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_exceeds_buffer_size_in_delay(config);
+}
+
+void retry_exceeds_buffer_size_in_delay_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_per_attempt_recv_timeout.cc b/test/core/end2end/tests/retry_per_attempt_recv_timeout.cc
new file mode 100644 (file)
index 0000000..016b6e9
--- /dev/null
@@ -0,0 +1,347 @@
+//
+// 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return reinterpret_cast<void*>(t); }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// 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(
+    grpc_end2end_test_config 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;
+
+  grpc_arg args[] = {
+      grpc_channel_arg_integer_create(
+          const_cast<char*>(GRPC_ARG_ENABLE_RETRIES), 1),
+      grpc_channel_arg_string_create(
+          const_cast<char*>(GRPC_ARG_SERVICE_CONFIG),
+          const_cast<char*>(
+              "{\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\": \"1s\",\n"
+              "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+              "    }\n"
+              "  } ]\n"
+              "}")),
+  };
+  grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  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<size_t>(op - ops), tag(1),
+                                nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  // 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, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  // 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_MDSTR_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);
+
+  // Server gets a second call.
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(201));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(201), true);
+  cq_verify(cqv);
+
+  // Now we can unref the first call.
+  grpc_call_unref(s0);
+
+  // 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_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS)) {
+      GPR_ASSERT(
+          grpc_slice_eq(request_metadata_recv.metadata[i].value, GRPC_MDSTR_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);
+
+  // 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<size_t>(op - ops), tag(202),
+                                nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(202), true);
+  cq_verify(cqv);
+
+  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);
+
+  // Server gets a third call.
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(301));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(301), true);
+  cq_verify(cqv);
+
+  // 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_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS)) {
+      GPR_ASSERT(
+          grpc_slice_eq(request_metadata_recv.metadata[i].value, GRPC_MDSTR_2));
+      found_retry_header = true;
+      break;
+    }
+  }
+  GPR_ASSERT(found_retry_header);
+
+  // 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_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++;
+  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<size_t>(op - ops), tag(302),
+                                nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(302), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  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(0 == call_details.flags);
+  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);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_per_attempt_recv_timeout(grpc_end2end_test_config 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) {}
diff --git a/test/core/end2end/tests/retry_recv_trailing_metadata_error.cc b/test/core/end2end/tests/retry_recv_trailing_metadata_error.cc
new file mode 100644 (file)
index 0000000..584bcd2
--- /dev/null
@@ -0,0 +1,378 @@
+//
+// 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/channel/channel_stack_builder.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return reinterpret_cast<void*>(t); }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// 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(
+    grpc_end2end_test_config 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<char*>(GRPC_ARG_ENABLE_RETRIES), 1),
+      grpc_channel_arg_string_create(
+          const_cast<char*>(GRPC_ARG_SERVICE_CONFIG),
+          const_cast<char*>(
+              "{\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};
+  grpc_end2end_test_fixture f = begin_test(
+      config, "retry_recv_trailing_metadata_error", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  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<size_t>(op - ops), 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, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  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<size_t>(op - ops), tag(102),
+                                nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  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<size_t>(op - ops), tag(2),
+                                nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(2), true);
+  cq_verify(cqv);
+
+  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(0 == call_details.flags);
+  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);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+namespace {
+
+// A filter that returns recv_trailing_metadata_ready with an error.
+class InjectStatusFilter {
+ public:
+  static grpc_channel_filter kFilterVtable;
+
+ public:
+  class CallData {
+   public:
+    static grpc_error_handle Init(grpc_call_element* elem,
+                                  const grpc_call_element_args* /*args*/) {
+      new (elem->call_data) CallData();
+      return GRPC_ERROR_NONE;
+    }
+
+    static void Destroy(grpc_call_element* elem,
+                        const grpc_call_final_info* /*final_info*/,
+                        grpc_closure* /*ignored*/) {
+      auto* calld = static_cast<CallData*>(elem->call_data);
+      calld->~CallData();
+    }
+
+    static void StartTransportStreamOpBatch(
+        grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+      auto* calld = static_cast<CallData*>(elem->call_data);
+      if (batch->recv_trailing_metadata) {
+        calld->original_recv_trailing_metadata_ready_ =
+            batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+        batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+            &calld->recv_trailing_metadata_ready_;
+      }
+      grpc_call_next_op(elem, batch);
+    }
+
+   private:
+    CallData() {
+      GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready_,
+                        RecvTrailingMetadataReady, this, nullptr);
+    }
+
+    static void RecvTrailingMetadataReady(void* arg,
+                                          grpc_error_handle /*error*/) {
+      auto* calld = static_cast<CallData*>(arg);
+      grpc_core::Closure::Run(
+          DEBUG_LOCATION, calld->original_recv_trailing_metadata_ready_,
+          grpc_error_set_int(
+              GRPC_ERROR_CREATE_FROM_STATIC_STRING("injected error"),
+              GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_INVALID_ARGUMENT));
+    }
+
+    grpc_closure recv_trailing_metadata_ready_;
+    grpc_closure* original_recv_trailing_metadata_ready_ = nullptr;
+  };
+
+  static grpc_error_handle Init(grpc_channel_element* /*elem*/,
+                                grpc_channel_element_args* /*args*/) {
+    return GRPC_ERROR_NONE;
+  }
+
+  static void Destroy(grpc_channel_element* /*elem*/) {}
+};
+
+grpc_channel_filter InjectStatusFilter::kFilterVtable = {
+    CallData::StartTransportStreamOpBatch,
+    grpc_channel_next_op,
+    sizeof(CallData),
+    CallData::Init,
+    grpc_call_stack_ignore_set_pollset_or_pollset_set,
+    CallData::Destroy,
+    0,
+    Init,
+    Destroy,
+    grpc_channel_next_get_info,
+    "InjectStatusFilter",
+};
+
+bool g_enable_filter = false;
+
+bool MaybeAddFilter(grpc_channel_stack_builder* builder, void* /*arg*/) {
+  // Skip if filter is not enabled.
+  if (!g_enable_filter) return true;
+  // Skip on proxy (which explicitly disables retries).
+  const grpc_channel_args* args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  if (!grpc_channel_args_find_bool(args, GRPC_ARG_ENABLE_RETRIES, true)) {
+    return true;
+  }
+  // Install filter.
+  return grpc_channel_stack_builder_prepend_filter(
+      builder, &InjectStatusFilter::kFilterVtable, nullptr, nullptr);
+}
+
+void InitPlugin(void) {
+  grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, 0, MaybeAddFilter,
+                                   nullptr);
+}
+
+void DestroyPlugin(void) {}
+
+}  // namespace
+
+void retry_recv_trailing_metadata_error(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  g_enable_filter = true;
+  test_retry_recv_trailing_metadata_error(config);
+  g_enable_filter = false;
+}
+
+void retry_recv_trailing_metadata_error_pre_init() {
+  grpc_register_plugin(InitPlugin, DestroyPlugin);
+}
diff --git a/test/core/end2end/tests/retry_send_op_fails.cc b/test/core/end2end/tests/retry_send_op_fails.cc
new file mode 100644 (file)
index 0000000..8f70e8c
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/channel/channel_stack_builder.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return reinterpret_cast<void*>(t); }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// 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(grpc_end2end_test_config 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<char*>(GRPC_ARG_ENABLE_RETRIES), 1),
+      grpc_channel_arg_string_create(
+          const_cast<char*>(GRPC_ARG_SERVICE_CONFIG),
+          const_cast<char*>(
+              "{\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};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_send_op_fails", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  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<size_t>(op - ops), 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<size_t>(op - ops), tag(2),
+                                nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  // Client send ops should now complete.
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  // Server should get a call.
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  // 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<size_t>(op - ops), 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.
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(2), true);
+  cq_verify(cqv);
+
+  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(0 == call_details.flags);
+  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_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS)) {
+      GPR_ASSERT(
+          grpc_slice_eq(request_metadata_recv.metadata[i].value, GRPC_MDSTR_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);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+namespace {
+
+// A filter that, for the first call it sees, will fail the batch
+// containing send_initial_metadata and then fail the call with status
+// ABORTED.  All subsequent calls are allowed through without failures.
+class FailFirstSendOpFilter {
+ public:
+  static grpc_channel_filter kFilterVtable;
+
+ public:
+  class CallData {
+   public:
+    static grpc_error_handle Init(grpc_call_element* elem,
+                                  const grpc_call_element_args* args) {
+      new (elem->call_data) CallData(args);
+      return GRPC_ERROR_NONE;
+    }
+
+    static void Destroy(grpc_call_element* elem,
+                        const grpc_call_final_info* /*final_info*/,
+                        grpc_closure* /*ignored*/) {
+      auto* calld = static_cast<CallData*>(elem->call_data);
+      calld->~CallData();
+    }
+
+    static void StartTransportStreamOpBatch(
+        grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+      auto* chand = static_cast<FailFirstSendOpFilter*>(elem->channel_data);
+      auto* calld = static_cast<CallData*>(elem->call_data);
+      if (!chand->seen_first_) {
+        chand->seen_first_ = true;
+        calld->fail_ = true;
+      }
+      if (calld->fail_ && !batch->cancel_stream) {
+        grpc_transport_stream_op_batch_finish_with_failure(
+            batch,
+            grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                   "FailFirstSendOpFilter failing batch"),
+                               GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_ABORTED),
+            calld->call_combiner_);
+        return;
+      }
+      grpc_call_next_op(elem, batch);
+    }
+
+   private:
+    explicit CallData(const grpc_call_element_args* args)
+        : call_combiner_(args->call_combiner) {}
+
+    grpc_core::CallCombiner* call_combiner_;
+    bool fail_ = false;
+  };
+
+  static grpc_error_handle Init(grpc_channel_element* elem,
+                                grpc_channel_element_args* /*args*/) {
+    new (elem->channel_data) FailFirstSendOpFilter();
+    return GRPC_ERROR_NONE;
+  }
+
+  static void Destroy(grpc_channel_element* elem) {
+    auto* chand = static_cast<FailFirstSendOpFilter*>(elem->channel_data);
+    chand->~FailFirstSendOpFilter();
+  }
+
+  bool seen_first_ = false;
+};
+
+grpc_channel_filter FailFirstSendOpFilter::kFilterVtable = {
+    CallData::StartTransportStreamOpBatch,
+    grpc_channel_next_op,
+    sizeof(CallData),
+    CallData::Init,
+    grpc_call_stack_ignore_set_pollset_or_pollset_set,
+    CallData::Destroy,
+    sizeof(FailFirstSendOpFilter),
+    Init,
+    Destroy,
+    grpc_channel_next_get_info,
+    "FailFirstSendOpFilter",
+};
+
+bool g_enable_filter = false;
+
+bool MaybeAddFilter(grpc_channel_stack_builder* builder, void* /*arg*/) {
+  // Skip if filter is not enabled.
+  if (!g_enable_filter) return true;
+  // Skip on proxy (which explicitly disables retries).
+  const grpc_channel_args* args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  if (!grpc_channel_args_find_bool(args, GRPC_ARG_ENABLE_RETRIES, true)) {
+    return true;
+  }
+  // Install filter.
+  return grpc_channel_stack_builder_prepend_filter(
+      builder, &FailFirstSendOpFilter::kFilterVtable, nullptr, nullptr);
+}
+
+void InitPlugin(void) {
+  grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, 0, MaybeAddFilter,
+                                   nullptr);
+}
+
+void DestroyPlugin(void) {}
+
+}  // namespace
+
+void retry_send_op_fails(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  g_enable_filter = true;
+  test_retry_send_op_fails(config);
+  g_enable_filter = false;
+}
+
+void retry_send_op_fails_pre_init(void) {
+  grpc_register_plugin(InitPlugin, DestroyPlugin);
+}
diff --git a/test/core/event_engine/BUILD b/test/core/event_engine/BUILD
new file mode 100644 (file)
index 0000000..15bd7fe
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package")
+
+licenses(["notice"])
+
+grpc_package(name = "test/core/event_engine")
+
+grpc_cc_test(
+    name = "endpoint_config_test",
+    srcs = ["endpoint_config_test.cc"],
+    external_deps = ["gtest"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+        "//test/core/util:grpc_test_util",
+    ],
+)
diff --git a/test/core/event_engine/endpoint_config_test.cc b/test/core/event_engine/endpoint_config_test.cc
new file mode 100644 (file)
index 0000000..bcae9c0
--- /dev/null
@@ -0,0 +1,46 @@
+// Copyright 2021 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 <grpc/support/port_platform.h>
+
+#include "grpc/event_engine/endpoint_config.h"
+
+#include <gmock/gmock.h>
+#include <grpc/grpc.h>
+#include <gtest/gtest.h>
+
+#include "test/core/util/test_config.h"
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/event_engine/endpoint_config_internal.h"
+
+using ::grpc_event_engine::experimental::ChannelArgsEndpointConfig;
+
+TEST(EndpointConfigTest, CanSRetrieveValuesFromChannelArgs) {
+  grpc_arg arg = grpc_channel_arg_integer_create(const_cast<char*>("arst"), 3);
+  const grpc_channel_args args = {1, &arg};
+  ChannelArgsEndpointConfig config(&args);
+  EXPECT_EQ(absl::get<int>(config.Get("arst")), 3);
+}
+
+TEST(EndpointConfigTest, ReturnsMonostateForMissingKeys) {
+  ChannelArgsEndpointConfig config(nullptr);
+  EXPECT_TRUE(
+      absl::holds_alternative<absl::monostate>(config.Get("nonexistent")));
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  auto result = RUN_ALL_TESTS();
+  return result;
+}
index 24dc143..8ead408 100644 (file)
@@ -83,19 +83,20 @@ grpc_cc_test(
     ],
 )
 
-grpc_cc_test(
-    name = "handshake_verify_peer_options_test",
-    srcs = ["verify_peer_options.cc"],
-    data = [
-        "//src/core/tsi/test_creds:ca.pem",
-        "//src/core/tsi/test_creds:server1.key",
-        "//src/core/tsi/test_creds:server1.pem",
-    ],
-    language = "C++",
-    tags = ["no_windows"],
-    deps = [
-        "//:gpr",
-        "//:grpc",
-        "//test/core/util:grpc_test_util",
-    ],
-)
+# Disabled as per b/178094682
+#grpc_cc_test(
+#    name = "handshake_verify_peer_options_test",
+#    srcs = ["verify_peer_options.cc"],
+#    data = [
+#        "//src/core/tsi/test_creds:ca.pem",
+#        "//src/core/tsi/test_creds:server1.key",
+#        "//src/core/tsi/test_creds:server1.pem",
+#    ],
+#    language = "C++",
+#    tags = ["no_mac", no_windows"],
+#    deps = [
+#        "//:gpr",
+#        "//:grpc",
+#        "//test/core/util:grpc_test_util",
+#    ],
+#)
index 86a7d76..8ec239a 100644 (file)
@@ -38,6 +38,8 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/load_file.h"
 #include "test/core/util/port.h"
 #define SSL_KEY_PATH "src/core/tsi/test_creds/server1.key"
 #define SSL_CA_PATH "src/core/tsi/test_creds/ca.pem"
 
+grpc_core::TraceFlag client_ssl_tsi_tracing_enabled(false, "tsi");
+
+class SslLibraryInfo {
+ public:
+  SslLibraryInfo() {}
+
+  void Notify() {
+    grpc_core::MutexLock lock(&mu_);
+    ready_ = true;
+    cv_.Signal();
+  }
+
+  void Await() {
+    grpc_core::MutexLock lock(&mu_);
+    grpc_core::WaitUntil(&cv_, &mu_, [this] { return ready_; });
+  }
+
+ private:
+  grpc_core::Mutex mu_;
+  grpc_core::CondVar cv_;
+  bool ready_ = false;
+};
+
 // Arguments for TLS server thread.
 typedef struct {
   int socket;
   char* alpn_preferred;
+  SslLibraryInfo* ssl_library_info;
 } server_args;
 
 // Based on https://wiki.openssl.org/index.php/Simple_TLS_Server.
@@ -135,6 +161,28 @@ static int alpn_select_cb(SSL* /*ssl*/, const uint8_t** out, uint8_t* out_len,
   return SSL_TLSEXT_ERR_OK;
 }
 
+static void ssl_log_where_info(const SSL* ssl, int where, int flag,
+                               const char* msg) {
+  if ((where & flag) &&
+      GRPC_TRACE_FLAG_ENABLED(client_ssl_tsi_tracing_enabled)) {
+    gpr_log(GPR_INFO, "%20.20s - %30.30s  - %5.10s", msg,
+            SSL_state_string_long(ssl), SSL_state_string(ssl));
+  }
+}
+
+static void ssl_server_info_callback(const SSL* ssl, int where, int ret) {
+  if (ret == 0) {
+    gpr_log(GPR_ERROR, "ssl_server_info_callback: error occurred.\n");
+    return;
+  }
+
+  ssl_log_where_info(ssl, where, SSL_CB_LOOP, "Server: LOOP");
+  ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_START,
+                     "Server: HANDSHAKE START");
+  ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_DONE,
+                     "Server: HANDSHAKE DONE");
+}
+
 // Minimal TLS server. This is largely based on the example at
 // https://wiki.openssl.org/index.php/Simple_TLS_Server and the gRPC core
 // internals in src/core/tsi/ssl_transport_security.c.
@@ -143,6 +191,7 @@ static void server_thread(void* arg) {
 
   SSL_load_error_strings();
   OpenSSL_add_ssl_algorithms();
+  args->ssl_library_info->Notify();
 
   const SSL_METHOD* method = TLSv1_2_server_method();
   SSL_CTX* ctx = SSL_CTX_new(method);
@@ -154,16 +203,23 @@ static void server_thread(void* arg) {
 
   // Load key pair.
   if (SSL_CTX_use_certificate_file(ctx, SSL_CERT_PATH, SSL_FILETYPE_PEM) < 0) {
+    perror("Unable to use certificate file.");
     ERR_print_errors_fp(stderr);
     abort();
   }
   if (SSL_CTX_use_PrivateKey_file(ctx, SSL_KEY_PATH, SSL_FILETYPE_PEM) < 0) {
+    perror("Unable to use private key file.");
+    ERR_print_errors_fp(stderr);
+    abort();
+  }
+  if (SSL_CTX_check_private_key(ctx) != 1) {
+    perror("Check private key failed.");
     ERR_print_errors_fp(stderr);
     abort();
   }
 
   // Set the cipher list to match the one expressed in
-  // src/core/tsi/ssl_transport_security.c.
+  // src/core/tsi/ssl_transport_security.cc.
   const char* cipher_list =
       "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-"
       "SHA384:ECDHE-RSA-AES256-GCM-SHA384";
@@ -173,6 +229,14 @@ static void server_thread(void* arg) {
     abort();
   }
 
+  // Enable automatic curve selection. This is a NO-OP when using OpenSSL
+  // versions > 1.0.2.
+  if (!SSL_CTX_set_ecdh_auto(ctx, /*onoff=*/1)) {
+    ERR_print_errors_fp(stderr);
+    gpr_log(GPR_ERROR, "Couldn't set automatic curve selection.");
+    abort();
+  }
+
   // Register the ALPN selection callback.
   SSL_CTX_set_alpn_select_cb(ctx, alpn_select_cb, args->alpn_preferred);
 
@@ -190,6 +254,7 @@ static void server_thread(void* arg) {
 
   // Establish a SSL* and accept at SSL layer.
   SSL* ssl = SSL_new(ctx);
+  SSL_set_info_callback(ssl, ssl_server_info_callback);
   GPR_ASSERT(ssl);
   SSL_set_fd(ssl, client);
   if (SSL_accept(ssl) <= 0) {
@@ -212,7 +277,6 @@ static void server_thread(void* arg) {
   close(client);
   close(sock);
   SSL_CTX_free(ctx);
-  EVP_cleanup();
 }
 
 // This test launches a minimal TLS server on a separate thread and then
@@ -238,11 +302,13 @@ static bool client_ssl_test(char* server_alpn_preferred) {
   GPR_ASSERT(server_socket > 0 && port > 0);
 
   // Launch the TLS server thread.
-  server_args args = {server_socket, server_alpn_preferred};
+  SslLibraryInfo ssl_library_info;
+  server_args args = {server_socket, server_alpn_preferred, &ssl_library_info};
   bool ok;
   grpc_core::Thread thd("grpc_client_ssl_test", server_thread, &args, &ok);
   GPR_ASSERT(ok);
   thd.Start();
+  ssl_library_info.Await();
 
   // Load key pair and establish client SSL credentials.
   grpc_ssl_pem_key_cert_pair pem_key_cert_pair;
@@ -326,6 +392,8 @@ int main(int argc, char* argv[]) {
   // preference. This validates the client is correctly validating ALPN returns
   // and sanity checks the client_ssl_test.
   GPR_ASSERT(!client_ssl_test(const_cast<char*>("foo")));
+  // Clean up the SSL libraries.
+  EVP_cleanup();
   return 0;
 }
 
index ec1cdc4..a70ba39 100644 (file)
@@ -83,6 +83,7 @@ int main(int /*argc*/, char* /*argv*/[]) {
       absl::make_unique<grpc_core::ReadAheadHandshakerFactory>());
   const char* full_alpn_list[] = {"grpc-exp", "h2"};
   GPR_ASSERT(server_ssl_test(full_alpn_list, 2, "grpc-exp"));
+  CleanupSslLibrary();
   grpc_shutdown();
   return 0;
 }
index 096cfaf..3358022 100644 (file)
@@ -54,5 +54,6 @@ int main(int argc, char* argv[]) {
   // and sanity checks the server_ssl_test.
   const char* fake_alpn_list[] = {"foo"};
   GPR_ASSERT(!server_ssl_test(fake_alpn_list, 1, "foo"));
+  CleanupSslLibrary();
   return 0;
 }
index e16fc0e..026a52c 100644 (file)
@@ -269,7 +269,6 @@ bool server_ssl_test(const char* alpn_list[], unsigned int alpn_list_len,
   SSL_free(ssl);
   gpr_free(alpn_protos);
   SSL_CTX_free(ctx);
-  EVP_cleanup();
   close(sock);
 
   thd.Join();
@@ -278,3 +277,5 @@ bool server_ssl_test(const char* alpn_list[], unsigned int alpn_list_len,
 
   return success;
 }
+
+void CleanupSslLibrary() { EVP_cleanup(); }
index 32bc6f9..c64a007 100644 (file)
@@ -33,4 +33,9 @@
 bool server_ssl_test(const char* alpn_list[], unsigned int alpn_list_len,
                      const char* alpn_expected);
 
+/** Cleans up the SSL library. To be called after the last call to
+ *  server_ssl_test returns. This is a NO-OP when gRPC is built against OpenSSL
+ *  versions > 1.0.2. */
+void CleanupSslLibrary();
+
 #endif  // GRPC_SERVER_SSL_COMMON_H
index 0382757..b06aedd 100644 (file)
@@ -29,7 +29,10 @@ grpc_cc_library(
     srcs = ["endpoint_tests.cc"],
     hdrs = ["endpoint_tests.h"],
     language = "C++",
-    visibility = ["//test:__subpackages__"],
+    visibility = [
+        "//test:__subpackages__",
+        "@grpc:endpoint_tests",
+    ],
     deps = [
         "//:gpr",
         "//:grpc",
index c8a4d15..4a2c714 100644 (file)
@@ -28,6 +28,7 @@
 #include <string.h>
 
 #include "src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h"
+#include "src/core/lib/event_engine/sockaddr.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/sync.h"
 #include "src/core/lib/iomgr/executor.h"
index f6f0b37..899eeb4 100644 (file)
@@ -60,6 +60,26 @@ static bool mutate_fd(int fd, grpc_socket_mutator* mutator) {
   return true;
 }
 
+static bool mutate_fd_2(const grpc_mutate_socket_info* info,
+                        grpc_socket_mutator* mutator) {
+  int newval;
+  socklen_t intlen = sizeof(newval);
+  struct test_socket_mutator* m =
+      reinterpret_cast<struct test_socket_mutator*>(mutator);
+
+  if (0 != setsockopt(info->fd, IPPROTO_IP, IP_TOS, &m->option_value,
+                      sizeof(m->option_value))) {
+    return false;
+  }
+  if (0 != getsockopt(info->fd, IPPROTO_IP, IP_TOS, &newval, &intlen)) {
+    return false;
+  }
+  if (newval != m->option_value) {
+    return false;
+  }
+  return true;
+}
+
 static void destroy_test_mutator(grpc_socket_mutator* mutator) {
   struct test_socket_mutator* m =
       reinterpret_cast<struct test_socket_mutator*>(mutator);
@@ -76,11 +96,46 @@ static int compare_test_mutator(grpc_socket_mutator* a,
 }
 
 static const grpc_socket_mutator_vtable mutator_vtable = {
-    mutate_fd, compare_test_mutator, destroy_test_mutator};
+    mutate_fd, compare_test_mutator, destroy_test_mutator, nullptr};
+
+static const grpc_socket_mutator_vtable mutator_vtable2 = {
+    nullptr, compare_test_mutator, destroy_test_mutator, mutate_fd_2};
+
+static void test_with_vtable(const grpc_socket_mutator_vtable* vtable) {
+  int sock = socket(PF_INET, SOCK_STREAM, 0);
+  GPR_ASSERT(sock > 0);
+
+  struct test_socket_mutator mutator;
+  grpc_socket_mutator_init(&mutator.base, vtable);
+
+  mutator.option_value = IPTOS_LOWDELAY;
+  GPR_ASSERT(GRPC_LOG_IF_ERROR(
+      "set_socket_with_mutator",
+      grpc_set_socket_with_mutator(sock, GRPC_FD_CLIENT_CONNECTION_USAGE,
+                                   (grpc_socket_mutator*)&mutator)));
+
+  mutator.option_value = IPTOS_THROUGHPUT;
+  GPR_ASSERT(GRPC_LOG_IF_ERROR(
+      "set_socket_with_mutator",
+      grpc_set_socket_with_mutator(sock, GRPC_FD_CLIENT_CONNECTION_USAGE,
+                                   (grpc_socket_mutator*)&mutator)));
+
+  mutator.option_value = IPTOS_RELIABILITY;
+  GPR_ASSERT(GRPC_LOG_IF_ERROR(
+      "set_socket_with_mutator",
+      grpc_set_socket_with_mutator(sock, GRPC_FD_CLIENT_CONNECTION_USAGE,
+                                   (grpc_socket_mutator*)&mutator)));
+
+  mutator.option_value = -1;
+  auto err = grpc_set_socket_with_mutator(
+      sock, GRPC_FD_CLIENT_CONNECTION_USAGE,
+      reinterpret_cast<grpc_socket_mutator*>(&mutator));
+  GPR_ASSERT(err != GRPC_ERROR_NONE);
+  GRPC_ERROR_UNREF(err);
+}
 
 int main(int argc, char** argv) {
   int sock;
-  grpc_error_handle err;
   grpc::testing::TestEnvironment env(argc, argv);
 
   sock = socket(PF_INET, SOCK_STREAM, 0);
@@ -103,29 +158,8 @@ int main(int argc, char** argv) {
   GPR_ASSERT(GRPC_LOG_IF_ERROR("set_socket_low_latency",
                                grpc_set_socket_low_latency(sock, 0)));
 
-  struct test_socket_mutator mutator;
-  grpc_socket_mutator_init(&mutator.base, &mutator_vtable);
-
-  mutator.option_value = IPTOS_LOWDELAY;
-  GPR_ASSERT(GRPC_LOG_IF_ERROR(
-      "set_socket_with_mutator",
-      grpc_set_socket_with_mutator(sock, (grpc_socket_mutator*)&mutator)));
-
-  mutator.option_value = IPTOS_THROUGHPUT;
-  GPR_ASSERT(GRPC_LOG_IF_ERROR(
-      "set_socket_with_mutator",
-      grpc_set_socket_with_mutator(sock, (grpc_socket_mutator*)&mutator)));
-
-  mutator.option_value = IPTOS_RELIABILITY;
-  GPR_ASSERT(GRPC_LOG_IF_ERROR(
-      "set_socket_with_mutator",
-      grpc_set_socket_with_mutator(sock, (grpc_socket_mutator*)&mutator)));
-
-  mutator.option_value = -1;
-  err = grpc_set_socket_with_mutator(
-      sock, reinterpret_cast<grpc_socket_mutator*>(&mutator));
-  GPR_ASSERT(err != GRPC_ERROR_NONE);
-  GRPC_ERROR_UNREF(err);
+  test_with_vtable(&mutator_vtable);
+  test_with_vtable(&mutator_vtable2);
 
   close(sock);
 
index ac6fc3f..1b8c697 100644 (file)
@@ -44,7 +44,7 @@ static void test_constructor_option(void) {
 }
 
 // Simple functor for testing. It will count how many times being called.
-class SimpleFunctorForAdd : public grpc_experimental_completion_queue_functor {
+class SimpleFunctorForAdd : public grpc_completion_queue_functor {
  public:
   friend class SimpleFunctorCheckForAdd;
   SimpleFunctorForAdd() {
@@ -54,8 +54,7 @@ class SimpleFunctorForAdd : public grpc_experimental_completion_queue_functor {
     internal_success = 0;
   }
   ~SimpleFunctorForAdd() {}
-  static void Run(struct grpc_experimental_completion_queue_functor* cb,
-                  int /*ok*/) {
+  static void Run(struct grpc_completion_queue_functor* cb, int /*ok*/) {
     auto* callback = static_cast<SimpleFunctorForAdd*>(cb);
     callback->count_.FetchAdd(1, grpc_core::MemoryOrder::RELAXED);
   }
@@ -138,8 +137,7 @@ static void test_multi_add(void) {
 }
 
 // Checks the current count with a given number.
-class SimpleFunctorCheckForAdd
-    : public grpc_experimental_completion_queue_functor {
+class SimpleFunctorCheckForAdd : public grpc_completion_queue_functor {
  public:
   SimpleFunctorCheckForAdd(int ok, int* count) : count_(count) {
     functor_run = &SimpleFunctorCheckForAdd::Run;
@@ -147,8 +145,7 @@ class SimpleFunctorCheckForAdd
     internal_success = ok;
   }
   ~SimpleFunctorCheckForAdd() {}
-  static void Run(struct grpc_experimental_completion_queue_functor* cb,
-                  int /*ok*/) {
+  static void Run(struct grpc_completion_queue_functor* cb, int /*ok*/) {
     auto* callback = static_cast<SimpleFunctorCheckForAdd*>(cb);
     (*callback->count_)++;
     GPR_ASSERT(*callback->count_ == callback->internal_success);
index 248e0bb..3885b24 100644 (file)
@@ -117,9 +117,8 @@ grpc_cc_test(
     deps = [
         "//:gpr",
         "//:grpc",
-        "//:grpc_rbac_engine",
+        "//:grpc_secure",
         "//test/core/util:grpc_test_util",
-        "//test/core/util:grpc_test_util_base",
     ],
 )
 
@@ -451,3 +450,16 @@ grpc_cc_test(
         "//test/core/util:grpc_test_util",
     ],
 )
+
+grpc_cc_test(
+    name = "grpc_authorization_policy_provider_test",
+    srcs = ["grpc_authorization_policy_provider_test.cc"],
+    external_deps = ["gtest"],
+    language = "C++",
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//:grpc_authorization_provider",
+        "//test/core/util:grpc_test_util",
+    ],
+)
index 36b3862..dc3646d 100644 (file)
@@ -36,12 +36,6 @@ TEST_F(AuthorizationMatchersTest, AlwaysAuthorizationMatcher) {
   EXPECT_TRUE(matcher.Matches(args));
 }
 
-TEST_F(AuthorizationMatchersTest, NotAlwaysAuthorizationMatcher) {
-  EvaluateArgs args = args_.MakeEvaluateArgs();
-  AlwaysAuthorizationMatcher matcher(/*not_rule=*/true);
-  EXPECT_FALSE(matcher.Matches(args));
-}
-
 TEST_F(AuthorizationMatchersTest, AndAuthorizationMatcherSuccessfulMatch) {
   args_.AddPairToMetadata("foo", "bar");
   args_.SetLocalEndpoint("ipv4:255.255.255.255:123");
@@ -54,8 +48,9 @@ TEST_F(AuthorizationMatchersTest, AndAuthorizationMatcherSuccessfulMatch) {
           .value()));
   rules.push_back(absl::make_unique<Rbac::Permission>(
       Rbac::Permission::RuleType::kDestPort, /*port=*/123));
-  AndAuthorizationMatcher matcher(std::move(rules));
-  EXPECT_TRUE(matcher.Matches(args));
+  auto matcher = AuthorizationMatcher::Create(
+      Rbac::Permission(Rbac::Permission::RuleType::kAnd, std::move(rules)));
+  EXPECT_TRUE(matcher->Matches(args));
 }
 
 TEST_F(AuthorizationMatchersTest, AndAuthorizationMatcherFailedMatch) {
@@ -70,42 +65,28 @@ TEST_F(AuthorizationMatchersTest, AndAuthorizationMatcherFailedMatch) {
           .value()));
   rules.push_back(absl::make_unique<Rbac::Permission>(
       Rbac::Permission::RuleType::kDestPort, /*port=*/123));
-  AndAuthorizationMatcher matcher(std::move(rules));
+  auto matcher = AuthorizationMatcher::Create(
+      Rbac::Permission(Rbac::Permission::RuleType::kAnd, std::move(rules)));
   // Header rule fails. Expected value "bar", got "not_bar" for key "foo".
-  EXPECT_FALSE(matcher.Matches(args));
-}
-
-TEST_F(AuthorizationMatchersTest, NotAndAuthorizationMatcher) {
-  args_.AddPairToMetadata(":path", "/expected/foo");
-  EvaluateArgs args = args_.MakeEvaluateArgs();
-  StringMatcher string_matcher =
-      StringMatcher::Create(StringMatcher::Type::kExact,
-                            /*matcher=*/"/expected/foo",
-                            /*case_sensitive=*/false)
-          .value();
-  std::vector<std::unique_ptr<Rbac::Permission>> ids;
-  ids.push_back(absl::make_unique<Rbac::Permission>(
-      Rbac::Permission::RuleType::kPath, std::move(string_matcher)));
-  AndAuthorizationMatcher matcher(std::move(ids), /*not_rule=*/true);
-  EXPECT_FALSE(matcher.Matches(args));
+  EXPECT_FALSE(matcher->Matches(args));
 }
 
 TEST_F(AuthorizationMatchersTest, OrAuthorizationMatcherSuccessfulMatch) {
   args_.AddPairToMetadata("foo", "bar");
   args_.SetLocalEndpoint("ipv4:255.255.255.255:123");
   EvaluateArgs args = args_.MakeEvaluateArgs();
-  HeaderMatcher header_matcher =
-      HeaderMatcher::Create(/*name=*/"foo", HeaderMatcher::Type::kExact,
-                            /*matcher=*/"bar")
-          .value();
   std::vector<std::unique_ptr<Rbac::Permission>> rules;
   rules.push_back(absl::make_unique<Rbac::Permission>(
-      Rbac::Permission::RuleType::kHeader, header_matcher));
+      Rbac::Permission::RuleType::kHeader,
+      HeaderMatcher::Create(/*name=*/"foo", HeaderMatcher::Type::kExact,
+                            /*matcher=*/"bar")
+          .value()));
   rules.push_back(absl::make_unique<Rbac::Permission>(
       Rbac::Permission::RuleType::kDestPort, /*port=*/456));
-  OrAuthorizationMatcher matcher(std::move(rules));
+  auto matcher = AuthorizationMatcher::Create(
+      Rbac::Permission(Rbac::Permission::RuleType::kOr, std::move(rules)));
   // Matches as header rule matches even though port rule fails.
-  EXPECT_TRUE(matcher.Matches(args));
+  EXPECT_TRUE(matcher->Matches(args));
 }
 
 TEST_F(AuthorizationMatchersTest, OrAuthorizationMatcherFailedMatch) {
@@ -117,22 +98,36 @@ TEST_F(AuthorizationMatchersTest, OrAuthorizationMatcherFailedMatch) {
       HeaderMatcher::Create(/*name=*/"foo", HeaderMatcher::Type::kExact,
                             /*matcher=*/"bar")
           .value()));
-  OrAuthorizationMatcher matcher(std::move(rules));
+  auto matcher = AuthorizationMatcher::Create(
+      Rbac::Permission(Rbac::Permission::RuleType::kOr, std::move(rules)));
   // Header rule fails. Expected value "bar", got "not_bar" for key "foo".
-  EXPECT_FALSE(matcher.Matches(args));
+  EXPECT_FALSE(matcher->Matches(args));
 }
 
-TEST_F(AuthorizationMatchersTest, NotOrAuthorizationMatcher) {
-  args_.AddPairToMetadata("foo", "not_bar");
+TEST_F(AuthorizationMatchersTest, NotAuthorizationMatcherSuccessfulMatch) {
+  args_.AddPairToMetadata(":path", "/different/foo");
   EvaluateArgs args = args_.MakeEvaluateArgs();
-  std::vector<std::unique_ptr<Rbac::Permission>> rules;
-  rules.push_back(absl::make_unique<Rbac::Permission>(
-      Rbac::Permission::RuleType::kHeader,
-      HeaderMatcher::Create(/*name=*/"foo", HeaderMatcher::Type::kExact,
-                            /*matcher=*/"bar")
-          .value()));
-  OrAuthorizationMatcher matcher(std::move(rules), /*not_rule=*/true);
-  EXPECT_TRUE(matcher.Matches(args));
+  auto matcher = AuthorizationMatcher::Create(Rbac::Principal(
+      Rbac::Principal::RuleType::kNot,
+      Rbac::Principal(Rbac::Principal::RuleType::kPath,
+                      StringMatcher::Create(StringMatcher::Type::kExact,
+                                            /*matcher=*/"/expected/foo",
+                                            /*case_sensitive=*/false)
+                          .value())));
+  EXPECT_TRUE(matcher->Matches(args));
+}
+
+TEST_F(AuthorizationMatchersTest, NotAuthorizationMatcherFailedMatch) {
+  args_.AddPairToMetadata(":path", "/expected/foo");
+  EvaluateArgs args = args_.MakeEvaluateArgs();
+  auto matcher = AuthorizationMatcher::Create(Rbac::Principal(
+      Rbac::Principal::RuleType::kNot,
+      Rbac::Principal(Rbac::Principal::RuleType::kPath,
+                      StringMatcher::Create(StringMatcher::Type::kExact,
+                                            /*matcher=*/"/expected/foo",
+                                            /*case_sensitive=*/false)
+                          .value())));
+  EXPECT_FALSE(matcher->Matches(args));
 }
 
 TEST_F(AuthorizationMatchersTest, HybridAuthorizationMatcherSuccessfulMatch) {
@@ -153,8 +148,9 @@ TEST_F(AuthorizationMatchersTest, HybridAuthorizationMatcherSuccessfulMatch) {
       Rbac::Permission::RuleType::kAnd, std::move(sub_and_rules)));
   and_rules.push_back(absl::make_unique<Rbac::Permission>(
       Rbac::Permission::RuleType::kOr, std::move(std::move(sub_or_rules))));
-  AndAuthorizationMatcher matcher(std::move(and_rules));
-  EXPECT_TRUE(matcher.Matches(args));
+  auto matcher = AuthorizationMatcher::Create(
+      Rbac::Permission(Rbac::Permission::RuleType::kAnd, std::move(and_rules)));
+  EXPECT_TRUE(matcher->Matches(args));
 }
 
 TEST_F(AuthorizationMatchersTest, HybridAuthorizationMatcherFailedMatch) {
@@ -180,9 +176,10 @@ TEST_F(AuthorizationMatchersTest, HybridAuthorizationMatcherFailedMatch) {
       Rbac::Permission::RuleType::kAnd, std::move(sub_and_rules)));
   and_rules.push_back(absl::make_unique<Rbac::Permission>(
       Rbac::Permission::RuleType::kOr, std::move(std::move(sub_or_rules))));
-  AndAuthorizationMatcher matcher(std::move(and_rules));
+  auto matcher = AuthorizationMatcher::Create(
+      Rbac::Permission(Rbac::Permission::RuleType::kAnd, std::move(and_rules)));
   // Fails as "absent_key" header was not present.
-  EXPECT_FALSE(matcher.Matches(args));
+  EXPECT_FALSE(matcher->Matches(args));
 }
 
 TEST_F(AuthorizationMatchersTest, PathAuthorizationMatcherSuccessfulMatch) {
@@ -207,16 +204,6 @@ TEST_F(AuthorizationMatchersTest, PathAuthorizationMatcherFailedMatch) {
   EXPECT_FALSE(matcher.Matches(args));
 }
 
-TEST_F(AuthorizationMatchersTest, NotPathAuthorizationMatcher) {
-  args_.AddPairToMetadata(":path", "expected/path");
-  EvaluateArgs args = args_.MakeEvaluateArgs();
-  PathAuthorizationMatcher matcher(
-      StringMatcher::Create(StringMatcher::Type::kExact, "expected/path", false)
-          .value(),
-      /*not_rule=*/true);
-  EXPECT_FALSE(matcher.Matches(args));
-}
-
 TEST_F(AuthorizationMatchersTest,
        PathAuthorizationMatcherFailedMatchMissingPath) {
   EvaluateArgs args = args_.MakeEvaluateArgs();
@@ -270,17 +257,50 @@ TEST_F(AuthorizationMatchersTest,
   EXPECT_FALSE(matcher.Matches(args));
 }
 
-TEST_F(AuthorizationMatchersTest, NotHeaderAuthorizationMatcher) {
-  args_.AddPairToMetadata("key123", "foo");
+TEST_F(AuthorizationMatchersTest,
+       IpAuthorizationMatcherLocalIpSuccessfulMatch) {
+  args_.SetLocalEndpoint("ipv4:1.2.3.4:123");
   EvaluateArgs args = args_.MakeEvaluateArgs();
-  HeaderAuthorizationMatcher matcher(
-      HeaderMatcher::Create(/*name=*/"key123", HeaderMatcher::Type::kExact,
-                            /*matcher=*/"bar")
-          .value(),
-      /*not_rule=*/true);
+  IpAuthorizationMatcher matcher(
+      IpAuthorizationMatcher::Type::kDestIp,
+      Rbac::CidrRange(/*address_prefix=*/"1.7.8.9", /*prefix_len=*/8));
   EXPECT_TRUE(matcher.Matches(args));
 }
 
+TEST_F(AuthorizationMatchersTest, IpAuthorizationMatcherLocalIpFailedMatch) {
+  args_.SetLocalEndpoint("ipv4:1.2.3.4:123");
+  EvaluateArgs args = args_.MakeEvaluateArgs();
+  IpAuthorizationMatcher matcher(
+      IpAuthorizationMatcher::Type::kDestIp,
+      Rbac::CidrRange(/*address_prefix=*/"1.2.3.9", /*prefix_len=*/32));
+  EXPECT_FALSE(matcher.Matches(args));
+}
+
+TEST_F(AuthorizationMatchersTest, IpAuthorizationMatcherPeerIpSuccessfulMatch) {
+  args_.SetPeerEndpoint("ipv6:[1:2:3::]:456");
+  EvaluateArgs args = args_.MakeEvaluateArgs();
+  IpAuthorizationMatcher matcher(
+      IpAuthorizationMatcher::Type::kSourceIp,
+      Rbac::CidrRange(/*address_prefix=*/"1:2:4::", /*prefix_len=*/32));
+  EXPECT_TRUE(matcher.Matches(args));
+}
+
+TEST_F(AuthorizationMatchersTest, IpAuthorizationMatcherPeerIpFailedMatch) {
+  args_.SetPeerEndpoint("ipv6:[1:2::]:456");
+  EvaluateArgs args = args_.MakeEvaluateArgs();
+  IpAuthorizationMatcher matcher(
+      IpAuthorizationMatcher::Type::kSourceIp,
+      Rbac::CidrRange(/*address_prefix=*/"1:3::", /*prefix_len=*/32));
+  EXPECT_FALSE(matcher.Matches(args));
+}
+
+TEST_F(AuthorizationMatchersTest,
+       IpAuthorizationMatcherUnsupportedIpFailedMatch) {
+  EvaluateArgs args = args_.MakeEvaluateArgs();
+  IpAuthorizationMatcher matcher(IpAuthorizationMatcher::Type::kRemoteIp, {});
+  EXPECT_FALSE(matcher.Matches(args));
+}
+
 TEST_F(AuthorizationMatchersTest, PortAuthorizationMatcherSuccessfulMatch) {
   args_.SetLocalEndpoint("ipv4:255.255.255.255:123");
   EvaluateArgs args = args_.MakeEvaluateArgs();
@@ -295,13 +315,6 @@ TEST_F(AuthorizationMatchersTest, PortAuthorizationMatcherFailedMatch) {
   EXPECT_FALSE(matcher.Matches(args));
 }
 
-TEST_F(AuthorizationMatchersTest, NotPortAuthorizationMatcher) {
-  args_.SetLocalEndpoint("ipv4:255.255.255.255:123");
-  EvaluateArgs args = args_.MakeEvaluateArgs();
-  PortAuthorizationMatcher matcher(/*port=*/123, /*not_rule=*/true);
-  EXPECT_FALSE(matcher.Matches(args));
-}
-
 TEST_F(AuthorizationMatchersTest,
        AuthenticatedMatcherUnAuthenticatedConnection) {
   EvaluateArgs args = args_.MakeEvaluateArgs();
@@ -326,12 +339,13 @@ TEST_F(AuthorizationMatchersTest,
   EXPECT_TRUE(matcher.Matches(args));
 }
 
-TEST_F(AuthorizationMatchersTest,
-       AuthenticatedMatcherSuccessfulSpiffeIdMatches) {
+TEST_F(AuthorizationMatchersTest, AuthenticatedMatcherSuccessfulUriSanMatches) {
   args_.AddPropertyToAuthContext(GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
                                  GRPC_SSL_TRANSPORT_SECURITY_TYPE);
-  args_.AddPropertyToAuthContext(GRPC_PEER_SPIFFE_ID_PROPERTY_NAME,
+  args_.AddPropertyToAuthContext(GRPC_PEER_URI_PROPERTY_NAME,
                                  "spiffe://foo.abc");
+  args_.AddPropertyToAuthContext(GRPC_PEER_URI_PROPERTY_NAME,
+                                 "https://foo.domain.com");
   EvaluateArgs args = args_.MakeEvaluateArgs();
   AuthenticatedAuthorizationMatcher matcher(
       StringMatcher::Create(StringMatcher::Type::kExact,
@@ -341,10 +355,10 @@ TEST_F(AuthorizationMatchersTest,
   EXPECT_TRUE(matcher.Matches(args));
 }
 
-TEST_F(AuthorizationMatchersTest, AuthenticatedMatcherFailedSpiffeIdMatches) {
+TEST_F(AuthorizationMatchersTest, AuthenticatedMatcherFailedUriSanMatches) {
   args_.AddPropertyToAuthContext(GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
                                  GRPC_SSL_TRANSPORT_SECURITY_TYPE);
-  args_.AddPropertyToAuthContext(GRPC_PEER_SPIFFE_ID_PROPERTY_NAME,
+  args_.AddPropertyToAuthContext(GRPC_PEER_URI_PROPERTY_NAME,
                                  "spiffe://bar.abc");
   EvaluateArgs args = args_.MakeEvaluateArgs();
   AuthenticatedAuthorizationMatcher matcher(
@@ -355,28 +369,49 @@ TEST_F(AuthorizationMatchersTest, AuthenticatedMatcherFailedSpiffeIdMatches) {
   EXPECT_FALSE(matcher.Matches(args));
 }
 
-TEST_F(AuthorizationMatchersTest, AuthenticatedMatcherFailedNothingMatches) {
+TEST_F(AuthorizationMatchersTest, AuthenticatedMatcherSuccessfulDnsSanMatches) {
   args_.AddPropertyToAuthContext(GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
                                  GRPC_SSL_TRANSPORT_SECURITY_TYPE);
+  args_.AddPropertyToAuthContext(GRPC_PEER_URI_PROPERTY_NAME,
+                                 "spiffe://bar.abc");
+  args_.AddPropertyToAuthContext(GRPC_PEER_DNS_PROPERTY_NAME,
+                                 "foo.test.domain.com");
+  args_.AddPropertyToAuthContext(GRPC_PEER_DNS_PROPERTY_NAME,
+                                 "bar.test.domain.com");
   EvaluateArgs args = args_.MakeEvaluateArgs();
+  // No match found in URI SANs, finds match in DNS SANs.
   AuthenticatedAuthorizationMatcher matcher(
       StringMatcher::Create(StringMatcher::Type::kExact,
-                            /*matcher=*/"foo",
+                            /*matcher=*/"bar.test.domain.com",
+                            /*case_sensitive=*/false)
+          .value());
+  EXPECT_TRUE(matcher.Matches(args));
+}
+
+TEST_F(AuthorizationMatchersTest, AuthenticatedMatcherFailedDnsSanMatches) {
+  args_.AddPropertyToAuthContext(GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
+                                 GRPC_SSL_TRANSPORT_SECURITY_TYPE);
+  args_.AddPropertyToAuthContext(GRPC_PEER_DNS_PROPERTY_NAME,
+                                 "foo.test.domain.com");
+  EvaluateArgs args = args_.MakeEvaluateArgs();
+  AuthenticatedAuthorizationMatcher matcher(
+      StringMatcher::Create(StringMatcher::Type::kExact,
+                            /*matcher=*/"bar.test.domain.com",
                             /*case_sensitive=*/false)
           .value());
   EXPECT_FALSE(matcher.Matches(args));
 }
 
-TEST_F(AuthorizationMatchersTest, NotAuthenticatedMatcher) {
+TEST_F(AuthorizationMatchersTest, AuthenticatedMatcherFailedNothingMatches) {
   args_.AddPropertyToAuthContext(GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
                                  GRPC_SSL_TRANSPORT_SECURITY_TYPE);
   EvaluateArgs args = args_.MakeEvaluateArgs();
   AuthenticatedAuthorizationMatcher matcher(
-      StringMatcher::Create(StringMatcher::Type::kExact, /*matcher=*/"foo",
+      StringMatcher::Create(StringMatcher::Type::kExact,
+                            /*matcher=*/"foo",
                             /*case_sensitive=*/false)
-          .value(),
-      /*not_rule=*/true);
-  EXPECT_TRUE(matcher.Matches(args));
+          .value());
+  EXPECT_FALSE(matcher.Matches(args));
 }
 
 TEST_F(AuthorizationMatchersTest, PolicyAuthorizationMatcherSuccessfulMatch) {
index de98537..c0d0a9e 100644 (file)
@@ -17,6 +17,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "src/core/lib/address_utils/sockaddr_utils.h"
 #include "src/core/lib/security/authorization/evaluate_args.h"
 #include "test/core/util/evaluate_args_test_util.h"
 #include "test/core/util/test_config.h"
@@ -75,38 +76,32 @@ TEST_F(EvaluateArgsTest, GetHeaderValueSuccess) {
   EXPECT_EQ(value.value(), "value123");
 }
 
-TEST_F(EvaluateArgsTest, TestIpv4LocalAddressAndPort) {
-  util_.SetLocalEndpoint("ipv4:255.255.255.255:123");
-  EvaluateArgs args = util_.MakeEvaluateArgs();
-  EXPECT_EQ(args.GetLocalAddress(), "255.255.255.255");
-  EXPECT_EQ(args.GetLocalPort(), 123);
-}
-
-TEST_F(EvaluateArgsTest, TestIpv4PeerAddressAndPort) {
-  util_.SetPeerEndpoint("ipv4:128.128.128.128:321");
-  EvaluateArgs args = util_.MakeEvaluateArgs();
-  EXPECT_EQ(args.GetPeerAddress(), "128.128.128.128");
-  EXPECT_EQ(args.GetPeerPort(), 321);
-}
-
-TEST_F(EvaluateArgsTest, TestIpv6LocalAddressAndPort) {
+TEST_F(EvaluateArgsTest, TestLocalAddressAndPort) {
   util_.SetLocalEndpoint("ipv6:[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:456");
   EvaluateArgs args = util_.MakeEvaluateArgs();
-  EXPECT_EQ(args.GetLocalAddress(), "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+  grpc_resolved_address local_address = args.GetLocalAddress();
+  EXPECT_EQ(grpc_sockaddr_to_uri(&local_address),
+            "ipv6:[2001:db8:85a3::8a2e:370:7334]:456");
+  EXPECT_EQ(args.GetLocalAddressString(),
+            "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
   EXPECT_EQ(args.GetLocalPort(), 456);
 }
 
-TEST_F(EvaluateArgsTest, TestIpv6PeerAddressAndPort) {
-  util_.SetPeerEndpoint("ipv6:[2001:db8::1]:654");
+TEST_F(EvaluateArgsTest, TestPeerAddressAndPort) {
+  util_.SetPeerEndpoint("ipv4:255.255.255.255:123");
   EvaluateArgs args = util_.MakeEvaluateArgs();
-  EXPECT_EQ(args.GetPeerAddress(), "2001:db8::1");
-  EXPECT_EQ(args.GetPeerPort(), 654);
+  grpc_resolved_address peer_address = args.GetPeerAddress();
+  EXPECT_EQ(grpc_sockaddr_to_uri(&peer_address), "ipv4:255.255.255.255:123");
+  EXPECT_EQ(args.GetPeerAddressString(), "255.255.255.255");
+  EXPECT_EQ(args.GetPeerPort(), 123);
 }
 
 TEST_F(EvaluateArgsTest, EmptyAuthContext) {
   EvaluateArgs args = util_.MakeEvaluateArgs();
   EXPECT_TRUE(args.GetTransportSecurityType().empty());
   EXPECT_TRUE(args.GetSpiffeId().empty());
+  EXPECT_TRUE(args.GetUriSans().empty());
+  EXPECT_TRUE(args.GetDnsSans().empty());
   EXPECT_TRUE(args.GetCommonName().empty());
 }
 
@@ -139,6 +134,20 @@ TEST_F(EvaluateArgsTest, GetSpiffeIdFailDuplicateProperty) {
   EXPECT_TRUE(args.GetSpiffeId().empty());
 }
 
+TEST_F(EvaluateArgsTest, GetUriSanSuccessMultipleProperties) {
+  util_.AddPropertyToAuthContext(GRPC_PEER_URI_PROPERTY_NAME, "foo");
+  util_.AddPropertyToAuthContext(GRPC_PEER_URI_PROPERTY_NAME, "bar");
+  EvaluateArgs args = util_.MakeEvaluateArgs();
+  EXPECT_THAT(args.GetUriSans(), ::testing::ElementsAre("foo", "bar"));
+}
+
+TEST_F(EvaluateArgsTest, GetDnsSanSuccessMultipleProperties) {
+  util_.AddPropertyToAuthContext(GRPC_PEER_DNS_PROPERTY_NAME, "foo");
+  util_.AddPropertyToAuthContext(GRPC_PEER_DNS_PROPERTY_NAME, "bar");
+  EvaluateArgs args = util_.MakeEvaluateArgs();
+  EXPECT_THAT(args.GetDnsSans(), ::testing::ElementsAre("foo", "bar"));
+}
+
 TEST_F(EvaluateArgsTest, GetCommonNameSuccessOneProperty) {
   util_.AddPropertyToAuthContext(GRPC_X509_CN_PROPERTY_NAME, "server123");
   EvaluateArgs args = util_.MakeEvaluateArgs();
index a2b5e11..e6dd4ad 100644 (file)
@@ -23,8 +23,10 @@ namespace grpc_core {
 
 TEST(GrpcAuthorizationEngineTest, AllowEngineWithMatchingPolicy) {
   Rbac::Policy policy1(
-      Rbac::Permission(Rbac::Permission::RuleType::kAny, /*not_rule=*/true),
-      Rbac::Principal(Rbac::Principal::RuleType::kAny, /*not_rule=*/true));
+      Rbac::Permission(Rbac::Permission::RuleType::kNot,
+                       Rbac::Permission(Rbac::Permission::RuleType::kAny)),
+      Rbac::Principal(Rbac::Principal::RuleType::kNot,
+                      Rbac::Principal(Rbac::Principal::RuleType::kAny)));
   Rbac::Policy policy2((Rbac::Permission(Rbac::Permission::RuleType::kAny)),
                        (Rbac::Principal(Rbac::Principal::RuleType::kAny)));
   std::map<std::string, Rbac::Policy> policies;
@@ -40,8 +42,10 @@ TEST(GrpcAuthorizationEngineTest, AllowEngineWithMatchingPolicy) {
 
 TEST(GrpcAuthorizationEngineTest, AllowEngineWithNoMatchingPolicy) {
   Rbac::Policy policy1(
-      Rbac::Permission(Rbac::Permission::RuleType::kAny, /*not_rule=*/true),
-      Rbac::Principal(Rbac::Principal::RuleType::kAny, /*not_rule=*/true));
+      Rbac::Permission(Rbac::Permission::RuleType::kNot,
+                       Rbac::Permission(Rbac::Permission::RuleType::kAny)),
+      Rbac::Principal(Rbac::Principal::RuleType::kNot,
+                      Rbac::Principal(Rbac::Principal::RuleType::kAny)));
   std::map<std::string, Rbac::Policy> policies;
   policies["policy1"] = std::move(policy1);
   Rbac rbac(Rbac::Action::kAllow, std::move(policies));
@@ -62,8 +66,10 @@ TEST(GrpcAuthorizationEngineTest, AllowEngineWithEmptyPolicies) {
 
 TEST(GrpcAuthorizationEngineTest, DenyEngineWithMatchingPolicy) {
   Rbac::Policy policy1(
-      Rbac::Permission(Rbac::Permission::RuleType::kAny, /*not_rule=*/true),
-      Rbac::Principal(Rbac::Principal::RuleType::kAny, /*not_rule=*/true));
+      Rbac::Permission(Rbac::Permission::RuleType::kNot,
+                       Rbac::Permission(Rbac::Permission::RuleType::kAny)),
+      Rbac::Principal(Rbac::Principal::RuleType::kNot,
+                      Rbac::Principal(Rbac::Principal::RuleType::kAny)));
   Rbac::Policy policy2((Rbac::Permission(Rbac::Permission::RuleType::kAny)),
                        (Rbac::Principal(Rbac::Principal::RuleType::kAny)));
   std::map<std::string, Rbac::Policy> policies;
@@ -79,8 +85,10 @@ TEST(GrpcAuthorizationEngineTest, DenyEngineWithMatchingPolicy) {
 
 TEST(GrpcAuthorizationEngineTest, DenyEngineWithNoMatchingPolicy) {
   Rbac::Policy policy1(
-      Rbac::Permission(Rbac::Permission::RuleType::kAny, /*not_rule=*/true),
-      Rbac::Principal(Rbac::Principal::RuleType::kAny, /*not_rule=*/true));
+      Rbac::Permission(Rbac::Permission::RuleType::kNot,
+                       Rbac::Permission(Rbac::Permission::RuleType::kAny)),
+      Rbac::Principal(Rbac::Principal::RuleType::kNot,
+                      Rbac::Principal(Rbac::Principal::RuleType::kAny)));
   std::map<std::string, Rbac::Policy> policies;
   policies["policy1"] = std::move(policy1);
   Rbac rbac(Rbac::Action::kDeny, std::move(policies));
diff --git a/test/core/security/grpc_authorization_policy_provider_test.cc b/test/core/security/grpc_authorization_policy_provider_test.cc
new file mode 100644 (file)
index 0000000..40f008d
--- /dev/null
@@ -0,0 +1,62 @@
+// 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 <grpc/support/port_platform.h>
+
+#include <gmock/gmock.h>
+#include <grpc/grpc_security.h>
+#include <gtest/gtest.h>
+
+#include "src/core/lib/security/authorization/grpc_authorization_engine.h"
+#include "src/core/lib/security/authorization/grpc_authorization_policy_provider.h"
+#include "test/core/util/test_config.h"
+
+namespace grpc_core {
+
+TEST(AuthorizationPolicyProviderTest, StaticDataInitializationSuccessful) {
+  const char* authz_policy =
+      "{"
+      "  \"name\": \"authz\","
+      "  \"allow_rules\": ["
+      "    {"
+      "      \"name\": \"allow_policy\""
+      "    }"
+      "  ]"
+      "}";
+  auto provider = StaticDataAuthorizationPolicyProvider::Create(authz_policy);
+  ASSERT_TRUE(provider.ok());
+  auto* allow_engine =
+      dynamic_cast<GrpcAuthorizationEngine*>((*provider)->allow_engine().get());
+  ASSERT_NE(allow_engine, nullptr);
+  EXPECT_EQ(allow_engine->action(), Rbac::Action::kAllow);
+  auto* deny_engine =
+      dynamic_cast<GrpcAuthorizationEngine*>((*provider)->deny_engine().get());
+  ASSERT_NE(deny_engine, nullptr);
+  EXPECT_EQ(deny_engine->action(), Rbac::Action::kDeny);
+}
+
+TEST(AuthorizationPolicyProviderTest,
+     StaticDataInitializationFailedInvalidPolicy) {
+  const char* authz_policy = "{}";
+  auto provider = StaticDataAuthorizationPolicyProvider::Create(authz_policy);
+  EXPECT_EQ(provider.status().code(), absl::StatusCode::kInvalidArgument);
+  EXPECT_EQ(provider.status().message(), "\"name\" field is not present.");
+}
+
+}  // namespace grpc_core
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index da7f2f0..5e9bf0a 100644 (file)
@@ -487,6 +487,23 @@ static void test_dns_peer_to_auth_context(void) {
   ctx.reset(DEBUG_LOCATION, "test");
 }
 
+static void test_uri_peer_to_auth_context(void) {
+  tsi_peer peer;
+  const std::vector<std::string> expected_uri = {"uri1", "uri2", "uri3"};
+  GPR_ASSERT(tsi_construct_peer(expected_uri.size(), &peer) == TSI_OK);
+  for (size_t i = 0; i < expected_uri.size(); ++i) {
+    GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
+                   TSI_X509_URI_PEER_PROPERTY, expected_uri[i].c_str(),
+                   &peer.properties[i]) == TSI_OK);
+  }
+  grpc_core::RefCountedPtr<grpc_auth_context> ctx =
+      grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE);
+  GPR_ASSERT(ctx != nullptr);
+  GPR_ASSERT(check_sans(ctx.get(), GRPC_PEER_URI_PROPERTY_NAME, expected_uri));
+  tsi_peer_destruct(&peer);
+  ctx.reset(DEBUG_LOCATION, "test");
+}
+
 static void test_email_peer_to_auth_context(void) {
   tsi_peer peer;
   const std::vector<std::string> expected_emails = {"email1", "email2"};
@@ -753,6 +770,7 @@ int main(int argc, char** argv) {
   test_cn_and_multiple_sans_ssl_peer_to_auth_context();
   test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context();
   test_dns_peer_to_auth_context();
+  test_uri_peer_to_auth_context();
   test_email_peer_to_auth_context();
   test_ip_peer_to_auth_context();
   test_spiffe_id_peer_to_auth_context();
index f207c0e..e3b5165 100644 (file)
@@ -123,6 +123,7 @@ grpc_cc_test(
     deps = [
         "//:gpr",
         "//:grpc",
+        "//:grpc_authorization_provider",
         "//test/core/util:grpc_test_util",
     ],
 )
index cbf536f..e3001c7 100644 (file)
@@ -378,14 +378,14 @@ static void test_callback(void) {
   LOG_TEST("test_callback");
 
   bool got_shutdown = false;
-  class ShutdownCallback : public grpc_experimental_completion_queue_functor {
+  class ShutdownCallback : public grpc_completion_queue_functor {
    public:
     explicit ShutdownCallback(bool* done) : done_(done) {
       functor_run = &ShutdownCallback::Run;
       inlineable = false;
     }
     ~ShutdownCallback() {}
-    static void Run(grpc_experimental_completion_queue_functor* cb, int ok) {
+    static void Run(grpc_completion_queue_functor* cb, int ok) {
       gpr_mu_lock(&shutdown_mu);
       *static_cast<ShutdownCallback*>(cb)->done_ = static_cast<bool>(ok);
       // Signal when the shutdown callback is completed.
@@ -413,7 +413,7 @@ static void test_callback(void) {
       cc = grpc_completion_queue_create(
           grpc_completion_queue_factory_lookup(&attr), &attr, nullptr);
 
-      class TagCallback : public grpc_experimental_completion_queue_functor {
+      class TagCallback : public grpc_completion_queue_functor {
        public:
         TagCallback(int* counter, int tag) : counter_(counter), tag_(tag) {
           functor_run = &TagCallback::Run;
@@ -421,8 +421,7 @@ static void test_callback(void) {
           inlineable = false;
         }
         ~TagCallback() {}
-        static void Run(grpc_experimental_completion_queue_functor* cb,
-                        int ok) {
+        static void Run(grpc_completion_queue_functor* cb, int ok) {
           GPR_ASSERT(static_cast<bool>(ok));
           auto* callback = static_cast<TagCallback*>(cb);
           gpr_mu_lock(&mu);
index 6651afb..36ccd2e 100644 (file)
@@ -153,6 +153,7 @@ int main(int argc, char **argv) {
   printf("%lx", (unsigned long) grpc_channelz_get_channel);
   printf("%lx", (unsigned long) grpc_channelz_get_subchannel);
   printf("%lx", (unsigned long) grpc_channelz_get_socket);
+  printf("%lx", (unsigned long) grpc_authorization_policy_provider_arg_vtable);
   printf("%lx", (unsigned long) grpc_auth_property_iterator_next);
   printf("%lx", (unsigned long) grpc_auth_context_property_iterator);
   printf("%lx", (unsigned long) grpc_auth_context_peer_identity);
@@ -226,6 +227,8 @@ int main(int argc, char **argv) {
   printf("%lx", (unsigned long) grpc_tls_server_authorization_check_config_release);
   printf("%lx", (unsigned long) grpc_xds_credentials_create);
   printf("%lx", (unsigned long) grpc_xds_server_credentials_create);
+  printf("%lx", (unsigned long) grpc_authorization_policy_provider_static_data_create);
+  printf("%lx", (unsigned long) grpc_authorization_policy_provider_release);
   printf("%lx", (unsigned long) grpc_raw_byte_buffer_create);
   printf("%lx", (unsigned long) grpc_raw_compressed_byte_buffer_create);
   printf("%lx", (unsigned long) grpc_byte_buffer_copy);
index 6662d78..b509a93 100644 (file)
@@ -27,7 +27,6 @@
 #include "src/proto/grpc/testing/compiler_test.pb.h"
 
 #include <functional>
-#include <grpc/impl/codegen/port_platform.h>
 #include <grpcpp/impl/codegen/async_generic_service.h>
 #include <grpcpp/impl/codegen/async_stream.h>
 #include <grpcpp/impl/codegen/async_unary_call.h>
@@ -107,49 +106,29 @@ class ServiceA final {
       return std::unique_ptr< ::grpc::ClientAsyncReaderWriterInterface< ::grpc::testing::Request, ::grpc::testing::Response>>(PrepareAsyncMethodA4Raw(context, cq));
     }
     // Method A4 trailing comment 1
-    class experimental_async_interface {
+    class async_interface {
      public:
-      virtual ~experimental_async_interface() {}
+      virtual ~async_interface() {}
       // MethodA1 leading comment 1
       virtual void MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, std::function<void(::grpc::Status)>) = 0;
-      #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
       virtual void MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::ClientUnaryReactor* reactor) = 0;
-      #else
-      virtual void MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0;
-      #endif
       // MethodA1 trailing comment 1
       // MethodA2 detached leading comment 1
       //
       // Method A2 leading comment 1
       // Method A2 leading comment 2
-      #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
       virtual void MethodA2(::grpc::ClientContext* context, ::grpc::testing::Response* response, ::grpc::ClientWriteReactor< ::grpc::testing::Request>* reactor) = 0;
-      #else
-      virtual void MethodA2(::grpc::ClientContext* context, ::grpc::testing::Response* response, ::grpc::experimental::ClientWriteReactor< ::grpc::testing::Request>* reactor) = 0;
-      #endif
       // MethodA2 trailing comment 1
       // Method A3 leading comment 1
-      #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
       virtual void MethodA3(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::ClientReadReactor< ::grpc::testing::Response>* reactor) = 0;
-      #else
-      virtual void MethodA3(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::experimental::ClientReadReactor< ::grpc::testing::Response>* reactor) = 0;
-      #endif
       // Method A3 trailing comment 1
       // Method A4 leading comment 1
-      #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
       virtual void MethodA4(::grpc::ClientContext* context, ::grpc::ClientBidiReactor< ::grpc::testing::Request,::grpc::testing::Response>* reactor) = 0;
-      #else
-      virtual void MethodA4(::grpc::ClientContext* context, ::grpc::experimental::ClientBidiReactor< ::grpc::testing::Request,::grpc::testing::Response>* reactor) = 0;
-      #endif
       // Method A4 trailing comment 1
     };
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-    typedef class experimental_async_interface async_interface;
-    #endif
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-    async_interface* async() { return experimental_async(); }
-    #endif
-    virtual class experimental_async_interface* experimental_async() { return nullptr; }
+    typedef class async_interface experimental_async_interface;
+    virtual class async_interface* async() { return nullptr; }
+    class async_interface* experimental_async() { return async(); }
   private:
     virtual ::grpc::ClientAsyncResponseReaderInterface< ::grpc::testing::Response>* AsyncMethodA1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) = 0;
     virtual ::grpc::ClientAsyncResponseReaderInterface< ::grpc::testing::Response>* PrepareAsyncMethodA1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) = 0;
@@ -200,41 +179,25 @@ class ServiceA final {
     std::unique_ptr<  ::grpc::ClientAsyncReaderWriter< ::grpc::testing::Request, ::grpc::testing::Response>> PrepareAsyncMethodA4(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq) {
       return std::unique_ptr< ::grpc::ClientAsyncReaderWriter< ::grpc::testing::Request, ::grpc::testing::Response>>(PrepareAsyncMethodA4Raw(context, cq));
     }
-    class experimental_async final :
-      public StubInterface::experimental_async_interface {
+    class async final :
+      public StubInterface::async_interface {
      public:
       void MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, std::function<void(::grpc::Status)>) override;
-      #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
       void MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::ClientUnaryReactor* reactor) override;
-      #else
-      void MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::experimental::ClientUnaryReactor* reactor) override;
-      #endif
-      #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
       void MethodA2(::grpc::ClientContext* context, ::grpc::testing::Response* response, ::grpc::ClientWriteReactor< ::grpc::testing::Request>* reactor) override;
-      #else
-      void MethodA2(::grpc::ClientContext* context, ::grpc::testing::Response* response, ::grpc::experimental::ClientWriteReactor< ::grpc::testing::Request>* reactor) override;
-      #endif
-      #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
       void MethodA3(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::ClientReadReactor< ::grpc::testing::Response>* reactor) override;
-      #else
-      void MethodA3(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::experimental::ClientReadReactor< ::grpc::testing::Response>* reactor) override;
-      #endif
-      #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
       void MethodA4(::grpc::ClientContext* context, ::grpc::ClientBidiReactor< ::grpc::testing::Request,::grpc::testing::Response>* reactor) override;
-      #else
-      void MethodA4(::grpc::ClientContext* context, ::grpc::experimental::ClientBidiReactor< ::grpc::testing::Request,::grpc::testing::Response>* reactor) override;
-      #endif
      private:
       friend class Stub;
-      explicit experimental_async(Stub* stub): stub_(stub) { }
+      explicit async(Stub* stub): stub_(stub) { }
       Stub* stub() { return stub_; }
       Stub* stub_;
     };
-    class experimental_async_interface* experimental_async() override { return &async_stub_; }
+    class async* async() override { return &async_stub_; }
 
    private:
     std::shared_ptr< ::grpc::ChannelInterface> channel_;
-    class experimental_async async_stub_{this};
+    class async async_stub_{this};
     ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>* AsyncMethodA1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) override;
     ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>* PrepareAsyncMethodA1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) override;
     ::grpc::ClientWriter< ::grpc::testing::Request>* MethodA2Raw(::grpc::ClientContext* context, ::grpc::testing::Response* response) override;
@@ -355,36 +318,22 @@ class ServiceA final {
   };
   typedef WithAsyncMethod_MethodA1<WithAsyncMethod_MethodA2<WithAsyncMethod_MethodA3<WithAsyncMethod_MethodA4<Service > > > > AsyncService;
   template <class BaseClass>
-  class ExperimentalWithCallbackMethod_MethodA1 : public BaseClass {
+  class WithCallbackMethod_MethodA1 : public BaseClass {
    private:
     void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
    public:
-    ExperimentalWithCallbackMethod_MethodA1() {
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-      ::grpc::Service::
-    #else
-      ::grpc::Service::experimental().
-    #endif
-        MarkMethodCallback(0,
+    WithCallbackMethod_MethodA1() {
+      ::grpc::Service::MarkMethodCallback(0,
           new ::grpc::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>(
             [this](
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-                   ::grpc::CallbackServerContext*
-    #else
-                   ::grpc::experimental::CallbackServerContext*
-    #endif
-                     context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) { return this->MethodA1(context, request, response); }));}
+                   ::grpc::CallbackServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) { return this->MethodA1(context, request, response); }));}
     void SetMessageAllocatorFor_MethodA1(
-        ::grpc::experimental::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) {
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
+        ::grpc::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) {
       ::grpc::internal::MethodHandler* const handler = ::grpc::Service::GetHandler(0);
-    #else
-      ::grpc::internal::MethodHandler* const handler = ::grpc::Service::experimental().GetHandler(0);
-    #endif
       static_cast<::grpc::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>(handler)
               ->SetMessageAllocator(allocator);
     }
-    ~ExperimentalWithCallbackMethod_MethodA1() override {
+    ~WithCallbackMethod_MethodA1() override {
       BaseClassMustBeDerivedFromService(this);
     }
     // disable synchronous version of this method
@@ -392,37 +341,21 @@ class ServiceA final {
       abort();
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
     virtual ::grpc::ServerUnaryReactor* MethodA1(
-      ::grpc::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/, ::grpc::testing::Response* /*response*/)
-    #else
-    virtual ::grpc::experimental::ServerUnaryReactor* MethodA1(
-      ::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/, ::grpc::testing::Response* /*response*/)
-    #endif
-      { return nullptr; }
+      ::grpc::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/, ::grpc::testing::Response* /*response*/)  { return nullptr; }
   };
   template <class BaseClass>
-  class ExperimentalWithCallbackMethod_MethodA2 : public BaseClass {
+  class WithCallbackMethod_MethodA2 : public BaseClass {
    private:
     void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
    public:
-    ExperimentalWithCallbackMethod_MethodA2() {
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-      ::grpc::Service::
-    #else
-      ::grpc::Service::experimental().
-    #endif
-        MarkMethodCallback(1,
+    WithCallbackMethod_MethodA2() {
+      ::grpc::Service::MarkMethodCallback(1,
           new ::grpc::internal::CallbackClientStreamingHandler< ::grpc::testing::Request, ::grpc::testing::Response>(
             [this](
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-                   ::grpc::CallbackServerContext*
-    #else
-                   ::grpc::experimental::CallbackServerContext*
-    #endif
-                     context, ::grpc::testing::Response* response) { return this->MethodA2(context, response); }));
-    }
-    ~ExperimentalWithCallbackMethod_MethodA2() override {
+                   ::grpc::CallbackServerContext* context, ::grpc::testing::Response* response) { return this->MethodA2(context, response); }));
+    }
+    ~WithCallbackMethod_MethodA2() override {
       BaseClassMustBeDerivedFromService(this);
     }
     // disable synchronous version of this method
@@ -430,37 +363,21 @@ class ServiceA final {
       abort();
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
     virtual ::grpc::ServerReadReactor< ::grpc::testing::Request>* MethodA2(
-      ::grpc::CallbackServerContext* /*context*/, ::grpc::testing::Response* /*response*/)
-    #else
-    virtual ::grpc::experimental::ServerReadReactor< ::grpc::testing::Request>* MethodA2(
-      ::grpc::experimental::CallbackServerContext* /*context*/, ::grpc::testing::Response* /*response*/)
-    #endif
-      { return nullptr; }
+      ::grpc::CallbackServerContext* /*context*/, ::grpc::testing::Response* /*response*/)  { return nullptr; }
   };
   template <class BaseClass>
-  class ExperimentalWithCallbackMethod_MethodA3 : public BaseClass {
+  class WithCallbackMethod_MethodA3 : public BaseClass {
    private:
     void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
    public:
-    ExperimentalWithCallbackMethod_MethodA3() {
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-      ::grpc::Service::
-    #else
-      ::grpc::Service::experimental().
-    #endif
-        MarkMethodCallback(2,
+    WithCallbackMethod_MethodA3() {
+      ::grpc::Service::MarkMethodCallback(2,
           new ::grpc::internal::CallbackServerStreamingHandler< ::grpc::testing::Request, ::grpc::testing::Response>(
             [this](
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-                   ::grpc::CallbackServerContext*
-    #else
-                   ::grpc::experimental::CallbackServerContext*
-    #endif
-                     context, const ::grpc::testing::Request* request) { return this->MethodA3(context, request); }));
-    }
-    ~ExperimentalWithCallbackMethod_MethodA3() override {
+                   ::grpc::CallbackServerContext* context, const ::grpc::testing::Request* request) { return this->MethodA3(context, request); }));
+    }
+    ~WithCallbackMethod_MethodA3() override {
       BaseClassMustBeDerivedFromService(this);
     }
     // disable synchronous version of this method
@@ -468,37 +385,21 @@ class ServiceA final {
       abort();
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
     virtual ::grpc::ServerWriteReactor< ::grpc::testing::Response>* MethodA3(
-      ::grpc::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/)
-    #else
-    virtual ::grpc::experimental::ServerWriteReactor< ::grpc::testing::Response>* MethodA3(
-      ::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/)
-    #endif
-      { return nullptr; }
+      ::grpc::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/)  { return nullptr; }
   };
   template <class BaseClass>
-  class ExperimentalWithCallbackMethod_MethodA4 : public BaseClass {
+  class WithCallbackMethod_MethodA4 : public BaseClass {
    private:
     void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
    public:
-    ExperimentalWithCallbackMethod_MethodA4() {
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-      ::grpc::Service::
-    #else
-      ::grpc::Service::experimental().
-    #endif
-        MarkMethodCallback(3,
+    WithCallbackMethod_MethodA4() {
+      ::grpc::Service::MarkMethodCallback(3,
           new ::grpc::internal::CallbackBidiHandler< ::grpc::testing::Request, ::grpc::testing::Response>(
             [this](
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-                   ::grpc::CallbackServerContext*
-    #else
-                   ::grpc::experimental::CallbackServerContext*
-    #endif
-                     context) { return this->MethodA4(context); }));
-    }
-    ~ExperimentalWithCallbackMethod_MethodA4() override {
+                   ::grpc::CallbackServerContext* context) { return this->MethodA4(context); }));
+    }
+    ~WithCallbackMethod_MethodA4() override {
       BaseClassMustBeDerivedFromService(this);
     }
     // disable synchronous version of this method
@@ -506,20 +407,12 @@ class ServiceA final {
       abort();
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
     virtual ::grpc::ServerBidiReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA4(
       ::grpc::CallbackServerContext* /*context*/)
-    #else
-    virtual ::grpc::experimental::ServerBidiReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA4(
-      ::grpc::experimental::CallbackServerContext* /*context*/)
-    #endif
       { return nullptr; }
   };
-  #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-  typedef ExperimentalWithCallbackMethod_MethodA1<ExperimentalWithCallbackMethod_MethodA2<ExperimentalWithCallbackMethod_MethodA3<ExperimentalWithCallbackMethod_MethodA4<Service > > > > CallbackService;
-  #endif
-
-  typedef ExperimentalWithCallbackMethod_MethodA1<ExperimentalWithCallbackMethod_MethodA2<ExperimentalWithCallbackMethod_MethodA3<ExperimentalWithCallbackMethod_MethodA4<Service > > > > ExperimentalCallbackService;
+  typedef WithCallbackMethod_MethodA1<WithCallbackMethod_MethodA2<WithCallbackMethod_MethodA3<WithCallbackMethod_MethodA4<Service > > > > CallbackService;
+  typedef CallbackService ExperimentalCallbackService;
   template <class BaseClass>
   class WithGenericMethod_MethodA1 : public BaseClass {
    private:
@@ -669,27 +562,17 @@ class ServiceA final {
     }
   };
   template <class BaseClass>
-  class ExperimentalWithRawCallbackMethod_MethodA1 : public BaseClass {
+  class WithRawCallbackMethod_MethodA1 : public BaseClass {
    private:
     void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
    public:
-    ExperimentalWithRawCallbackMethod_MethodA1() {
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-      ::grpc::Service::
-    #else
-      ::grpc::Service::experimental().
-    #endif
-        MarkMethodRawCallback(0,
+    WithRawCallbackMethod_MethodA1() {
+      ::grpc::Service::MarkMethodRawCallback(0,
           new ::grpc::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>(
             [this](
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-                   ::grpc::CallbackServerContext*
-    #else
-                   ::grpc::experimental::CallbackServerContext*
-    #endif
-                     context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response) { return this->MethodA1(context, request, response); }));
-    }
-    ~ExperimentalWithRawCallbackMethod_MethodA1() override {
+                   ::grpc::CallbackServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response) { return this->MethodA1(context, request, response); }));
+    }
+    ~WithRawCallbackMethod_MethodA1() override {
       BaseClassMustBeDerivedFromService(this);
     }
     // disable synchronous version of this method
@@ -697,37 +580,21 @@ class ServiceA final {
       abort();
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
     virtual ::grpc::ServerUnaryReactor* MethodA1(
-      ::grpc::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/)
-    #else
-    virtual ::grpc::experimental::ServerUnaryReactor* MethodA1(
-      ::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/)
-    #endif
-      { return nullptr; }
+      ::grpc::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/)  { return nullptr; }
   };
   template <class BaseClass>
-  class ExperimentalWithRawCallbackMethod_MethodA2 : public BaseClass {
+  class WithRawCallbackMethod_MethodA2 : public BaseClass {
    private:
     void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
    public:
-    ExperimentalWithRawCallbackMethod_MethodA2() {
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-      ::grpc::Service::
-    #else
-      ::grpc::Service::experimental().
-    #endif
-        MarkMethodRawCallback(1,
+    WithRawCallbackMethod_MethodA2() {
+      ::grpc::Service::MarkMethodRawCallback(1,
           new ::grpc::internal::CallbackClientStreamingHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>(
             [this](
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-                   ::grpc::CallbackServerContext*
-    #else
-                   ::grpc::experimental::CallbackServerContext*
-    #endif
-                     context, ::grpc::ByteBuffer* response) { return this->MethodA2(context, response); }));
-    }
-    ~ExperimentalWithRawCallbackMethod_MethodA2() override {
+                   ::grpc::CallbackServerContext* context, ::grpc::ByteBuffer* response) { return this->MethodA2(context, response); }));
+    }
+    ~WithRawCallbackMethod_MethodA2() override {
       BaseClassMustBeDerivedFromService(this);
     }
     // disable synchronous version of this method
@@ -735,37 +602,21 @@ class ServiceA final {
       abort();
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
     virtual ::grpc::ServerReadReactor< ::grpc::ByteBuffer>* MethodA2(
-      ::grpc::CallbackServerContext* /*context*/, ::grpc::ByteBuffer* /*response*/)
-    #else
-    virtual ::grpc::experimental::ServerReadReactor< ::grpc::ByteBuffer>* MethodA2(
-      ::grpc::experimental::CallbackServerContext* /*context*/, ::grpc::ByteBuffer* /*response*/)
-    #endif
-      { return nullptr; }
+      ::grpc::CallbackServerContext* /*context*/, ::grpc::ByteBuffer* /*response*/)  { return nullptr; }
   };
   template <class BaseClass>
-  class ExperimentalWithRawCallbackMethod_MethodA3 : public BaseClass {
+  class WithRawCallbackMethod_MethodA3 : public BaseClass {
    private:
     void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
    public:
-    ExperimentalWithRawCallbackMethod_MethodA3() {
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-      ::grpc::Service::
-    #else
-      ::grpc::Service::experimental().
-    #endif
-        MarkMethodRawCallback(2,
+    WithRawCallbackMethod_MethodA3() {
+      ::grpc::Service::MarkMethodRawCallback(2,
           new ::grpc::internal::CallbackServerStreamingHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>(
             [this](
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-                   ::grpc::CallbackServerContext*
-    #else
-                   ::grpc::experimental::CallbackServerContext*
-    #endif
-                     context, const::grpc::ByteBuffer* request) { return this->MethodA3(context, request); }));
-    }
-    ~ExperimentalWithRawCallbackMethod_MethodA3() override {
+                   ::grpc::CallbackServerContext* context, const::grpc::ByteBuffer* request) { return this->MethodA3(context, request); }));
+    }
+    ~WithRawCallbackMethod_MethodA3() override {
       BaseClassMustBeDerivedFromService(this);
     }
     // disable synchronous version of this method
@@ -773,37 +624,21 @@ class ServiceA final {
       abort();
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
     virtual ::grpc::ServerWriteReactor< ::grpc::ByteBuffer>* MethodA3(
-      ::grpc::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/)
-    #else
-    virtual ::grpc::experimental::ServerWriteReactor< ::grpc::ByteBuffer>* MethodA3(
-      ::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/)
-    #endif
-      { return nullptr; }
+      ::grpc::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/)  { return nullptr; }
   };
   template <class BaseClass>
-  class ExperimentalWithRawCallbackMethod_MethodA4 : public BaseClass {
+  class WithRawCallbackMethod_MethodA4 : public BaseClass {
    private:
     void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
    public:
-    ExperimentalWithRawCallbackMethod_MethodA4() {
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-      ::grpc::Service::
-    #else
-      ::grpc::Service::experimental().
-    #endif
-        MarkMethodRawCallback(3,
+    WithRawCallbackMethod_MethodA4() {
+      ::grpc::Service::MarkMethodRawCallback(3,
           new ::grpc::internal::CallbackBidiHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>(
             [this](
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-                   ::grpc::CallbackServerContext*
-    #else
-                   ::grpc::experimental::CallbackServerContext*
-    #endif
-                     context) { return this->MethodA4(context); }));
-    }
-    ~ExperimentalWithRawCallbackMethod_MethodA4() override {
+                   ::grpc::CallbackServerContext* context) { return this->MethodA4(context); }));
+    }
+    ~WithRawCallbackMethod_MethodA4() override {
       BaseClassMustBeDerivedFromService(this);
     }
     // disable synchronous version of this method
@@ -811,13 +646,8 @@ class ServiceA final {
       abort();
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
     virtual ::grpc::ServerBidiReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA4(
       ::grpc::CallbackServerContext* /*context*/)
-    #else
-    virtual ::grpc::experimental::ServerBidiReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA4(
-      ::grpc::experimental::CallbackServerContext* /*context*/)
-    #endif
       { return nullptr; }
   };
   template <class BaseClass>
@@ -897,25 +727,17 @@ class ServiceB final {
       return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::grpc::testing::Response>>(PrepareAsyncMethodB1Raw(context, request, cq));
     }
     // MethodB1 trailing comment 1
-    class experimental_async_interface {
+    class async_interface {
      public:
-      virtual ~experimental_async_interface() {}
+      virtual ~async_interface() {}
       // MethodB1 leading comment 1
       virtual void MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, std::function<void(::grpc::Status)>) = 0;
-      #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
       virtual void MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::ClientUnaryReactor* reactor) = 0;
-      #else
-      virtual void MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0;
-      #endif
       // MethodB1 trailing comment 1
     };
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-    typedef class experimental_async_interface async_interface;
-    #endif
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-    async_interface* async() { return experimental_async(); }
-    #endif
-    virtual class experimental_async_interface* experimental_async() { return nullptr; }
+    typedef class async_interface experimental_async_interface;
+    virtual class async_interface* async() { return nullptr; }
+    class async_interface* experimental_async() { return async(); }
   private:
     virtual ::grpc::ClientAsyncResponseReaderInterface< ::grpc::testing::Response>* AsyncMethodB1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) = 0;
     virtual ::grpc::ClientAsyncResponseReaderInterface< ::grpc::testing::Response>* PrepareAsyncMethodB1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) = 0;
@@ -930,26 +752,22 @@ class ServiceB final {
     std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>> PrepareAsyncMethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) {
       return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>>(PrepareAsyncMethodB1Raw(context, request, cq));
     }
-    class experimental_async final :
-      public StubInterface::experimental_async_interface {
+    class async final :
+      public StubInterface::async_interface {
      public:
       void MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, std::function<void(::grpc::Status)>) override;
-      #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
       void MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::ClientUnaryReactor* reactor) override;
-      #else
-      void MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::experimental::ClientUnaryReactor* reactor) override;
-      #endif
      private:
       friend class Stub;
-      explicit experimental_async(Stub* stub): stub_(stub) { }
+      explicit async(Stub* stub): stub_(stub) { }
       Stub* stub() { return stub_; }
       Stub* stub_;
     };
-    class experimental_async_interface* experimental_async() override { return &async_stub_; }
+    class async* async() override { return &async_stub_; }
 
    private:
     std::shared_ptr< ::grpc::ChannelInterface> channel_;
-    class experimental_async async_stub_{this};
+    class async async_stub_{this};
     ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>* AsyncMethodB1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) override;
     ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>* PrepareAsyncMethodB1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) override;
     const ::grpc::internal::RpcMethod rpcmethod_MethodB1_;
@@ -986,36 +804,22 @@ class ServiceB final {
   };
   typedef WithAsyncMethod_MethodB1<Service > AsyncService;
   template <class BaseClass>
-  class ExperimentalWithCallbackMethod_MethodB1 : public BaseClass {
+  class WithCallbackMethod_MethodB1 : public BaseClass {
    private:
     void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
    public:
-    ExperimentalWithCallbackMethod_MethodB1() {
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-      ::grpc::Service::
-    #else
-      ::grpc::Service::experimental().
-    #endif
-        MarkMethodCallback(0,
+    WithCallbackMethod_MethodB1() {
+      ::grpc::Service::MarkMethodCallback(0,
           new ::grpc::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>(
             [this](
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-                   ::grpc::CallbackServerContext*
-    #else
-                   ::grpc::experimental::CallbackServerContext*
-    #endif
-                     context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) { return this->MethodB1(context, request, response); }));}
+                   ::grpc::CallbackServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) { return this->MethodB1(context, request, response); }));}
     void SetMessageAllocatorFor_MethodB1(
-        ::grpc::experimental::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) {
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
+        ::grpc::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) {
       ::grpc::internal::MethodHandler* const handler = ::grpc::Service::GetHandler(0);
-    #else
-      ::grpc::internal::MethodHandler* const handler = ::grpc::Service::experimental().GetHandler(0);
-    #endif
       static_cast<::grpc::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>(handler)
               ->SetMessageAllocator(allocator);
     }
-    ~ExperimentalWithCallbackMethod_MethodB1() override {
+    ~WithCallbackMethod_MethodB1() override {
       BaseClassMustBeDerivedFromService(this);
     }
     // disable synchronous version of this method
@@ -1023,20 +827,11 @@ class ServiceB final {
       abort();
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
     virtual ::grpc::ServerUnaryReactor* MethodB1(
-      ::grpc::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/, ::grpc::testing::Response* /*response*/)
-    #else
-    virtual ::grpc::experimental::ServerUnaryReactor* MethodB1(
-      ::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/, ::grpc::testing::Response* /*response*/)
-    #endif
-      { return nullptr; }
+      ::grpc::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/, ::grpc::testing::Response* /*response*/)  { return nullptr; }
   };
-  #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-  typedef ExperimentalWithCallbackMethod_MethodB1<Service > CallbackService;
-  #endif
-
-  typedef ExperimentalWithCallbackMethod_MethodB1<Service > ExperimentalCallbackService;
+  typedef WithCallbackMethod_MethodB1<Service > CallbackService;
+  typedef CallbackService ExperimentalCallbackService;
   template <class BaseClass>
   class WithGenericMethod_MethodB1 : public BaseClass {
    private:
@@ -1075,27 +870,17 @@ class ServiceB final {
     }
   };
   template <class BaseClass>
-  class ExperimentalWithRawCallbackMethod_MethodB1 : public BaseClass {
+  class WithRawCallbackMethod_MethodB1 : public BaseClass {
    private:
     void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
    public:
-    ExperimentalWithRawCallbackMethod_MethodB1() {
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-      ::grpc::Service::
-    #else
-      ::grpc::Service::experimental().
-    #endif
-        MarkMethodRawCallback(0,
+    WithRawCallbackMethod_MethodB1() {
+      ::grpc::Service::MarkMethodRawCallback(0,
           new ::grpc::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>(
             [this](
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
-                   ::grpc::CallbackServerContext*
-    #else
-                   ::grpc::experimental::CallbackServerContext*
-    #endif
-                     context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response) { return this->MethodB1(context, request, response); }));
-    }
-    ~ExperimentalWithRawCallbackMethod_MethodB1() override {
+                   ::grpc::CallbackServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response) { return this->MethodB1(context, request, response); }));
+    }
+    ~WithRawCallbackMethod_MethodB1() override {
       BaseClassMustBeDerivedFromService(this);
     }
     // disable synchronous version of this method
@@ -1103,14 +888,8 @@ class ServiceB final {
       abort();
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
-    #ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
     virtual ::grpc::ServerUnaryReactor* MethodB1(
-      ::grpc::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/)
-    #else
-    virtual ::grpc::experimental::ServerUnaryReactor* MethodB1(
-      ::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/)
-    #endif
-      { return nullptr; }
+      ::grpc::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/)  { return nullptr; }
   };
   template <class BaseClass>
   class WithStreamedUnaryMethod_MethodB1 : public BaseClass {
index e0a3d15..6238caa 100644 (file)
@@ -95,13 +95,13 @@ TEST(AlarmTest, CallbackRegularExpiry) {
   Alarm alarm;
 
   auto c = std::make_shared<Completion>();
-  alarm.experimental().Set(
-      std::chrono::system_clock::now() + std::chrono::seconds(1), [c](bool ok) {
-        EXPECT_TRUE(ok);
-        std::lock_guard<std::mutex> l(c->mu);
-        c->completed = true;
-        c->cv.notify_one();
-      });
+  alarm.Set(std::chrono::system_clock::now() + std::chrono::seconds(1),
+            [c](bool ok) {
+              EXPECT_TRUE(ok);
+              std::lock_guard<std::mutex> l(c->mu);
+              c->completed = true;
+              c->cv.notify_one();
+            });
 
   std::unique_lock<std::mutex> l(c->mu);
   EXPECT_TRUE(c->cv.wait_until(
@@ -113,7 +113,7 @@ TEST(AlarmTest, CallbackZeroExpiry) {
   Alarm alarm;
 
   auto c = std::make_shared<Completion>();
-  alarm.experimental().Set(grpc_timeout_seconds_to_deadline(0), [c](bool ok) {
+  alarm.Set(grpc_timeout_seconds_to_deadline(0), [c](bool ok) {
     EXPECT_TRUE(ok);
     std::lock_guard<std::mutex> l(c->mu);
     c->completed = true;
@@ -130,14 +130,13 @@ TEST(AlarmTest, CallbackNegativeExpiry) {
   Alarm alarm;
 
   auto c = std::make_shared<Completion>();
-  alarm.experimental().Set(
-      std::chrono::system_clock::now() + std::chrono::seconds(-1),
-      [c](bool ok) {
-        EXPECT_TRUE(ok);
-        std::lock_guard<std::mutex> l(c->mu);
-        c->completed = true;
-        c->cv.notify_one();
-      });
+  alarm.Set(std::chrono::system_clock::now() + std::chrono::seconds(-1),
+            [c](bool ok) {
+              EXPECT_TRUE(ok);
+              std::lock_guard<std::mutex> l(c->mu);
+              c->completed = true;
+              c->cv.notify_one();
+            });
 
   std::unique_lock<std::mutex> l(c->mu);
   EXPECT_TRUE(c->cv.wait_until(
@@ -288,14 +287,13 @@ TEST(AlarmTest, CallbackCancellation) {
   Alarm alarm;
 
   auto c = std::make_shared<Completion>();
-  alarm.experimental().Set(
-      std::chrono::system_clock::now() + std::chrono::seconds(10),
-      [c](bool ok) {
-        EXPECT_FALSE(ok);
-        std::lock_guard<std::mutex> l(c->mu);
-        c->completed = true;
-        c->cv.notify_one();
-      });
+  alarm.Set(std::chrono::system_clock::now() + std::chrono::seconds(10),
+            [c](bool ok) {
+              EXPECT_FALSE(ok);
+              std::lock_guard<std::mutex> l(c->mu);
+              c->completed = true;
+              c->cv.notify_one();
+            });
   alarm.Cancel();
 
   std::unique_lock<std::mutex> l(c->mu);
@@ -308,14 +306,13 @@ TEST(AlarmTest, CallbackCancellationLocked) {
   Alarm alarm;
 
   auto c = std::make_shared<Completion>();
-  alarm.experimental().Set(
-      std::chrono::system_clock::now() + std::chrono::seconds(10),
-      [c](bool ok) {
-        EXPECT_FALSE(ok);
-        std::lock_guard<std::mutex> l(c->mu);
-        c->completed = true;
-        c->cv.notify_one();
-      });
+  alarm.Set(std::chrono::system_clock::now() + std::chrono::seconds(10),
+            [c](bool ok) {
+              EXPECT_FALSE(ok);
+              std::lock_guard<std::mutex> l(c->mu);
+              c->completed = true;
+              c->cv.notify_one();
+            });
   std::unique_lock<std::mutex> l(c->mu);
   alarm.Cancel();
 
@@ -346,14 +343,13 @@ TEST(AlarmTest, CallbackSetDestruction) {
   auto c = std::make_shared<Completion>();
   {
     Alarm alarm;
-    alarm.experimental().Set(
-        std::chrono::system_clock::now() + std::chrono::seconds(10),
-        [c](bool ok) {
-          EXPECT_FALSE(ok);
-          std::lock_guard<std::mutex> l(c->mu);
-          c->completed = true;
-          c->cv.notify_one();
-        });
+    alarm.Set(std::chrono::system_clock::now() + std::chrono::seconds(10),
+              [c](bool ok) {
+                EXPECT_FALSE(ok);
+                std::lock_guard<std::mutex> l(c->mu);
+                c->completed = true;
+                c->cv.notify_one();
+              });
   }
 
   std::unique_lock<std::mutex> l(c->mu);
index 53050a8..6765bb3 100644 (file)
@@ -62,7 +62,8 @@ void test_mutator_destroy(grpc_socket_mutator* mutator) {
 }
 
 grpc_socket_mutator_vtable test_mutator_vtable = {
-    test_mutator_mutate_fd, test_mutator_compare, test_mutator_destroy};
+    test_mutator_mutate_fd, test_mutator_compare, test_mutator_destroy,
+    nullptr};
 
 //
 // TestSocketMutator implementation
index 45f30eb..f4338e1 100644 (file)
@@ -191,7 +191,7 @@ class ClientCallbackEnd2endTest
       std::mutex mu;
       std::condition_variable cv;
       bool done = false;
-      stub_->experimental_async()->Echo(
+      stub_->async()->Echo(
           &cli_ctx, &request, &response,
           [&cli_ctx, &request, &response, &done, &mu, &cv, val,
            with_binary_metadata](Status s) {
@@ -234,7 +234,7 @@ class ClientCallbackEnd2endTest
       std::condition_variable cv;
       bool done = false;
       StubOptions options(suffix_for_stats);
-      generic_stub_->experimental().UnaryCall(
+      generic_stub_->UnaryCall(
           &cli_ctx, kMethodName, options, send_buf.get(), &recv_buf,
           [&request, &recv_buf, &done, &mu, &cv, maybe_except](Status s) {
             GPR_ASSERT(s.ok());
@@ -266,8 +266,7 @@ class ClientCallbackEnd2endTest
     std::string test_string("");
     for (int i = 0; i < num_rpcs; i++) {
       test_string += "Hello world. ";
-      class Client : public grpc::experimental::ClientBidiReactor<ByteBuffer,
-                                                                  ByteBuffer> {
+      class Client : public grpc::ClientBidiReactor<ByteBuffer, ByteBuffer> {
        public:
         Client(ClientCallbackEnd2endTest* test, const std::string& method_name,
                const char* suffix_for_stats, const std::string& test_str,
@@ -278,7 +277,7 @@ class ClientCallbackEnd2endTest
               cli_ctx_ = absl::make_unique<ClientContext>();
               reuses_remaining_--;
               StubOptions options(suffix_for_stats);
-              test->generic_stub_->experimental().PrepareBidiStreamingCall(
+              test->generic_stub_->PrepareBidiStreamingCall(
                   cli_ctx_.get(), method_name, options, this);
               request_.set_message(test_str);
               send_buf_ = SerializeToByteBuffer(&request_);
@@ -365,16 +364,16 @@ TEST_P(ClientCallbackEnd2endTest, SimpleRpcExpectedError) {
   std::condition_variable cv;
   bool done = false;
 
-  stub_->experimental_async()->Echo(
-      &cli_ctx, &request, &response,
-      [&response, &done, &mu, &cv, &error_status](Status s) {
-        EXPECT_EQ("", response.message());
-        EXPECT_EQ(error_status.code(), s.error_code());
-        EXPECT_EQ(error_status.error_message(), s.error_message());
-        std::lock_guard<std::mutex> l(mu);
-        done = true;
-        cv.notify_one();
-      });
+  stub_->async()->Echo(&cli_ctx, &request, &response,
+                       [&response, &done, &mu, &cv, &error_status](Status s) {
+                         EXPECT_EQ("", response.message());
+                         EXPECT_EQ(error_status.code(), s.error_code());
+                         EXPECT_EQ(error_status.error_message(),
+                                   s.error_message());
+                         std::lock_guard<std::mutex> l(mu);
+                         done = true;
+                         cv.notify_one();
+                       });
 
   std::unique_lock<std::mutex> l(mu);
   while (!done) {
@@ -415,19 +414,18 @@ TEST_P(ClientCallbackEnd2endTest, SimpleRpcUnderLockNested) {
   std::function<void(int)> nested_call = [this, &nested_call,
                                           &rpc_state](int index) {
     std::lock_guard<std::mutex> l(rpc_state[index].mu);
-    stub_->experimental_async()->Echo(
-        &rpc_state[index].cli_ctx, &rpc_state[index].request,
-        &rpc_state[index].response,
-        [index, &nested_call, &rpc_state](Status s) {
-          std::lock_guard<std::mutex> l1(rpc_state[index].mu);
-          EXPECT_TRUE(s.ok());
-          rpc_state[index].done = true;
-          rpc_state[index].cv.notify_all();
-          // Call the next level of nesting if possible
-          if (index + 1 < int(rpc_state.size())) {
-            nested_call(index + 1);
-          }
-        });
+    stub_->async()->Echo(&rpc_state[index].cli_ctx, &rpc_state[index].request,
+                         &rpc_state[index].response,
+                         [index, &nested_call, &rpc_state](Status s) {
+                           std::lock_guard<std::mutex> l1(rpc_state[index].mu);
+                           EXPECT_TRUE(s.ok());
+                           rpc_state[index].done = true;
+                           rpc_state[index].cv.notify_all();
+                           // Call the next level of nesting if possible
+                           if (index + 1 < int(rpc_state.size())) {
+                             nested_call(index + 1);
+                           }
+                         });
   };
 
   nested_call(0);
@@ -453,15 +451,14 @@ TEST_P(ClientCallbackEnd2endTest, SimpleRpcUnderLock) {
   ClientContext cli_ctx;
   {
     std::lock_guard<std::mutex> l(mu);
-    stub_->experimental_async()->Echo(
-        &cli_ctx, &request, &response,
-        [&mu, &cv, &done, &request, &response](Status s) {
-          std::lock_guard<std::mutex> l(mu);
-          EXPECT_TRUE(s.ok());
-          EXPECT_EQ(request.message(), response.message());
-          done = true;
-          cv.notify_one();
-        });
+    stub_->async()->Echo(&cli_ctx, &request, &response,
+                         [&mu, &cv, &done, &request, &response](Status s) {
+                           std::lock_guard<std::mutex> l(mu);
+                           EXPECT_TRUE(s.ok());
+                           EXPECT_EQ(request.message(), response.message());
+                           done = true;
+                           cv.notify_one();
+                         });
   }
   std::unique_lock<std::mutex> l(mu);
   while (!done) {
@@ -486,7 +483,7 @@ TEST_P(ClientCallbackEnd2endTest, SendClientInitialMetadata) {
   std::mutex mu;
   std::condition_variable cv;
   bool done = false;
-  stub_->experimental_async()->CheckClientInitialMetadata(
+  stub_->async()->CheckClientInitialMetadata(
       &cli_ctx, &request, &response, [&done, &mu, &cv](Status s) {
         GPR_ASSERT(s.ok());
 
@@ -589,14 +586,14 @@ TEST_P(ClientCallbackEnd2endTest, CancelRpcBeforeStart) {
   std::mutex mu;
   std::condition_variable cv;
   bool done = false;
-  stub_->experimental_async()->Echo(
-      &context, &request, &response, [&response, &done, &mu, &cv](Status s) {
-        EXPECT_EQ("", response.message());
-        EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
-        std::lock_guard<std::mutex> l(mu);
-        done = true;
-        cv.notify_one();
-      });
+  stub_->async()->Echo(&context, &request, &response,
+                       [&response, &done, &mu, &cv](Status s) {
+                         EXPECT_EQ("", response.message());
+                         EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
+                         std::lock_guard<std::mutex> l(mu);
+                         done = true;
+                         cv.notify_one();
+                       });
   std::unique_lock<std::mutex> l(mu);
   while (!done) {
     cv.wait(l);
@@ -618,14 +615,14 @@ TEST_P(ClientCallbackEnd2endTest, RequestEchoServerCancel) {
   std::mutex mu;
   std::condition_variable cv;
   bool done = false;
-  stub_->experimental_async()->Echo(
-      &context, &request, &response, [&done, &mu, &cv](Status s) {
-        EXPECT_FALSE(s.ok());
-        EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
-        std::lock_guard<std::mutex> l(mu);
-        done = true;
-        cv.notify_one();
-      });
+  stub_->async()->Echo(&context, &request, &response,
+                       [&done, &mu, &cv](Status s) {
+                         EXPECT_FALSE(s.ok());
+                         EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
+                         std::lock_guard<std::mutex> l(mu);
+                         done = true;
+                         cv.notify_one();
+                       });
   std::unique_lock<std::mutex> l(mu);
   while (!done) {
     cv.wait(l);
@@ -640,7 +637,7 @@ struct ClientCancelInfo {
   explicit ClientCancelInfo(int ops) : cancel{true}, ops_before_cancel{ops} {}
 };
 
-class WriteClient : public grpc::experimental::ClientWriteReactor<EchoRequest> {
+class WriteClient : public grpc::ClientWriteReactor<EchoRequest> {
  public:
   WriteClient(grpc::testing::EchoTestService::Stub* stub,
               ServerTryCancelRequestPhase server_try_cancel,
@@ -658,7 +655,7 @@ class WriteClient : public grpc::experimental::ClientWriteReactor<EchoRequest> {
                            std::to_string(server_try_cancel));
     }
     context_.set_initial_metadata_corked(true);
-    stub->experimental_async()->RequestStream(&context_, &response_, this);
+    stub->async()->RequestStream(&context_, &response_, this);
     StartCall();
     request_.set_message(msg);
     MaybeWrite();
@@ -792,14 +789,14 @@ TEST_P(ClientCallbackEnd2endTest, RequestStreamServerCancelAfterReads) {
 
 TEST_P(ClientCallbackEnd2endTest, UnaryReactor) {
   ResetStub();
-  class UnaryClient : public grpc::experimental::ClientUnaryReactor {
+  class UnaryClient : public grpc::ClientUnaryReactor {
    public:
     explicit UnaryClient(grpc::testing::EchoTestService::Stub* stub) {
       cli_ctx_.AddMetadata("key1", "val1");
       cli_ctx_.AddMetadata("key2", "val2");
       request_.mutable_param()->set_echo_metadata_initially(true);
       request_.set_message("Hello metadata");
-      stub->experimental_async()->Echo(&cli_ctx_, &request_, &response_, this);
+      stub->async()->Echo(&cli_ctx_, &request_, &response_, this);
       StartCall();
     }
     void OnReadInitialMetadataDone(bool ok) override {
@@ -853,7 +850,7 @@ TEST_P(ClientCallbackEnd2endTest, GenericUnaryReactor) {
   constexpr char kSuffixForStats[] = "TestSuffixForStats";
   ResetStub(
       absl::make_unique<TestInterceptorFactory>(kMethodName, kSuffixForStats));
-  class UnaryClient : public grpc::experimental::ClientUnaryReactor {
+  class UnaryClient : public grpc::ClientUnaryReactor {
    public:
     UnaryClient(grpc::GenericStub* stub, const std::string& method_name,
                 const char* suffix_for_stats) {
@@ -864,8 +861,8 @@ TEST_P(ClientCallbackEnd2endTest, GenericUnaryReactor) {
       send_buf_ = SerializeToByteBuffer(&request_);
 
       StubOptions options(suffix_for_stats);
-      stub->experimental().PrepareUnaryCall(&cli_ctx_, method_name, options,
-                                            send_buf_.get(), &recv_buf_, this);
+      stub->PrepareUnaryCall(&cli_ctx_, method_name, options, send_buf_.get(),
+                             &recv_buf_, this);
       StartCall();
     }
     void OnReadInitialMetadataDone(bool ok) override {
@@ -917,7 +914,7 @@ TEST_P(ClientCallbackEnd2endTest, GenericUnaryReactor) {
   }
 }
 
-class ReadClient : public grpc::experimental::ClientReadReactor<EchoResponse> {
+class ReadClient : public grpc::ClientReadReactor<EchoResponse> {
  public:
   ReadClient(grpc::testing::EchoTestService::Stub* stub,
              ServerTryCancelRequestPhase server_try_cancel,
@@ -929,7 +926,7 @@ class ReadClient : public grpc::experimental::ClientReadReactor<EchoResponse> {
                            std::to_string(server_try_cancel));
     }
     request_.set_message("Hello client ");
-    stub->experimental_async()->ResponseStream(&context_, &request_, this);
+    stub->async()->ResponseStream(&context_, &request_, this);
     if (client_cancel_.cancel &&
         reads_complete_ == client_cancel_.ops_before_cancel) {
       context_.TryCancel();
@@ -1069,8 +1066,7 @@ TEST_P(ClientCallbackEnd2endTest, ResponseStreamServerCancelAfter) {
   }
 }
 
-class BidiClient
-    : public grpc::experimental::ClientBidiReactor<EchoRequest, EchoResponse> {
+class BidiClient : public grpc::ClientBidiReactor<EchoRequest, EchoResponse> {
  public:
   BidiClient(grpc::testing::EchoTestService::Stub* stub,
              ServerTryCancelRequestPhase server_try_cancel,
@@ -1086,7 +1082,7 @@ class BidiClient
     }
     request_.set_message("Hello fren ");
     context_.set_initial_metadata_corked(cork_metadata);
-    stub->experimental_async()->BidiStream(&context_, this);
+    stub->async()->BidiStream(&context_, this);
     MaybeAsyncWrite(first_write_async);
     StartRead(&response_);
     StartCall();
@@ -1325,12 +1321,11 @@ TEST_P(ClientCallbackEnd2endTest, BidiStreamServerCancelAfter) {
 
 TEST_P(ClientCallbackEnd2endTest, SimultaneousReadAndWritesDone) {
   ResetStub();
-  class Client : public grpc::experimental::ClientBidiReactor<EchoRequest,
-                                                              EchoResponse> {
+  class Client : public grpc::ClientBidiReactor<EchoRequest, EchoResponse> {
    public:
     explicit Client(grpc::testing::EchoTestService::Stub* stub) {
       request_.set_message("Hello bidi ");
-      stub->experimental_async()->BidiStream(&context_, this);
+      stub->async()->BidiStream(&context_, this);
       StartWrite(&request_);
       StartCall();
     }
@@ -1388,7 +1383,7 @@ TEST_P(ClientCallbackEnd2endTest, UnimplementedRpc) {
   std::mutex mu;
   std::condition_variable cv;
   bool done = false;
-  stub->experimental_async()->Unimplemented(
+  stub->async()->Unimplemented(
       &cli_ctx, &request, &response, [&done, &mu, &cv](Status s) {
         EXPECT_EQ(StatusCode::UNIMPLEMENTED, s.error_code());
         EXPECT_EQ("", s.error_message());
@@ -1403,16 +1398,56 @@ TEST_P(ClientCallbackEnd2endTest, UnimplementedRpc) {
   }
 }
 
+TEST_P(ClientCallbackEnd2endTest, TestTrailersOnlyOnError) {
+  // Note that trailers-only is an HTTP/2 concept so we shouldn't do this test
+  // for any other transport such as inproc.
+  if (GetParam().protocol != Protocol::TCP) {
+    return;
+  }
+
+  ResetStub();
+  class Reactor : public grpc::ClientBidiReactor<EchoRequest, EchoResponse> {
+   public:
+    explicit Reactor(grpc::testing::EchoTestService::Stub* stub) {
+      stub->async()->UnimplementedBidi(&context_, this);
+      StartCall();
+    }
+    void Await() {
+      std::unique_lock<std::mutex> l(mu_);
+      while (!done_) {
+        done_cv_.wait(l);
+      }
+    }
+
+   private:
+    void OnReadInitialMetadataDone(bool ok) override { EXPECT_FALSE(ok); }
+    void OnDone(const Status& s) override {
+      EXPECT_EQ(s.error_code(), grpc::StatusCode::UNIMPLEMENTED);
+      EXPECT_EQ(s.error_message(), "");
+      std::unique_lock<std::mutex> l(mu_);
+      done_ = true;
+      done_cv_.notify_one();
+    }
+
+    ClientContext context_;
+    std::mutex mu_;
+    std::condition_variable done_cv_;
+    bool done_ = false;
+  } client(stub_.get());
+
+  client.Await();
+}
+
 TEST_P(ClientCallbackEnd2endTest,
        ResponseStreamExtraReactionFlowReadsUntilDone) {
   ResetStub();
   class ReadAllIncomingDataClient
-      : public grpc::experimental::ClientReadReactor<EchoResponse> {
+      : public grpc::ClientReadReactor<EchoResponse> {
    public:
     explicit ReadAllIncomingDataClient(
         grpc::testing::EchoTestService::Stub* stub) {
       request_.set_message("Hello client ");
-      stub->experimental_async()->ResponseStream(&context_, &request_, this);
+      stub->async()->ResponseStream(&context_, &request_, this);
     }
     bool WaitForReadDone() {
       std::unique_lock<std::mutex> l(mu_);
index 79b272c..8035134 100644 (file)
@@ -213,12 +213,11 @@ class HijackingInterceptorMakesAnotherCall : public experimental::Interceptor {
           methods->GetInterceptedChannel());
       ctx_.AddMetadata(metadata_map_.begin()->first,
                        metadata_map_.begin()->second);
-      stub_->experimental_async()->Echo(&ctx_, &req_, &resp_,
-                                        [this, methods](Status s) {
-                                          EXPECT_EQ(s.ok(), true);
-                                          EXPECT_EQ(resp_.message(), "Hello");
-                                          methods->Hijack();
-                                        });
+      stub_->async()->Echo(&ctx_, &req_, &resp_, [this, methods](Status s) {
+        EXPECT_EQ(s.ok(), true);
+        EXPECT_EQ(resp_.message(), "Hello");
+        methods->Hijack();
+      });
       // This is a Unary RPC and we have got nothing interesting to do in the
       // PRE_SEND_CLOSE interception hook point for this interceptor, so let's
       // return here. (We do not want to call methods->Proceed(). When the new
index 2197927..a2e202d 100644 (file)
@@ -49,10 +49,6 @@ namespace {
 
 enum class Protocol { INPROC, TCP };
 
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-using experimental::GenericCallbackServerContext;
-#endif
-
 class TestScenario {
  public:
   TestScenario(Protocol protocol, const std::string& creds_type)
@@ -96,7 +92,7 @@ class ContextAllocatorEnd2endTestBase
       server_address_ << "localhost:" << picked_port_;
       builder.AddListeningPort(server_address_.str(), server_creds);
     }
-    builder.experimental().SetContextAllocator(std::move(context_allocator));
+    builder.SetContextAllocator(std::move(context_allocator));
     builder.RegisterService(&callback_service_);
 
     server_ = builder.BuildAndStart();
@@ -149,7 +145,7 @@ class ContextAllocatorEnd2endTestBase
       std::mutex mu;
       std::condition_variable cv;
       bool done = false;
-      stub_->experimental_async()->Echo(
+      stub_->async()->Echo(
           &cli_ctx, &request, &response,
           [&request, &response, &done, &mu, &cv, val](Status s) {
             GPR_ASSERT(s.ok());
index 4af06c6..b36b547 100644 (file)
@@ -1268,6 +1268,9 @@ TEST_P(End2endTest, ClientCancelsBidi) {
   ClientContext context;
   std::string msg("hello");
 
+  // Send server_try_cancel value in the client metadata
+  context.AddMetadata(kClientTryCancelRequest, std::to_string(1));
+
   auto stream = stub_->BidiStream(&context);
 
   request.set_message(msg + "0");
index 76e1f83..c399930 100644 (file)
@@ -43,12 +43,6 @@ namespace grpc {
 namespace testing {
 namespace {
 
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-using ::grpc::experimental::CallbackGenericService;
-using ::grpc::experimental::GenericCallbackServerContext;
-using ::grpc::experimental::ServerGenericBidiReactor;
-#endif
-
 void* tag(int i) { return reinterpret_cast<void*>(i); }
 
 bool VerifyReturnSuccess(CompletionQueue* cq, int i) {
@@ -273,12 +267,7 @@ class HybridEnd2endTest : public ::testing::TestWithParam<bool> {
       builder.RegisterAsyncGenericService(generic_service);
     }
     if (callback_generic_service) {
-#ifdef GRPC_CALLBACK_API_NONEXPERIMENTAL
       builder.RegisterCallbackGenericService(callback_generic_service);
-#else
-      builder.experimental().RegisterCallbackGenericService(
-          callback_generic_service);
-#endif
     }
 
     if (max_message_size != 0) {
index b90c8dc..55684a4 100644 (file)
@@ -165,14 +165,13 @@ void MakeCallbackCall(const std::shared_ptr<Channel>& channel) {
   ctx.AddMetadata("testkey", "testvalue");
   req.set_message("Hello");
   EchoResponse resp;
-  stub->experimental_async()->Echo(&ctx, &req, &resp,
-                                   [&resp, &mu, &done, &cv](Status s) {
-                                     EXPECT_EQ(s.ok(), true);
-                                     EXPECT_EQ(resp.message(), "Hello");
-                                     std::lock_guard<std::mutex> l(mu);
-                                     done = true;
-                                     cv.notify_one();
-                                   });
+  stub->async()->Echo(&ctx, &req, &resp, [&resp, &mu, &done, &cv](Status s) {
+    EXPECT_EQ(s.ok(), true);
+    EXPECT_EQ(resp.message(), "Hello");
+    std::lock_guard<std::mutex> l(mu);
+    done = true;
+    cv.notify_one();
+  });
   std::unique_lock<std::mutex> l(mu);
   while (!done) {
     cv.wait(l);
index ee632c0..7924715 100644 (file)
@@ -49,21 +49,20 @@ namespace grpc {
 namespace testing {
 namespace {
 
-class CallbackTestServiceImpl
-    : public EchoTestService::ExperimentalCallbackService {
+class CallbackTestServiceImpl : public EchoTestService::CallbackService {
  public:
   explicit CallbackTestServiceImpl() {}
 
   void SetAllocatorMutator(
-      std::function<void(experimental::RpcAllocatorState* allocator_state,
+      std::function<void(RpcAllocatorState* allocator_state,
                          const EchoRequest* req, EchoResponse* resp)>
           mutator) {
     allocator_mutator_ = std::move(mutator);
   }
 
-  experimental::ServerUnaryReactor* Echo(
-      experimental::CallbackServerContext* context, const EchoRequest* request,
-      EchoResponse* response) override {
+  ServerUnaryReactor* Echo(CallbackServerContext* context,
+                           const EchoRequest* request,
+                           EchoResponse* response) override {
     response->set_message(request->message());
     if (allocator_mutator_) {
       allocator_mutator_(context->GetRpcAllocatorState(), request, response);
@@ -74,8 +73,8 @@ class CallbackTestServiceImpl
   }
 
  private:
-  std::function<void(experimental::RpcAllocatorState* allocator_state,
-                     const EchoRequest* req, EchoResponse* resp)>
+  std::function<void(RpcAllocatorState* allocator_state, const EchoRequest* req,
+                     EchoResponse* resp)>
       allocator_mutator_;
 };
 
@@ -110,8 +109,7 @@ class MessageAllocatorEnd2endTestBase
 
   ~MessageAllocatorEnd2endTestBase() override = default;
 
-  void CreateServer(
-      experimental::MessageAllocator<EchoRequest, EchoResponse>* allocator) {
+  void CreateServer(MessageAllocator<EchoRequest, EchoResponse>* allocator) {
     ServerBuilder builder;
 
     auto server_creds = GetCredentialsProvider()->GetServerCredentials(
@@ -174,7 +172,7 @@ class MessageAllocatorEnd2endTestBase
       std::mutex mu;
       std::condition_variable cv;
       bool done = false;
-      stub_->experimental_async()->Echo(
+      stub_->async()->Echo(
           &cli_ctx, &request, &response,
           [&request, &response, &done, &mu, &cv, val](Status s) {
             GPR_ASSERT(s.ok());
@@ -209,11 +207,9 @@ TEST_P(NullAllocatorTest, SimpleRpc) {
 
 class SimpleAllocatorTest : public MessageAllocatorEnd2endTestBase {
  public:
-  class SimpleAllocator
-      : public experimental::MessageAllocator<EchoRequest, EchoResponse> {
+  class SimpleAllocator : public MessageAllocator<EchoRequest, EchoResponse> {
    public:
-    class MessageHolderImpl
-        : public experimental::MessageHolder<EchoRequest, EchoResponse> {
+    class MessageHolderImpl : public MessageHolder<EchoRequest, EchoResponse> {
      public:
       MessageHolderImpl(std::atomic_int* request_deallocation_count,
                         std::atomic_int* messages_deallocation_count)
@@ -244,8 +240,7 @@ class SimpleAllocatorTest : public MessageAllocatorEnd2endTestBase {
       std::atomic_int* const request_deallocation_count_;
       std::atomic_int* const messages_deallocation_count_;
     };
-    experimental::MessageHolder<EchoRequest, EchoResponse>* AllocateMessages()
-        override {
+    MessageHolder<EchoRequest, EchoResponse>* AllocateMessages() override {
       allocation_count++;
       return new MessageHolderImpl(&request_deallocation_count,
                                    &messages_deallocation_count);
@@ -273,8 +268,8 @@ TEST_P(SimpleAllocatorTest, SimpleRpc) {
 TEST_P(SimpleAllocatorTest, RpcWithEarlyFreeRequest) {
   const int kRpcCount = 10;
   std::unique_ptr<SimpleAllocator> allocator(new SimpleAllocator);
-  auto mutator = [](experimental::RpcAllocatorState* allocator_state,
-                    const EchoRequest* req, EchoResponse* resp) {
+  auto mutator = [](RpcAllocatorState* allocator_state, const EchoRequest* req,
+                    EchoResponse* resp) {
     auto* info =
         static_cast<SimpleAllocator::MessageHolderImpl*>(allocator_state);
     EXPECT_EQ(req, info->request());
@@ -298,9 +293,9 @@ TEST_P(SimpleAllocatorTest, RpcWithReleaseRequest) {
   const int kRpcCount = 10;
   std::unique_ptr<SimpleAllocator> allocator(new SimpleAllocator);
   std::vector<EchoRequest*> released_requests;
-  auto mutator = [&released_requests](
-                     experimental::RpcAllocatorState* allocator_state,
-                     const EchoRequest* req, EchoResponse* resp) {
+  auto mutator = [&released_requests](RpcAllocatorState* allocator_state,
+                                      const EchoRequest* req,
+                                      EchoResponse* resp) {
     auto* info =
         static_cast<SimpleAllocator::MessageHolderImpl*>(allocator_state);
     EXPECT_EQ(req, info->request());
@@ -326,11 +321,9 @@ TEST_P(SimpleAllocatorTest, RpcWithReleaseRequest) {
 
 class ArenaAllocatorTest : public MessageAllocatorEnd2endTestBase {
  public:
-  class ArenaAllocator
-      : public experimental::MessageAllocator<EchoRequest, EchoResponse> {
+  class ArenaAllocator : public MessageAllocator<EchoRequest, EchoResponse> {
    public:
-    class MessageHolderImpl
-        : public experimental::MessageHolder<EchoRequest, EchoResponse> {
+    class MessageHolderImpl : public MessageHolder<EchoRequest, EchoResponse> {
      public:
       MessageHolderImpl() {
         set_request(
@@ -344,8 +337,7 @@ class ArenaAllocatorTest : public MessageAllocatorEnd2endTestBase {
      private:
       google::protobuf::Arena arena_;
     };
-    experimental::MessageHolder<EchoRequest, EchoResponse>* AllocateMessages()
-        override {
+    MessageHolder<EchoRequest, EchoResponse>* AllocateMessages() override {
       allocation_count++;
       return new MessageHolderImpl;
     }
index 1e83ad5..82b955d 100644 (file)
@@ -160,12 +160,11 @@ class FakeClient {
   EchoTestService::StubInterface* stub_;
 };
 
-class CallbackTestServiceImpl
-    : public EchoTestService::ExperimentalCallbackService {
+class CallbackTestServiceImpl : public EchoTestService::CallbackService {
  public:
-  experimental::ServerUnaryReactor* Echo(
-      experimental::CallbackServerContext* context, const EchoRequest* request,
-      EchoResponse* response) override {
+  ServerUnaryReactor* Echo(CallbackServerContext* context,
+                           const EchoRequest* request,
+                           EchoResponse* response) override {
     // Make the mock service explicitly treat empty input messages as invalid
     // arguments so that we can test various results of status. In general, a
     // mocked service should just use the original service methods, but we are
@@ -189,7 +188,7 @@ class MockCallbackTest : public ::testing::Test {
 };
 
 TEST_F(MockCallbackTest, MockedCallSucceedsWithWait) {
-  experimental::CallbackServerContext ctx;
+  CallbackServerContext ctx;
   EchoRequest req;
   EchoResponse resp;
   grpc::internal::Mutex mu;
@@ -218,7 +217,7 @@ TEST_F(MockCallbackTest, MockedCallSucceedsWithWait) {
 }
 
 TEST_F(MockCallbackTest, MockedCallSucceeds) {
-  experimental::CallbackServerContext ctx;
+  CallbackServerContext ctx;
   EchoRequest req;
   EchoResponse resp;
   DefaultReactorTestPeer peer(&ctx);
@@ -231,7 +230,7 @@ TEST_F(MockCallbackTest, MockedCallSucceeds) {
 }
 
 TEST_F(MockCallbackTest, MockedCallFails) {
-  experimental::CallbackServerContext ctx;
+  CallbackServerContext ctx;
   EchoRequest req;
   EchoResponse resp;
   DefaultReactorTestPeer peer(&ctx);
index 3ef49e6..d8d85fb 100644 (file)
@@ -38,8 +38,8 @@ namespace internal {
 
 // When echo_deadline is requested, deadline seen in the ServerContext is set in
 // the response in seconds.
-void MaybeEchoDeadline(experimental::ServerContextBase* context,
-                       const EchoRequest* request, EchoResponse* response) {
+void MaybeEchoDeadline(ServerContextBase* context, const EchoRequest* request,
+                       EchoResponse* response) {
   if (request->has_param() && request->param().echo_deadline()) {
     gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
     if (context->deadline() != system_clock::time_point::max()) {
@@ -49,7 +49,7 @@ void MaybeEchoDeadline(experimental::ServerContextBase* context,
   }
 }
 
-void CheckServerAuthContext(const experimental::ServerContextBase* context,
+void CheckServerAuthContext(const ServerContextBase* context,
                             const std::string& expected_transport_security_type,
                             const std::string& expected_client_identity) {
   std::shared_ptr<const AuthContext> auth_ctx = context->auth_context();
@@ -115,7 +115,7 @@ void ServerTryCancel(ServerContext* context) {
   }
 }
 
-void ServerTryCancelNonblocking(experimental::CallbackServerContext* context) {
+void ServerTryCancelNonblocking(CallbackServerContext* context) {
   EXPECT_FALSE(context->IsCancelled());
   context->TryCancel();
   gpr_log(GPR_INFO,
@@ -124,13 +124,12 @@ void ServerTryCancelNonblocking(experimental::CallbackServerContext* context) {
 
 }  // namespace internal
 
-experimental::ServerUnaryReactor* CallbackTestServiceImpl::Echo(
-    experimental::CallbackServerContext* context, const EchoRequest* request,
+ServerUnaryReactor* CallbackTestServiceImpl::Echo(
+    CallbackServerContext* context, const EchoRequest* request,
     EchoResponse* response) {
-  class Reactor : public ::grpc::experimental::ServerUnaryReactor {
+  class Reactor : public ::grpc::ServerUnaryReactor {
    public:
-    Reactor(CallbackTestServiceImpl* service,
-            experimental::CallbackServerContext* ctx,
+    Reactor(CallbackTestServiceImpl* service, CallbackServerContext* ctx,
             const EchoRequest* request, EchoResponse* response)
         : service_(service), ctx_(ctx), req_(request), resp_(response) {
       // It should be safe to call IsCancelled here, even though we don't know
@@ -159,7 +158,7 @@ experimental::ServerUnaryReactor* CallbackTestServiceImpl::Echo(
     void StartRpc() {
       if (req_->has_param() && req_->param().server_sleep_us() > 0) {
         // Set an alarm for that much time
-        alarm_.experimental().Set(
+        alarm_.Set(
             gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
                          gpr_time_from_micros(req_->param().server_sleep_us(),
                                               GPR_TIMESPAN)),
@@ -241,12 +240,11 @@ experimental::ServerUnaryReactor* CallbackTestServiceImpl::Echo(
         FinishWhenCancelledAsync();
         return;
       } else if (req_->has_param() && req_->param().server_cancel_after_us()) {
-        alarm_.experimental().Set(
-            gpr_time_add(
-                gpr_now(GPR_CLOCK_REALTIME),
-                gpr_time_from_micros(req_->param().server_cancel_after_us(),
-                                     GPR_TIMESPAN)),
-            [this](bool) { Finish(Status::CANCELLED); });
+        alarm_.Set(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+                                gpr_time_from_micros(
+                                    req_->param().server_cancel_after_us(),
+                                    GPR_TIMESPAN)),
+                   [this](bool) { Finish(Status::CANCELLED); });
         return;
       } else if (!req_->has_param() || !req_->param().skip_cancelled_check()) {
         EXPECT_FALSE(ctx_->IsCancelled());
@@ -305,7 +303,7 @@ experimental::ServerUnaryReactor* CallbackTestServiceImpl::Echo(
     }
 
     CallbackTestServiceImpl* const service_;
-    experimental::CallbackServerContext* const ctx_;
+    CallbackServerContext* const ctx_;
     const EchoRequest* const req_;
     EchoResponse* const resp_;
     Alarm alarm_;
@@ -322,13 +320,11 @@ experimental::ServerUnaryReactor* CallbackTestServiceImpl::Echo(
   return new Reactor(this, context, request, response);
 }
 
-experimental::ServerUnaryReactor*
-CallbackTestServiceImpl::CheckClientInitialMetadata(
-    experimental::CallbackServerContext* context, const SimpleRequest*,
-    SimpleResponse*) {
-  class Reactor : public ::grpc::experimental::ServerUnaryReactor {
+ServerUnaryReactor* CallbackTestServiceImpl::CheckClientInitialMetadata(
+    CallbackServerContext* context, const SimpleRequest*, SimpleResponse*) {
+  class Reactor : public ::grpc::ServerUnaryReactor {
    public:
-    explicit Reactor(experimental::CallbackServerContext* ctx) {
+    explicit Reactor(CallbackServerContext* ctx) {
       EXPECT_EQ(internal::MetadataMatchCount(ctx->client_metadata(),
                                              kCheckClientInitialMetadataKey,
                                              kCheckClientInitialMetadataVal),
@@ -343,9 +339,8 @@ CallbackTestServiceImpl::CheckClientInitialMetadata(
   return new Reactor(context);
 }
 
-experimental::ServerReadReactor<EchoRequest>*
-CallbackTestServiceImpl::RequestStream(
-    experimental::CallbackServerContext* context, EchoResponse* response) {
+ServerReadReactor<EchoRequest>* CallbackTestServiceImpl::RequestStream(
+    CallbackServerContext* context, EchoResponse* response) {
   // If 'server_try_cancel' is set in the metadata, the RPC is cancelled by
   // the server by calling ServerContext::TryCancel() depending on the
   // value:
@@ -362,9 +357,9 @@ CallbackTestServiceImpl::RequestStream(
     return nullptr;
   }
 
-  class Reactor : public ::grpc::experimental::ServerReadReactor<EchoRequest> {
+  class Reactor : public ::grpc::ServerReadReactor<EchoRequest> {
    public:
-    Reactor(experimental::CallbackServerContext* ctx, EchoResponse* response,
+    Reactor(CallbackServerContext* ctx, EchoResponse* response,
             int server_try_cancel)
         : ctx_(ctx),
           response_(response),
@@ -414,7 +409,7 @@ CallbackTestServiceImpl::RequestStream(
       }
     }
 
-    experimental::CallbackServerContext* const ctx_;
+    CallbackServerContext* const ctx_;
     EchoResponse* const response_;
     EchoRequest request_;
     int num_msgs_read_{0};
@@ -429,9 +424,8 @@ CallbackTestServiceImpl::RequestStream(
 
 // Return 'kNumResponseStreamMsgs' messages.
 // TODO(yangg) make it generic by adding a parameter into EchoRequest
-experimental::ServerWriteReactor<EchoResponse>*
-CallbackTestServiceImpl::ResponseStream(
-    experimental::CallbackServerContext* context, const EchoRequest* request) {
+ServerWriteReactor<EchoResponse>* CallbackTestServiceImpl::ResponseStream(
+    CallbackServerContext* context, const EchoRequest* request) {
   // If 'server_try_cancel' is set in the metadata, the RPC is cancelled by
   // the server by calling ServerContext::TryCancel() depending on the
   // value:
@@ -446,11 +440,10 @@ CallbackTestServiceImpl::ResponseStream(
     internal::ServerTryCancelNonblocking(context);
   }
 
-  class Reactor
-      : public ::grpc::experimental::ServerWriteReactor<EchoResponse> {
+  class Reactor : public ::grpc::ServerWriteReactor<EchoResponse> {
    public:
-    Reactor(experimental::CallbackServerContext* ctx,
-            const EchoRequest* request, int server_try_cancel)
+    Reactor(CallbackServerContext* ctx, const EchoRequest* request,
+            int server_try_cancel)
         : ctx_(ctx), request_(request), server_try_cancel_(server_try_cancel) {
       server_coalescing_api_ = internal::GetIntValueFromMetadata(
           kServerUseCoalescingApi, ctx->client_metadata(), 0);
@@ -518,7 +511,7 @@ CallbackTestServiceImpl::ResponseStream(
         }
       }
     }
-    experimental::CallbackServerContext* const ctx_;
+    CallbackServerContext* const ctx_;
     const EchoRequest* const request_;
     EchoResponse response_;
     int num_msgs_sent_{0};
@@ -532,13 +525,11 @@ CallbackTestServiceImpl::ResponseStream(
   return new Reactor(context, request, server_try_cancel);
 }
 
-experimental::ServerBidiReactor<EchoRequest, EchoResponse>*
-CallbackTestServiceImpl::BidiStream(
-    experimental::CallbackServerContext* context) {
-  class Reactor : public ::grpc::experimental::ServerBidiReactor<EchoRequest,
-                                                                 EchoResponse> {
+ServerBidiReactor<EchoRequest, EchoResponse>*
+CallbackTestServiceImpl::BidiStream(CallbackServerContext* context) {
+  class Reactor : public ::grpc::ServerBidiReactor<EchoRequest, EchoResponse> {
    public:
-    explicit Reactor(experimental::CallbackServerContext* ctx) : ctx_(ctx) {
+    explicit Reactor(CallbackServerContext* ctx) : ctx_(ctx) {
       // If 'server_try_cancel' is set in the metadata, the RPC is cancelled by
       // the server by calling ServerContext::TryCancel() depending on the
       // value:
@@ -551,6 +542,8 @@ CallbackTestServiceImpl::BidiStream(
           kServerTryCancelRequest, ctx->client_metadata(), DO_NOT_CANCEL);
       server_write_last_ = internal::GetIntValueFromMetadata(
           kServerFinishAfterNReads, ctx->client_metadata(), 0);
+      client_try_cancel_ = static_cast<bool>(internal::GetIntValueFromMetadata(
+          kClientTryCancelRequest, ctx->client_metadata(), 0));
       if (server_try_cancel_ == CANCEL_BEFORE_PROCESSING) {
         internal::ServerTryCancelNonblocking(ctx);
       } else {
@@ -589,6 +582,8 @@ CallbackTestServiceImpl::BidiStream(
             return;
           }
         }
+      } else if (client_try_cancel_) {
+        EXPECT_TRUE(ctx_->IsCancelled());
       }
 
       if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
@@ -619,7 +614,7 @@ CallbackTestServiceImpl::BidiStream(
       }
     }
 
-    experimental::CallbackServerContext* const ctx_;
+    CallbackServerContext* const ctx_;
     EchoRequest request_;
     EchoResponse response_;
     int num_msgs_read_{0};
@@ -629,6 +624,7 @@ CallbackTestServiceImpl::BidiStream(
     bool finished_{false};
     bool setup_done_{false};
     std::thread finish_thread_;
+    bool client_try_cancel_ = false;
   };
 
   return new Reactor(context);
index c90c070..682ef31 100644 (file)
@@ -42,6 +42,7 @@ namespace testing {
 const int kServerDefaultResponseStreamsToSend = 3;
 const char* const kServerResponseStreamsToSend = "server_responses_to_send";
 const char* const kServerTryCancelRequest = "server_try_cancel";
+const char* const kClientTryCancelRequest = "client_try_cancel";
 const char* const kDebugInfoTrailerKey = "debug-info-bin";
 const char* const kServerFinishAfterNReads = "server_finish_after_n_reads";
 const char* const kServerUseCoalescingApi = "server_use_coalescing_api";
@@ -58,10 +59,10 @@ typedef enum {
 namespace internal {
 // When echo_deadline is requested, deadline seen in the ServerContext is set in
 // the response in seconds.
-void MaybeEchoDeadline(experimental::ServerContextBase* context,
-                       const EchoRequest* request, EchoResponse* response);
+void MaybeEchoDeadline(ServerContextBase* context, const EchoRequest* request,
+                       EchoResponse* response);
 
-void CheckServerAuthContext(const experimental::ServerContextBase* context,
+void CheckServerAuthContext(const ServerContextBase* context,
                             const std::string& expected_transport_security_type,
                             const std::string& expected_client_identity);
 
@@ -442,30 +443,28 @@ class TestMultipleServiceImpl : public RpcService {
 };
 
 class CallbackTestServiceImpl
-    : public ::grpc::testing::EchoTestService::ExperimentalCallbackService {
+    : public ::grpc::testing::EchoTestService::CallbackService {
  public:
   CallbackTestServiceImpl() : signal_client_(false), host_() {}
   explicit CallbackTestServiceImpl(const std::string& host)
       : signal_client_(false), host_(new std::string(host)) {}
 
-  experimental::ServerUnaryReactor* Echo(
-      experimental::CallbackServerContext* context, const EchoRequest* request,
-      EchoResponse* response) override;
+  ServerUnaryReactor* Echo(CallbackServerContext* context,
+                           const EchoRequest* request,
+                           EchoResponse* response) override;
 
-  experimental::ServerUnaryReactor* CheckClientInitialMetadata(
-      experimental::CallbackServerContext* context, const SimpleRequest*,
-      SimpleResponse*) override;
+  ServerUnaryReactor* CheckClientInitialMetadata(CallbackServerContext* context,
+                                                 const SimpleRequest*,
+                                                 SimpleResponse*) override;
 
-  experimental::ServerReadReactor<EchoRequest>* RequestStream(
-      experimental::CallbackServerContext* context,
-      EchoResponse* response) override;
+  ServerReadReactor<EchoRequest>* RequestStream(
+      CallbackServerContext* context, EchoResponse* response) override;
 
-  experimental::ServerWriteReactor<EchoResponse>* ResponseStream(
-      experimental::CallbackServerContext* context,
-      const EchoRequest* request) override;
+  ServerWriteReactor<EchoResponse>* ResponseStream(
+      CallbackServerContext* context, const EchoRequest* request) override;
 
-  experimental::ServerBidiReactor<EchoRequest, EchoResponse>* BidiStream(
-      experimental::CallbackServerContext* context) override;
+  ServerBidiReactor<EchoRequest, EchoResponse>* BidiStream(
+      CallbackServerContext* context) override;
 
   // Unimplemented is left unimplemented to test the returned error.
   bool signal_client() {
index 2a80c54..61f413f 100644 (file)
@@ -32,6 +32,7 @@
 #include "absl/functional/bind_front.h"
 #include "absl/memory/memory.h"
 #include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
 #include "absl/strings/str_join.h"
 #include "absl/types/optional.h"
 
@@ -1614,28 +1615,28 @@ grpc_millis NowFromCycleCounter() {
   return grpc_cycle_counter_to_millis_round_up(now);
 }
 
-// Returns the number of RPCs needed to pass error_tolerance at 99.99% chance.
+// Returns the number of RPCs needed to pass error_tolerance at 99.995% chance.
 // Rolling dices in drop/fault-injection generates a binomial distribution (if
 // our code is not horribly wrong). Let's make "n" the number of samples, "p"
 // the probabilty. If we have np>5 & n(1-p)>5, we can approximately treat the
 // binomial distribution as a normal distribution.
 //
 // For normal distribution, we can easily look up how many standard deviation we
-// need to reach 99.99%. Based on Wiki's table
-// https://en.wikipedia.org/wiki/Standard_normal_table, we need 3.89 sigma
-// (standard deviation) to cover the probability area of 99.99%. In another
+// need to reach 99.995%. Based on Wiki's table
+// https://en.wikipedia.org/wiki/Standard_normal_table, we need 4.00 sigma
+// (standard deviation) to cover the probability area of 99.995%. In another
 // word, for a sample with size "n" probability "p" error-tolerance "k", we want
-// the error always land within 3.89 sigma. The sigma of binominal distribution
+// the error always land within 4.00 sigma. The sigma of binominal distribution
 // and be computed as sqrt(np(1-p)). Hence, we have the equation:
 //
-//   kn <= 3.89 * sqrt(np(1-p))
+//   kn <= 4.00 * sqrt(np(1-p))
 //
-// E.g., with p=0.5 k=0.1, n >= 378; with p=0.5 k=0.05, n >= 1513; with p=0.5
-// k=0.01, n >= 37830.
+// E.g., with p=0.5 k=0.1, n >= 400; with p=0.5 k=0.05, n >= 1600; with p=0.5
+// k=0.01, n >= 40000.
 size_t ComputeIdealNumRpcs(double p, double error_tolerance) {
   GPR_ASSERT(p >= 0 && p <= 1);
   size_t num_rpcs =
-      ceil(p * (1 - p) * 3.89 * 3.89 / error_tolerance / error_tolerance);
+      ceil(p * (1 - p) * 4.00 * 4.00 / error_tolerance / error_tolerance);
   gpr_log(GPR_INFO,
           "Sending %" PRIuPTR " RPCs for percentage=%.3f error_tolerance=%.3f",
           num_rpcs, p, error_tolerance);
@@ -2095,6 +2096,14 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
     return addresses;
   }
 
+  std::string CreateMetadataValueThatHashesToBackendPort(int port) {
+    return absl::StrCat(ipv6_only_ ? "[::1]" : "127.0.0.1", ":", port, "_0");
+  }
+
+  std::string CreateMetadataValueThatHashesToBackend(int index) {
+    return CreateMetadataValueThatHashesToBackendPort(backends_[index]->port());
+  }
+
   void SetNextResolution(
       const std::vector<int>& ports,
       grpc_core::FakeResolverResponseGenerator* response_generator = nullptr) {
@@ -2236,6 +2245,13 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
     return listener;
   }
 
+  AdsServiceImpl::EdsResourceArgs::Endpoint CreateEndpoint(
+      size_t backend_idx, HealthStatus health_status = HealthStatus::UNKNOWN,
+      int lb_weight = 1) {
+    return AdsServiceImpl::EdsResourceArgs::Endpoint(
+        backends_[backend_idx]->port(), health_status, lb_weight);
+  }
+
   std::vector<AdsServiceImpl::EdsResourceArgs::Endpoint>
   CreateEndpointsForBackends(size_t start_index = 0, size_t stop_index = 0,
                              HealthStatus health_status = HealthStatus::UNKNOWN,
@@ -2243,7 +2259,7 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
     if (stop_index == 0) stop_index = backends_.size();
     std::vector<AdsServiceImpl::EdsResourceArgs::Endpoint> endpoints;
     for (size_t i = start_index; i < stop_index; ++i) {
-      endpoints.emplace_back(backends_[i]->port(), health_status, lb_weight);
+      endpoints.emplace_back(CreateEndpoint(i, health_status, lb_weight));
     }
     return endpoints;
   }
@@ -2272,6 +2288,11 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
             locality.endpoints[i].health_status != HealthStatus::UNKNOWN) {
           lb_endpoints->set_health_status(locality.endpoints[i].health_status);
         }
+        if (locality.endpoints.size() > i &&
+            locality.endpoints[i].lb_weight >= 1) {
+          lb_endpoints->mutable_load_balancing_weight()->set_value(
+              locality.endpoints[i].lb_weight);
+        }
         auto* endpoint = lb_endpoints->mutable_endpoint();
         auto* address = endpoint->mutable_address();
         auto* socket_address = address->mutable_socket_address();
@@ -2659,18 +2680,17 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
       ConcurrentRpc* rpc = &rpcs[i];
       rpc_options.SetupRpc(&rpc->context, &request);
       grpc_millis t0 = NowFromCycleCounter();
-      stub->experimental_async()->Echo(
-          &rpc->context, &request, &rpc->response,
-          [rpc, &mu, &completed, &cv, num_rpcs, t0](Status s) {
-            rpc->status = s;
-            rpc->elapsed_time = NowFromCycleCounter() - t0;
-            bool done;
-            {
-              absl::MutexLock lock(&mu);
-              done = (++completed) == num_rpcs;
-            }
-            if (done) cv.Signal();
-          });
+      stub->async()->Echo(&rpc->context, &request, &rpc->response,
+                          [rpc, &mu, &completed, &cv, num_rpcs, t0](Status s) {
+                            rpc->status = s;
+                            rpc->elapsed_time = NowFromCycleCounter() - t0;
+                            bool done;
+                            {
+                              absl::MutexLock lock(&mu);
+                              done = (++completed) == num_rpcs;
+                            }
+                            if (done) cv.Signal();
+                          });
     }
     {
       absl::MutexLock lock(&mu);
@@ -2766,8 +2786,8 @@ TEST_P(BasicTest, IgnoresUnhealthyEndpoints) {
   }
 }
 
-// Tests that subchannel sharing works when the same backend is listed multiple
-// times.
+// Tests that subchannel sharing works when the same backend is listed
+// multiple times.
 TEST_P(BasicTest, SameBackendListedMultipleTimes) {
   SetNextResolution({});
   SetNextResolutionForLbChannelAllBalancers();
@@ -2852,8 +2872,8 @@ TEST_P(BasicTest, AllServersUnreachableFailFast) {
   EXPECT_EQ(StatusCode::UNAVAILABLE, status.error_code());
 }
 
-// Tests that RPCs fail when the backends are down, and will succeed again after
-// the backends are restarted.
+// Tests that RPCs fail when the backends are down, and will succeed again
+// after the backends are restarted.
 TEST_P(BasicTest, BackendsRestart) {
   SetNextResolution({});
   SetNextResolutionForLbChannelAllBalancers();
@@ -2867,13 +2887,13 @@ TEST_P(BasicTest, BackendsRestart) {
   ShutdownAllBackends();
   // Sending multiple failed requests instead of just one to ensure that the
   // client notices that all backends are down before we restart them. If we
-  // didn't do this, then a single RPC could fail here due to the race condition
-  // between the LB pick and the GOAWAY from the chosen backend being shut down,
-  // which would not actually prove that the client noticed that all of the
-  // backends are down. Then, when we send another request below (which we
-  // expect to succeed), if the callbacks happen in the wrong order, the same
-  // race condition could happen again due to the client not yet having noticed
-  // that the backends were all down.
+  // didn't do this, then a single RPC could fail here due to the race
+  // condition between the LB pick and the GOAWAY from the chosen backend
+  // being shut down, which would not actually prove that the client noticed
+  // that all of the backends are down. Then, when we send another request
+  // below (which we expect to succeed), if the callbacks happen in the wrong
+  // order, the same race condition could happen again due to the client not
+  // yet having noticed that the backends were all down.
   CheckRpcSendFailure(num_backends_);
   // Restart all backends.  RPCs should start succeeding again.
   StartAllBackends();
@@ -3501,7 +3521,8 @@ TEST_P(LdsTest, RdsMissingConfigSource) {
 }
 
 // Tests that LDS client should send a NACK if the rds message in the
-// http_connection_manager has a config_source field that does not specify ADS.
+// http_connection_manager has a config_source field that does not specify
+// ADS.
 TEST_P(LdsTest, RdsConfigSourceDoesNotSpecifyAds) {
   auto listener = default_listener_;
   HttpConnectionManager http_connection_manager;
@@ -3519,10 +3540,9 @@ TEST_P(LdsTest, RdsConfigSourceDoesNotSpecifyAds) {
   const auto response_state =
       balancers_[0]->ads_service()->lds_response_state();
   EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
-  EXPECT_THAT(
-      response_state.error_message,
-      ::testing::HasSubstr(
-          "HttpConnectionManager ConfigSource for RDS does not specify ADS."));
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr("HttpConnectionManager ConfigSource for "
+                                   "RDS does not specify ADS."));
 }
 
 // Tests that we ignore filters after the router filter.
@@ -3893,8 +3913,8 @@ TEST_P(LdsRdsTest, NoMatchedDomain) {
   EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::ACKED);
 }
 
-// Tests that LDS client should choose the virtual host with matching domain if
-// multiple virtual hosts exist in the LDS response.
+// Tests that LDS client should choose the virtual host with matching domain
+// if multiple virtual hosts exist in the LDS response.
 TEST_P(LdsRdsTest, ChooseMatchedDomain) {
   RouteConfiguration route_config = default_route_config_;
   *(route_config.add_virtual_hosts()) = route_config.virtual_hosts(0);
@@ -4233,10 +4253,9 @@ TEST_P(LdsRdsTest, RouteActionWeightedTargetClusterHasEmptyClusterName) {
   CheckRpcSendFailure();
   const auto response_state = RouteConfigurationResponseState(0);
   EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
-  EXPECT_THAT(
-      response_state.error_message,
-      ::testing::HasSubstr(
-          "RouteAction weighted_cluster cluster contains empty cluster name."));
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr("RouteAction weighted_cluster cluster "
+                                   "contains empty cluster name."));
 }
 
 TEST_P(LdsRdsTest, RouteActionWeightedTargetClusterHasNoWeight) {
@@ -4992,8 +5011,8 @@ TEST_P(LdsRdsTest, XdsRoutingWeightedClusterUpdateWeights) {
   // Change Route Configurations: same clusters different weights.
   weighted_cluster1->mutable_weight()->set_value(kWeight50);
   weighted_cluster2->mutable_weight()->set_value(kWeight50);
-  // Change default route to a new cluster to help to identify when new polices
-  // are seen by the client.
+  // Change default route to a new cluster to help to identify when new
+  // polices are seen by the client.
   default_route->mutable_route()->set_cluster(kNewCluster3Name);
   SetRouteConfiguration(0, new_route_config);
   ResetBackendCounters();
@@ -5258,8 +5277,8 @@ TEST_P(LdsRdsTest, XdsRoutingClusterUpdateClustersWithPickingDelays) {
       new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
   default_route->mutable_route()->set_cluster(kNewClusterName);
   SetRouteConfiguration(0, new_route_config);
-  // Wait for RPCs to go to the new backend: 1, this ensures that the client has
-  // processed the update.
+  // Wait for RPCs to go to the new backend: 1, this ensures that the client
+  // has processed the update.
   WaitForBackend(
       1, WaitForBackendOptions().set_reset_counters(false).set_allow_failures(
              true));
@@ -6429,6 +6448,14 @@ TEST_P(CdsTest, LogicalDNSClusterType) {
   // Create Logical DNS Cluster
   auto cluster = default_cluster_;
   cluster.set_type(Cluster::LOGICAL_DNS);
+  auto* address = cluster.mutable_load_assignment()
+                      ->add_endpoints()
+                      ->add_lb_endpoints()
+                      ->mutable_endpoint()
+                      ->mutable_address()
+                      ->mutable_socket_address();
+  address->set_address(kServerName);
+  address->set_port_value(443);
   balancers_[0]->ads_service()->SetCdsResource(cluster);
   // Set Logical DNS result
   {
@@ -6444,6 +6471,307 @@ TEST_P(CdsTest, LogicalDNSClusterType) {
       "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
 }
 
+TEST_P(CdsTest, LogicalDNSClusterTypeMissingLoadAssignment) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER",
+             "true");
+  SetNextResolution({});
+  SetNextResolutionForLbChannelAllBalancers();
+  // Create Logical DNS Cluster
+  auto cluster = default_cluster_;
+  cluster.set_type(Cluster::LOGICAL_DNS);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  // Wait until xDS server sees NACK.
+  do {
+    CheckRpcSendFailure();
+  } while (balancers_[0]->ads_service()->cds_response_state().state ==
+           AdsServiceImpl::ResponseState::SENT);
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr(
+                  "load_assignment not present for LOGICAL_DNS cluster"));
+  gpr_unsetenv(
+      "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
+}
+
+TEST_P(CdsTest, LogicalDNSClusterTypeMissingLocalities) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER",
+             "true");
+  SetNextResolution({});
+  SetNextResolutionForLbChannelAllBalancers();
+  // Create Logical DNS Cluster
+  auto cluster = default_cluster_;
+  cluster.set_type(Cluster::LOGICAL_DNS);
+  cluster.mutable_load_assignment();
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  // Wait until xDS server sees NACK.
+  do {
+    CheckRpcSendFailure();
+  } while (balancers_[0]->ads_service()->cds_response_state().state ==
+           AdsServiceImpl::ResponseState::SENT);
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(
+      response_state.error_message,
+      ::testing::HasSubstr("load_assignment for LOGICAL_DNS cluster must have "
+                           "exactly one locality, found 0"));
+  gpr_unsetenv(
+      "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
+}
+
+TEST_P(CdsTest, LogicalDNSClusterTypeMultipleLocalities) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER",
+             "true");
+  SetNextResolution({});
+  SetNextResolutionForLbChannelAllBalancers();
+  // Create Logical DNS Cluster
+  auto cluster = default_cluster_;
+  cluster.set_type(Cluster::LOGICAL_DNS);
+  auto* load_assignment = cluster.mutable_load_assignment();
+  load_assignment->add_endpoints();
+  load_assignment->add_endpoints();
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  // Wait until xDS server sees NACK.
+  do {
+    CheckRpcSendFailure();
+  } while (balancers_[0]->ads_service()->cds_response_state().state ==
+           AdsServiceImpl::ResponseState::SENT);
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(
+      response_state.error_message,
+      ::testing::HasSubstr("load_assignment for LOGICAL_DNS cluster must have "
+                           "exactly one locality, found 2"));
+  gpr_unsetenv(
+      "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
+}
+
+TEST_P(CdsTest, LogicalDNSClusterTypeMissingEndpoints) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER",
+             "true");
+  SetNextResolution({});
+  SetNextResolutionForLbChannelAllBalancers();
+  // Create Logical DNS Cluster
+  auto cluster = default_cluster_;
+  cluster.set_type(Cluster::LOGICAL_DNS);
+  cluster.mutable_load_assignment()->add_endpoints();
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  // Wait until xDS server sees NACK.
+  do {
+    CheckRpcSendFailure();
+  } while (balancers_[0]->ads_service()->cds_response_state().state ==
+           AdsServiceImpl::ResponseState::SENT);
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr(
+                  "locality for LOGICAL_DNS cluster must have exactly one "
+                  "endpoint, found 0"));
+  gpr_unsetenv(
+      "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
+}
+
+TEST_P(CdsTest, LogicalDNSClusterTypeMultipleEndpoints) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER",
+             "true");
+  SetNextResolution({});
+  SetNextResolutionForLbChannelAllBalancers();
+  // Create Logical DNS Cluster
+  auto cluster = default_cluster_;
+  cluster.set_type(Cluster::LOGICAL_DNS);
+  auto* locality = cluster.mutable_load_assignment()->add_endpoints();
+  locality->add_lb_endpoints();
+  locality->add_lb_endpoints();
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  // Wait until xDS server sees NACK.
+  do {
+    CheckRpcSendFailure();
+  } while (balancers_[0]->ads_service()->cds_response_state().state ==
+           AdsServiceImpl::ResponseState::SENT);
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr(
+                  "locality for LOGICAL_DNS cluster must have exactly one "
+                  "endpoint, found 2"));
+  gpr_unsetenv(
+      "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
+}
+
+TEST_P(CdsTest, LogicalDNSClusterTypeEmptyEndpoint) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER",
+             "true");
+  SetNextResolution({});
+  SetNextResolutionForLbChannelAllBalancers();
+  // Create Logical DNS Cluster
+  auto cluster = default_cluster_;
+  cluster.set_type(Cluster::LOGICAL_DNS);
+  cluster.mutable_load_assignment()->add_endpoints()->add_lb_endpoints();
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  // Wait until xDS server sees NACK.
+  do {
+    CheckRpcSendFailure();
+  } while (balancers_[0]->ads_service()->cds_response_state().state ==
+           AdsServiceImpl::ResponseState::SENT);
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr("LbEndpoint endpoint field not set"));
+  gpr_unsetenv(
+      "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
+}
+
+TEST_P(CdsTest, LogicalDNSClusterTypeEndpointMissingAddress) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER",
+             "true");
+  SetNextResolution({});
+  SetNextResolutionForLbChannelAllBalancers();
+  // Create Logical DNS Cluster
+  auto cluster = default_cluster_;
+  cluster.set_type(Cluster::LOGICAL_DNS);
+  cluster.mutable_load_assignment()
+      ->add_endpoints()
+      ->add_lb_endpoints()
+      ->mutable_endpoint();
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  // Wait until xDS server sees NACK.
+  do {
+    CheckRpcSendFailure();
+  } while (balancers_[0]->ads_service()->cds_response_state().state ==
+           AdsServiceImpl::ResponseState::SENT);
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr("Endpoint address field not set"));
+  gpr_unsetenv(
+      "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
+}
+
+TEST_P(CdsTest, LogicalDNSClusterTypeAddressMissingSocketAddress) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER",
+             "true");
+  SetNextResolution({});
+  SetNextResolutionForLbChannelAllBalancers();
+  // Create Logical DNS Cluster
+  auto cluster = default_cluster_;
+  cluster.set_type(Cluster::LOGICAL_DNS);
+  cluster.mutable_load_assignment()
+      ->add_endpoints()
+      ->add_lb_endpoints()
+      ->mutable_endpoint()
+      ->mutable_address();
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  // Wait until xDS server sees NACK.
+  do {
+    CheckRpcSendFailure();
+  } while (balancers_[0]->ads_service()->cds_response_state().state ==
+           AdsServiceImpl::ResponseState::SENT);
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr("Address socket_address field not set"));
+  gpr_unsetenv(
+      "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
+}
+
+TEST_P(CdsTest, LogicalDNSClusterTypeSocketAddressHasResolverName) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER",
+             "true");
+  SetNextResolution({});
+  SetNextResolutionForLbChannelAllBalancers();
+  // Create Logical DNS Cluster
+  auto cluster = default_cluster_;
+  cluster.set_type(Cluster::LOGICAL_DNS);
+  cluster.mutable_load_assignment()
+      ->add_endpoints()
+      ->add_lb_endpoints()
+      ->mutable_endpoint()
+      ->mutable_address()
+      ->mutable_socket_address()
+      ->set_resolver_name("foo");
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  // Wait until xDS server sees NACK.
+  do {
+    CheckRpcSendFailure();
+  } while (balancers_[0]->ads_service()->cds_response_state().state ==
+           AdsServiceImpl::ResponseState::SENT);
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr("LOGICAL_DNS clusters must NOT have a "
+                                   "custom resolver name set"));
+  gpr_unsetenv(
+      "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
+}
+
+TEST_P(CdsTest, LogicalDNSClusterTypeSocketAddressMissingAddress) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER",
+             "true");
+  SetNextResolution({});
+  SetNextResolutionForLbChannelAllBalancers();
+  // Create Logical DNS Cluster
+  auto cluster = default_cluster_;
+  cluster.set_type(Cluster::LOGICAL_DNS);
+  cluster.mutable_load_assignment()
+      ->add_endpoints()
+      ->add_lb_endpoints()
+      ->mutable_endpoint()
+      ->mutable_address()
+      ->mutable_socket_address();
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  // Wait until xDS server sees NACK.
+  do {
+    CheckRpcSendFailure();
+  } while (balancers_[0]->ads_service()->cds_response_state().state ==
+           AdsServiceImpl::ResponseState::SENT);
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr("SocketAddress address field not set"));
+  gpr_unsetenv(
+      "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
+}
+
+TEST_P(CdsTest, LogicalDNSClusterTypeSocketAddressMissingPort) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER",
+             "true");
+  SetNextResolution({});
+  SetNextResolutionForLbChannelAllBalancers();
+  // Create Logical DNS Cluster
+  auto cluster = default_cluster_;
+  cluster.set_type(Cluster::LOGICAL_DNS);
+  cluster.mutable_load_assignment()
+      ->add_endpoints()
+      ->add_lb_endpoints()
+      ->mutable_endpoint()
+      ->mutable_address()
+      ->mutable_socket_address()
+      ->set_address(kServerName);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  // Wait until xDS server sees NACK.
+  do {
+    CheckRpcSendFailure();
+  } while (balancers_[0]->ads_service()->cds_response_state().state ==
+           AdsServiceImpl::ResponseState::SENT);
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr("SocketAddress port_value field not set"));
+  gpr_unsetenv(
+      "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
+}
+
 TEST_P(CdsTest, AggregateClusterType) {
   gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER",
              "true");
@@ -6522,6 +6850,14 @@ TEST_P(CdsTest, AggregateClusterEdsToLogicalDns) {
   auto logical_dns_cluster = default_cluster_;
   logical_dns_cluster.set_name(kLogicalDNSClusterName);
   logical_dns_cluster.set_type(Cluster::LOGICAL_DNS);
+  auto* address = logical_dns_cluster.mutable_load_assignment()
+                      ->add_endpoints()
+                      ->add_lb_endpoints()
+                      ->mutable_endpoint()
+                      ->mutable_address()
+                      ->mutable_socket_address();
+  address->set_address(kServerName);
+  address->set_port_value(443);
   balancers_[0]->ads_service()->SetCdsResource(logical_dns_cluster);
   // Create Aggregate Cluster
   auto cluster = default_cluster_;
@@ -6578,6 +6914,14 @@ TEST_P(CdsTest, AggregateClusterLogicalDnsToEds) {
   auto logical_dns_cluster = default_cluster_;
   logical_dns_cluster.set_name(kLogicalDNSClusterName);
   logical_dns_cluster.set_type(Cluster::LOGICAL_DNS);
+  auto* address = logical_dns_cluster.mutable_load_assignment()
+                      ->add_endpoints()
+                      ->add_lb_endpoints()
+                      ->mutable_endpoint()
+                      ->mutable_address()
+                      ->mutable_socket_address();
+  address->set_address(kServerName);
+  address->set_port_value(443);
   balancers_[0]->ads_service()->SetCdsResource(logical_dns_cluster);
   // Create Aggregate Cluster
   auto cluster = default_cluster_;
@@ -6648,8 +6992,8 @@ TEST_P(CdsTest, AggregateClusterTypeDisabled) {
               ::testing::HasSubstr("DiscoveryType is not valid."));
 }
 
-// Tests that CDS client should send a NACK if the cluster type in CDS response
-// is unsupported.
+// Tests that CDS client should send a NACK if the cluster type in CDS
+// response is unsupported.
 TEST_P(CdsTest, UnsupportedClusterType) {
   auto cluster = default_cluster_;
   cluster.set_type(Cluster::STATIC);
@@ -6695,8 +7039,8 @@ TEST_P(CdsTest, MultipleBadResources) {
                       kClusterName2, ": DiscoveryType is not valid."))));
 }
 
-// Tests that CDS client should send a NACK if the eds_config in CDS response is
-// other than ADS.
+// Tests that CDS client should send a NACK if the eds_config in CDS response
+// is other than ADS.
 TEST_P(CdsTest, WrongEdsConfig) {
   auto cluster = default_cluster_;
   cluster.mutable_eds_cluster_config()->mutable_eds_config()->mutable_self();
@@ -6711,8 +7055,8 @@ TEST_P(CdsTest, WrongEdsConfig) {
               ::testing::HasSubstr("EDS ConfigSource is not ADS."));
 }
 
-// Tests that CDS client should send a NACK if the lb_policy in CDS response is
-// other than ROUND_ROBIN.
+// Tests that CDS client should send a NACK if the lb_policy in CDS response
+// is other than ROUND_ROBIN.
 TEST_P(CdsTest, WrongLbPolicy) {
   auto cluster = default_cluster_;
   cluster.set_lb_policy(Cluster::LEAST_REQUEST);
@@ -6727,8 +7071,8 @@ TEST_P(CdsTest, WrongLbPolicy) {
               ::testing::HasSubstr("LB policy is not supported."));
 }
 
-// Tests that CDS client should send a NACK if the lrs_server in CDS response is
-// other than SELF.
+// Tests that CDS client should send a NACK if the lrs_server in CDS response
+// is other than SELF.
 TEST_P(CdsTest, WrongLrsServer) {
   auto cluster = default_cluster_;
   cluster.mutable_lrs_server()->mutable_ads();
@@ -6743,25 +7087,820 @@ TEST_P(CdsTest, WrongLrsServer) {
               ::testing::HasSubstr("LRS ConfigSource is not self."));
 }
 
-class XdsSecurityTest : public BasicTest {
- protected:
-  static void SetUpTestCase() {
-    gpr_setenv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT", "true");
-    BasicTest::SetUpTestCase();
-  }
-
-  static void TearDownTestCase() {
-    BasicTest::TearDownTestCase();
-    gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT");
-  }
-
-  void SetUp() override {
+// Tests that ring hash policy that hashes using channel id ensures all RPCs
+// to go 1 particular backend.
+TEST_P(CdsTest, RingHashChannelIdHashing) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_filter_state()->set_key("io.grpc.channel_id");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", CreateEndpointsForBackends()},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  CheckRpcSendOk(100);
+  bool found = false;
+  for (size_t i = 0; i < backends_.size(); ++i) {
+    if (backends_[i]->backend_service()->request_count() > 0) {
+      EXPECT_EQ(backends_[i]->backend_service()->request_count(), 100)
+          << "backend " << i;
+      EXPECT_FALSE(found) << "backend " << i;
+      found = true;
+    }
+  }
+  EXPECT_TRUE(found);
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Tests that ring hash policy that hashes using a header value can spread
+// RPCs across all the backends.
+TEST_P(CdsTest, RingHashHeaderHashing) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_header()->set_header_name("address_hash");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", CreateEndpointsForBackends()},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  // Note each type of RPC will contains a header value that will always be
+  // hashed to a specific backend as the header value matches the value used
+  // to create the entry in the ring.
+  std::vector<std::pair<std::string, std::string>> metadata = {
+      {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
+  std::vector<std::pair<std::string, std::string>> metadata1 = {
+      {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
+  std::vector<std::pair<std::string, std::string>> metadata2 = {
+      {"address_hash", CreateMetadataValueThatHashesToBackend(2)}};
+  std::vector<std::pair<std::string, std::string>> metadata3 = {
+      {"address_hash", CreateMetadataValueThatHashesToBackend(3)}};
+  const auto rpc_options = RpcOptions().set_metadata(std::move(metadata));
+  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
+  const auto rpc_options2 = RpcOptions().set_metadata(std::move(metadata2));
+  const auto rpc_options3 = RpcOptions().set_metadata(std::move(metadata3));
+  WaitForBackend(0, WaitForBackendOptions(), rpc_options);
+  WaitForBackend(1, WaitForBackendOptions(), rpc_options1);
+  WaitForBackend(2, WaitForBackendOptions(), rpc_options2);
+  WaitForBackend(3, WaitForBackendOptions(), rpc_options3);
+  CheckRpcSendOk(100, rpc_options);
+  CheckRpcSendOk(100, rpc_options1);
+  CheckRpcSendOk(100, rpc_options2);
+  CheckRpcSendOk(100, rpc_options3);
+  for (size_t i = 0; i < backends_.size(); ++i) {
+    EXPECT_EQ(100, backends_[i]->backend_service()->request_count());
+  }
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Tests that ring hash policy that hashes using a header value and regex
+// rewrite to aggregate RPCs to 1 backend.
+TEST_P(CdsTest, RingHashHeaderHashingWithRegexRewrite) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_header()->set_header_name("address_hash");
+  hash_policy->mutable_header()
+      ->mutable_regex_rewrite()
+      ->mutable_pattern()
+      ->set_regex("[0-9]+");
+  hash_policy->mutable_header()->mutable_regex_rewrite()->set_substitution(
+      "foo");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", CreateEndpointsForBackends()},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  std::vector<std::pair<std::string, std::string>> metadata = {
+      {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
+  std::vector<std::pair<std::string, std::string>> metadata1 = {
+      {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
+  std::vector<std::pair<std::string, std::string>> metadata2 = {
+      {"address_hash", CreateMetadataValueThatHashesToBackend(2)}};
+  std::vector<std::pair<std::string, std::string>> metadata3 = {
+      {"address_hash", CreateMetadataValueThatHashesToBackend(3)}};
+  const auto rpc_options = RpcOptions().set_metadata(std::move(metadata));
+  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
+  const auto rpc_options2 = RpcOptions().set_metadata(std::move(metadata2));
+  const auto rpc_options3 = RpcOptions().set_metadata(std::move(metadata3));
+  CheckRpcSendOk(100, rpc_options);
+  CheckRpcSendOk(100, rpc_options1);
+  CheckRpcSendOk(100, rpc_options2);
+  CheckRpcSendOk(100, rpc_options3);
+  bool found = false;
+  for (size_t i = 0; i < backends_.size(); ++i) {
+    if (backends_[i]->backend_service()->request_count() > 0) {
+      EXPECT_EQ(backends_[i]->backend_service()->request_count(), 400)
+          << "backend " << i;
+      EXPECT_FALSE(found) << "backend " << i;
+      found = true;
+    }
+  }
+  EXPECT_TRUE(found);
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Tests that ring hash policy that hashes using a random value.
+TEST_P(CdsTest, RingHashNoHashPolicy) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  const double kDistribution50Percent = 0.5;
+  const double kErrorTolerance = 0.05;
+  const uint32_t kRpcTimeoutMs = 10000;
+  const size_t kNumRpcs =
+      ComputeIdealNumRpcs(kDistribution50Percent, kErrorTolerance);
+  auto cluster = default_cluster_;
+  // Increasing min ring size for random distribution.
+  cluster.mutable_ring_hash_lb_config()->mutable_minimum_ring_size()->set_value(
+      100000);
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  AdsServiceImpl::EdsResourceArgs args(
+      {{"locality0", CreateEndpointsForBackends(0, 2)}});
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  // TODO(donnadionne): remove extended timeout after ring creation
+  // optimization.
+  WaitForAllBackends(0, 2, WaitForBackendOptions(),
+                     RpcOptions().set_timeout_ms(kRpcTimeoutMs));
+  CheckRpcSendOk(kNumRpcs);
+  const int request_count_1 = backends_[0]->backend_service()->request_count();
+  const int request_count_2 = backends_[1]->backend_service()->request_count();
+  EXPECT_THAT(static_cast<double>(request_count_1) / kNumRpcs,
+              ::testing::DoubleNear(kDistribution50Percent, kErrorTolerance));
+  EXPECT_THAT(static_cast<double>(request_count_2) / kNumRpcs,
+              ::testing::DoubleNear(kDistribution50Percent, kErrorTolerance));
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test that ring hash policy evaluation will continue past the terminal
+// policy if no results are produced yet.
+TEST_P(CdsTest, RingHashContinuesPastTerminalPolicyThatDoesNotProduceResult) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_header()->set_header_name("header_not_present");
+  hash_policy->set_terminal(true);
+  auto* hash_policy2 = route->mutable_route()->add_hash_policy();
+  hash_policy2->mutable_header()->set_header_name("address_hash");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args(
+      {{"locality0", CreateEndpointsForBackends(0, 2)}});
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  std::vector<std::pair<std::string, std::string>> metadata = {
+      {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
+  const auto rpc_options = RpcOptions().set_metadata(std::move(metadata));
+  CheckRpcSendOk(100, rpc_options);
+  EXPECT_EQ(backends_[0]->backend_service()->request_count(), 100);
+  EXPECT_EQ(backends_[1]->backend_service()->request_count(), 0);
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test random hash is used when header hashing specified a header field that
+// the RPC did not have.
+TEST_P(CdsTest, RingHashOnHeaderThatIsNotPresent) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  const double kDistribution50Percent = 0.5;
+  const double kErrorTolerance = 0.05;
+  const uint32_t kRpcTimeoutMs = 10000;
+  const size_t kNumRpcs =
+      ComputeIdealNumRpcs(kDistribution50Percent, kErrorTolerance);
+  auto cluster = default_cluster_;
+  // Increasing min ring size for random distribution.
+  cluster.mutable_ring_hash_lb_config()->mutable_minimum_ring_size()->set_value(
+      100000);
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_header()->set_header_name("header_not_present");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args(
+      {{"locality0", CreateEndpointsForBackends(0, 2)}});
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  std::vector<std::pair<std::string, std::string>> metadata = {
+      {"unmatched_header", absl::StrFormat("%" PRIu32, rand())},
+  };
+  const auto rpc_options = RpcOptions().set_metadata(std::move(metadata));
+  // TODO(donnadionne): remove extended timeout after ring creation
+  // optimization.
+  WaitForAllBackends(0, 2, WaitForBackendOptions(),
+                     RpcOptions().set_timeout_ms(kRpcTimeoutMs));
+  CheckRpcSendOk(kNumRpcs, rpc_options);
+  const int request_count_1 = backends_[0]->backend_service()->request_count();
+  const int request_count_2 = backends_[1]->backend_service()->request_count();
+  EXPECT_THAT(static_cast<double>(request_count_1) / kNumRpcs,
+              ::testing::DoubleNear(kDistribution50Percent, kErrorTolerance));
+  EXPECT_THAT(static_cast<double>(request_count_2) / kNumRpcs,
+              ::testing::DoubleNear(kDistribution50Percent, kErrorTolerance));
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test random hash is used when only unsupported hash policies are
+// configured.
+TEST_P(CdsTest, RingHashUnsupportedHashPolicyDefaultToRandomHashing) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  const double kDistribution50Percent = 0.5;
+  const double kErrorTolerance = 0.05;
+  const uint32_t kRpcTimeoutMs = 10000;
+  const size_t kNumRpcs =
+      ComputeIdealNumRpcs(kDistribution50Percent, kErrorTolerance);
+  auto cluster = default_cluster_;
+  // Increasing min ring size for random distribution.
+  cluster.mutable_ring_hash_lb_config()->mutable_minimum_ring_size()->set_value(
+      100000);
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy_unsupported_1 = route->mutable_route()->add_hash_policy();
+  hash_policy_unsupported_1->mutable_cookie()->set_name("cookie");
+  auto* hash_policy_unsupported_2 = route->mutable_route()->add_hash_policy();
+  hash_policy_unsupported_2->mutable_connection_properties()->set_source_ip(
+      true);
+  auto* hash_policy_unsupported_3 = route->mutable_route()->add_hash_policy();
+  hash_policy_unsupported_3->mutable_query_parameter()->set_name(
+      "query_parameter");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args(
+      {{"locality0", CreateEndpointsForBackends(0, 2)}});
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  // TODO(donnadionne): remove extended timeout after ring creation
+  // optimization.
+  WaitForAllBackends(0, 2, WaitForBackendOptions(),
+                     RpcOptions().set_timeout_ms(kRpcTimeoutMs));
+  CheckRpcSendOk(kNumRpcs);
+  const int request_count_1 = backends_[0]->backend_service()->request_count();
+  const int request_count_2 = backends_[1]->backend_service()->request_count();
+  EXPECT_THAT(static_cast<double>(request_count_1) / kNumRpcs,
+              ::testing::DoubleNear(kDistribution50Percent, kErrorTolerance));
+  EXPECT_THAT(static_cast<double>(request_count_2) / kNumRpcs,
+              ::testing::DoubleNear(kDistribution50Percent, kErrorTolerance));
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Tests that ring hash policy that hashes using a random value can spread
+// RPCs across all the backends according to locality weight.
+TEST_P(CdsTest, RingHashRandomHashingDistributionAccordingToEndpointWeight) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  const size_t kWeight1 = 1;
+  const size_t kWeight2 = 2;
+  const size_t kWeightTotal = kWeight1 + kWeight2;
+  const double kWeight33Percent = static_cast<double>(kWeight1) / kWeightTotal;
+  const double kWeight66Percent = static_cast<double>(kWeight2) / kWeightTotal;
+  const double kErrorTolerance = 0.05;
+  const uint32_t kRpcTimeoutMs = 10000;
+  const size_t kNumRpcs =
+      ComputeIdealNumRpcs(kWeight33Percent, kErrorTolerance);
+  auto cluster = default_cluster_;
+  // Increasing min ring size for random distribution.
+  cluster.mutable_ring_hash_lb_config()->mutable_minimum_ring_size()->set_value(
+      100000);
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  AdsServiceImpl::EdsResourceArgs args(
+      {{"locality0",
+        {CreateEndpoint(0, HealthStatus::UNKNOWN, 1),
+         CreateEndpoint(1, HealthStatus::UNKNOWN, 2)}}});
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  // TODO(donnadionne): remove extended timeout after ring creation
+  // optimization.
+  WaitForAllBackends(0, 2, WaitForBackendOptions(),
+                     RpcOptions().set_timeout_ms(kRpcTimeoutMs));
+  CheckRpcSendOk(kNumRpcs);
+  const int weight_33_request_count =
+      backends_[0]->backend_service()->request_count();
+  const int weight_66_request_count =
+      backends_[1]->backend_service()->request_count();
+  EXPECT_THAT(static_cast<double>(weight_33_request_count) / kNumRpcs,
+              ::testing::DoubleNear(kWeight33Percent, kErrorTolerance));
+  EXPECT_THAT(static_cast<double>(weight_66_request_count) / kNumRpcs,
+              ::testing::DoubleNear(kWeight66Percent, kErrorTolerance));
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Tests that ring hash policy that hashes using a random value can spread
+// RPCs across all the backends according to locality weight.
+TEST_P(CdsTest,
+       RingHashRandomHashingDistributionAccordingToLocalityAndEndpointWeight) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  const size_t kWeight1 = 1 * 1;
+  const size_t kWeight2 = 2 * 2;
+  const size_t kWeightTotal = kWeight1 + kWeight2;
+  const double kWeight20Percent = static_cast<double>(kWeight1) / kWeightTotal;
+  const double kWeight80Percent = static_cast<double>(kWeight2) / kWeightTotal;
+  const double kErrorTolerance = 0.05;
+  const uint32_t kRpcTimeoutMs = 10000;
+  const size_t kNumRpcs =
+      ComputeIdealNumRpcs(kWeight20Percent, kErrorTolerance);
+  auto cluster = default_cluster_;
+  // Increasing min ring size for random distribution.
+  cluster.mutable_ring_hash_lb_config()->mutable_minimum_ring_size()->set_value(
+      100000);
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  AdsServiceImpl::EdsResourceArgs args(
+      {{"locality0", {CreateEndpoint(0, HealthStatus::UNKNOWN, 1)}, 1},
+       {"locality1", {CreateEndpoint(1, HealthStatus::UNKNOWN, 2)}, 2}});
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  // TODO(donnadionne): remove extended timeout after ring creation
+  // optimization.
+  WaitForAllBackends(0, 2, WaitForBackendOptions(),
+                     RpcOptions().set_timeout_ms(kRpcTimeoutMs));
+  CheckRpcSendOk(kNumRpcs);
+  const int weight_20_request_count =
+      backends_[0]->backend_service()->request_count();
+  const int weight_80_request_count =
+      backends_[1]->backend_service()->request_count();
+  EXPECT_THAT(static_cast<double>(weight_20_request_count) / kNumRpcs,
+              ::testing::DoubleNear(kWeight20Percent, kErrorTolerance));
+  EXPECT_THAT(static_cast<double>(weight_80_request_count) / kNumRpcs,
+              ::testing::DoubleNear(kWeight80Percent, kErrorTolerance));
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Tests round robin is not implacted by the endpoint weight, and that the
+// localities in a locality map are picked according to their weights.
+TEST_P(CdsTest, RingHashEndpointWeightDoesNotImpactWeightedRoundRobin) {
+  SetNextResolution({});
+  SetNextResolutionForLbChannelAllBalancers();
+  const int kLocalityWeight0 = 2;
+  const int kLocalityWeight1 = 8;
+  const int kTotalLocalityWeight = kLocalityWeight0 + kLocalityWeight1;
+  const double kLocalityWeightRate0 =
+      static_cast<double>(kLocalityWeight0) / kTotalLocalityWeight;
+  const double kLocalityWeightRate1 =
+      static_cast<double>(kLocalityWeight1) / kTotalLocalityWeight;
+  const double kErrorTolerance = 0.05;
+  const size_t kNumRpcs =
+      ComputeIdealNumRpcs(kLocalityWeightRate0, kErrorTolerance);
+  // ADS response contains 2 localities, each of which contains 1 backend.
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0",
+       {CreateEndpoint(0, HealthStatus::UNKNOWN, 8)},
+       kLocalityWeight0},
+      {"locality1",
+       {CreateEndpoint(1, HealthStatus::UNKNOWN, 2)},
+       kLocalityWeight1},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  // Wait for both backends to be ready.
+  WaitForAllBackends(0, 2);
+  // Send kNumRpcs RPCs.
+  CheckRpcSendOk(kNumRpcs);
+  // The locality picking rates should be roughly equal to the expectation.
+  const double locality_picked_rate_0 =
+      static_cast<double>(backends_[0]->backend_service()->request_count()) /
+      kNumRpcs;
+  const double locality_picked_rate_1 =
+      static_cast<double>(backends_[1]->backend_service()->request_count()) /
+      kNumRpcs;
+  EXPECT_THAT(locality_picked_rate_0,
+              ::testing::DoubleNear(kLocalityWeightRate0, kErrorTolerance));
+  EXPECT_THAT(locality_picked_rate_1,
+              ::testing::DoubleNear(kLocalityWeightRate1, kErrorTolerance));
+}
+
+// Tests that ring hash policy that hashes using a fixed string ensures all
+// RPCs to go 1 particular backend; and that subsequent hashing policies are
+// ignored due to the setting of terminal.
+TEST_P(CdsTest, RingHashFixedHashingTerminalPolicy) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_header()->set_header_name("fixed_string");
+  hash_policy->set_terminal(true);
+  auto* hash_policy_to_be_ignored = route->mutable_route()->add_hash_policy();
+  hash_policy_to_be_ignored->mutable_header()->set_header_name("random_string");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", CreateEndpointsForBackends()},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  std::vector<std::pair<std::string, std::string>> metadata = {
+      {"fixed_string", "fixed_value"},
+      {"random_string", absl::StrFormat("%" PRIu32, rand())},
+  };
+  const auto rpc_options = RpcOptions().set_metadata(std::move(metadata));
+  CheckRpcSendOk(100, rpc_options);
+  bool found = false;
+  for (size_t i = 0; i < backends_.size(); ++i) {
+    if (backends_[i]->backend_service()->request_count() > 0) {
+      EXPECT_EQ(backends_[i]->backend_service()->request_count(), 100)
+          << "backend " << i;
+      EXPECT_FALSE(found) << "backend " << i;
+      found = true;
+    }
+  }
+  EXPECT_TRUE(found);
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test that the channel will go from idle to ready via connecting;
+// (tho it is not possible to catch the connecting state before moving to
+// ready)
+TEST_P(CdsTest, RingHashIdleToReady) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_filter_state()->set_key("io.grpc.channel_id");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", CreateEndpointsForBackends()},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  EXPECT_EQ(GRPC_CHANNEL_IDLE, channel_->GetState(false));
+  CheckRpcSendOk();
+  EXPECT_EQ(GRPC_CHANNEL_READY, channel_->GetState(false));
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test that when the first pick is down leading to a transient failure, we
+// will move on to the next ring hash entry.
+TEST_P(CdsTest, RingHashTransientFailureCheckNextOne) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_header()->set_header_name("address_hash");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  std::vector<AdsServiceImpl::EdsResourceArgs::Endpoint> endpoints;
+  const int unused_port = grpc_pick_unused_port_or_die();
+  endpoints.emplace_back(unused_port);
+  endpoints.emplace_back(backends_[1]->port());
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", std::move(endpoints)},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  std::vector<std::pair<std::string, std::string>> metadata = {
+      {"address_hash",
+       CreateMetadataValueThatHashesToBackendPort(unused_port)}};
+  const auto rpc_options = RpcOptions().set_metadata(std::move(metadata));
+  WaitForBackend(1, WaitForBackendOptions(), rpc_options);
+  CheckRpcSendOk(100, rpc_options);
+  EXPECT_EQ(0, backends_[0]->backend_service()->request_count());
+  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test that when a backend goes down, we will move on to the next subchannel
+// (with a lower priority).  When the backend comes back up, traffic will move
+// back.
+TEST_P(CdsTest, RingHashSwitchToLowerPrioirtyAndThenBack) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_header()->set_header_name("address_hash");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", CreateEndpointsForBackends(0, 1), kDefaultLocalityWeight,
+       0},
+      {"locality1", CreateEndpointsForBackends(1, 2), kDefaultLocalityWeight,
+       1},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  std::vector<std::pair<std::string, std::string>> metadata = {
+      {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
+  const auto rpc_options = RpcOptions().set_metadata(std::move(metadata));
+  WaitForBackend(0, WaitForBackendOptions(), rpc_options);
+  ShutdownBackend(0);
+  WaitForBackend(1, WaitForBackendOptions().set_allow_failures(true),
+                 rpc_options);
+  StartBackend(0);
+  WaitForBackend(0, WaitForBackendOptions(), rpc_options);
+  CheckRpcSendOk(100, rpc_options);
+  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
+  EXPECT_EQ(0, backends_[1]->backend_service()->request_count());
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test that when all backends are down, we will keep reattempting.
+TEST_P(CdsTest, RingHashAllFailReattempt) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  const uint32_t kConnectionTimeoutMilliseconds = 5000;
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_header()->set_header_name("address_hash");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  std::vector<AdsServiceImpl::EdsResourceArgs::Endpoint> endpoints;
+  endpoints.emplace_back(grpc_pick_unused_port_or_die());
+  endpoints.emplace_back(backends_[1]->port());
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", std::move(endpoints)},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  std::vector<std::pair<std::string, std::string>> metadata = {
+      {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
+  const auto rpc_options = RpcOptions().set_metadata(std::move(metadata));
+  EXPECT_EQ(GRPC_CHANNEL_IDLE, channel_->GetState(false));
+  ShutdownBackend(1);
+  CheckRpcSendFailure(1, rpc_options);
+  StartBackend(1);
+  // Ensure we are actively connecting without any traffic.
+  EXPECT_TRUE(channel_->WaitForConnected(
+      grpc_timeout_milliseconds_to_deadline(kConnectionTimeoutMilliseconds)));
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test that when all backends are down and then up, we may pick a TF backend
+// and we will then jump to ready backend.
+TEST_P(CdsTest, RingHashTransientFailureSkipToAvailableReady) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  const uint32_t kConnectionTimeoutMilliseconds = 5000;
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_header()->set_header_name("address_hash");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  std::vector<AdsServiceImpl::EdsResourceArgs::Endpoint> endpoints;
+  // Make sure we include some unused ports to fill the ring.
+  endpoints.emplace_back(backends_[0]->port());
+  endpoints.emplace_back(backends_[1]->port());
+  endpoints.emplace_back(grpc_pick_unused_port_or_die());
+  endpoints.emplace_back(grpc_pick_unused_port_or_die());
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", std::move(endpoints)},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  std::vector<std::pair<std::string, std::string>> metadata = {
+      {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
+  const auto rpc_options = RpcOptions().set_metadata(std::move(metadata));
+  EXPECT_EQ(GRPC_CHANNEL_IDLE, channel_->GetState(false));
+  ShutdownBackend(0);
+  ShutdownBackend(1);
+  CheckRpcSendFailure(1, rpc_options);
+  EXPECT_EQ(GRPC_CHANNEL_TRANSIENT_FAILURE, channel_->GetState(false));
+  // Bring up 0, should be picked as the RPC is hashed to it.
+  StartBackend(0);
+  EXPECT_TRUE(channel_->WaitForConnected(
+      grpc_timeout_milliseconds_to_deadline(kConnectionTimeoutMilliseconds)));
+  WaitForBackend(0, WaitForBackendOptions(), rpc_options);
+  // Bring down 0 and bring up 1.
+  // Note the RPC contains a header value that will always be hashed to
+  // backend 0. So by purposely bring down backend 0 and bring up another
+  // backend, this will ensure Picker's first choice of backend 0 will fail
+  // and it will
+  // 1. reattempt backend 0 and
+  // 2. go through the remaining subchannels to find one in READY.
+  // Since the the entries in the ring is pretty distributed and we have
+  // unused ports to fill the ring, it is almost guaranteed that the Picker
+  // will go through some non-READY entries and skip them as per design.
+  ShutdownBackend(0);
+  CheckRpcSendFailure(1, rpc_options);
+  StartBackend(1);
+  EXPECT_TRUE(channel_->WaitForConnected(
+      grpc_timeout_milliseconds_to_deadline(kConnectionTimeoutMilliseconds)));
+  WaitForBackend(1, WaitForBackendOptions(), rpc_options);
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test unspported hash policy types are all ignored before a supported
+// policy.
+TEST_P(CdsTest, RingHashUnsupportedHashPolicyUntilChannelIdHashing) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy_unsupported_1 = route->mutable_route()->add_hash_policy();
+  hash_policy_unsupported_1->mutable_cookie()->set_name("cookie");
+  auto* hash_policy_unsupported_2 = route->mutable_route()->add_hash_policy();
+  hash_policy_unsupported_2->mutable_connection_properties()->set_source_ip(
+      true);
+  auto* hash_policy_unsupported_3 = route->mutable_route()->add_hash_policy();
+  hash_policy_unsupported_3->mutable_query_parameter()->set_name(
+      "query_parameter");
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_filter_state()->set_key("io.grpc.channel_id");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", CreateEndpointsForBackends()},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  CheckRpcSendOk(100);
+  bool found = false;
+  for (size_t i = 0; i < backends_.size(); ++i) {
+    if (backends_[i]->backend_service()->request_count() > 0) {
+      EXPECT_EQ(backends_[i]->backend_service()->request_count(), 100)
+          << "backend " << i;
+      EXPECT_FALSE(found) << "backend " << i;
+      found = true;
+    }
+  }
+  EXPECT_TRUE(found);
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test we nack when ring hash policy has invalid hash function (something
+// other than XX_HASH.
+TEST_P(CdsTest, RingHashPolicyHasInvalidHashFunction) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  cluster.mutable_ring_hash_lb_config()->set_hash_function(
+      Cluster::RingHashLbConfig::MURMUR_HASH_2);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_filter_state()->set_key("io.grpc.channel_id");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", CreateEndpointsForBackends()},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  CheckRpcSendFailure();
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(
+      response_state.error_message,
+      ::testing::HasSubstr("ring hash lb config has invalid hash function."));
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test we nack when ring hash policy has invalid ring size.
+TEST_P(CdsTest, RingHashPolicyHasInvalidMinimumRingSize) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  cluster.mutable_ring_hash_lb_config()->mutable_minimum_ring_size()->set_value(
+      0);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_filter_state()->set_key("io.grpc.channel_id");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", CreateEndpointsForBackends()},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  CheckRpcSendFailure();
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr(
+                  "min_ring_size is not in the range of 1 to 8388608."));
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test we nack when ring hash policy has invalid ring size.
+TEST_P(CdsTest, RingHashPolicyHasInvalidMaxmumRingSize) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  cluster.mutable_ring_hash_lb_config()->mutable_maximum_ring_size()->set_value(
+      8388609);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_filter_state()->set_key("io.grpc.channel_id");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", CreateEndpointsForBackends()},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  CheckRpcSendFailure();
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr(
+                  "max_ring_size is not in the range of 1 to 8388608."));
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+// Test we nack when ring hash policy has invalid ring size.
+TEST_P(CdsTest, RingHashPolicyHasInvalidRingSizeMinGreaterThanMax) {
+  gpr_setenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", "true");
+  auto cluster = default_cluster_;
+  cluster.set_lb_policy(Cluster::RING_HASH);
+  cluster.mutable_ring_hash_lb_config()->mutable_maximum_ring_size()->set_value(
+      5000);
+  cluster.mutable_ring_hash_lb_config()->mutable_minimum_ring_size()->set_value(
+      5001);
+  balancers_[0]->ads_service()->SetCdsResource(cluster);
+  auto new_route_config = default_route_config_;
+  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
+  auto* hash_policy = route->mutable_route()->add_hash_policy();
+  hash_policy->mutable_filter_state()->set_key("io.grpc.channel_id");
+  SetListenerAndRouteConfiguration(0, default_listener_, new_route_config);
+  AdsServiceImpl::EdsResourceArgs args({
+      {"locality0", CreateEndpointsForBackends()},
+  });
+  balancers_[0]->ads_service()->SetEdsResource(
+      BuildEdsResource(args, DefaultEdsServiceName()));
+  SetNextResolutionForLbChannelAllBalancers();
+  CheckRpcSendFailure();
+  const auto response_state =
+      balancers_[0]->ads_service()->cds_response_state();
+  EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
+  EXPECT_THAT(response_state.error_message,
+              ::testing::HasSubstr(
+                  "min_ring_size cannot be greater than max_ring_size."));
+  gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
+}
+
+class XdsSecurityTest : public BasicTest {
+ protected:
+  static void SetUpTestCase() {
+    gpr_setenv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT", "true");
+    BasicTest::SetUpTestCase();
+  }
+
+  static void TearDownTestCase() {
+    BasicTest::TearDownTestCase();
+    gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT");
+  }
+
+  void SetUp() override {
     BasicTest::SetUp();
     root_cert_ = ReadFile(kCaCertPath);
     bad_root_cert_ = ReadFile(kBadClientCertPath);
     identity_pair_ = ReadTlsIdentityPair(kClientKeyPath, kClientCertPath);
-    // TODO(yashykt): Use different client certs here instead of reusing server
-    // certs after https://github.com/grpc/grpc/pull/24876 is merged
+    // TODO(yashykt): Use different client certs here instead of reusing
+    // server certs after https://github.com/grpc/grpc/pull/24876 is merged
     fallback_identity_pair_ =
         ReadTlsIdentityPair(kServerKeyPath, kServerCertPath);
     bad_identity_pair_ =
@@ -7562,7 +8701,8 @@ TEST_P(XdsEnabledServerTest,
   EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::ACKED);
 }
 
-// Verify that a mismatch of listening address results in "not serving" status.
+// Verify that a mismatch of listening address results in "not serving"
+// status.
 TEST_P(XdsEnabledServerTest, ListenerAddressMismatch) {
   Listener listener;
   listener.set_name(
@@ -8237,7 +9377,7 @@ TEST_P(XdsEnabledServerStatusNotificationTest, ExistingRpcsOnResourceDeletion) {
     std::shared_ptr<Channel> channel;
     std::unique_ptr<grpc::testing::EchoTestService::Stub> stub;
     ClientContext context;
-    std::unique_ptr<ClientWriter<EchoRequest>> writer;
+    std::unique_ptr<ClientReaderWriter<EchoRequest, EchoResponse>> stream;
   } streaming_rpcs[kNumChannels];
   EchoRequest request;
   EchoResponse response;
@@ -8247,9 +9387,11 @@ TEST_P(XdsEnabledServerStatusNotificationTest, ExistingRpcsOnResourceDeletion) {
     streaming_rpcs[i].stub =
         grpc::testing::EchoTestService::NewStub(streaming_rpcs[i].channel);
     streaming_rpcs[i].context.set_wait_for_ready(true);
-    streaming_rpcs[i].writer = streaming_rpcs[i].stub->RequestStream(
-        &streaming_rpcs[i].context, &response);
-    EXPECT_TRUE(streaming_rpcs[i].writer->Write(request));
+    streaming_rpcs[i].stream =
+        streaming_rpcs[i].stub->BidiStream(&streaming_rpcs[i].context);
+    EXPECT_TRUE(streaming_rpcs[i].stream->Write(request));
+    streaming_rpcs[i].stream->Read(&response);
+    EXPECT_EQ(request.message(), response.message());
   }
   // Deleting the resource will make the server start rejecting connections
   UnsetLdsUpdate();
@@ -8259,9 +9401,14 @@ TEST_P(XdsEnabledServerStatusNotificationTest, ExistingRpcsOnResourceDeletion) {
   SendRpc([this]() { return CreateInsecureChannel(); }, {}, {},
           true /* test_expects_failure */);
   for (int i = 0; i < kNumChannels; i++) {
-    EXPECT_TRUE(streaming_rpcs[i].writer->Write(request));
-    EXPECT_TRUE(streaming_rpcs[i].writer->WritesDone());
-    EXPECT_TRUE(streaming_rpcs[i].writer->Finish().ok());
+    EXPECT_TRUE(streaming_rpcs[i].stream->Write(request));
+    streaming_rpcs[i].stream->Read(&response);
+    EXPECT_EQ(request.message(), response.message());
+    EXPECT_TRUE(streaming_rpcs[i].stream->WritesDone());
+    auto status = streaming_rpcs[i].stream->Finish();
+    EXPECT_TRUE(status.ok())
+        << status.error_message() << ", " << status.error_details() << ", "
+        << streaming_rpcs[i].context.debug_error_string();
     // New RPCs on the existing channels should fail.
     ClientContext new_context;
     new_context.set_deadline(grpc_timeout_milliseconds_to_deadline(1000));
@@ -8421,8 +9568,8 @@ TEST_P(XdsServerFilterChainMatchTest,
       HttpConnectionManager());
   filter_chain->mutable_filter_chain_match()->add_application_protocols("h2");
   balancers_[0]->ads_service()->SetLdsResource(listener);
-  // A successful RPC proves that filter chains that mention "raw_buffer" as the
-  // transport protocol are chosen as the best match in the round.
+  // A successful RPC proves that filter chains that mention "raw_buffer" as
+  // the transport protocol are chosen as the best match in the round.
   SendRpc([this]() { return CreateInsecureChannel(); }, {}, {});
 }
 
@@ -8449,8 +9596,8 @@ TEST_P(XdsServerFilterChainMatchTest,
   prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1");
   prefix_range->mutable_prefix_len()->set_value(16);
   filter_chain->mutable_filter_chain_match()->add_server_names("server_name");
-  // Add filter chain with two prefix ranges (length 8 and 24). Since 24 is the
-  // highest match, it should be chosen.
+  // Add filter chain with two prefix ranges (length 8 and 24). Since 24 is
+  // the highest match, it should be chosen.
   filter_chain = listener.add_filter_chains();
   filter_chain->add_filters()->mutable_typed_config()->PackFrom(
       HttpConnectionManager());
@@ -8462,7 +9609,8 @@ TEST_P(XdsServerFilterChainMatchTest,
       filter_chain->mutable_filter_chain_match()->add_prefix_ranges();
   prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1");
   prefix_range->mutable_prefix_len()->set_value(24);
-  // Add another filter chain with a non-matching prefix range (with length 30)
+  // Add another filter chain with a non-matching prefix range (with length
+  // 30)
   filter_chain = listener.add_filter_chains();
   filter_chain->add_filters()->mutable_typed_config()->PackFrom(
       HttpConnectionManager());
@@ -8528,10 +9676,10 @@ TEST_P(XdsServerFilterChainMatchTest,
   auto* socket_address = listener.mutable_address()->mutable_socket_address();
   socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1");
   socket_address->set_port_value(backends_[0]->port());
-  // Add filter chain with source prefix range (length 16) but with a bad source
-  // port mentioned. (Prefix range is matched first.)
-  // Note that backends_[0]->port() will never be a match for the source port
-  // because it is already being used by a backend.
+  // Add filter chain with source prefix range (length 16) but with a bad
+  // source port mentioned. (Prefix range is matched first.) Note that
+  // backends_[0]->port() will never be a match for the source port because it
+  // is already being used by a backend.
   auto* filter_chain = listener.add_filter_chains();
   filter_chain->add_filters()->mutable_typed_config()->PackFrom(
       HttpConnectionManager());
@@ -8545,8 +9693,8 @@ TEST_P(XdsServerFilterChainMatchTest,
   source_prefix_range->mutable_prefix_len()->set_value(16);
   filter_chain->mutable_filter_chain_match()->add_source_ports(
       backends_[0]->port());
-  // Add filter chain with two source prefix ranges (length 8 and 24). Since 24
-  // is the highest match, it should be chosen.
+  // Add filter chain with two source prefix ranges (length 8 and 24). Since
+  // 24 is the highest match, it should be chosen.
   filter_chain = listener.add_filter_chains();
   filter_chain->add_filters()->mutable_typed_config()->PackFrom(
       HttpConnectionManager());
@@ -8716,8 +9864,8 @@ TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnTransportProtocolNacked) {
       HttpConnectionManager());
   filter_chain->mutable_filter_chain_match()->set_transport_protocol(
       "raw_buffer");
-  // Add a duplicate filter chain with the same "raw_buffer" transport protocol
-  // entry
+  // Add a duplicate filter chain with the same "raw_buffer" transport
+  // protocol entry
   filter_chain = listener.add_filter_chains();
   filter_chain->add_filters()->mutable_typed_config()->PackFrom(
       HttpConnectionManager());
@@ -9083,8 +10231,8 @@ TEST_P(LocalityMapTest, StressTest) {
   delayed_resource_setter.join();
 }
 
-// Tests that the localities in a locality map are picked correctly after update
-// (addition, modification, deletion).
+// Tests that the localities in a locality map are picked correctly after
+// update (addition, modification, deletion).
 TEST_P(LocalityMapTest, UpdateMap) {
   SetNextResolution({});
   SetNextResolutionForLbChannelAllBalancers();
@@ -9148,8 +10296,8 @@ TEST_P(LocalityMapTest, UpdateMap) {
       BuildEdsResource(args, DefaultEdsServiceName()));
   // Backend 3 hasn't received any request.
   EXPECT_EQ(0U, backends_[3]->backend_service()->request_count());
-  // Wait until the locality update has been processed, as signaled by backend 3
-  // receiving a request.
+  // Wait until the locality update has been processed, as signaled by backend
+  // receiving a request.
   WaitForAllBackends(3, 4);
   gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
   // Send kNumRpcs RPCs.
@@ -9268,8 +10416,8 @@ TEST_P(FailoverTest, DoesNotUseLocalityWithNoEndpoints) {
   EXPECT_EQ(0, std::get<1>(counts));
 }
 
-// If the higher priority localities are not reachable, failover to the highest
-// priority among the rest.
+// If the higher priority localities are not reachable, failover to the
+// highest priority among the rest.
 TEST_P(FailoverTest, Failover) {
   SetNextResolution({});
   SetNextResolutionForLbChannelAllBalancers();
@@ -9372,8 +10520,8 @@ TEST_P(FailoverTest, UpdateInitialUnavailable) {
   delayed_resource_setter.join();
 }
 
-// Tests that after the localities' priorities are updated, we still choose the
-// highest READY priority with the updated localities.
+// Tests that after the localities' priorities are updated, we still choose
+// the highest READY priority with the updated localities.
 TEST_P(FailoverTest, UpdatePriority) {
   SetNextResolution({});
   SetNextResolutionForLbChannelAllBalancers();
@@ -9624,8 +10772,8 @@ TEST_P(DropTest, Update) {
                           {kThrottleDropType, kDropPerMillionForThrottle}};
   balancers_[0]->ads_service()->SetEdsResource(
       BuildEdsResource(args, DefaultEdsServiceName()));
-  // Wait until the drop rate increases to the middle of the two configs, which
-  // implies that the update has been in effect.
+  // Wait until the drop rate increases to the middle of the two configs,
+  // which implies that the update has been in effect.
   const double kDropRateThreshold =
       (kDropRateForLb + kDropRateForLbAndThrottle) / 2;
   size_t num_rpcs = kNumRpcsBoth;
@@ -9693,8 +10841,8 @@ class BalancerUpdateTest : public XdsEnd2endTest {
   BalancerUpdateTest() : XdsEnd2endTest(4, 3) {}
 };
 
-// Tests that the old LB call is still used after the balancer address update as
-// long as that call is still alive.
+// Tests that the old LB call is still used after the balancer address update
+// as long as that call is still alive.
 TEST_P(BalancerUpdateTest, UpdateBalancersButKeepUsingOriginalBalancer) {
   SetNextResolution({});
   SetNextResolutionForLbChannelAllBalancers();
@@ -9752,10 +10900,10 @@ TEST_P(BalancerUpdateTest, UpdateBalancersButKeepUsingOriginalBalancer) {
 }
 
 // Tests that the old LB call is still used after multiple balancer address
-// updates as long as that call is still alive. Send an update with the same set
-// of LBs as the one in SetUp() in order to verify that the LB channel inside
-// xds keeps the initial connection (which by definition is also present in the
-// update).
+// updates as long as that call is still alive. Send an update with the same
+// set of LBs as the one in SetUp() in order to verify that the LB channel
+// inside xds keeps the initial connection (which by definition is also
+// present in the update).
 TEST_P(BalancerUpdateTest, Repeated) {
   SetNextResolution({});
   SetNextResolutionForLbChannelAllBalancers();
@@ -10187,9 +11335,9 @@ class FaultInjectionTest : public XdsEnd2endTest {
  public:
   FaultInjectionTest() : XdsEnd2endTest(1, 1) {}
 
-  // Builds a Listener with Fault Injection filter config. If the http_fault is
-  // nullptr, then assign an empty filter config. This filter config is required
-  // to enable the fault injection features.
+  // Builds a Listener with Fault Injection filter config. If the http_fault
+  // is nullptr, then assign an empty filter config. This filter config is
+  // required to enable the fault injection features.
   static Listener BuildListenerWithFaultInjection(
       const HTTPFault& http_fault = HTTPFault()) {
     HttpConnectionManager http_connection_manager;
@@ -10501,9 +11649,9 @@ TEST_P(FaultInjectionTest, XdsFaultInjectionAlwaysDelayPercentageAbort) {
               ::testing::DoubleNear(kAbortRate, kErrorTolerance));
 }
 
-// This test and the above test apply different denominators to delay and abort.
-// This ensures that we are using the right denominator for each injected fault
-// in our code.
+// This test and the above test apply different denominators to delay and
+// abort. This ensures that we are using the right denominator for each
+// injected fault in our code.
 TEST_P(FaultInjectionTest,
        XdsFaultInjectionAlwaysDelayPercentageAbortSwitchDenominator) {
   const uint32_t kAbortPercentagePerMillion = 500000;
index 0623280..412bbed 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h"
 #include "src/core/lib/address_utils/sockaddr_utils.h"
+#include "src/core/lib/event_engine/sockaddr.h"
 #include "src/core/lib/iomgr/sockaddr.h"
 #include "src/proto/grpc/lb/v1/load_balancer.pb.h"  // C++ version
 
index caa86fc..1cee4eb 100644 (file)
@@ -151,7 +151,7 @@ void TcpUserTimeoutDestroy(grpc_socket_mutator* mutator) { gpr_free(mutator); }
 
 const grpc_socket_mutator_vtable kTcpUserTimeoutMutatorVtable =
     grpc_socket_mutator_vtable{TcpUserTimeoutMutateFd, TcpUserTimeoutCompare,
-                               TcpUserTimeoutDestroy};
+                               TcpUserTimeoutDestroy, nullptr};
 
 std::unique_ptr<TestService::Stub> CreateFallbackTestStub() {
   grpc::ChannelArguments channel_args;
@@ -280,11 +280,14 @@ int main(int argc, char** argv) {
     abort();
   }
 }
+
 #else
+
 int main(int argc, char** argv) {
   grpc::testing::InitTest(&argc, &argv, true);
   gpr_log(GPR_ERROR,
           "This test requires TCP_USER_TIMEOUT, which isn't available");
   abort();
 }
+
 #endif  // SOCKET_SUPPORTS_TCP_USER_TIMEOUT
index 92c6ccd..eeefe3e 100644 (file)
@@ -606,6 +606,7 @@ static void BM_TransportStreamRecv(benchmark::State& state) {
         op.on_complete = do_nothing.get();
         op.recv_message = true;
         op.payload->recv_message.recv_message = &recv_stream;
+        op.payload->recv_message.call_failed_before_recv_message = nullptr;
         op.payload->recv_message.recv_message_ready = drain_start.get();
         s->Op(&op);
         f.PushInput(grpc_slice_ref(incoming_data));
index e7b2824..9a5c6e7 100644 (file)
@@ -162,14 +162,14 @@ static gpr_mu shutdown_mu, mu;
 static gpr_cv shutdown_cv, cv;
 
 // Tag completion queue iterate times
-class TagCallback : public grpc_experimental_completion_queue_functor {
+class TagCallback : public grpc_completion_queue_functor {
  public:
   explicit TagCallback(int* iter) : iter_(iter) {
     functor_run = &TagCallback::Run;
     inlineable = false;
   }
   ~TagCallback() {}
-  static void Run(grpc_experimental_completion_queue_functor* cb, int ok) {
+  static void Run(grpc_completion_queue_functor* cb, int ok) {
     gpr_mu_lock(&mu);
     GPR_ASSERT(static_cast<bool>(ok));
     *static_cast<TagCallback*>(cb)->iter_ += 1;
@@ -182,14 +182,14 @@ class TagCallback : public grpc_experimental_completion_queue_functor {
 };
 
 // Check if completion queue is shut down
-class ShutdownCallback : public grpc_experimental_completion_queue_functor {
+class ShutdownCallback : public grpc_completion_queue_functor {
  public:
   explicit ShutdownCallback(bool* done) : done_(done) {
     functor_run = &ShutdownCallback::Run;
     inlineable = false;
   }
   ~ShutdownCallback() {}
-  static void Run(grpc_experimental_completion_queue_functor* cb, int ok) {
+  static void Run(grpc_completion_queue_functor* cb, int ok) {
     gpr_mu_lock(&shutdown_mu);
     *static_cast<ShutdownCallback*>(cb)->done_ = static_cast<bool>(ok);
     gpr_cv_signal(&shutdown_cv);
index cb6110a..b06deee 100644 (file)
@@ -62,7 +62,7 @@ class BlockingCounter {
 // number passed in (num_add) is greater than 0. Otherwise, it will decrement
 // the counter to indicate that task is finished. This functor will suicide at
 // the end, therefore, no need for caller to do clean-ups.
-class AddAnotherFunctor : public grpc_experimental_completion_queue_functor {
+class AddAnotherFunctor : public grpc_completion_queue_functor {
  public:
   AddAnotherFunctor(grpc_core::ThreadPool* pool, BlockingCounter* counter,
                     int num_add)
@@ -74,7 +74,7 @@ class AddAnotherFunctor : public grpc_experimental_completion_queue_functor {
   }
   // When the functor gets to run in thread pool, it will take itself as first
   // argument and internal_success as second one.
-  static void Run(grpc_experimental_completion_queue_functor* cb, int /*ok*/) {
+  static void Run(grpc_completion_queue_functor* cb, int /*ok*/) {
     auto* callback = static_cast<AddAnotherFunctor*>(cb);
     if (--callback->num_add_ > 0) {
       callback->pool_->Add(new AddAnotherFunctor(
@@ -128,7 +128,7 @@ BENCHMARK_TEMPLATE(ThreadPoolAddAnother, 2048)
     ->RangePair(524288, 524288, 1, 1024);
 
 // A functor class that will delete self on end of running.
-class SuicideFunctorForAdd : public grpc_experimental_completion_queue_functor {
+class SuicideFunctorForAdd : public grpc_completion_queue_functor {
  public:
   explicit SuicideFunctorForAdd(BlockingCounter* counter) : counter_(counter) {
     functor_run = &SuicideFunctorForAdd::Run;
@@ -137,7 +137,7 @@ class SuicideFunctorForAdd : public grpc_experimental_completion_queue_functor {
     internal_success = 0;
   }
 
-  static void Run(grpc_experimental_completion_queue_functor* cb, int /*ok*/) {
+  static void Run(grpc_completion_queue_functor* cb, int /*ok*/) {
     // On running, the first argument would be itself.
     auto* callback = static_cast<SuicideFunctorForAdd*>(cb);
     callback->counter_->DecrementCount();
@@ -179,7 +179,7 @@ BENCHMARK(BM_ThreadPoolExternalAdd)
 
 // Functor (closure) that adds itself into pool repeatedly. By adding self, the
 // overhead would be low and can measure the time of add more accurately.
-class AddSelfFunctor : public grpc_experimental_completion_queue_functor {
+class AddSelfFunctor : public grpc_completion_queue_functor {
  public:
   AddSelfFunctor(grpc_core::ThreadPool* pool, BlockingCounter* counter,
                  int num_add)
@@ -191,7 +191,7 @@ class AddSelfFunctor : public grpc_experimental_completion_queue_functor {
   }
   // When the functor gets to run in thread pool, it will take itself as first
   // argument and internal_success as second one.
-  static void Run(grpc_experimental_completion_queue_functor* cb, int /*ok*/) {
+  static void Run(grpc_completion_queue_functor* cb, int /*ok*/) {
     auto* callback = static_cast<AddSelfFunctor*>(cb);
     if (--callback->num_add_ > 0) {
       callback->pool_->Add(cb);
@@ -258,8 +258,7 @@ BENCHMARK_TEMPLATE(ThreadPoolAddSelf, 2048)->RangePair(524288, 524288, 1, 1024);
 
 // A functor (closure) that simulates closures with small but non-trivial amount
 // of work.
-class ShortWorkFunctorForAdd
-    : public grpc_experimental_completion_queue_functor {
+class ShortWorkFunctorForAdd : public grpc_completion_queue_functor {
  public:
   BlockingCounter* counter_;
 
@@ -270,7 +269,7 @@ class ShortWorkFunctorForAdd
     internal_success = 0;
     val_ = 0;
   }
-  static void Run(grpc_experimental_completion_queue_functor* cb, int /*ok*/) {
+  static void Run(grpc_completion_queue_functor* cb, int /*ok*/) {
     auto* callback = static_cast<ShortWorkFunctorForAdd*>(cb);
     // Uses pad to avoid compiler complaining unused variable error.
     callback->pad[0] = 0;
index 4807f53..436286b 100644 (file)
@@ -34,8 +34,7 @@ namespace testing {
  * BENCHMARKING KERNELS
  */
 
-class BidiClient
-    : public grpc::experimental::ClientBidiReactor<EchoRequest, EchoResponse> {
+class BidiClient : public grpc::ClientBidiReactor<EchoRequest, EchoResponse> {
  public:
   BidiClient(benchmark::State* state, EchoTestService::Stub* stub,
              ClientContext* cli_ctx, EchoRequest* request,
@@ -84,7 +83,7 @@ class BidiClient
     cli_ctx_->~ClientContext();
     new (cli_ctx_) ClientContext();
     cli_ctx_->AddMetadata(kServerMessageSize, std::to_string(msgs_size_));
-    stub_->experimental_async()->BidiStream(cli_ctx_, this);
+    stub_->async()->BidiStream(cli_ctx_, this);
     MaybeWrite();
     StartCall();
   }
index 3d52bce..049e361 100644 (file)
@@ -46,9 +46,9 @@ int GetIntValueFromMetadata(
 }
 }  // namespace
 
-experimental::ServerUnaryReactor* CallbackStreamingTestService::Echo(
-    experimental::CallbackServerContext* context,
-    const EchoRequest* /*request*/, EchoResponse* response) {
+ServerUnaryReactor* CallbackStreamingTestService::Echo(
+    CallbackServerContext* context, const EchoRequest* /*request*/,
+    EchoResponse* response) {
   int response_msgs_size = GetIntValueFromMetadata(
       kServerMessageSize, context->client_metadata(), 0);
   if (response_msgs_size > 0) {
@@ -61,13 +61,11 @@ experimental::ServerUnaryReactor* CallbackStreamingTestService::Echo(
   return reactor;
 }
 
-experimental::ServerBidiReactor<EchoRequest, EchoResponse>*
-CallbackStreamingTestService::BidiStream(
-    experimental::CallbackServerContext* context) {
-  class Reactor
-      : public experimental::ServerBidiReactor<EchoRequest, EchoResponse> {
+ServerBidiReactor<EchoRequest, EchoResponse>*
+CallbackStreamingTestService::BidiStream(CallbackServerContext* context) {
+  class Reactor : public ServerBidiReactor<EchoRequest, EchoResponse> {
    public:
-    explicit Reactor(experimental::CallbackServerContext* context) {
+    explicit Reactor(CallbackServerContext* context) {
       message_size_ = GetIntValueFromMetadata(kServerMessageSize,
                                               context->client_metadata(), 0);
       StartRead(&request_);
index f793d48..010c8e8 100644 (file)
@@ -32,17 +32,16 @@ namespace testing {
 
 const char* const kServerMessageSize = "server_message_size";
 
-class CallbackStreamingTestService
-    : public EchoTestService::ExperimentalCallbackService {
+class CallbackStreamingTestService : public EchoTestService::CallbackService {
  public:
   CallbackStreamingTestService() {}
 
-  experimental::ServerUnaryReactor* Echo(
-      experimental::CallbackServerContext* context, const EchoRequest* request,
-      EchoResponse* response) override;
+  ServerUnaryReactor* Echo(CallbackServerContext* context,
+                           const EchoRequest* request,
+                           EchoResponse* response) override;
 
-  experimental::ServerBidiReactor<EchoRequest, EchoResponse>* BidiStream(
-      experimental::CallbackServerContext* context) override;
+  ServerBidiReactor<EchoRequest, EchoResponse>* BidiStream(
+      CallbackServerContext* context) override;
 };
 }  // namespace testing
 }  // namespace grpc
index 1caa4f4..80e9310 100644 (file)
@@ -42,7 +42,7 @@ inline void SendCallbackUnaryPingPong(
     std::mutex* mu, std::condition_variable* cv) {
   int response_msgs_size = state->range(1);
   cli_ctx->AddMetadata(kServerMessageSize, std::to_string(response_msgs_size));
-  stub_->experimental_async()->Echo(
+  stub_->async()->Echo(
       cli_ctx, request, response,
       [state, cli_ctx, request, response, stub_, done, mu, cv](Status s) {
         GPR_ASSERT(s.ok());
index ca97bf3..3230792 100644 (file)
@@ -22,6 +22,7 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
+#include "src/core/lib/event_engine/sockaddr.h"
 #include "test/cpp/naming/dns_test_util.h"
 
 #ifdef GPR_WINDOWS
index c8dbee7..45c7ab3 100755 (executable)
@@ -85,5 +85,8 @@ def generate_resolver_component_tests():
                 "--test_bin_name=resolver_component_test%s" % unsecure_build_config_suffix,
                 "--running_under_bazel=true",
             ],
-            tags = ["no_windows", "no_mac"],
+            # The test is highly flaky on AWS workers that we use for running ARM64 tests.
+            # The "no_arm64" tag can be used to skip it.
+            # (see https://github.com/grpc/grpc/issues/25289).
+            tags = ["no_windows", "no_mac", "no_arm64"],
         )
index d0d3d4c..95e5a86 100644 (file)
@@ -176,10 +176,10 @@ class CallbackUnaryClient final : public CallbackClient {
       if (ctx_[vector_idx]->alarm_ == nullptr) {
         ctx_[vector_idx]->alarm_ = absl::make_unique<Alarm>();
       }
-      ctx_[vector_idx]->alarm_->experimental().Set(
-          next_issue_time, [this, t, vector_idx](bool /*ok*/) {
-            IssueUnaryCallbackRpc(t, vector_idx);
-          });
+      ctx_[vector_idx]->alarm_->Set(next_issue_time,
+                                    [this, t, vector_idx](bool /*ok*/) {
+                                      IssueUnaryCallbackRpc(t, vector_idx);
+                                    });
     } else {
       IssueUnaryCallbackRpc(t, vector_idx);
     }
@@ -188,7 +188,7 @@ class CallbackUnaryClient final : public CallbackClient {
   void IssueUnaryCallbackRpc(Thread* t, size_t vector_idx) {
     GPR_TIMER_SCOPE("CallbackUnaryClient::ThreadFunc", 0);
     double start = UsageTimer::Now();
-    ctx_[vector_idx]->stub_->experimental_async()->UnaryCall(
+    ctx_[vector_idx]->stub_->async()->UnaryCall(
         (&ctx_[vector_idx]->context_), &request_, &ctx_[vector_idx]->response_,
         [this, t, start, vector_idx](grpc::Status s) {
           // Update Histogram with data from the callback run
@@ -251,8 +251,7 @@ class CallbackStreamingPingPongClient : public CallbackStreamingClient {
 };
 
 class CallbackStreamingPingPongReactor final
-    : public grpc::experimental::ClientBidiReactor<SimpleRequest,
-                                                   SimpleResponse> {
+    : public grpc::ClientBidiReactor<SimpleRequest, SimpleResponse> {
  public:
   CallbackStreamingPingPongReactor(
       CallbackStreamingPingPongClient* client,
@@ -260,7 +259,7 @@ class CallbackStreamingPingPongReactor final
       : client_(client), ctx_(std::move(ctx)), messages_issued_(0) {}
 
   void StartNewRpc() {
-    ctx_->stub_->experimental_async()->StreamingCall(&(ctx_->context_), this);
+    ctx_->stub_->async()->StreamingCall(&(ctx_->context_), this);
     write_time_ = UsageTimer::Now();
     StartWrite(client_->request());
     writes_done_started_.clear();
@@ -296,7 +295,7 @@ class CallbackStreamingPingPongReactor final
       gpr_timespec next_issue_time = client_->NextRPCIssueTime();
       // Start an alarm callback to run the internal callback after
       // next_issue_time
-      ctx_->alarm_->experimental().Set(next_issue_time, [this](bool /*ok*/) {
+      ctx_->alarm_->Set(next_issue_time, [this](bool /*ok*/) {
         write_time_ = UsageTimer::Now();
         StartWrite(client_->request());
       });
@@ -323,8 +322,8 @@ class CallbackStreamingPingPongReactor final
       if (ctx_->alarm_ == nullptr) {
         ctx_->alarm_ = absl::make_unique<Alarm>();
       }
-      ctx_->alarm_->experimental().Set(next_issue_time,
-                                       [this](bool /*ok*/) { StartNewRpc(); });
+      ctx_->alarm_->Set(next_issue_time,
+                        [this](bool /*ok*/) { StartNewRpc(); });
     } else {
       StartNewRpc();
     }
index 8fe6848..e633c7e 100755 (executable)
 import gen_build_yaml as gen
 import json
 
+COPYRIGHT = """
+# Copyright 2021 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.
+"""
+
 
 def generate_args():
     all_scenario_set = gen.generate_yaml()
@@ -34,6 +50,7 @@ def generate_args():
 
     serialized_scenarios_str = str(all_scenarios).encode('ascii', 'ignore')
     with open('json_run_localhost_scenarios.bzl', 'wb') as f:
+        f.write(COPYRIGHT)
         f.write('"""Scenarios run on localhost."""\n\n')
         f.write('JSON_RUN_LOCALHOST_SCENARIOS = ' + serialized_scenarios_str +
                 '\n')
index 81b2a41..b864297 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 """Scenarios run on localhost."""
 
 JSON_RUN_LOCALHOST_SCENARIOS = {"cpp_protobuf_async_unary_75Kqps_600channel_60Krpcs_300Breq_50Bresp": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_75Kqps_600channel_60Krpcs_300Breq_50Bresp", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_server_threads": 16, "threads_per_cq": 1, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "UNARY", "load_params": {"poisson": {"offered_load": 37500}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 50, "req_size": 300}}, "client_channels": 300, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_from_client_qps_unconstrained_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_from_client_qps_unconstrained_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_CLIENT", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_unary_qps_unconstrained_1cq_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_qps_unconstrained_1cq_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 1000000, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 13, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 1000000, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_from_client_ping_pong_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_from_client_ping_pong_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_CLIENT", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_generic_async_streaming_qps_1channel_1MBmsg_secure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_1channel_1MBmsg_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 1048576, "req_size": 1048576}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 1048576, "req_size": 1048576}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_from_server_ping_pong_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_from_server_ping_pong_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_SERVER", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_1mps_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_1mps_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "messages_per_stream": 1, "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_64KBmsg_secure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_64KBmsg_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 65536, "req_size": 65536}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 65536, "req_size": 65536}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_ping_pong_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_ping_pong_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_generic_async_streaming_qps_unconstrained_1mps_secure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_1mps_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "messages_per_stream": 1, "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_1cq_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_1cq_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 1000000, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 13, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 1000000, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_10mps_secure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_10mps_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "messages_per_stream": 10, "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_unary_ping_pong_insecure_1MB": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_ping_pong_insecure_1MB", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 1048576, "req_size": 1048576}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_generic_async_streaming_qps_one_server_core_secure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_one_server_core_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 1, "security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 13, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_qps_unconstrained_10mps_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_qps_unconstrained_10mps_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "messages_per_stream": 10, "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_unary_qps_unconstrained_2waysharedcq_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_qps_unconstrained_2waysharedcq_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 2, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 2, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_from_server_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_from_server_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING_FROM_SERVER", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_2waysharedcq_secure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_2waysharedcq_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 2, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 2, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_ping_pong_secure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_ping_pong_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 1, "security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_sync_streaming_from_server_qps_unconstrained_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_from_server_qps_unconstrained_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_SERVER", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_qps_unconstrained_10mps_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_qps_unconstrained_10mps_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "messages_per_stream": 10, "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_unary_qps_unconstrained_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_unary_qps_unconstrained_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_from_client_ping_pong_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_from_client_ping_pong_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_CLIENT", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_sync_streaming_from_client_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_from_client_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_CLIENT", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_from_client_ping_pong_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_from_client_ping_pong_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_CLIENT", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_2waysharedcq_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_2waysharedcq_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 2, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 2, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_1cq_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_1cq_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 1000000, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 13, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 1000000, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_10mps_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_10mps_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "messages_per_stream": 10, "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_64KBmsg_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_64KBmsg_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "payload_config": {"bytebuf_params": {"resp_size": 65536, "req_size": 65536}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 65536, "req_size": 65536}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_qps_unconstrained_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_qps_unconstrained_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_from_server_qps_unconstrained_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_from_server_qps_unconstrained_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING_FROM_SERVER", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_client_unary_1channel_64wide_128Breq_8MBresp_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_client_unary_1channel_64wide_128Breq_8MBresp_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 8388608, "req_size": 128}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_streaming_ping_pong_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_ping_pong_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_sync_streaming_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_from_client_ping_pong_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_from_client_ping_pong_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_CLIENT", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_streaming_from_client_1channel_1MB": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_from_client_1channel_1MB", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_CLIENT", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 1048576, "req_size": 1048576}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_unary_1channel_100rpcs_1MB": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_1channel_100rpcs_1MB", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 1048576, "req_size": 1048576}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_generic_async_streaming_qps_unconstrained_secure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_1channel_1MBmsg_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_1channel_1MBmsg_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "payload_config": {"bytebuf_params": {"resp_size": 1048576, "req_size": 1048576}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 1048576, "req_size": 1048576}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_1mps_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_1mps_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "messages_per_stream": 1, "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_2waysharedcq_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_2waysharedcq_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 2, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 2, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_ping_pong_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_ping_pong_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_sync_streaming_from_server_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_from_server_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_SERVER", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_client_sync_server_unary_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_client_sync_server_unary_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 10, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_unary_ping_pong_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_ping_pong_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_client_unary_1channel_64wide_128Breq_8MBresp_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_client_unary_1channel_64wide_128Breq_8MBresp_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 8388608, "req_size": 128}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_generic_async_streaming_ping_pong_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_ping_pong_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 1, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_sync_streaming_from_server_ping_pong_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_from_server_ping_pong_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_SERVER", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_unary_qps_unconstrained_1cq_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_qps_unconstrained_1cq_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 1000000, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 13, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 1000000, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_unary_ping_pong_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_unary_ping_pong_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 10, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_one_server_core_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_one_server_core_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 1, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 13, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 10, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_from_server_ping_pong_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_from_server_ping_pong_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_SERVER", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_generic_async_streaming_qps_unconstrained_2waysharedcq_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_2waysharedcq_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 2, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 2, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_qps_unconstrained_1mps_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_qps_unconstrained_1mps_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "messages_per_stream": 1, "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_unary_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_unary_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_unary_ping_pong_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_ping_pong_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_unary_qps_unconstrained_2waysharedcq_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_qps_unconstrained_2waysharedcq_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 2, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 2, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_from_client_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_from_client_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING_FROM_CLIENT", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_unary_ping_pong_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_unary_ping_pong_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_unary_qps_unconstrained_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_qps_unconstrained_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_from_server_ping_pong_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_from_server_ping_pong_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_SERVER", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_client_sync_server_unary_qps_unconstrained_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_client_sync_server_unary_qps_unconstrained_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 10, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_unary_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_1cq_secure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_1cq_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 1000000, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 13, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 1000000, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_10mps_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_10mps_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "messages_per_stream": 10, "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_10mps_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_10mps_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "messages_per_stream": 10, "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_ping_pong_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_ping_pong_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_server_threads": 1, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_streaming_from_client_qps_unconstrained_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_from_client_qps_unconstrained_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING_FROM_CLIENT", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_1cq_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_1cq_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 1000000, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 13, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 1000000, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_qps_unconstrained_1mps_secure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_qps_unconstrained_1mps_secure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "messages_per_stream": 1, "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_1mps_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_1mps_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "messages_per_stream": 1, "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_unary_ping_pong_secure_1MB": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_ping_pong_secure_1MB", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": {"use_test_ca": true, "server_host_override": "foo.test.google.fr"}, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 1048576, "req_size": 1048576}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\''}
index 01970a9..5c0fde0 100755 (executable)
 import gen_build_yaml as gen
 import json
 
+COPYRIGHT = """
+# Copyright 2021 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.
+"""
+
 
 def generate_args():
     all_scenario_set = gen.generate_yaml()
@@ -34,6 +50,7 @@ def generate_args():
 
     serialized_scenarios_str = str(all_scenarios).encode('ascii', 'ignore')
     with open('qps_json_driver_scenarios.bzl', 'w') as f:
+        f.write(COPYRIGHT)
         f.write('"""Scenarios of qps driver."""\n\n')
         f.write('QPS_JSON_DRIVER_SCENARIOS = ' + serialized_scenarios_str +
                 '\n')
index 35ca167..43401be 100644 (file)
@@ -1,3 +1,16 @@
+# Copyright 2021 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.
 """Scenarios of qps driver."""
 
 QPS_JSON_DRIVER_SCENARIOS = {"cpp_protobuf_sync_streaming_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 16, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_qps_unconstrained_1mps_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_qps_unconstrained_1mps_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 16, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "messages_per_stream": 1, "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_unary_ping_pong_insecure_1MB": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_ping_pong_insecure_1MB", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 1048576, "req_size": 1048576}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_sync_streaming_qps_unconstrained_10mps_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_qps_unconstrained_10mps_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 16, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "messages_per_stream": 10, "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_unary_qps_unconstrained_2waysharedcq_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_qps_unconstrained_2waysharedcq_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 2, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 2, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_unary_qps_unconstrained_1cq_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_qps_unconstrained_1cq_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 1000000, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 13, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 1000000, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_2waysharedcq_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_2waysharedcq_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 2, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 2, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_1cq_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_1cq_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 1000000, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 13, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 1000000, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_10mps_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_10mps_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "messages_per_stream": 10, "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_64KBmsg_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_64KBmsg_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "payload_config": {"bytebuf_params": {"resp_size": 65536, "req_size": 65536}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 65536, "req_size": 65536}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_1cq_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_1cq_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 1000000, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 13, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 1000000, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_from_server_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_from_server_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING_FROM_SERVER", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_from_client_1channel_1MB": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_from_client_1channel_1MB", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING_FROM_CLIENT", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 1048576, "req_size": 1048576}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_unary_1channel_100rpcs_1MB": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_1channel_100rpcs_1MB", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 1048576, "req_size": 1048576}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_generic_async_streaming_qps_1channel_1MBmsg_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_1channel_1MBmsg_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "payload_config": {"bytebuf_params": {"resp_size": 1048576, "req_size": 1048576}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 1048576, "req_size": 1048576}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_1mps_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_1mps_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "messages_per_stream": 1, "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_client_sync_server_unary_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_client_sync_server_unary_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_client_unary_1channel_64wide_128Breq_8MBresp_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_client_unary_1channel_64wide_128Breq_8MBresp_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 8388608, "req_size": 128}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_sync_streaming_from_client_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_from_client_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 16, "rpc_type": "STREAMING_FROM_CLIENT", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_streaming_from_client_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_from_client_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING_FROM_CLIENT", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_2waysharedcq_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_2waysharedcq_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 2, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 2, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_unary_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_unary_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 16, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_sync_streaming_from_server_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_sync_streaming_from_server_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "SYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 16, "rpc_type": "STREAMING_FROM_SERVER", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "SYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_protobuf_async_unary_qps_unconstrained_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_unary_qps_unconstrained_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 3, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "UNARY", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 3, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_qps_unconstrained_10mps_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_qps_unconstrained_10mps_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 0, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "messages_per_stream": 10, "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\'', "cpp_generic_async_streaming_ping_pong_insecure": '\'{\'scenarios\' : [{"name": "cpp_generic_async_streaming_ping_pong_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"async_server_threads": 1, "security_params": null, "server_type": "ASYNC_GENERIC_SERVER", "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "latency", "name": "grpc.optimization_target"}], "async_client_threads": 1, "outstanding_rpcs_per_channel": 1, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "payload_config": {"bytebuf_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 1, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 1}]}\'', "cpp_protobuf_async_streaming_qps_unconstrained_1mps_insecure": '\'{\'scenarios\' : [{"name": "cpp_protobuf_async_streaming_qps_unconstrained_1mps_insecure", "warmup_seconds": 0, "benchmark_seconds": 1, "num_servers": 1, "server_config": {"security_params": null, "server_type": "ASYNC_SERVER", "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_server_threads": 0, "threads_per_cq": 0, "server_processes": 0}, "client_config": {"security_params": null, "channel_args": [{"str_value": "throughput", "name": "grpc.optimization_target"}, {"int_value": 1, "name": "grpc.minimal_stack"}], "async_client_threads": 0, "outstanding_rpcs_per_channel": 100, "rpc_type": "STREAMING", "load_params": {"closed_loop": {}}, "histogram_params": {"resolution": 0.01, "max_possible": 60000000000.0}, "client_type": "ASYNC_CLIENT", "messages_per_stream": 1, "payload_config": {"simple_params": {"resp_size": 0, "req_size": 0}}, "client_channels": 64, "threads_per_cq": 0, "client_processes": 0}, "num_clients": 0}]}\''}
index a0a9531..e05f736 100644 (file)
@@ -31,22 +31,22 @@ namespace grpc {
 namespace testing {
 
 class BenchmarkCallbackServiceImpl final
-    : public BenchmarkService::ExperimentalCallbackService {
+    : public BenchmarkService::CallbackService {
  public:
-  ::grpc::experimental::ServerUnaryReactor* UnaryCall(
-      ::grpc::experimental::CallbackServerContext* context,
-      const SimpleRequest* request, SimpleResponse* response) override {
+  ::grpc::ServerUnaryReactor* UnaryCall(::grpc::CallbackServerContext* context,
+                                        const SimpleRequest* request,
+                                        SimpleResponse* response) override {
     auto* reactor = context->DefaultReactor();
     reactor->Finish(SetResponse(request, response));
     return reactor;
   }
 
-  ::grpc::experimental::ServerBidiReactor<::grpc::testing::SimpleRequest,
-                                          ::grpc::testing::SimpleResponse>*
-  StreamingCall(::grpc::experimental::CallbackServerContext*) override {
+  ::grpc::ServerBidiReactor<::grpc::testing::SimpleRequest,
+                            ::grpc::testing::SimpleResponse>*
+  StreamingCall(::grpc::CallbackServerContext*) override {
     class Reactor
-        : public ::grpc::experimental::ServerBidiReactor<
-              ::grpc::testing::SimpleRequest, ::grpc::testing::SimpleResponse> {
+        : public ::grpc::ServerBidiReactor<::grpc::testing::SimpleRequest,
+                                           ::grpc::testing::SimpleResponse> {
      public:
       Reactor() { StartRead(&request_); }
 
index cb1972a..549c859 100644 (file)
@@ -78,3 +78,18 @@ grpc_cc_test(
         "//test/core/util:grpc_test_util",
     ],
 )
+
+grpc_cc_test(
+    name = "authorization_policy_provider_test",
+    srcs = ["authorization_policy_provider_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//:grpc++",
+        "//:grpc++_authorization_provider",
+        "//test/core/util:grpc_test_util",
+    ],
+)
diff --git a/test/cpp/server/authorization_policy_provider_test.cc b/test/cpp/server/authorization_policy_provider_test.cc
new file mode 100644 (file)
index 0000000..16e2a5d
--- /dev/null
@@ -0,0 +1,57 @@
+// 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 <grpcpp/security/authorization_policy_provider.h>
+#include <gtest/gtest.h>
+
+#include "test/core/util/test_config.h"
+
+namespace grpc {
+
+TEST(AuthorizationPolicyProviderTest, StaticDataCreateReturnsProvider) {
+  const char* authz_policy =
+      "{"
+      "  \"name\": \"authz\","
+      "  \"allow_rules\": ["
+      "    {"
+      "      \"name\": \"allow_policy\""
+      "    }"
+      "  ]"
+      "}";
+  grpc::Status status;
+  auto provider = experimental::StaticDataAuthorizationPolicyProvider::Create(
+      authz_policy, &status);
+  ASSERT_NE(provider, nullptr);
+  EXPECT_NE(provider->c_provider(), nullptr);
+  EXPECT_TRUE(status.ok());
+  EXPECT_TRUE(status.error_message().empty());
+}
+
+TEST(AuthorizationPolicyProviderTest, StaticDataCreateReturnsErrorStatus) {
+  const char* authz_policy = "{}";
+  grpc::Status status;
+  auto provider = experimental::StaticDataAuthorizationPolicyProvider::Create(
+      authz_policy, &status);
+  ASSERT_EQ(provider, nullptr);
+  EXPECT_EQ(status.error_code(), grpc::StatusCode::INVALID_ARGUMENT);
+  EXPECT_EQ(status.error_message(), "\"name\" field is not present.");
+}
+
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  grpc::testing::TestEnvironment env(argc, argv);
+  return RUN_ALL_TESTS();
+}
index b0bedeb..285f68b 100644 (file)
@@ -46,6 +46,7 @@ const grpc_socket_mutator_vtable mock_socket_mutator_vtable = {
     mock_socket_mutator_mutate_fd,
     mock_socket_mutator_compare,
     mock_socket_mutator_destroy,
+    nullptr,
 };
 
 class MockSocketMutator : public grpc_socket_mutator {
index 6c6b94b..13d816c 100644 (file)
@@ -39,6 +39,22 @@ grpc_cc_test(
 )
 
 grpc_cc_test(
+    name = "client_context_test_peer_test",
+    srcs = ["client_context_test_peer_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//:grpc++",
+        "//:grpc++_test",
+        "//test/core/util:grpc_test_util",
+        "//test/cpp/util:test_util",
+    ],
+)
+
+grpc_cc_test(
     name = "server_context_test_spouse_test",
     srcs = ["server_context_test_spouse_test.cc"],
     external_deps = [
diff --git a/test/cpp/test/client_context_test_peer_test.cc b/test/cpp/test/client_context_test_peer_test.cc
new file mode 100644 (file)
index 0000000..35f376f
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *
+ * 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 <grpcpp/test/client_context_test_peer.h>
+
+#include <cstring>
+#include <vector>
+
+#include <grpcpp/impl/grpc_library.h>
+#include <gtest/gtest.h>
+
+namespace grpc {
+namespace testing {
+
+static internal::GrpcLibraryInitializer g_initializer;
+
+const char key1[] = "metadata-key1";
+const char key2[] = "metadata-key2";
+const char val1[] = "metadata-val1";
+const char val2[] = "metadata-val2";
+
+bool ServerInitialMetadataContains(const ClientContext& context,
+                                   const grpc::string_ref& key,
+                                   const grpc::string_ref& value) {
+  const auto& server_metadata = context.GetServerInitialMetadata();
+  for (auto iter = server_metadata.begin(); iter != server_metadata.end();
+       ++iter) {
+    if (iter->first == key && iter->second == value) {
+      return true;
+    }
+  }
+  return true;
+}
+
+TEST(ClientContextTestPeerTest, AddServerInitialMetadata) {
+  ClientContext context;
+  ClientContextTestPeer peer(&context);
+
+  peer.AddServerInitialMetadata(key1, val1);
+  ASSERT_TRUE(ServerInitialMetadataContains(context, key1, val1));
+  peer.AddServerInitialMetadata(key2, val2);
+  ASSERT_TRUE(ServerInitialMetadataContains(context, key1, val1));
+  ASSERT_TRUE(ServerInitialMetadataContains(context, key2, val2));
+}
+
+TEST(ClientContextTestPeerTest, GetSendInitialMetadata) {
+  ClientContext context;
+  ClientContextTestPeer peer(&context);
+  std::multimap<std::string, std::string> metadata;
+
+  context.AddMetadata(key1, val1);
+  metadata.insert(std::pair<std::string, std::string>(key1, val1));
+  ASSERT_EQ(metadata, peer.GetSendInitialMetadata());
+
+  context.AddMetadata(key2, val2);
+  metadata.insert(std::pair<std::string, std::string>(key2, val2));
+  ASSERT_EQ(metadata, peer.GetSendInitialMetadata());
+}
+
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 6545762..3338f32 100644 (file)
@@ -52,6 +52,16 @@ TEST(MockStreamTest, Basic) {
   auto carw = absl::make_unique<
       grpc::testing::MockClientAsyncReaderWriter<EchoRequest, EchoResponse>>();
   ASSERT_NE(carw, nullptr);
+
+  auto sr = absl::make_unique<grpc::testing::MockServerReader<EchoRequest>>();
+  ASSERT_NE(sr, nullptr);
+
+  auto sw = absl::make_unique<grpc::testing::MockServerWriter<EchoResponse>>();
+  ASSERT_NE(sw, nullptr);
+
+  auto srw = absl::make_unique<
+      grpc::testing::MockServerReaderWriter<EchoResponse, EchoRequest>>();
+  ASSERT_NE(srw, nullptr);
 }
 
 int main(int argc, char** argv) {
index c63f351..5d6c183 100644 (file)
@@ -123,6 +123,35 @@ TEST_F(ByteBufferTest, SerializationMakesCopy) {
   EXPECT_TRUE(send_buffer.Valid());
 }
 
+TEST_F(ByteBufferTest, TrySingleSliceWithSingleSlice) {
+  std::vector<Slice> slices;
+  slices.emplace_back(kContent1);
+  ByteBuffer buffer(&slices[0], 1);
+  Slice slice;
+  EXPECT_TRUE(buffer.TrySingleSlice(&slice).ok());
+  EXPECT_EQ(slice.size(), slices[0].size());
+  EXPECT_EQ(memcmp(slice.begin(), slices[0].begin(), slice.size()), 0);
+}
+
+TEST_F(ByteBufferTest, TrySingleSliceWithMultipleSlices) {
+  std::vector<Slice> slices;
+  slices.emplace_back(kContent1);
+  slices.emplace_back(kContent2);
+  ByteBuffer buffer(&slices[0], 2);
+  Slice slice;
+  EXPECT_FALSE(buffer.TrySingleSlice(&slice).ok());
+}
+
+TEST_F(ByteBufferTest, DumpToSingleSlice) {
+  std::vector<Slice> slices;
+  slices.emplace_back(kContent1);
+  slices.emplace_back(kContent2);
+  ByteBuffer buffer(&slices[0], 2);
+  Slice slice;
+  EXPECT_TRUE(buffer.DumpToSingleSlice(&slice).ok());
+  EXPECT_EQ(strlen(kContent1) + strlen(kContent2), slice.size());
+}
+
 }  // namespace
 }  // namespace grpc
 
index 870d476..bc45cbf 100644 (file)
@@ -62,7 +62,8 @@ using grpc::testing::EchoResponse;
   "RequestStream\n"               \
   "ResponseStream\n"              \
   "BidiStream\n"                  \
-  "Unimplemented\n"
+  "Unimplemented\n"               \
+  "UnimplementedBidi\n"
 
 #define ECHO_TEST_SERVICE_DESCRIPTION                                          \
   "filename: src/proto/grpc/testing/echo.proto\n"                              \
@@ -88,6 +89,8 @@ using grpc::testing::EchoResponse;
   "grpc.testing.EchoResponse) {}\n"                                            \
   "  rpc Unimplemented(grpc.testing.EchoRequest) returns "                     \
   "(grpc.testing.EchoResponse) {}\n"                                           \
+  "  rpc UnimplementedBidi(stream grpc.testing.EchoRequest) returns (stream "  \
+  "grpc.testing.EchoResponse) {}\n"                                            \
   "}\n"                                                                        \
   "\n"
 
index aa27a0e..25041de 100644 (file)
@@ -124,6 +124,12 @@ TEST_F(SliceTest, Add) {
   CheckSlice(spp, kContent);
 }
 
+TEST_F(SliceTest, Sub) {
+  Slice spp("0123456789");
+  Slice sub = spp.sub(1, 9);
+  CheckSlice(sub, "12345678");
+}
+
 TEST_F(SliceTest, Cslice) {
   grpc_slice s = grpc_slice_from_copied_string(kContent);
   Slice spp(s, Slice::STEAL_REF);
diff --git a/test/spm_build/test.cc b/test/spm_build/test.cc
new file mode 100644 (file)
index 0000000..2eab4ac
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ * 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 "grpcpp/channel.h"
+#include "grpcpp/client_context.h"
+#include "grpcpp/completion_queue.h"
+#include "grpcpp/create_channel.h"
+#include "grpcpp/generic/generic_stub.h"
+#include "grpcpp/grpcpp.h"
+#include "grpcpp/impl/codegen/grpc_library.h"
+#include "grpcpp/support/byte_buffer.h"
+#include "grpcpp/support/status.h"
+#include "grpcpp/support/status_code_enum.h"
+#include "grpcpp/support/string_ref.h"
index 1e510e9..793e45d 100644 (file)
@@ -19,9 +19,8 @@ because they're not going well yet on some of our test machinaries or
 platforms it supports. The following is a list of targets that are NOT
 ready to use.
 
-- `absl/synchronization:*`: This will be ready from the LTS version in 2021.
+- `absl/synchronization:*`: Blocked by b/186685878.
 - `absl/random`: [WIP](https://github.com/grpc/grpc/pull/23346).
-- `absl/types:variant`: [WIP](https://github.com/grpc/grpc/pull/22961).
 
 ## Implemetation only
 
index b048db0..423082f 100644 (file)
@@ -1,6 +1,6 @@
 genrule(
     name = "copy_six",
-    srcs = ["six-1.12.0/six.py"],
+    srcs = ["six-1.16.0/six.py"],
     outs = ["__init__.py"],
     cmd = "cp $< $(@)",
 )
index 63ed717..177d0ec 100755 (executable)
@@ -123,7 +123,6 @@ proto_files=( \
   "google/protobuf/timestamp.proto" \
   "google/protobuf/wrappers.proto" \
   "google/rpc/status.proto" \
-  "src/proto/grpc/auth/v1/authz_policy.proto" \
   "src/proto/grpc/gcp/altscontext.proto" \
   "src/proto/grpc/gcp/handshaker.proto" \
   "src/proto/grpc/gcp/transport_security_common.proto" \
index b8cc7a8..9254956 100755 (executable)
@@ -62,6 +62,8 @@ LICENSE_PREFIX = {
     '.proto': r'//\s*',
     '.cs': r'//\s*',
     '.mak': r'#\s*',
+    '.bazel': r'#\s*',
+    '.bzl': r'#\s*',
     'Makefile': r'#\s*',
     'Dockerfile': r'#\s*',
     'BUILD': r'#\s*',
index 060c2b7..83faa93 100755 (executable)
@@ -32,6 +32,7 @@ DIRS=(
 
 TEST_DIRS=(
     'src/python/grpcio_tests/tests'
+    'src/python/grpcio_tests/tests_gevent'
 )
 
 VIRTUALENV=python_pylint_venv
index c04d415..18cb1fc 100644 (file)
@@ -14,5 +14,5 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!!
 
-VERSION = '1.38.1'
+VERSION = '1.39.0'
 PROTOBUF_VERSION = '3.15.8'
index e4158c2..68b66d6 100644 (file)
@@ -14,5 +14,5 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!!
 
-VERSION = '1.38.1'
+VERSION = '1.39.0'
 PROTOBUF_VERSION = '3.15.8'
index f0796a4..6f1195c 100644 (file)
@@ -35,4 +35,7 @@ RUN cp -rL /workdir/bazel-bin/test/cpp/interop/xds_interop_client /artifacts/
 FROM phusion/baseimage:master@sha256:65ea10d5f757e5e86272625f8675d437dd83d8db64bdb429e2354d58f5462750
 COPY --from=0 /artifacts ./
 
+ENV GRPC_VERBOSITY="DEBUG"
+ENV GRPC_TRACE="xds_client,xds_resolver,xds_cluster_manager_lb,cds_lb,eds_lb,xds_cluster_resolver_lb,priority_lb,xds_cluster_impl_lb,weighted_target_lb,lrs_lb,xds_server_config_fetcher"
+
 ENTRYPOINT ["/xds_interop_client"]
index cd081e1..9f90cbd 100644 (file)
@@ -35,4 +35,7 @@ RUN cp -rL /workdir/bazel-bin/test/cpp/interop/xds_interop_server /artifacts/
 FROM phusion/baseimage:master@sha256:65ea10d5f757e5e86272625f8675d437dd83d8db64bdb429e2354d58f5462750
 COPY --from=0 /artifacts ./
 
+ENV GRPC_VERBOSITY="DEBUG"
+ENV GRPC_TRACE="xds_client,xds_resolver,xds_cluster_manager_lb,cds_lb,eds_lb,xds_cluster_resolver_lb,priority_lb,weighted_target_lb,lrs_lb,xds_cluster_impl_lb,xds_server_config_fetcher"
+
 ENTRYPOINT ["/xds_interop_server"]
diff --git a/tools/dockerfile/test/cxx_buster_openssl102_x64/Dockerfile b/tools/dockerfile/test/cxx_buster_openssl102_x64/Dockerfile
new file mode 100644 (file)
index 0000000..f133760
--- /dev/null
@@ -0,0 +1,98 @@
+# Copyright 2021 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.
+
+FROM debian:buster
+
+# Install Git and basic packages.
+RUN apt-get update && apt-get install -y \
+  autoconf \
+  autotools-dev \
+  build-essential \
+  bzip2 \
+  ccache \
+  curl \
+  dnsutils \
+  gcc \
+  gcc-multilib \
+  git \
+  golang \
+  gyp \
+  lcov \
+  libc6 \
+  libc6-dbg \
+  libc6-dev \
+  libgtest-dev \
+  libtool \
+  make \
+  perl \
+  strace \
+  python-dev \
+  python-setuptools \
+  python-yaml \
+  telnet \
+  unzip \
+  wget \
+  zip && apt-get clean
+
+#================
+# Build profiling
+RUN apt-get update && apt-get install -y time && apt-get clean
+
+#====================
+# Python dependencies
+
+# Install dependencies
+
+RUN apt-get update && apt-get install -y \
+    python-all-dev \
+    python3-all-dev \
+    python-setuptools
+
+# Install Python packages from PyPI
+RUN curl https://bootstrap.pypa.io/pip/2.7/get-pip.py | python2.7
+RUN pip install --upgrade pip==19.3.1
+RUN pip install virtualenv==16.7.9
+RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.15.0 twisted==17.5.0
+
+# Google Cloud platform API libraries
+RUN pip install --upgrade google-auth==1.24.0 google-api-python-client==1.12.8 oauth2client==4.1.0
+
+#=================
+# C++ dependencies
+RUN apt-get update && apt-get -y install libgflags-dev libgtest-dev libc++-dev clang && apt-get clean
+
+#=================
+# Install cmake
+# Note that this step should be only used for distributions that have new enough cmake to satisfy gRPC's cmake version requirement.
+
+RUN apt-get update && apt-get install -y cmake && apt-get clean
+
+
+RUN mkdir /var/local/jenkins
+
+
+# Install openssl 1.0.2 from source
+RUN apt-get update && apt-get install -y build-essential zlib1g-dev
+RUN cd /tmp && \
+    wget https://www.openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz && \
+    tar -xf openssl-1.0.2u.tar.gz && \
+    cd openssl-1.0.2u && \
+    ./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared zlib && \
+    make -j 4 && \
+    make install && \
+    rm -rf /tmp/openssl-1.0.2u*
+ENV OPENSSL_ROOT_DIR=/usr/local/ssl
+
+# Define the default command.
+CMD ["bash"]
index cde7829..576c296 100644 (file)
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC C++"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 1.38.1
+PROJECT_NUMBER         = 1.39.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
@@ -881,7 +881,7 @@ include/grpc/byte_buffer.h \
 include/grpc/byte_buffer_reader.h \
 include/grpc/census.h \
 include/grpc/compression.h \
-include/grpc/event_engine/channel_args.h \
+include/grpc/event_engine/endpoint_config.h \
 include/grpc/event_engine/event_engine.h \
 include/grpc/event_engine/port.h \
 include/grpc/event_engine/slice_allocator.h \
@@ -1013,6 +1013,7 @@ include/grpcpp/impl/service_type.h \
 include/grpcpp/resource_quota.h \
 include/grpcpp/security/auth_context.h \
 include/grpcpp/security/auth_metadata_processor.h \
+include/grpcpp/security/authorization_policy_provider.h \
 include/grpcpp/security/credentials.h \
 include/grpcpp/security/server_credentials.h \
 include/grpcpp/security/tls_certificate_provider.h \
index c1afc03..9423a77 100644 (file)
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC C++"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 1.38.1
+PROJECT_NUMBER         = 1.39.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
@@ -881,7 +881,7 @@ include/grpc/byte_buffer.h \
 include/grpc/byte_buffer_reader.h \
 include/grpc/census.h \
 include/grpc/compression.h \
-include/grpc/event_engine/channel_args.h \
+include/grpc/event_engine/endpoint_config.h \
 include/grpc/event_engine/event_engine.h \
 include/grpc/event_engine/port.h \
 include/grpc/event_engine/slice_allocator.h \
@@ -1013,6 +1013,7 @@ include/grpcpp/impl/service_type.h \
 include/grpcpp/resource_quota.h \
 include/grpcpp/security/auth_context.h \
 include/grpcpp/security/auth_metadata_processor.h \
+include/grpcpp/security/authorization_policy_provider.h \
 include/grpcpp/security/credentials.h \
 include/grpcpp/security/server_credentials.h \
 include/grpcpp/security/tls_certificate_provider.h \
@@ -1113,11 +1114,13 @@ src/core/ext/filters/client_channel/resolver.cc \
 src/core/ext/filters/client_channel/resolver.h \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h \
+src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h \
+src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc \
@@ -1688,8 +1691,12 @@ 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/endpoint_config.cc \
+src/core/lib/event_engine/endpoint_config_internal.h \
+src/core/lib/event_engine/event_engine.cc \
 src/core/lib/event_engine/slice_allocator.cc \
 src/core/lib/event_engine/sockaddr.cc \
+src/core/lib/event_engine/sockaddr.h \
 src/core/lib/gpr/alloc.cc \
 src/core/lib/gpr/alloc.h \
 src/core/lib/gpr/arena.h \
@@ -1795,6 +1802,7 @@ src/core/lib/iomgr/endpoint.h \
 src/core/lib/iomgr/endpoint_cfstream.cc \
 src/core/lib/iomgr/endpoint_cfstream.h \
 src/core/lib/iomgr/endpoint_pair.h \
+src/core/lib/iomgr/endpoint_pair_event_engine.cc \
 src/core/lib/iomgr/endpoint_pair_posix.cc \
 src/core/lib/iomgr/endpoint_pair_uv.cc \
 src/core/lib/iomgr/endpoint_pair_windows.cc \
@@ -1814,6 +1822,20 @@ src/core/lib/iomgr/ev_poll_posix.h \
 src/core/lib/iomgr/ev_posix.cc \
 src/core/lib/iomgr/ev_posix.h \
 src/core/lib/iomgr/ev_windows.cc \
+src/core/lib/iomgr/event_engine/closure.cc \
+src/core/lib/iomgr/event_engine/closure.h \
+src/core/lib/iomgr/event_engine/endpoint.cc \
+src/core/lib/iomgr/event_engine/endpoint.h \
+src/core/lib/iomgr/event_engine/iomgr.cc \
+src/core/lib/iomgr/event_engine/iomgr.h \
+src/core/lib/iomgr/event_engine/pollset.cc \
+src/core/lib/iomgr/event_engine/pollset.h \
+src/core/lib/iomgr/event_engine/promise.h \
+src/core/lib/iomgr/event_engine/resolved_address_internal.cc \
+src/core/lib/iomgr/event_engine/resolved_address_internal.h \
+src/core/lib/iomgr/event_engine/resolver.cc \
+src/core/lib/iomgr/event_engine/tcp.cc \
+src/core/lib/iomgr/event_engine/timer.cc \
 src/core/lib/iomgr/exec_ctx.cc \
 src/core/lib/iomgr/exec_ctx.h \
 src/core/lib/iomgr/executor.cc \
@@ -1955,6 +1977,11 @@ src/core/lib/matchers/matchers.h \
 src/core/lib/profiling/basic_timers.cc \
 src/core/lib/profiling/stap_timers.cc \
 src/core/lib/profiling/timers.h \
+src/core/lib/security/authorization/authorization_engine.h \
+src/core/lib/security/authorization/authorization_policy_provider.h \
+src/core/lib/security/authorization/authorization_policy_provider_vtable.cc \
+src/core/lib/security/authorization/evaluate_args.cc \
+src/core/lib/security/authorization/evaluate_args.h \
 src/core/lib/security/context/security_context.cc \
 src/core/lib/security/context/security_context.h \
 src/core/lib/security/credentials/alts/alts_credentials.cc \
index 1db9688..2524a5c 100644 (file)
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC Core"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 16.0.0
+PROJECT_NUMBER         = 18.0.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
@@ -811,7 +811,7 @@ include/grpc/byte_buffer.h \
 include/grpc/byte_buffer_reader.h \
 include/grpc/census.h \
 include/grpc/compression.h \
-include/grpc/event_engine/channel_args.h \
+include/grpc/event_engine/endpoint_config.h \
 include/grpc/event_engine/event_engine.h \
 include/grpc/event_engine/port.h \
 include/grpc/event_engine/slice_allocator.h \
index ebf1a4d..257541c 100644 (file)
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC Core"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 16.0.0
+PROJECT_NUMBER         = 18.0.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
@@ -811,7 +811,7 @@ include/grpc/byte_buffer.h \
 include/grpc/byte_buffer_reader.h \
 include/grpc/census.h \
 include/grpc/compression.h \
-include/grpc/event_engine/channel_args.h \
+include/grpc/event_engine/endpoint_config.h \
 include/grpc/event_engine/event_engine.h \
 include/grpc/event_engine/port.h \
 include/grpc/event_engine/slice_allocator.h \
@@ -939,11 +939,13 @@ src/core/ext/filters/client_channel/resolver.h \
 src/core/ext/filters/client_channel/resolver/README.md \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h \
+src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_libuv.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h \
+src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_libuv.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc \
 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc \
@@ -1525,8 +1527,12 @@ 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/endpoint_config.cc \
+src/core/lib/event_engine/endpoint_config_internal.h \
+src/core/lib/event_engine/event_engine.cc \
 src/core/lib/event_engine/slice_allocator.cc \
 src/core/lib/event_engine/sockaddr.cc \
+src/core/lib/event_engine/sockaddr.h \
 src/core/lib/gpr/README.md \
 src/core/lib/gpr/alloc.cc \
 src/core/lib/gpr/alloc.h \
@@ -1635,6 +1641,7 @@ src/core/lib/iomgr/endpoint.h \
 src/core/lib/iomgr/endpoint_cfstream.cc \
 src/core/lib/iomgr/endpoint_cfstream.h \
 src/core/lib/iomgr/endpoint_pair.h \
+src/core/lib/iomgr/endpoint_pair_event_engine.cc \
 src/core/lib/iomgr/endpoint_pair_posix.cc \
 src/core/lib/iomgr/endpoint_pair_uv.cc \
 src/core/lib/iomgr/endpoint_pair_windows.cc \
@@ -1654,6 +1661,20 @@ src/core/lib/iomgr/ev_poll_posix.h \
 src/core/lib/iomgr/ev_posix.cc \
 src/core/lib/iomgr/ev_posix.h \
 src/core/lib/iomgr/ev_windows.cc \
+src/core/lib/iomgr/event_engine/closure.cc \
+src/core/lib/iomgr/event_engine/closure.h \
+src/core/lib/iomgr/event_engine/endpoint.cc \
+src/core/lib/iomgr/event_engine/endpoint.h \
+src/core/lib/iomgr/event_engine/iomgr.cc \
+src/core/lib/iomgr/event_engine/iomgr.h \
+src/core/lib/iomgr/event_engine/pollset.cc \
+src/core/lib/iomgr/event_engine/pollset.h \
+src/core/lib/iomgr/event_engine/promise.h \
+src/core/lib/iomgr/event_engine/resolved_address_internal.cc \
+src/core/lib/iomgr/event_engine/resolved_address_internal.h \
+src/core/lib/iomgr/event_engine/resolver.cc \
+src/core/lib/iomgr/event_engine/tcp.cc \
+src/core/lib/iomgr/event_engine/timer.cc \
 src/core/lib/iomgr/exec_ctx.cc \
 src/core/lib/iomgr/exec_ctx.h \
 src/core/lib/iomgr/executor.cc \
@@ -1795,6 +1816,11 @@ src/core/lib/matchers/matchers.h \
 src/core/lib/profiling/basic_timers.cc \
 src/core/lib/profiling/stap_timers.cc \
 src/core/lib/profiling/timers.h \
+src/core/lib/security/authorization/authorization_engine.h \
+src/core/lib/security/authorization/authorization_policy_provider.h \
+src/core/lib/security/authorization/authorization_policy_provider_vtable.cc \
+src/core/lib/security/authorization/evaluate_args.cc \
+src/core/lib/security/authorization/evaluate_args.h \
 src/core/lib/security/context/security_context.cc \
 src/core/lib/security/context/security_context.h \
 src/core/lib/security/credentials/alts/alts_credentials.cc \
index e22f211..a8e2bed 100644 (file)
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC Objective-C"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 1.38.1
+PROJECT_NUMBER         = 1.39.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
index 5959545..efa4ea1 100644 (file)
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC Objective-C"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 1.38.1
+PROJECT_NUMBER         = 1.39.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
index 7f4e0fc..161b2cd 100644 (file)
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC PHP"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 1.38.1
+PROJECT_NUMBER         = 1.39.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
diff --git a/tools/internal_ci/helper_scripts/list_leftover_loadtests.sh b/tools/internal_ci/helper_scripts/list_leftover_loadtests.sh
new file mode 100644 (file)
index 0000000..ff8f326
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+# Copyright 2021 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.
+
+set -ex
+
+echo "BEGIN Listing leftover tests."
+
+# Find tests that have running pods and are in Errored state.
+kubectl get pods --no-headers -o jsonpath='{range .items[*]}{.metadata.ownerReferences[0].name}{" "}{.status.phase}{"\n"}{end}' \
+    | grep Running \
+    | cut -f1 -d' ' \
+    | sort -u \
+    | xargs -r kubectl get loadtest --no-headers -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.metadata.annotations.pool}{" "}{.metadata.annotations.scenario}{" "}{.status}{"\n"}{end}' \
+    | grep '"state":"Errored"' || true
+
+echo "END Listing leftover tests."
index 6ea523c..e1a7ca0 100644 (file)
@@ -47,11 +47,11 @@ fi
 if [ "${PREPARE_BUILD_INSTALL_DEPS_RUBY}" == "true" ]
 then
   # Fetch keys per https://rvm.io/rvm/install
-  # Force use of an IPv4 key server to avoid running into https://github.com/rvm/rvm/issues/4215
   gpg_recv_keys_success=0
   for ((i=0;i<5;i++)); do
-    KEYSERVER_IPV4_ADDRESS="$(host pool.sks-keyservers.net | grep 'has address' | head -n1 | awk '{print $4}')"
-    gpg --keyserver "hkp://${KEYSERVER_IPV4_ADDRESS}" --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB \
+    # Use the Ubuntu keyserver instead of pool.sks-keyservers.net because sks-keyservers is now deprecated.
+    GPG_KEYSERVER_ADDRESS="keyserver.ubuntu.com"
+    gpg --keyserver "hkp://${GPG_KEYSERVER_ADDRESS}" --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB \
       && gpg_recv_keys_success=1
     [[ "$gpg_recv_keys_success" == 1 ]] && break
     sleep 3
@@ -135,12 +135,18 @@ fi
 
 if [ "${PREPARE_BUILD_INSTALL_DEPS_PHP}" == "true" ]
 then
-  # Install PHP 7.2 explictly to address missing php header files and
+  # It's required to update the brew because it won't work with the default version Kokoro has.
+  # This can be fragile, though because the future version of brew can break. In that case,
+  # please consider to fix the certain version like https://github.com/grpc/grpc/pull/24837.
+  brew update || true
+  brew config
+
+  # Install PHP 7.3 explictly to address missing php header files and
   # to work well with the pre-installed phpunit 8.4
-  brew install php@7.2
-  export LDFLAGS="-L/usr/local/opt/php@7.2/lib $(LDFLAGS)"
-  export CPPFLAGS="-I/usr/local/opt/php@7.2/include $(CPPFLAGS)"
-  export PATH="/usr/local/opt/php@7.2/bin:/usr/local/opt/php@7.2/sbin:$PATH"
+  brew install php@7.3 || true
+  export LDFLAGS="-L/usr/local/opt/php@7.3/lib $(LDFLAGS)"
+  export CPPFLAGS="-I/usr/local/opt/php@7.3/include $(CPPFLAGS)"
+  export PATH="/usr/local/opt/php@7.3/bin:/usr/local/opt/php@7.3/sbin:$PATH"
 
   # Workaround for https://github.com/Homebrew/homebrew-core/issues/41081
   mkdir -p /usr/local/lib/php/pecl
diff --git a/tools/internal_ci/linux/aws/grpc_aws_basictests_csharp.cfg b/tools/internal_ci/linux/aws/grpc_aws_basictests_csharp.cfg
new file mode 100644 (file)
index 0000000..40b0745
--- /dev/null
@@ -0,0 +1,36 @@
+# Copyright 2021 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/aws/grpc_aws_run_remote_test.sh"
+timeout_mins: 90
+before_action {
+    fetch_keystore {
+        keystore_resource {
+            keystore_config_id: 73836
+            keyname: "grpc_aws_ec2_credentials"
+        }
+    }
+}
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+  }
+}
+env_vars {
+  key: "REMOTE_WORKLOAD_SCRIPT"
+  value: "tools/internal_ci/linux/aws/grpc_run_basictests_csharp_aarch64.sh"
+}
diff --git a/tools/internal_ci/linux/aws/grpc_aws_basictests_php.cfg b/tools/internal_ci/linux/aws/grpc_aws_basictests_php.cfg
new file mode 100644 (file)
index 0000000..7ea0432
--- /dev/null
@@ -0,0 +1,36 @@
+# Copyright 2021 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/aws/grpc_aws_run_remote_test.sh"
+timeout_mins: 90
+before_action {
+    fetch_keystore {
+        keystore_resource {
+            keystore_config_id: 73836
+            keyname: "grpc_aws_ec2_credentials"
+        }
+    }
+}
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+  }
+}
+env_vars {
+  key: "REMOTE_WORKLOAD_SCRIPT"
+  value: "tools/internal_ci/linux/aws/grpc_run_basictests_php_aarch64.sh"
+}
diff --git a/tools/internal_ci/linux/aws/grpc_aws_basictests_python.cfg b/tools/internal_ci/linux/aws/grpc_aws_basictests_python.cfg
new file mode 100644 (file)
index 0000000..b336059
--- /dev/null
@@ -0,0 +1,36 @@
+# Copyright 2021 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/aws/grpc_aws_run_remote_test.sh"
+timeout_mins: 90
+before_action {
+    fetch_keystore {
+        keystore_resource {
+            keystore_config_id: 73836
+            keyname: "grpc_aws_ec2_credentials"
+        }
+    }
+}
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+  }
+}
+env_vars {
+  key: "REMOTE_WORKLOAD_SCRIPT"
+  value: "tools/internal_ci/linux/aws/grpc_run_basictests_python_aarch64.sh"
+}
diff --git a/tools/internal_ci/linux/aws/grpc_aws_basictests_ruby.cfg b/tools/internal_ci/linux/aws/grpc_aws_basictests_ruby.cfg
new file mode 100644 (file)
index 0000000..46c47a9
--- /dev/null
@@ -0,0 +1,36 @@
+# Copyright 2021 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/aws/grpc_aws_run_remote_test.sh"
+timeout_mins: 90
+before_action {
+    fetch_keystore {
+        keystore_resource {
+            keystore_config_id: 73836
+            keyname: "grpc_aws_ec2_credentials"
+        }
+    }
+}
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+  }
+}
+env_vars {
+  key: "REMOTE_WORKLOAD_SCRIPT"
+  value: "tools/internal_ci/linux/aws/grpc_run_basictests_ruby_aarch64.sh"
+}
diff --git a/tools/internal_ci/linux/aws/grpc_aws_bazel_test_c_cpp.cfg b/tools/internal_ci/linux/aws/grpc_aws_bazel_test_c_cpp.cfg
new file mode 100644 (file)
index 0000000..58b7e94
--- /dev/null
@@ -0,0 +1,36 @@
+# Copyright 2021 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/aws/grpc_aws_run_remote_test_8core.sh"
+timeout_mins: 120
+before_action {
+    fetch_keystore {
+        keystore_resource {
+            keystore_config_id: 73836
+            keyname: "grpc_aws_ec2_credentials"
+        }
+    }
+}
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+  }
+}
+env_vars {
+  key: "REMOTE_WORKLOAD_SCRIPT"
+  value: "tools/internal_ci/linux/aws/grpc_bazel_test_c_cpp_aarch64.sh"
+}
diff --git a/tools/internal_ci/linux/aws/grpc_aws_experiment.cfg b/tools/internal_ci/linux/aws/grpc_aws_experiment.cfg
new file mode 100644 (file)
index 0000000..d6a9e24
--- /dev/null
@@ -0,0 +1,32 @@
+# Copyright 2021 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/aws/grpc_aws_run_remote_test.sh"
+timeout_mins: 90
+before_action {
+    fetch_keystore {
+        keystore_resource {
+            keystore_config_id: 73836
+            keyname: "grpc_aws_ec2_credentials"
+        }
+    }
+}
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+  }
+}
diff --git a/tools/internal_ci/linux/aws/grpc_aws_experiment_remote.sh b/tools/internal_ci/linux/aws/grpc_aws_experiment_remote.sh
new file mode 100755 (executable)
index 0000000..66a661e
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+# Copyright 2021 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.
+
+set -ex
+
+#install ubuntu pre-requisites
+sudo apt update
+sudo apt install -y build-essential autoconf libtool pkg-config cmake python python-pip clang
+sudo pip install six
+
+cd grpc
+
+# without port server running, many tests will fail
+python tools/run_tests/start_port_server.py
+
+# build with bazel
+tools/bazel build --config=opt //test/...
diff --git a/tools/internal_ci/linux/aws/grpc_aws_run_remote_test.sh b/tools/internal_ci/linux/aws/grpc_aws_run_remote_test.sh
new file mode 100755 (executable)
index 0000000..ad95eae
--- /dev/null
@@ -0,0 +1,147 @@
+#!/usr/bin/env bash
+# Copyright 2021 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.
+
+# WARNING: this script has been reviewed by the security team, so
+# any changes need to be made with great care.
+# Contact @jtattermusch or @amidlash if in doubt.
+
+# This script is responsible for remotely running tests on an ARM instance.
+# At the start, it provisions a new AWS ARM64 instance and then uses
+# it to execute a test script (and cleans up afterwards).
+# It should return a status code useful to the kokoro infrastructure.
+
+# TODO(jtattermusch): make the script safe to run under "set -ex"
+set -e
+
+if [ -z "$KOKORO_KEYSTORE_DIR" ]; then
+    echo "KOKORO_KEYSTORE_DIR is unset. This must be run from kokoro"
+    exit 1
+fi
+
+AWS_CREDENTIALS=${KOKORO_KEYSTORE_DIR}/73836_grpc_aws_ec2_credentials
+
+# Setup aws cli
+curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
+unzip -q awscliv2.zip
+sudo ./aws/install 
+aws --version
+
+# authenticate with aws cli
+mkdir ~/.aws/
+echo "[default]" >> ~/.aws/config
+ln -s $AWS_CREDENTIALS ~/.aws/credentials
+
+# setup instance 
+sudo apt update && sudo apt install -y jq 
+
+# ubuntu 18.04 lts(arm64)
+# https://aws.amazon.com/amazon-linux-ami/
+AWS_MACHINE_IMAGE=ami-026141f3d5c6d2d0c
+# use 4-core instance by default
+AWS_INSTANCE_TYPE=${AWS_INSTANCE_TYPE:-t4g.xlarge}
+AWS_SECURITY_GROUP=sg-021240e886feba750
+# Max allowed lifespan of the AWS instance. After this period of time, the instance will
+# self-terminate (delete itself). This is very important to ensure that there will
+# be no orphaned AWS instances if the initiating kokoro job fails / gets cancelled etc.
+AWS_INSTANCE_MAX_LIFESPAN_MINS=120
+# increase the size of the root volume so that builds don't run out of disk space
+AWS_STORAGE_SIZE_GB=75
+AWS_DEVICE_MAPPING="DeviceName='/dev/sda1',Ebs={VolumeSize=${AWS_STORAGE_SIZE_GB}}"
+AWS_INSTANCE_TAGS="ResourceType='instance',Tags=[{Key='kokoro_job_name',Value='${KOKORO_JOB_NAME}'},{Key='kokoro_build_number',Value='${KOKORO_BUILD_NUMBER}'},{Key='kokoro_aws_integration',Value='true'}]"
+
+ssh-keygen -N '' -t rsa -b 4096 -f ~/.ssh/temp_client_key
+ssh-keygen -N '' -t ecdsa -b 256 -f ~/.ssh/temp_server_key
+SERVER_PRIVATE_KEY=$(cat ~/.ssh/temp_server_key | sed 's/\(.*\)/    \1/')
+SERVER_PUBLIC_KEY=$(cat ~/.ssh/temp_server_key.pub | awk '{print $1 " " $2 " root@localhost"}')
+SERVER_HOST_KEY_ENTRY=$(cat ~/.ssh/temp_server_key.pub | awk '{print $1 " " $2}')
+CLIENT_PUBLIC_KEY=$(cat ~/.ssh/temp_client_key.pub)
+
+echo '#cloud-config' > userdata
+echo 'ssh_authorized_keys:' >> userdata
+echo " - $CLIENT_PUBLIC_KEY" >> userdata
+echo 'ssh_keys:' >> userdata
+echo '  ecdsa_private: |' >> userdata
+echo "$SERVER_PRIVATE_KEY" >> userdata
+echo "  ecdsa_public: $SERVER_PUBLIC_KEY" >> userdata
+echo '' >> userdata
+echo 'runcmd:' >> userdata
+echo " - sleep ${AWS_INSTANCE_MAX_LIFESPAN_MINS}m" >> userdata
+echo ' - shutdown' >> userdata
+
+ID=$(aws ec2 run-instances --image-id $AWS_MACHINE_IMAGE --instance-initiated-shutdown-behavior=terminate \
+    --instance-type $AWS_INSTANCE_TYPE \
+    --security-group-ids $AWS_SECURITY_GROUP \
+    --user-data file://userdata \
+    --block-device-mapping "$AWS_DEVICE_MAPPING" \
+    --tag-specifications "$AWS_INSTANCE_TAGS" \
+    --region us-east-2 | jq .Instances[0].InstanceId | sed 's/"//g')
+echo "instance-id=$ID"
+echo "Waiting 1m for instance ip..."
+sleep 1m
+IP=$(aws ec2 describe-instances \
+    --instance-id=$ID \
+    --region us-east-2 | jq .Reservations[0].Instances[0].NetworkInterfaces[0].Association.PublicIp | sed 's/"//g')
+SERVER_HOST_KEY_ENTRY="$IP $SERVER_HOST_KEY_ENTRY"
+echo $SERVER_HOST_KEY_ENTRY >> ~/.ssh/known_hosts
+echo "Waiting 2m for instance to initialize..."
+sleep 2m
+
+echo "Copying workspace to remote instance..."
+# use rsync over ssh since it's much faster than scp
+time rsync -e "ssh -i ~/.ssh/temp_client_key" -a github/grpc ubuntu@$IP:~/workspace
+echo "Beginning CI workload..."
+
+# filename of the test script to execute remotely, relative to gRPC repository root
+# use a default value if the env variable is not set
+REMOTE_WORKLOAD_SCRIPT=${REMOTE_WORKLOAD_SCRIPT:-tools/internal_ci/linux/aws/grpc_aws_experiment_remote.sh}
+
+# run remote workload script in the background, with redirected stdout and stderr
+# to avoid problems with ssh session not closing after the remote script finishes
+# but stdout and stderr are still open because the remote has spawned subprocesses
+# that keep stdout and stderr open.
+# * PID of the process that executes the remote script will be stored in aws_build.pid
+# * stderr and stdout will be streamed to aws_build.log
+# * once done, the exitcode of the remote script will be in aws_build.exitcode
+REMOTE_WORKLOAD_COMMAND="nohup bash -c '(bash grpc/${REMOTE_WORKLOAD_SCRIPT}; echo \$? >/tmp/aws_build.exitcode) >>/tmp/aws_build.log 2>&1' >/dev/null 2>&1 & echo \$! >/tmp/aws_build.pid"
+
+# the tail command simply streams the contents of aws_build.log as they become available
+# and stops when the remote workload exits (determined based on the PID)
+SSH_COMMAND='uname -a; rm -f /tmp/aws_build.log /tmp/aws_build.exitcode /tmp/aws_build.pid; touch /tmp/aws_build.log; cd ~/workspace; '"${REMOTE_WORKLOAD_COMMAND};"' tail -f /tmp/aws_build.log --pid $(cat /tmp/aws_build.pid); exit $(cat /tmp/aws_build.exitcode)'
+
+REMOTE_SCRIPT_EXITCODE=0
+time ssh -i ~/.ssh/temp_client_key ubuntu@$IP "${SSH_COMMAND}" || REMOTE_SCRIPT_EXITCODE=$?
+
+echo "Copying artifacts from the remote instance..."
+ARTIFACT_RSYNC_PATTERN="**/*sponge_log.*"
+# NOTE: the include "*/" rule and --prune-empty-dirs are important for not
+# excluding parent directories that contain artifacts before they have
+# get a chance to be examined (see man rsync)
+COPY_ARTIFACTS_EXITCODE=0
+time rsync -av -e "ssh -i ~/.ssh/temp_client_key" --include="${ARTIFACT_RSYNC_PATTERN}" --include="*/" --exclude="*" --prune-empty-dirs ubuntu@$IP:~/workspace/grpc github || COPY_ARTIFACTS_EXITCODE=$?
+
+# Regardless of the remote script's result (success or failure), initiate shutdown of AWS instance a minute from now.
+# The small delay is useful to make sure the ssh session doesn't hang up on us if shutdown happens too quickly.
+echo "Shutting down instance $ID."
+ssh -i ~/.ssh/temp_client_key ubuntu@$IP "sudo shutdown +1" || echo "WARNING: Failed to initiate AWS instance shutdown."
+
+if [ "$REMOTE_SCRIPT_EXITCODE" == "0" ] && [ "$COPY_ARTIFACTS_EXITCODE" != "0" ]
+then
+  echo "Exiting with exitcode $COPY_ARTIFACTS_EXITCODE since remote script has passed, but copying artifacts has failed."
+  exit $COPY_ARTIFACTS_EXITCODE
+fi
+
+# Match exitcode
+echo "Exiting with exitcode $REMOTE_SCRIPT_EXITCODE based on remote script output."
+exit $REMOTE_SCRIPT_EXITCODE
diff --git a/tools/internal_ci/linux/aws/grpc_aws_run_remote_test_8core.sh b/tools/internal_ci/linux/aws/grpc_aws_run_remote_test_8core.sh
new file mode 100755 (executable)
index 0000000..257d083
--- /dev/null
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+# Copyright 2021 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.
+
+set -e
+
+AWS_INSTANCE_TYPE="t4g.2xlarge" "$(dirname $0)/grpc_aws_run_remote_test.sh"
diff --git a/tools/internal_ci/linux/aws/grpc_bazel_test_c_cpp_aarch64.sh b/tools/internal_ci/linux/aws/grpc_bazel_test_c_cpp_aarch64.sh
new file mode 100755 (executable)
index 0000000..f6855be
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+# Copyright 2021 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.
+
+set -ex
+
+# install pre-requisites for gRPC C core build
+sudo apt update
+sudo apt install -y build-essential autoconf libtool pkg-config cmake python python-pip clang
+sudo pip install six
+
+cd grpc
+
+# tests require port server to be running
+python tools/run_tests/start_port_server.py
+
+# test gRPC C/C++ with bazel
+tools/bazel test --config=opt --test_output=errors --test_tag_filters=-no_linux,-no_arm64 --build_tag_filters=-no_linux,-no_arm64 --flaky_test_attempts=1 --runs_per_test=1 //test/...
diff --git a/tools/internal_ci/linux/aws/grpc_run_basictests_csharp_aarch64.sh b/tools/internal_ci/linux/aws/grpc_run_basictests_csharp_aarch64.sh
new file mode 100755 (executable)
index 0000000..ef1aa2c
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# Copyright 2021 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.
+
+set -ex
+
+# install pre-requisites for gRPC C core build
+sudo apt update
+sudo apt install -y build-essential autoconf libtool pkg-config cmake python python-pip clang
+sudo pip install six
+
+# install gRPC C# pre-requisites
+curl -sSL -o dotnet-install.sh https://dot.net/v1/dotnet-install.sh
+chmod u+x dotnet-install.sh
+./dotnet-install.sh --channel 2.1  # needed for the netcoreapp2.1 targets
+./dotnet-install.sh --channel 5.0
+export PATH="$HOME/.dotnet:$PATH"
+
+# Disable some unwanted dotnet options
+export NUGET_XMLDOC_MODE=skip
+export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true
+export DOTNET_CLI_TELEMETRY_OPTOUT=true
+
+dotnet --list-sdks
+
+cd grpc
+
+git submodule update --init
+
+# build and test C#
+tools/run_tests/run_tests.py -l csharp -c opt --compiler coreclr -t -x run_tests/csharp_linux_aarch64_opt_native/sponge_log.xml --report_suite_name csharp_linux_aarch64_opt_native --report_multi_target
diff --git a/tools/internal_ci/linux/aws/grpc_run_basictests_php_aarch64.sh b/tools/internal_ci/linux/aws/grpc_run_basictests_php_aarch64.sh
new file mode 100755 (executable)
index 0000000..6c98881
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+# Copyright 2021 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.
+
+set -ex
+
+# install pre-requisites for gRPC C core build
+sudo apt update
+sudo apt install -y build-essential autoconf libtool pkg-config cmake python python-pip clang
+sudo pip install six
+
+# install gRPC Ruby pre-requisites
+sudo apt install -y php7.2-cli php7.2-dev
+sudo apt install -y composer phpunit
+sudo apt install -y php-bcmath  # required by protobuf PHP, is it really needed for gRPC?
+
+cd grpc
+
+git submodule update --init
+
+# build and test php
+tools/run_tests/run_tests.py -l php7 -c opt -t -x run_tests/php7_linux_aarch64_opt_native/sponge_log.xml --report_suite_name php7_linux_aarch64_opt_native --report_multi_target
diff --git a/tools/internal_ci/linux/aws/grpc_run_basictests_python_aarch64.sh b/tools/internal_ci/linux/aws/grpc_run_basictests_python_aarch64.sh
new file mode 100755 (executable)
index 0000000..231e393
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+# Copyright 2021 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.
+
+set -ex
+
+# install pre-requisites for gRPC C core build
+sudo apt update
+sudo apt install -y build-essential autoconf libtool pkg-config cmake python python-pip clang
+sudo pip install six
+
+# install python3.6 and pip
+sudo apt install -y python3 python3-pip
+python3 --version
+
+cd grpc
+
+git submodule update --init
+
+# build and test python (currently we only test with python3.6, but that's ok since our aarch64 testing resources are limited)
+tools/run_tests/run_tests.py -l python --compiler python3.6 -c opt --iomgr_platform native -t -x run_tests/python_linux_opt_native/sponge_log.xml --report_suite_name python_linux_opt_native --report_multi_target || FAILED=true
+tools/run_tests/run_tests.py -l python --compiler python3.6 -c opt --iomgr_platform asyncio -t -x run_tests/python_linux_opt_asyncio/sponge_log.xml --report_suite_name python_linux_opt_asyncio --report_multi_target || FAILED=true
+tools/run_tests/run_tests.py -l python --compiler python3.6 -c opt --iomgr_platform gevent -t -x run_tests/python_linux_opt_gevent/sponge_log.xml --report_suite_name python_linux_opt_gevent --report_multi_target || FAILED=true
+
+if [ "$FAILED" != "" ]
+then
+  exit 1
+fi
diff --git a/tools/internal_ci/linux/aws/grpc_run_basictests_ruby_aarch64.sh b/tools/internal_ci/linux/aws/grpc_run_basictests_ruby_aarch64.sh
new file mode 100755 (executable)
index 0000000..54707e2
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+# Copyright 2021 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.
+
+set -ex
+
+# install pre-requisites for gRPC C core build
+sudo apt update
+sudo apt install -y build-essential autoconf libtool pkg-config cmake python python-pip clang
+sudo pip install six
+
+# install gRPC Ruby pre-requisites
+sudo apt install -y ruby ruby-dev
+sudo gem install bundler
+ruby --version
+
+cd grpc
+
+git submodule update --init
+
+# build and test ruby
+tools/run_tests/run_tests.py -l ruby -c opt -t -x run_tests/ruby_linux_aarch64_opt_native/sponge_log.xml --report_suite_name ruby_linux_aarch64_opt_native --report_multi_target
diff --git a/tools/internal_ci/linux/grpc_e2e_performance_gke.cfg b/tools/internal_ci/linux/grpc_e2e_performance_gke.cfg
new file mode 100644 (file)
index 0000000..4d92aa0
--- /dev/null
@@ -0,0 +1,25 @@
+# Copyright 2021 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/grpc_e2e_performance_gke.sh"
+timeout_mins: 240
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.*"
+    regex: "**/perf_reports/**"
+  }
+}
diff --git a/tools/internal_ci/linux/grpc_e2e_performance_gke.sh b/tools/internal_ci/linux/grpc_e2e_performance_gke.sh
new file mode 100755 (executable)
index 0000000..2fef583
--- /dev/null
@@ -0,0 +1,117 @@
+#!/usr/bin/env bash
+# Copyright 2021 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.
+set -ex
+
+# Enter the gRPC repo root.
+cd "$(dirname "$0")/../../.."
+
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
+
+# This is to ensure we can push and pull images from gcr.io. We do not
+# necessarily need it to run load tests, but will need it when we employ
+# pre-built images in the optimization.
+gcloud auth configure-docker
+
+# Connect to benchmarks-prod cluster.
+gcloud config set project grpc-testing
+gcloud container clusters get-credentials benchmarks-prod \
+    --zone us-central1-b --project grpc-testing
+
+# List tests that have running pods and are in errored state.
+# This is an unexpected condition, and it is logged here for monitoring.
+source tools/internal_ci/helper_scripts/list_leftover_loadtests.sh
+
+# Set up environment variables.
+LOAD_TEST_PREFIX="${KOKORO_BUILD_INITIATOR}"
+# BEGIN differentiate experimental configuration from master configuration.
+# Use the "official" BQ tables so that the measurements will show up in the
+# "official" public dashboard.
+BIGQUERY_TABLE_8CORE=e2e_benchmarks.ci_master_results_8core
+BIGQUERY_TABLE_32CORE=e2e_benchmarks.ci_master_results_32core
+# END differentiate experimental configuration from master configuration.
+PREBUILT_IMAGE_PREFIX="gcr.io/grpc-testing/e2etesting/pre_built_workers/${LOAD_TEST_PREFIX}"
+UNIQUE_IDENTIFIER="$(date +%Y%m%d%H%M%S)"
+ROOT_DIRECTORY_OF_DOCKERFILES="../test-infra/containers/pre_built_workers/"
+# Prebuilt workers for core languages are always built from grpc/grpc.
+if [[ "${KOKORO_GITHUB_COMMIT_URL%/*}" == "https://github.com/grpc/grpc/commit" ]]; then
+    GRPC_CORE_GITREF="${KOKORO_GIT_COMMIT}"
+else
+    GRPC_CORE_GITREF="$(git ls-remote https://github.com/grpc/grpc.git master | cut -f1)"
+fi
+GRPC_GO_GITREF="$(git ls-remote https://github.com/grpc/grpc-go.git master | cut -f1)"
+GRPC_JAVA_GITREF="$(git ls-remote https://github.com/grpc/grpc-java.git master | cut -f1)"
+# Kokoro jobs run on dedicated pools.
+DRIVER_POOL=drivers-ci
+WORKER_POOL_8CORE=workers-8core-ci
+WORKER_POOL_32CORE=workers-32core-ci
+
+# Clone test-infra repository to one upper level directory than grpc.
+pushd ..
+git clone --recursive https://github.com/grpc/test-infra.git
+cd test-infra
+make all-tools
+popd
+
+# Build test configurations.
+buildConfigs() {
+    local -r pool="$1"
+    local -r table="$2"
+    shift 2
+    tools/run_tests/performance/loadtest_config.py "$@" \
+        -t ./tools/run_tests/performance/templates/loadtest_template_prebuilt_all_languages.yaml \
+        -s driver_pool="${DRIVER_POOL}" -s driver_image= \
+        -s client_pool="${pool}" -s server_pool="${pool}" \
+        -s big_query_table="${table}" -s timeout_seconds=900 \
+        -s prebuilt_image_prefix="${PREBUILT_IMAGE_PREFIX}" \
+        -s prebuilt_image_tag="${UNIQUE_IDENTIFIER}" \
+        --prefix="${LOAD_TEST_PREFIX}" -u "${UNIQUE_IDENTIFIER}" -u "${pool}" \
+        -a pool="${pool}" --category=scalable \
+        --allow_client_language=c++ --allow_server_language=c++ \
+        -o "./loadtest_with_prebuilt_workers_${pool}.yaml"
+}
+
+buildConfigs "${WORKER_POOL_8CORE}" "${BIGQUERY_TABLE_8CORE}" -l c++ -l csharp -l go -l java -l python -l ruby
+buildConfigs "${WORKER_POOL_32CORE}" "${BIGQUERY_TABLE_32CORE}" -l c++ -l csharp -l go -l java
+
+# Delete prebuilt images on exit.
+deleteImages() {
+    echo "deleting images on exit"
+    ../test-infra/bin/delete_prebuilt_workers \
+    -p "${PREBUILT_IMAGE_PREFIX}" \
+    -t "${UNIQUE_IDENTIFIER}"
+}
+trap deleteImages EXIT
+
+# Build and push prebuilt images for running tests.
+time ../test-infra/bin/prepare_prebuilt_workers \
+    -l "cxx:${GRPC_CORE_GITREF}" \
+    -l "csharp:${GRPC_CORE_GITREF}" \
+    -l "go:${GRPC_GO_GITREF}" \
+    -l "java:${GRPC_JAVA_GITREF}" \
+    -l "python:${GRPC_CORE_GITREF}" \
+    -l "ruby:${GRPC_CORE_GITREF}" \
+    -p "${PREBUILT_IMAGE_PREFIX}" \
+    -t "${UNIQUE_IDENTIFIER}" \
+    -r "${ROOT_DIRECTORY_OF_DOCKERFILES}"
+
+# Create reports directory.
+mkdir -p runner
+
+# Run tests.
+time ../test-infra/bin/runner \
+    -i "../grpc/loadtest_with_prebuilt_workers_${WORKER_POOL_8CORE}.yaml" \
+    -i "../grpc/loadtest_with_prebuilt_workers_${WORKER_POOL_32CORE}.yaml" \
+    -c "${WORKER_POOL_8CORE}:2" -c "${WORKER_POOL_32CORE}:2" \
+    -o "runner/sponge_log.xml"
index 8974fb2..781544b 100644 (file)
@@ -16,7 +16,7 @@
 
 # Location of the continuous shell script in repository.
 build_file: "grpc/tools/internal_ci/linux/grpc_e2e_performance_v2.sh"
-timeout_mins: 120
+timeout_mins: 720
 action {
   define_artifacts {
     regex: "**/*sponge_log.*"
index 63d2650..227d1c5 100755 (executable)
@@ -14,8 +14,8 @@
 # limitations under the License.
 set -ex
 
-# Enter the gRPC repo root
-cd $(dirname $0)/../../..
+# Enter the gRPC repo root.
+cd "$(dirname "$0")/../../.."
 
 source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
@@ -29,27 +29,90 @@ gcloud config set project grpc-testing
 gcloud container clusters get-credentials benchmarks-prod \
     --zone us-central1-b --project grpc-testing
 
-# This is subject to change. Runs a single test and does not wait for the
-# result.
-tools/run_tests/performance/loadtest_config.py -l c++ -l go \
-    -t ./tools/run_tests/performance/templates/loadtest_template_basic_all_languages.yaml \
-    -s client_pool=workers-8core -s server_pool=workers-8core \
-    -s big_query_table=e2e_benchmarks.experimental_results \
-    -s timeout_seconds=900 --prefix="kokoro-test" -u "$(date +%Y%m%d%H%M%S)" \
-    -r '(go_generic_sync_streaming_ping_pong_secure|go_protobuf_sync_unary_ping_pong_secure|cpp_protobuf_async_streaming_qps_unconstrained_secure)$' \
-    -o ./loadtest.yaml
-
-# Dump the contents of the loadtest.yaml (since loadtest_config.py doesn't
-# list the scenarios that will be run).
-cat ./loadtest.yaml
-
-# The original version of the client is a bit old, update to the latest release
-# version v1.21.0.
-kubectl version --client
-curl -sSL -O https://dl.k8s.io/release/v1.21.0/bin/linux/amd64/kubectl
-sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
-chmod +x kubectl
-sudo mv kubectl $(which kubectl)
-kubectl version --client
-
-kubectl apply -f ./loadtest.yaml
+# List tests that have running pods and are in errored state.
+# This is an unexpected condition, and it is logged here for monitoring.
+source tools/internal_ci/helper_scripts/list_leftover_loadtests.sh
+
+# Set up environment variables.
+LOAD_TEST_PREFIX="${KOKORO_BUILD_INITIATOR}"
+# BEGIN differentiate experimental configuration from master configuration.
+if [[ "${KOKORO_BUILD_INITIATOR}" == kokoro ]]; then
+    LOAD_TEST_PREFIX=kokoro-test
+fi
+BIGQUERY_TABLE_8CORE=e2e_benchmarks.experimental_results
+BIGQUERY_TABLE_32CORE=e2e_benchmarks.experimental_results_32core
+# END differentiate experimental configuration from master configuration.
+PREBUILT_IMAGE_PREFIX="gcr.io/grpc-testing/e2etesting/pre_built_workers/${LOAD_TEST_PREFIX}"
+UNIQUE_IDENTIFIER="$(date +%Y%m%d%H%M%S)"
+ROOT_DIRECTORY_OF_DOCKERFILES="../test-infra/containers/pre_built_workers/"
+# Prebuilt workers for core languages are always built from grpc/grpc.
+if [[ "${KOKORO_GITHUB_COMMIT_URL%/*}" == "https://github.com/grpc/grpc/commit" ]]; then
+    GRPC_CORE_GITREF="${KOKORO_GIT_COMMIT}"
+else
+    GRPC_CORE_GITREF="$(git ls-remote https://github.com/grpc/grpc.git master | cut -f1)"
+fi
+GRPC_GO_GITREF="$(git ls-remote https://github.com/grpc/grpc-go.git master | cut -f1)"
+GRPC_JAVA_GITREF="$(git ls-remote https://github.com/grpc/grpc-java.git master | cut -f1)"
+# Kokoro jobs run on dedicated pools.
+DRIVER_POOL=drivers-ci
+WORKER_POOL_8CORE=workers-8core-ci
+WORKER_POOL_32CORE=workers-32core-ci
+
+# Clone test-infra repository to one upper level directory than grpc.
+pushd ..
+git clone --recursive https://github.com/grpc/test-infra.git
+cd test-infra
+make all-tools
+popd
+
+# Build test configurations.
+buildConfigs() {
+    local -r pool="$1"
+    local -r table="$2"
+    shift 2
+    tools/run_tests/performance/loadtest_config.py "$@" \
+        -t ./tools/run_tests/performance/templates/loadtest_template_prebuilt_all_languages.yaml \
+        -s driver_pool="${DRIVER_POOL}" -s driver_image= \
+        -s client_pool="${pool}" -s server_pool="${pool}" \
+        -s big_query_table="${table}" -s timeout_seconds=900 \
+        -s prebuilt_image_prefix="${PREBUILT_IMAGE_PREFIX}" \
+        -s prebuilt_image_tag="${UNIQUE_IDENTIFIER}" \
+        --prefix="${LOAD_TEST_PREFIX}" -u "${UNIQUE_IDENTIFIER}" -u "${pool}" \
+        -a pool="${pool}" --category=scalable \
+        --allow_client_language=c++ --allow_server_language=c++ \
+        -o "./loadtest_with_prebuilt_workers_${pool}.yaml"
+}
+
+buildConfigs "${WORKER_POOL_8CORE}" "${BIGQUERY_TABLE_8CORE}" -l c++ -l csharp -l go -l java -l python -l ruby
+buildConfigs "${WORKER_POOL_32CORE}" "${BIGQUERY_TABLE_32CORE}" -l c++ -l csharp -l go -l java
+
+# Delete prebuilt images on exit.
+deleteImages() {
+    echo "deleting images on exit"
+    ../test-infra/bin/delete_prebuilt_workers \
+    -p "${PREBUILT_IMAGE_PREFIX}" \
+    -t "${UNIQUE_IDENTIFIER}"
+}
+trap deleteImages EXIT
+
+# Build and push prebuilt images for running tests.
+time ../test-infra/bin/prepare_prebuilt_workers \
+    -l "cxx:${GRPC_CORE_GITREF}" \
+    -l "csharp:${GRPC_CORE_GITREF}" \
+    -l "go:${GRPC_GO_GITREF}" \
+    -l "java:${GRPC_JAVA_GITREF}" \
+    -l "python:${GRPC_CORE_GITREF}" \
+    -l "ruby:${GRPC_CORE_GITREF}" \
+    -p "${PREBUILT_IMAGE_PREFIX}" \
+    -t "${UNIQUE_IDENTIFIER}" \
+    -r "${ROOT_DIRECTORY_OF_DOCKERFILES}"
+
+# Create reports directory.
+mkdir -p runner
+
+# Run tests.
+time ../test-infra/bin/runner \
+    -i "../grpc/loadtest_with_prebuilt_workers_${WORKER_POOL_8CORE}.yaml" \
+    -i "../grpc/loadtest_with_prebuilt_workers_${WORKER_POOL_32CORE}.yaml" \
+    -c "${WORKER_POOL_8CORE}:2" -c "${WORKER_POOL_32CORE}:2" \
+    -o "runner/sponge_log.xml"
index 6fb22b8..f3c6e71 100755 (executable)
@@ -64,6 +64,7 @@ bazel build //src/python/grpcio_tests/tests_py3_only/interop:xds_interop_client
 # because not all interop clients in all languages support these new tests.
 GRPC_VERBOSITY=debug GRPC_TRACE=xds_client,xds_resolver,xds_cluster_manager_lb,cds_lb,xds_cluster_resolver_lb,priority_lb,xds_cluster_impl_lb,weighted_target_lb "$PYTHON" \
   tools/run_tests/run_xds_tests.py \
+    --halt_after_fail \
     --test_case="all,circuit_breaking,timeout,fault_injection" \
     --project_id=grpc-testing \
     --project_num=830293263384 \
index 8825719..527f21f 100755 (executable)
@@ -67,6 +67,7 @@ bazel build test/cpp/interop:xds_interop_client
 # they are added into "all".
 GRPC_VERBOSITY=debug GRPC_TRACE=xds_client,xds_resolver,xds_cluster_manager_lb,cds_lb,xds_cluster_resolver_lb,priority_lb,xds_cluster_impl_lb,weighted_target_lb "$PYTHON" \
   tools/run_tests/run_xds_tests.py \
+    --halt_after_fail \
     --test_case="all,circuit_breaking,timeout,fault_injection,csds" \
     --project_id=grpc-testing \
     --project_num=830293263384 \
index 21b0d4e..300f466 100755 (executable)
@@ -67,6 +67,7 @@ python tools/run_tests/run_tests.py -l csharp -c opt --build_only
 # --test_case after they are added into "all".
 GRPC_VERBOSITY=debug GRPC_TRACE=xds_client,xds_resolver,xds_cluster_manager_lb,cds_lb,xds_cluster_resolver_lb,priority_lb,xds_cluster_impl_lb,weighted_target_lb "$PYTHON" \
   tools/run_tests/run_xds_tests.py \
+    --halt_after_fail \
     --test_case="all,path_matching,header_matching" \
     --project_id=grpc-testing \
     --project_num=830293263384 \
index d7f50fb..740581c 100644 (file)
@@ -19,7 +19,7 @@ set -eo pipefail
 readonly PYTHON_VERSION="3.6"
 # Test driver
 readonly TEST_DRIVER_REPO_NAME="grpc"
-readonly TEST_DRIVER_REPO_URL="https://github.com/grpc/grpc.git"
+readonly TEST_DRIVER_REPO_URL="https://github.com/${TEST_DRIVER_REPO_OWNER:-grpc}/grpc.git"
 readonly TEST_DRIVER_BRANCH="${TEST_DRIVER_BRANCH:-master}"
 readonly TEST_DRIVER_PATH="tools/run_tests/xds_k8s_test_driver"
 readonly TEST_DRIVER_PROTOS_PATH="src/proto/grpc/testing"
index 9d87920..18c860c 100755 (executable)
@@ -72,6 +72,7 @@ export CC=/usr/bin/gcc
 
 GRPC_VERBOSITY=debug GRPC_TRACE=xds_client,xds_resolver,xds_cluster_manager_lb,cds_lb,xds_cluster_resolver_lb,priority_lb,xds_cluster_impl_lb,weighted_target_lb "$PYTHON" \
   tools/run_tests/run_xds_tests.py \
+  --halt_after_fail \
   --test_case="timeout,fault_injection" \
   --project_id=grpc-testing \
   --project_num=830293263384 \
@@ -85,6 +86,7 @@ GRPC_VERBOSITY=debug GRPC_TRACE=xds_client,xds_resolver,xds_cluster_manager_lb,c
 
 GRPC_VERBOSITY=debug GRPC_TRACE=xds_client,xds_resolver,xds_cluster_manager_lb,cds_lb,xds_cluster_resolver_lb,priority_lb,xds_cluster_impl_lb,weighted_target_lb "$PYTHON" \
   tools/run_tests/run_xds_tests.py \
+  --halt_after_fail \
   --test_case="all,path_matching,header_matching" \
   --project_id=grpc-testing \
   --project_num=830293263384 \
index 6c76f92..43d71ce 100755 (executable)
@@ -62,6 +62,7 @@ touch "$TOOLS_DIR"/src/proto/grpc/health/v1/__init__.py
 
 GRPC_VERBOSITY=debug GRPC_TRACE=xds_client,xds_resolver,xds_cluster_manager_lb,cds_lb,xds_cluster_resolver_lb,priority_lb,xds_cluster_impl_lb,weighted_target_lb "$PYTHON" \
   tools/run_tests/run_xds_tests.py \
+    --halt_after_fail \
     --test_case="all,circuit_breaking,timeout,fault_injection" \
     --project_id=grpc-testing \
     --project_num=830293263384 \
diff --git a/tools/internal_ci/linux/grpc_xds_url_map.cfg b/tools/internal_ci/linux/grpc_xds_url_map.cfg
new file mode 100644 (file)
index 0000000..42bfb1e
--- /dev/null
@@ -0,0 +1,26 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/grpc_xds_url_map.sh"
+timeout_mins: 120
+action {
+  define_artifacts {
+    regex: "artifacts/**/*sponge_log.xml"
+    regex: "artifacts/**/*sponge_log.log"
+    strip_prefix: "artifacts"
+  }
+}
diff --git a/tools/internal_ci/linux/grpc_xds_url_map.sh b/tools/internal_ci/linux/grpc_xds_url_map.sh
new file mode 100755 (executable)
index 0000000..ef726c6
--- /dev/null
@@ -0,0 +1,142 @@
+#!/usr/bin/env bash
+# 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.
+
+set -ex -o igncr || set -ex
+
+# Constants
+readonly GITHUB_REPOSITORY_NAME="grpc"
+# GKE Cluster
+readonly GKE_CLUSTER_NAME="interop-test-psm-sec-v2-us-central1-a"
+readonly GKE_CLUSTER_ZONE="us-central1-a"
+## xDS test client Docker images
+readonly CLIENT_IMAGE_NAME="gcr.io/grpc-testing/xds-interop/cpp-client"
+readonly FORCE_IMAGE_BUILD="${FORCE_IMAGE_BUILD:-0}"
+readonly BUILD_APP_PATH="interop-testing/build/install/grpc-interop-testing"
+
+#######################################
+# Builds test app Docker images and pushes them to GCR
+# Globals:
+#   BUILD_APP_PATH
+#   CLIENT_IMAGE_NAME: Test client Docker image name
+#   GIT_COMMIT: SHA-1 of git commit being built
+# Arguments:
+#   None
+# Outputs:
+#   Writes the output of `gcloud builds submit` to stdout, stderr
+#######################################
+build_test_app_docker_images() {
+  echo "Building C++ xDS interop test app Docker images"
+  docker build -f "${SRC_DIR}/tools/dockerfile/interoptest/grpc_interop_cxx_xds/Dockerfile.xds_client" -t "${CLIENT_IMAGE_NAME}:${GIT_COMMIT}" "${SRC_DIR}"
+  gcloud -q auth configure-docker
+  docker push "${CLIENT_IMAGE_NAME}:${GIT_COMMIT}"
+}
+
+#######################################
+# Builds test app and its docker images unless they already exist
+# Globals:
+#   CLIENT_IMAGE_NAME: Test client Docker image name
+#   GIT_COMMIT: SHA-1 of git commit being built
+#   FORCE_IMAGE_BUILD
+# Arguments:
+#   None
+# Outputs:
+#   Writes the output to stdout, stderr
+#######################################
+build_docker_images_if_needed() {
+  # Check if images already exist
+  client_tags="$(gcloud_gcr_list_image_tags "${CLIENT_IMAGE_NAME}" "${GIT_COMMIT}")"
+  printf "Client image: %s:%s\n" "${CLIENT_IMAGE_NAME}" "${GIT_COMMIT}"
+  echo "${client_tags:-Client image not found}"
+
+  # Build if any of the images are missing, or FORCE_IMAGE_BUILD=1
+  if [[ "${FORCE_IMAGE_BUILD}" == "1" || -z "${client_tags}" ]]; then
+    build_test_app_docker_images
+  else
+    echo "Skipping C++ test app build"
+  fi
+}
+
+#######################################
+# Executes the test case
+# Globals:
+#   TEST_DRIVER_FLAGFILE: Relative path to test driver flagfile
+#   KUBE_CONTEXT: The name of kubectl context with GKE cluster access
+#   TEST_XML_OUTPUT_DIR: Output directory for the test xUnit XML report
+#   CLIENT_IMAGE_NAME: Test client Docker image name
+#   GIT_COMMIT: SHA-1 of git commit being built
+# Arguments:
+#   Test case name
+# Outputs:
+#   Writes the output of test execution to stdout, stderr
+#   Test xUnit report to ${TEST_XML_OUTPUT_DIR}/${test_name}/sponge_log.xml
+#######################################
+run_test() {
+  # Test driver usage:
+  # https://github.com/grpc/grpc/tree/master/tools/run_tests/xds_k8s_test_driver#basic-usage
+  local test_name="${1:?Usage: run_test test_name}"
+  set -x
+  # NOTE(lidiz) we pin the server image to java-server because: 1. only Java
+  # server understands the rpc-behavior metadata; 2. all UrlMap tests today are
+  # testing client-side logic.
+  python -m "tests.${test_name}" \
+    --flagfile="${TEST_DRIVER_FLAGFILE}" \
+    --kube_context="${KUBE_CONTEXT}" \
+    --namespace="interop-psm-url-map" \
+    --server_xds_port=8848 \
+    --server_image="gcr.io/grpc-testing/xds-interop/java-server:d22f93e1ade22a1e026b57210f6fc21f7a3ca0cf" \
+    --client_image="${CLIENT_IMAGE_NAME}:${GIT_COMMIT}" \
+    --xml_output_file="${TEST_XML_OUTPUT_DIR}/${test_name}/sponge_log.xml" \
+    --strategy="reuse"
+  set +x
+}
+
+#######################################
+# Main function: provision software necessary to execute tests, and run them
+# Globals:
+#   KOKORO_ARTIFACTS_DIR
+#   GITHUB_REPOSITORY_NAME
+#   SRC_DIR: Populated with absolute path to the source repo
+#   TEST_DRIVER_REPO_DIR: Populated with the path to the repo containing
+#                         the test driver
+#   TEST_DRIVER_FULL_DIR: Populated with the path to the test driver source code
+#   TEST_DRIVER_FLAGFILE: Populated with relative path to test driver flagfile
+#   TEST_XML_OUTPUT_DIR: Populated with the path to test xUnit XML report
+#   GIT_ORIGIN_URL: Populated with the origin URL of git repo used for the build
+#   GIT_COMMIT: Populated with the SHA-1 of git commit being built
+#   GIT_COMMIT_SHORT: Populated with the short SHA-1 of git commit being built
+#   KUBE_CONTEXT: Populated with name of kubectl context with GKE cluster access
+# Arguments:
+#   None
+# Outputs:
+#   Writes the output of test execution to stdout, stderr
+#######################################
+main() {
+  local script_dir
+  script_dir="$(dirname "$0")"
+  # shellcheck source=tools/internal_ci/linux/grpc_xds_k8s_install_test_driver.sh
+  source "${script_dir}/grpc_xds_k8s_install_test_driver.sh"
+  set -x
+  if [[ -n "${KOKORO_ARTIFACTS_DIR}" ]]; then
+    kokoro_setup_test_driver "${GITHUB_REPOSITORY_NAME}"
+  else
+    local_setup_test_driver "${script_dir}"
+  fi
+  build_docker_images_if_needed
+  # Run tests
+  cd "${TEST_DRIVER_FULL_DIR}"
+  run_test url_map
+}
+
+main "$@"
index ece18c1..ff4a05e 100644 (file)
@@ -17,7 +17,7 @@
 # Location of the continuous shell script in repository.
 build_file: "grpc/tools/internal_ci/macos/grpc_build_artifacts.sh"
 gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
-timeout_mins: 150
+timeout_mins: 240
 action {
   define_artifacts {
     regex: "**/*sponge_log.*"
index 5cd532e..0644999 100644 (file)
@@ -113,6 +113,7 @@ LANG_RELEASE_MATRIX = {
             ('v1.35.0', ReleaseInfo()),
             ('v1.36.3', ReleaseInfo()),
             ('v1.37.0', ReleaseInfo()),
+            ('v1.38.0', ReleaseInfo()),
         ]),
     'go':
         OrderedDict([
@@ -187,6 +188,7 @@ LANG_RELEASE_MATRIX = {
             ('v1.36.0', ReleaseInfo(runtimes=['go1.11'])),
             ('v1.37.0', ReleaseInfo(runtimes=['go1.11'])),
             # NOTE: starting from release v1.38.0, use runtimes=['go1.16']
+            ('v1.38.0', ReleaseInfo(runtimes=['go1.16'])),
         ]),
     'java':
         OrderedDict([
@@ -257,7 +259,8 @@ LANG_RELEASE_MATRIX = {
             ('v1.34.1', ReleaseInfo()),
             ('v1.35.1', ReleaseInfo()),
             ('v1.36.1', ReleaseInfo()),
-            ('v1.37.0', ReleaseInfo()),
+            ('v1.37.1', ReleaseInfo()),
+            ('v1.38.1', ReleaseInfo()),
         ]),
     'python':
         OrderedDict([
@@ -321,6 +324,7 @@ LANG_RELEASE_MATRIX = {
             ('v1.35.0', ReleaseInfo(runtimes=['python'])),
             ('v1.36.3', ReleaseInfo(runtimes=['python'])),
             ('v1.37.0', ReleaseInfo(runtimes=['python'])),
+            ('v1.38.0', ReleaseInfo(runtimes=['python'])),
         ]),
     'node':
         OrderedDict([
@@ -387,6 +391,7 @@ LANG_RELEASE_MATRIX = {
             ('v1.35.0', ReleaseInfo()),
             ('v1.36.3', ReleaseInfo()),
             ('v1.37.0', ReleaseInfo()),
+            ('v1.38.0', ReleaseInfo()),
         ]),
     'php':
         OrderedDict([
@@ -426,6 +431,7 @@ LANG_RELEASE_MATRIX = {
             ('v1.35.0', ReleaseInfo()),
             ('v1.36.3', ReleaseInfo()),
             ('v1.37.0', ReleaseInfo()),
+            ('v1.38.0', ReleaseInfo()),
         ]),
     'csharp':
         OrderedDict([
index f2af8ed..afa67a3 100644 (file)
@@ -32,7 +32,7 @@ import json
 
 content_header = """Draft Release Notes For {version}
 --
-Final release notes will be generated from the PR titles that have *"release notes:yes"* label. If you have any additional notes please add them below. These will be appended to auto generated release notes. Previous releases notes are [here](https://github.com/grpc/grpc/releases).
+Final release notes will be generated from the PR titles that have *"release notes:yes"* label. If you have any additional notes please add them below. These will be appended to auto generated release notes. Previous release notes are [here](https://github.com/grpc/grpc/releases).
 
 **Also, look at the PRs listed below against your name.** Please apply the missing labels and make necessary corrections (like fixing the title) to the PR in Github. Final release notes will be generated just before the release on {date}.
 
index 7fd4fb8..42eea0f 100644 (file)
@@ -41,6 +41,8 @@ _DEFAULT_PACKAGES = [
     "grpcio-reflection",
     "grpcio-channelz",
     "grpcio-testing",
+    "grpcio-admin",
+    "grpcio-csds",
 ]
 
 Artifact = collections.namedtuple("Artifact", ("filename", "checksum"))
index 713e480..370d345 100644 (file)
@@ -35,7 +35,7 @@ if "%ARCHITECTURE%" == "x64" (
 )
 
 call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" %ARCHITECTURE%
-cmake -G Ninja -DCMAKE_C_COMPILER="%MSVC_COMPILER%" -DCMAKE_CXX_COMPILER="%MSVC_COMPILER%" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DgRPC_BUILD_TESTS=OFF -DgRPC_MSVC_STATIC_RUNTIME=ON ../../.. || goto :error
+cmake -G Ninja -DCMAKE_C_COMPILER="%MSVC_COMPILER%" -DCMAKE_CXX_COMPILER="%MSVC_COMPILER%" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DgRPC_BUILD_TESTS=OFF -DgRPC_MSVC_STATIC_RUNTIME=ON -DgRPC_XDS_USER_AGENT_IS_CSHARP=ON ../../.. || goto :error
 cmake --build . --target grpc_csharp_ext
 cd ..\..\..
 
index 3ea389f..528f60f 100755 (executable)
@@ -23,6 +23,7 @@ cd cmake/build
 cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \
       -DgRPC_BACKWARDS_COMPATIBILITY_MODE=ON \
       -DgRPC_BUILD_TESTS=OFF \
+      -DgRPC_XDS_USER_AGENT_IS_CSHARP=ON \
       ../..
 
 make grpc_csharp_ext -j2
index b0e7f96..1def948 100644 (file)
     "ci_platforms": [
       "linux",
       "mac",
-      "posix"
-    ],
-    "cpu_cost": 1.0,
-    "exclude_configs": [],
-    "exclude_iomgrs": [],
-    "flaky": false,
-    "gtest": false,
-    "language": "c",
-    "name": "handshake_verify_peer_options_test",
-    "platforms": [
-      "linux",
-      "mac",
-      "posix"
-    ],
-    "uses_polling": true
-  },
-  {
-    "args": [],
-    "benchmark": false,
-    "ci_platforms": [
-      "linux",
-      "mac",
       "posix",
       "windows"
     ],
     "flaky": false,
     "gtest": true,
     "language": "c++",
+    "name": "authorization_policy_provider_test",
+    "platforms": [
+      "linux",
+      "mac",
+      "posix",
+      "windows"
+    ],
+    "uses_polling": true
+  },
+  {
+    "args": [],
+    "benchmark": false,
+    "ci_platforms": [
+      "linux",
+      "mac",
+      "posix",
+      "windows"
+    ],
+    "cpu_cost": 1.0,
+    "exclude_configs": [],
+    "exclude_iomgrs": [],
+    "flaky": false,
+    "gtest": true,
+    "language": "c++",
     "name": "aws_request_signer_test",
     "platforms": [
       "linux",
     "flaky": false,
     "gtest": true,
     "language": "c++",
+    "name": "client_context_test_peer_test",
+    "platforms": [
+      "linux",
+      "mac",
+      "posix",
+      "windows"
+    ],
+    "uses_polling": true
+  },
+  {
+    "args": [],
+    "benchmark": false,
+    "ci_platforms": [
+      "linux",
+      "mac",
+      "posix",
+      "windows"
+    ],
+    "cpu_cost": 1.0,
+    "exclude_configs": [],
+    "exclude_iomgrs": [],
+    "flaky": false,
+    "gtest": true,
+    "language": "c++",
     "name": "client_interceptors_end2end_test",
     "platforms": [
       "linux",
     "flaky": false,
     "gtest": true,
     "language": "c++",
+    "name": "endpoint_config_test",
+    "platforms": [
+      "linux",
+      "mac",
+      "posix",
+      "windows"
+    ],
+    "uses_polling": true
+  },
+  {
+    "args": [],
+    "benchmark": false,
+    "ci_platforms": [
+      "linux",
+      "mac",
+      "posix",
+      "windows"
+    ],
+    "cpu_cost": 1.0,
+    "exclude_configs": [],
+    "exclude_iomgrs": [],
+    "flaky": false,
+    "gtest": true,
+    "language": "c++",
     "name": "error_details_test",
     "platforms": [
       "linux",
     "flaky": false,
     "gtest": true,
     "language": "c++",
+    "name": "grpc_authorization_policy_provider_test",
+    "platforms": [
+      "linux",
+      "mac",
+      "posix",
+      "windows"
+    ],
+    "uses_polling": true
+  },
+  {
+    "args": [],
+    "benchmark": false,
+    "ci_platforms": [
+      "linux",
+      "mac",
+      "posix",
+      "windows"
+    ],
+    "cpu_cost": 1.0,
+    "exclude_configs": [],
+    "exclude_iomgrs": [],
+    "flaky": false,
+    "gtest": true,
+    "language": "c++",
     "name": "grpc_tls_certificate_distributor_test",
     "platforms": [
       "linux",
index 05c6cf0..c505528 100644 (file)
@@ -28,7 +28,7 @@ cd build
 mkdir %ARCHITECTURE%
 cd %ARCHITECTURE%
 
-cmake -G "Visual Studio 14 2015" -A %ARCHITECTURE% -DgRPC_BUILD_TESTS=OFF -DgRPC_MSVC_STATIC_RUNTIME=ON ../../.. || goto :error
+cmake -G "Visual Studio 14 2015" -A %ARCHITECTURE% -DgRPC_BUILD_TESTS=OFF -DgRPC_MSVC_STATIC_RUNTIME=ON  -DgRPC_XDS_USER_AGENT_IS_CSHARP=ON ../../.. || goto :error
 
 cd ..\..\..\src\csharp
 
index 7d83986..e334d7e 100755 (executable)
@@ -21,7 +21,7 @@ cd "$(dirname "$0")/../../.."
 mkdir -p cmake/build
 cd cmake/build
 
-cmake -DgRPC_BUILD_TESTS=OFF -DCMAKE_BUILD_TYPE="${MSBUILD_CONFIG}" ../..
+cmake -DgRPC_BUILD_TESTS=OFF -DCMAKE_BUILD_TYPE="${MSBUILD_CONFIG}" -DgRPC_XDS_USER_AGENT_IS_CSHARP=ON ../..
 
 cd ../../src/csharp
 
index 8e3ab27..8f2b0ff 100644 (file)
@@ -163,7 +163,7 @@ repository, [grpc/test-infra](https://github.com/grpc/test-infra).
 
 ### Generating scenarios
 
-The benchmarks framework uses the same test scenarios as the legacy one. These
+The benchmarks framework uses the same test scenarios as the legacy one. The
 script [scenario_config_exporter.py](./scenario_config_exporter.py) can be used
 to export these scenarios to files, and also to count and analyze existing
 scenarios.
@@ -231,6 +231,9 @@ run by applying the test to a cluster running the LoadTest controller with
 $ kubectl apply -f loadtest_config.yaml
 ```
 
+> Note: The most common way of running tests generated by this script is to use
+> a _test runner_. For details, see [running tests](#running-tests).
+
 A basic template for generating tests in various languages can be found here:
 [loadtest_template_basic_all_languages.yaml](./templates/loadtest_template_basic_all_languages.yaml).
 The following example generates configurations for C# and Java tests using this
@@ -240,8 +243,9 @@ twice:
 ```
 $ ./tools/run_tests/performance/loadtest_config.py -l go -l java \
     -t ./tools/run_tests/performance/templates/loadtest_template_basic_all_languages.yaml \
-    -s client_pool=workers-8core -s server_pool=workers-8core \
-    -s big_query_table=grpc-testing.e2e_benchmarks.experimental_results \
+    -s client_pool=workers-8core -s driver_pool=drivers \
+    -s server_pool=workers-8core \
+    -s big_query_table=e2e_benchmarks.experimental_results \
     -s timeout_seconds=3600 --category=scalable \
     -d --allow_client_language=c++ --allow_server_language=c++ \
     --runs_per_test=2 -o ./loadtest.yaml
@@ -253,7 +257,12 @@ The script `loadtest_config.py` takes the following options:
 - `-t`, `--template`<br> Template file. A template is a configuration file that
   may contain multiple client and server configuration, and may also include
   substitution keys.
-- `p`, `--prefix`<br> Test names consist of a prefix_joined with a uuid with a
+- `-s`, `--substitution` Substitution keys, in the format `key=value`. These
+  keys are substituted while processing the template. Environment variables that
+  are set by the load test controller at runtime are ignored by default
+  (`DRIVER_PORT`, `KILL_AFTER`, `POD_TIMEOUT`). The user can override this
+  behavior by specifying these variables as keys.
+- `-p`, `--prefix`<br> Test names consist of a prefix_joined with a uuid with a
   dash. Test names are stored in `metadata.name`. The prefix is also added as
   the `prefix` label in `metadata.labels`. The prefix defaults to the user name
   if not set.
@@ -280,6 +289,13 @@ The script `loadtest_config.py` takes the following options:
 - `--allow_server_language`<br> Allows cross-language scenarios where the server
   is of a specified language, different from the scenario language. This is
   typically `node` or `c++`. This flag may be repeated.
+- `--instances_per_client`<br> This option generates multiple instances of the
+  clients for each test. The instances are named with the name of the client
+  combined with an index (or only an index, if no name is specified). If the
+  template specifies more than one client for a given language, it must also
+  specify unique names for each client. In the most common case, the template
+  contains only one unnamed client for each language, and the instances will be
+  named `0`, `1`, ...
 - `--runs_per_test`<br> This option specifies that each test should be repeated
   `n` times, where `n` is the value of the flag. If `n` > 1, the index of each
   test run is added as a uniquifier element for that run.
@@ -325,6 +341,19 @@ script can be invoked as follows:
 $ loadtest_concat_yaml.py -i infile1.yaml infile2.yaml -o outfile.yaml
 ```
 
+### Generating load test examples
+
+The script [loadtest_examples.sh](./loadtest_examples.sh) is provided to
+generate example load test configurations in all supported languages. This
+script takes only one argument, which is the output directory where the
+configurations will be created. The script produces a set of basic
+configurations, as well as a set of template configurations intended to be used
+with prebuilt images.
+
+The [examples](https://github.com/grpc/test-infra/tree/master/config/samples)
+in the repository [grpc/test-infra](https://github.com/grpc/test-infra) are
+generated by this script.
+
 ### Generating configuration templates
 
 The script [loadtest_template.py](./loadtest_template.py) generates a load test
@@ -340,13 +369,26 @@ was generated from the example configurations in
 
 ```
 $ ./tools/run_tests/performance/loadtest_template.py \
-    -i ../test-infra/config/samples/*.yaml \
-    --inject_client_pool --inject_server_pool --inject_big_query_table \
-    --inject_timeout_seconds \
+    -i ../test-infra/config/samples/*_example_loadtest.yaml \
+    --inject_client_pool --inject_server_pool \
+    --inject_big_query_table --inject_timeout_seconds \
     -o ./tools/run_tests/performance/templates/loadtest_template_basic_all_languages.yaml \
     --name basic_all_languages
 ```
 
+The example template with prebuilt images in
+[loadtest_template_prebuilt_all_languages.yaml](./templates/loadtest_template_prebuilt_all_languages.yaml)
+was generated by the following command:
+
+```
+$ ./tools/run_tests/performance/loadtest_template.py \
+    -i ../test-infra/config/samples/templates/*_example_loadtest_with_prebuilt_workers.yaml \
+    --inject_client_pool --inject_driver_image --inject_driver_pool \
+    --inject_server_pool --inject_big_query_table --inject_timeout_seconds \
+    -o ./tools/run_tests/performance/templates/loadtest_template_prebuilt_all_languages.yaml \
+    --name prebuilt_all_languages
+```
+
 The script `loadtest_template.py` takes the following options:
 
 - `-i`, `--inputs`<br> Space-separated list of the names of input files
@@ -354,6 +396,11 @@ The script `loadtest_template.py` takes the following options:
 - `-o`, `--output`<br> Output file name. Outputs to `sys.stdout` if not set.
 - `--inject_client_pool`<br> If this option is set, the pool attribute of all
   clients in `spec.clients` is set to `${client_pool}`, for later substitution.
+- `--inject_driver_image`<br> If this option is set, the image attribute of the
+  driver(s) in `spec.drivers` is set to `${driver_image}`, for later
+  substitution.
+- `--inject_driver_pool`<br> If this attribute is set, the pool attribute of the
+  driver(s) is set to `${driver_pool}`, for later substitution.
 - `--inject_server_pool`<br> If this option is set, the pool attribute of all
   servers in `spec.servers` is set to `${server_pool}`, for later substitution.
 - `--inject_big_query_table`<br> If this option is set,
@@ -366,7 +413,7 @@ The script `loadtest_template.py` takes the following options:
 - `-a`, `--annotation`<br> Metadata annotation to be stored in
   `metadata.annotations`, in the form key=value. May be repeated.
 
-The four options that inject substitution keys are the most useful for template
+The options that inject substitution keys are the most useful for template
 reuse. When running tests on different node pools, it becomes necessary to set
 the pool, and usually also to store the data on a different table. When running
 as part of a larger collection of tests, it may also be necessary to adjust test
@@ -379,3 +426,20 @@ Annotations, on the other hand, are passed on to the test configurations, and
 may be set to values or to substitution keys in themselves, allowing future
 automation scripts to process the tests generated from these configurations in
 different ways.
+
+### Running tests
+
+Collections of tests generated by `loadtest_config.py` are intended to be run
+with a test runner. The code for the test runner is stored in a separate
+repository, [grpc/test-infra](https://github.com/grpc/test-infra).
+
+The test runner applies the tests to the cluster, and monitors the tests for
+completion while they are running. The test runner can also be set up to run
+collections of tests in parallel on separate node pools, and to limit the number
+of tests running in parallel on each pool.
+
+The test runner is used in the continuous integration setup defined in
+[grpc_e2e_performance_gke.sh] and [grpc_e2e_performance_v2.sh].
+
+[grpc_e2e_performance_gke.sh]: ../../internal_ci/linux/grpc_e2e_performance_gke.sh
+[grpc_e2e_performance_v2.sh]: ../../internal_ci/linux/grpc_e2e_performance_v2.sh
index b91b2ca..0d21ec0 100755 (executable)
@@ -22,6 +22,7 @@
 # https://github.com/grpc/grpc/blob/master/tools/run_tests/performance/README.md#grpc-oss-benchmarks
 
 import argparse
+import collections
 import copy
 import datetime
 import itertools
@@ -44,29 +45,10 @@ CONFIGURATION_FILE_HEADER_COMMENT = """
 # https://github.com/grpc/grpc/blob/master/tools/run_tests/performance/README.md#grpc-oss-benchmarks
 """
 
-# TODO(paulosjca): Merge label_language and image_language into one function.
-# These functions are necessary because 'c++' is not allowed as a label value in
-# kubernetes, and because languages share images in the existing templates. Once
-# the templates are reorganized and most image mapping is removed, the two
-# functions can be merged into one.
 
-
-def label_language(language: str) -> str:
-    """Convert scenario language to place in a resource label."""
-    return {
-        'c++': 'cxx',
-    }.get(language, language)
-
-
-def image_language(language: str) -> str:
-    """Convert scenario languages to image languages."""
-    return {
-        'c++': 'cxx',
-        'node_purejs': 'node',
-        'php7': 'php',
-        'php7_protobuf_c': 'php',
-        'python_asyncio': 'python',
-    }.get(language, language)
+def safe_name(language: str) -> str:
+    """Returns a name that is safe to use in labels and file names."""
+    return scenario_config.LANGUAGES[language].safename
 
 
 def default_prefix() -> str:
@@ -81,11 +63,13 @@ def now_string() -> str:
 
 def validate_loadtest_name(name: str) -> None:
     """Validates that a LoadTest name is in the expected format."""
-    if len(name) > 63:
+    if len(name) > 253:
         raise ValueError(
-            'LoadTest name must be less than 63 characters long: %s' % name)
-    if not all((s.isalnum() for s in name.split('-'))):
-        raise ValueError('Invalid elements in LoadTest name: %s' % name)
+            'LoadTest name must be less than 253 characters long: %s' % name)
+    if not all(c.isalnum() and not c.isupper() for c in name if c != '-'):
+        raise ValueError('Invalid characters in LoadTest name: %s' % name)
+    if not name or not name[0].isalpha() or name[-1] == '-':
+        raise ValueError('Invalid format for LoadTest name: %s' % name)
 
 
 def loadtest_base_name(scenario_name: str,
@@ -93,7 +77,7 @@ def loadtest_base_name(scenario_name: str,
     """Constructs and returns the base name for a LoadTest resource."""
     name_elements = scenario_name.split('_')
     name_elements.extend(uniquifier_elements)
-    return '-'.join(name_elements)
+    return '-'.join(element.lower() for element in name_elements)
 
 
 def loadtest_name(prefix: str, scenario_name: str,
@@ -103,12 +87,17 @@ def loadtest_name(prefix: str, scenario_name: str,
     name_elements = []
     if prefix:
         name_elements.append(prefix)
-    name_elements.append(str(uuid.uuid5(uuid.NAMESPACE_DNS, base_name)))
+    name_elements.append(base_name)
     name = '-'.join(name_elements)
     validate_loadtest_name(name)
     return name
 
 
+def component_name(elements: Iterable[str]) -> str:
+    """Constructs a component name from possibly empty elements."""
+    return '-'.join((e for e in elements if e))
+
+
 def validate_annotations(annotations: Dict[str, str]) -> None:
     """Validates that annotations do not contain reserved names.
 
@@ -139,6 +128,7 @@ def gen_loadtest_configs(
         loadtest_name_prefix: str,
         uniquifier_elements: Iterable[str],
         annotations: Mapping[str, str],
+        instances_per_client: int = 1,
         runs_per_test: int = 1) -> Iterable[Dict[str, Any]]:
     """Generates LoadTest configurations for a given language config.
 
@@ -146,10 +136,8 @@ def gen_loadtest_configs(
     """
     validate_annotations(annotations)
     prefix = loadtest_name_prefix or default_prefix()
-    cl = image_language(language_config.client_language or
-                        language_config.language)
-    sl = image_language(language_config.server_language or
-                        language_config.language)
+    cl = safe_name(language_config.client_language or language_config.language)
+    sl = safe_name(language_config.server_language or language_config.language)
     scenario_filter = scenario_config_exporter.scenario_filter(
         scenario_name_regex=scenario_name_regex,
         category=language_config.category,
@@ -172,8 +160,7 @@ def gen_loadtest_configs(
             metadata['name'] = name
             if 'labels' not in metadata:
                 metadata['labels'] = dict()
-            metadata['labels']['language'] = label_language(
-                language_config.language)
+            metadata['labels']['language'] = safe_name(language_config.language)
             metadata['labels']['prefix'] = prefix
             if 'annotations' not in metadata:
                 metadata['annotations'] = dict()
@@ -186,23 +173,68 @@ def gen_loadtest_configs(
             spec = config['spec']
 
             # Select clients with the required language.
-            spec['clients'] = [
+            clients = [
                 client for client in base_config_clients
                 if client['language'] == cl
             ]
-            if not spec['clients']:
+            if not clients:
                 raise IndexError('Client language not found in template: %s' %
                                  cl)
 
+            # Validate config for additional client instances.
+            if instances_per_client > 1:
+                c = collections.Counter(
+                    (client.get('name', '') for client in clients))
+                if max(c.values()) > 1:
+                    raise ValueError(
+                        ('Multiple instances of multiple clients requires '
+                         'unique names, name counts for language %s: %s') %
+                        (cl, c.most_common()))
+
+            # Name client instances with an index starting from zero.
+            client_instances = []
+            for i in range(instances_per_client):
+                client_instances.extend(copy.deepcopy(clients))
+                for client in client_instances[-len(clients):]:
+                    client['name'] = component_name((client.get('name',
+                                                                ''), str(i)))
+
+            # Set clients to named instances.
+            spec['clients'] = client_instances
+
             # Select servers with the required language.
-            spec['servers'] = [
+            servers = copy.deepcopy([
                 server for server in base_config_servers
                 if server['language'] == sl
-            ]
-            if not spec['servers']:
+            ])
+            if not servers:
                 raise IndexError('Server language not found in template: %s' %
                                  sl)
 
+            # Name servers with an index for consistency with clients.
+            for i, server in enumerate(servers):
+                server['name'] = component_name((server.get('name',
+                                                            ''), str(i)))
+
+            # Set servers to named instances.
+            spec['servers'] = servers
+
+            # Add driver, if needed.
+            if 'driver' not in spec:
+                spec['driver'] = dict()
+
+            # Ensure driver has language and run fields.
+            driver = spec['driver']
+            if 'language' not in driver:
+                driver['language'] = safe_name('c++')
+            if 'run' not in driver:
+                driver['run'] = dict()
+
+            # Name the driver with an index for consistency with workers.
+            # There is only one driver, so the index is zero.
+            if 'name' not in driver or not driver['name']:
+                driver['name'] = '0'
+
             spec['scenariosJSON'] = scenario_str
 
             yield config
@@ -221,6 +253,29 @@ def parse_key_value_args(args: Optional[Iterable[str]]) -> Dict[str, str]:
     return d
 
 
+def clear_empty_fields(config: Dict[str, Any]) -> None:
+    """Clears fields set to empty values by string substitution."""
+    spec = config['spec']
+    if 'clients' in spec:
+        for client in spec['clients']:
+            if 'pool' in client and not client['pool']:
+                del client['pool']
+    if 'servers' in spec:
+        for server in spec['servers']:
+            if 'pool' in server and not server['pool']:
+                del server['pool']
+    if 'driver' in spec:
+        driver = spec['driver']
+        if 'pool' in driver and not driver['pool']:
+            del driver['pool']
+        if ('run' in driver and 'image' in driver['run'] and
+                not driver['run']['image']):
+            del driver['run']['image']
+    if 'results' in spec and not ('bigQueryTable' in spec['results'] and
+                                  spec['results']['bigQueryTable']):
+        del spec['results']
+
+
 def config_dumper(header_comment: str) -> Type[yaml.SafeDumper]:
     """Returns a custom dumper to dump configurations in the expected format."""
 
@@ -232,15 +287,6 @@ def config_dumper(header_comment: str) -> Type[yaml.SafeDumper]:
                 self.write_indent()
                 self.write_indicator(header_comment, need_whitespace=False)
 
-        def expect_block_sequence(self):
-            super().expect_block_sequence()
-            self.increase_indent()
-
-        def expect_block_sequence_item(self, first=False):
-            if isinstance(self.event, yaml.SequenceEndEvent):
-                self.indent = self.indents.pop()
-            super().expect_block_sequence_item(first)
-
     def str_presenter(dumper, data):
         if '\n' in data:
             return dumper.represent_scalar('tag:yaml.org,2002:str',
@@ -321,6 +367,10 @@ def main() -> None:
         default=[],
         help='Allow cross-language scenarios with this server language.',
         dest='allow_server_languages')
+    argp.add_argument('--instances_per_client',
+                      default=1,
+                      type=int,
+                      help="Number of instances to generate for each client.")
     argp.add_argument('--runs_per_test',
                       default=1,
                       type=int,
@@ -331,7 +381,23 @@ def main() -> None:
                       help='Output file name. Output to stdout if not set.')
     args = argp.parse_args()
 
-    substitutions = parse_key_value_args(args.substitutions)
+    if args.instances_per_client < 1:
+        argp.error('instances_per_client must be greater than zero.')
+
+    if args.runs_per_test < 1:
+        argp.error('runs_per_test must be greater than zero.')
+
+    # Config generation ignores environment variables that are passed by the
+    # controller at runtime.
+    substitutions = {
+        'DRIVER_PORT': '${DRIVER_PORT}',
+        'KILL_AFTER': '${KILL_AFTER}',
+        'POD_TIMEOUT': '${POD_TIMEOUT}',
+    }
+
+    # The user can override the ignored variables above by passing them in as
+    # substitution keys.
+    substitutions.update(parse_key_value_args(args.substitutions))
 
     uniquifier_elements = args.uniquifier_elements
     if args.d:
@@ -343,6 +409,8 @@ def main() -> None:
         base_config = yaml.safe_load(
             string.Template(f.read()).substitute(substitutions))
 
+    clear_empty_fields(base_config)
+
     spec = base_config['spec']
     base_config_clients = spec['clients']
     del spec['clients']
@@ -368,6 +436,7 @@ def main() -> None:
                                  loadtest_name_prefix=args.prefix,
                                  uniquifier_elements=uniquifier_elements,
                                  annotations=annotations,
+                                 instances_per_client=args.instances_per_client,
                                  runs_per_test=args.runs_per_test))
     configs = (config for config in itertools.chain(*config_generators))
 
diff --git a/tools/run_tests/performance/loadtest_examples.sh b/tools/run_tests/performance/loadtest_examples.sh
new file mode 100755 (executable)
index 0000000..55efcad
--- /dev/null
@@ -0,0 +1,120 @@
+#!/bin/bash
+# Copyright 2021 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.
+
+# This script generates a set of load test examples from templates.
+
+LOADTEST_CONFIG=tools/run_tests/performance/loadtest_config.py
+
+if (( $# < 1 )); then
+    echo "Usage: ${0} <output directory>" >&2
+    exit 1
+fi
+
+if [[ ! -x "${LOADTEST_CONFIG}" ]]; then
+    echo "${LOADTEST_CONFIG} not found." >&2
+    exit 1
+fi
+
+outputbasedir="${1}"
+
+mkdir -p "${outputbasedir}/templates"
+
+example_file() {
+    local scenario="${1}"
+    local suffix="${2}"
+    if [[ "${scenario#cpp_}" != "${scenario}" ]]; then
+        echo "cxx${suffix}"
+        return
+    fi
+    if [[ "${scenario#python_asyncio_}" != "${scenario}" ]]; then
+        echo "python_asyncio${suffix}"
+        return
+    fi
+    echo "${scenario%%_*}${suffix}"
+}
+
+example_language() {
+    local filename="${1}"
+    if [[ "${filename#cxx_}" != "${filename}" ]]; then
+        echo "c++"
+        return
+    fi
+    if [[ "${filename#python_asyncio_}" != "${filename}" ]]; then
+        echo "python_asyncio"
+        return
+    fi
+    echo "${filename%%_*}"
+}
+
+scenarios=(
+    "cpp_generic_async_streaming_ping_pong_secure"
+    "csharp_protobuf_async_unary_ping_pong"
+    "go_generic_sync_streaming_ping_pong_secure"
+    "java_generic_async_streaming_ping_pong_secure"
+    "node_to_node_generic_async_streaming_ping_pong_secure"
+    "php7_protobuf_php_extension_to_cpp_protobuf_sync_unary_ping_pong"
+    "python_generic_sync_streaming_ping_pong"
+    "python_asyncio_generic_async_streaming_ping_pong"
+    "ruby_protobuf_sync_streaming_ping_pong"
+)
+
+# Basic examples are intended to be runnable _as is_, so substitution keys
+# are stripped. Fields can be inserted manually following the pattern of the
+# prebuilt examples.
+basic_example() {
+    local -r scenario="${1}"
+    local -r outputdir="${2}"
+    local -r outputfile="$(example_file "${scenario}" _example_loadtest.yaml)"
+    local -r language="$(example_language "${outputfile}")"
+    ${LOADTEST_CONFIG} \
+        -l "${language}" \
+        -t ./tools/run_tests/performance/templates/loadtest_template_basic_all_languages.yaml \
+        -s client_pool= -s server_pool= -s big_query_table= \
+        -s timeout_seconds=900 --prefix=examples -u basic -r "^${scenario}$" \
+        --allow_client_language=c++ --allow_server_language=c++ \
+        --allow_server_language=node \
+        -o "${outputdir}/${outputfile}"
+    echo "Created example: ${outputfile}"
+}
+
+# Prebuilt examples contain substitution keys, so must be processed before
+# running.
+prebuilt_example() {
+    local -r scenario="${1}"
+    local -r outputdir="${2}"
+    local -r outputfile="$(example_file "${scenario}" _example_loadtest_with_prebuilt_workers.yaml)"
+    local -r language="$(example_language "${outputfile}")"
+    ${LOADTEST_CONFIG} \
+        -l "${language}" \
+        -t ./tools/run_tests/performance/templates/loadtest_template_prebuilt_all_languages.yaml \
+        -s driver_pool="\${driver_pool}" -s driver_image="\${driver_image}" \
+        -s client_pool="\${workers_pool}" -s server_pool="\${workers_pool}" \
+        -s big_query_table="\${big_query_table}" -s timeout_seconds=900 \
+        -s prebuilt_image_prefix="\${prebuilt_image_prefix}" \
+        -s prebuilt_image_tag="\${prebuilt_image_tag}" --prefix=examples -u prebuilt \
+        -a pool="\${workers_pool}" -r "^${scenario}$" \
+        --allow_client_language=c++ --allow_server_language=c++ \
+        --allow_server_language=node \
+        -o "${outputdir}/${outputfile}"
+    echo "Created example: ${outputfile}"
+}
+
+for scenario in "${scenarios[@]}"; do
+    basic_example "${scenario}" "${outputbasedir}"
+done
+
+for scenario in "${scenarios[@]}"; do
+    prebuilt_example "${scenario}" "${outputbasedir}/templates"
+done
index 9684bb9..5814552 100755 (executable)
@@ -28,7 +28,7 @@
 import argparse
 import sys
 
-from typing import Any, Dict, Iterable, Mapping, Type
+from typing import Any, Dict, Iterable, List, Mapping, Type
 
 import yaml
 
@@ -48,20 +48,42 @@ TEMPLATE_FILE_HEADER_COMMENT = """
 """
 
 
+def insert_worker(worker: Dict[str, Any], workers: List[Dict[str,
+                                                             Any]]) -> None:
+    """Inserts client or server into a list, without inserting duplicates."""
+
+    def dump(w):
+        return yaml.dump(w, Dumper=yaml.SafeDumper, default_flow_style=False)
+
+    worker_str = dump(worker)
+    if any((worker_str == dump(w) for w in workers)):
+        return
+    workers.append(worker)
+
+
+def uniquify_workers(workermap: Dict[str, List[Dict[str, Any]]]) -> None:
+    """Name workers if there is more than one for the same map key."""
+    for workers in workermap.values():
+        if len(workers) <= 1:
+            continue
+        for i, worker in enumerate(workers):
+            worker['name'] = str(i)
+
+
 def loadtest_template(
         input_file_names: Iterable[str],
         metadata: Mapping[str, Any],
         inject_client_pool: bool,
+        inject_driver_image: bool,
+        inject_driver_pool: bool,
         inject_server_pool: bool,
         inject_big_query_table: bool,
         inject_timeout_seconds: bool,
         inject_ttl_seconds: bool) -> Dict[str, Any]:  # yapf: disable
     """Generates the load test template."""
-    clients = list()
-    servers = list()
-    spec = dict()
-    client_languages = set()
-    server_languages = set()
+    spec = dict()  # type: Dict[str, Any]
+    clientmap = dict()  # Dict[str, List[Dict[str, Any]]]
+    servermap = dict()  # Dict[Str, List[Dict[str, Any]]]
     template = {
         'apiVersion': 'e2etest.grpc.io/v1',
         'kind': 'LoadTest',
@@ -79,20 +101,20 @@ def loadtest_template(
                     input_file_name, input_config.get('kind')))
 
             for client in input_config['spec']['clients']:
-                if client['language'] in client_languages:
-                    continue
+                del client['name']
                 if inject_client_pool:
                     client['pool'] = '${client_pool}'
-                clients.append(client)
-                client_languages.add(client['language'])
+                if client['language'] not in clientmap:
+                    clientmap[client['language']] = []
+                insert_worker(client, clientmap[client['language']])
 
             for server in input_config['spec']['servers']:
-                if server['language'] in server_languages:
-                    continue
+                del server['name']
                 if inject_server_pool:
                     server['pool'] = '${server_pool}'
-                servers.append(server)
-                server_languages.add(server['language'])
+                if server['language'] not in servermap:
+                    servermap[server['language']] = []
+                insert_worker(server, servermap[server['language']])
 
             input_spec = input_config['spec']
             del input_spec['clients']
@@ -100,14 +122,36 @@ def loadtest_template(
             del input_spec['scenariosJSON']
             spec.update(input_config['spec'])
 
-    clients.sort(key=lambda x: x['language'])
-    servers.sort(key=lambda x: x['language'])
+    uniquify_workers(clientmap)
+    uniquify_workers(servermap)
 
     spec.update({
-        'clients': clients,
-        'servers': servers,
+        'clients':
+            sum((clientmap[language] for language in sorted(clientmap)),
+                start=[]),
+        'servers':
+            sum((servermap[language] for language in sorted(servermap)),
+                start=[]),
     })
 
+    if 'driver' not in spec:
+        spec['driver'] = {'language': 'cxx'}
+
+    driver = spec['driver']
+    if 'name' in driver:
+        del driver['name']
+    if inject_driver_image:
+        if 'run' not in driver:
+            driver['run'] = {}
+        driver['run']['image'] = '${driver_image}'
+    if inject_driver_pool:
+        driver['pool'] = '${driver_pool}'
+
+    if 'run' not in driver:
+        if inject_driver_pool:
+            raise ValueError('Cannot inject driver.pool: missing driver.run.')
+        del spec['driver']
+
     if inject_big_query_table:
         if 'results' not in spec:
             spec['results'] = dict()
@@ -133,14 +177,14 @@ def template_dumper(header_comment: str) -> Type[yaml.SafeDumper]:
                 self.write_indent()
                 self.write_indicator(header_comment, need_whitespace=False)
 
-        def expect_block_sequence(self):
-            super().expect_block_sequence()
-            self.increase_indent()
+    def str_presenter(dumper, data):
+        if '\n' in data:
+            return dumper.represent_scalar('tag:yaml.org,2002:str',
+                                           data,
+                                           style='|')
+        return dumper.represent_scalar('tag:yaml.org,2002:str', data)
 
-        def expect_block_sequence_item(self, first=False):
-            if isinstance(self.event, yaml.SequenceEndEvent):
-                self.indent = self.indents.pop()
-            super().expect_block_sequence_item(first)
+    TemplateDumper.add_representer(str, str_presenter)
 
     return TemplateDumper
 
@@ -164,6 +208,14 @@ def main() -> None:
         action='store_true',
         help='Set spec.client(s).pool values to \'${client_pool}\'.')
     argp.add_argument(
+        '--inject_driver_image',
+        action='store_true',
+        help='Set spec.driver(s).image values to \'${driver_image}\'.')
+    argp.add_argument(
+        '--inject_driver_pool',
+        action='store_true',
+        help='Set spec.driver(s).pool values to \'${driver_pool}\'.')
+    argp.add_argument(
         '--inject_server_pool',
         action='store_true',
         help='Set spec.server(s).pool values to \'${server_pool}\'.')
@@ -200,6 +252,8 @@ def main() -> None:
         input_file_names=args.inputs,
         metadata=metadata,
         inject_client_pool=args.inject_client_pool,
+        inject_driver_image=args.inject_driver_image,
+        inject_driver_pool=args.inject_driver_pool,
         inject_server_pool=args.inject_server_pool,
         inject_big_query_table=args.inject_big_query_table,
         inject_timeout_seconds=args.inject_timeout_seconds,
index ed2b532..0315da3 100644 (file)
@@ -24,7 +24,7 @@ SMOKETEST = 'smoketest'
 SCALABLE = 'scalable'
 INPROC = 'inproc'
 SWEEP = 'sweep'
-DEFAULT_CATEGORIES = [SCALABLE, SMOKETEST]
+DEFAULT_CATEGORIES = (SCALABLE, SMOKETEST)
 
 SECURE_SECARGS = {
     'use_test_ca': True,
@@ -127,13 +127,13 @@ def _ping_pong_scenario(name,
                         server_threads_per_cq=0,
                         client_threads_per_cq=0,
                         warmup_seconds=WARMUP_SECONDS,
-                        categories=DEFAULT_CATEGORIES,
+                        categories=None,
                         channels=None,
                         outstanding=None,
                         num_clients=None,
                         resource_quota_size=None,
                         messages_per_stream=None,
-                        excluded_poll_engines=[],
+                        excluded_poll_engines=None,
                         minimal_stack=False,
                         offered_load=None):
     """Creates a basic ping pong scenario."""
@@ -162,7 +162,9 @@ def _ping_pong_scenario(name,
             'channel_args': [],
         },
         'warmup_seconds': warmup_seconds,
-        'benchmark_seconds': BENCHMARK_SECONDS
+        'benchmark_seconds': BENCHMARK_SECONDS,
+        'CATEGORIES': list(DEFAULT_CATEGORIES),
+        'EXCLUDED_POLL_ENGINES': [],
     }
     if resource_quota_size:
         scenario['server_config']['resource_quota_size'] = resource_quota_size
@@ -232,10 +234,18 @@ def _ping_pong_scenario(name,
     return scenario
 
 
-class CXXLanguage:
+class Language(object):
 
-    def __init__(self):
-        self.safename = 'cxx'
+    @property
+    def safename(self):
+        return str(self)
+
+
+class CXXLanguage(Language):
+
+    @property
+    def safename(self):
+        return 'cxx'
 
     def worker_cmdline(self):
         return ['cmake/build/qps_worker']
@@ -619,10 +629,7 @@ class CXXLanguage:
         return 'c++'
 
 
-class CSharpLanguage:
-
-    def __init__(self):
-        self.safename = str(self)
+class CSharpLanguage(Language):
 
     def worker_cmdline(self):
         return ['tools/run_tests/performance/run_worker_csharp.sh']
@@ -747,10 +754,7 @@ class CSharpLanguage:
         return 'csharp'
 
 
-class PythonLanguage:
-
-    def __init__(self):
-        self.safename = 'python'
+class PythonLanguage(Language):
 
     def worker_cmdline(self):
         return ['tools/run_tests/performance/run_worker_python.sh']
@@ -824,10 +828,7 @@ class PythonLanguage:
         return 'python'
 
 
-class PythonAsyncIOLanguage:
-
-    def __init__(self):
-        self.safename = 'python_asyncio'
+class PythonAsyncIOLanguage(Language):
 
     def worker_cmdline(self):
         return ['tools/run_tests/performance/run_worker_python_asyncio.sh']
@@ -972,11 +973,7 @@ class PythonAsyncIOLanguage:
         return 'python_asyncio'
 
 
-class RubyLanguage:
-
-    def __init__(self):
-        pass
-        self.safename = str(self)
+class RubyLanguage(Language):
 
     def worker_cmdline(self):
         return ['tools/run_tests/performance/run_worker_ruby.sh']
@@ -1037,12 +1034,11 @@ class RubyLanguage:
         return 'ruby'
 
 
-class Php7Language:
+class Php7Language(Language):
 
     def __init__(self, php7_protobuf_c=False):
-        pass
+        super().__init__()
         self.php7_protobuf_c = php7_protobuf_c
-        self.safename = str(self)
 
     def worker_cmdline(self):
         if self.php7_protobuf_c:
@@ -1108,11 +1104,7 @@ class Php7Language:
         return 'php7'
 
 
-class JavaLanguage:
-
-    def __init__(self):
-        pass
-        self.safename = str(self)
+class JavaLanguage(Language):
 
     def worker_cmdline(self):
         return ['tools/run_tests/performance/run_worker_java.sh']
@@ -1212,11 +1204,7 @@ class JavaLanguage:
         return 'java'
 
 
-class GoLanguage:
-
-    def __init__(self):
-        pass
-        self.safename = str(self)
+class GoLanguage(Language):
 
     def worker_cmdline(self):
         return ['tools/run_tests/performance/run_worker_go.sh']
@@ -1297,12 +1285,11 @@ class GoLanguage:
         return 'go'
 
 
-class NodeLanguage:
+class NodeLanguage(Language):
 
     def __init__(self, node_purejs=False):
-        pass
+        super().__init__()
         self.node_purejs = node_purejs
-        self.safename = str(self)
 
     def worker_cmdline(self):
         fixture = 'native_js' if self.node_purejs else 'native_native'
index d8837fd..482249e 100755 (executable)
@@ -48,17 +48,13 @@ from typing import Any, Callable, Dict, Iterable, NamedTuple
 import scenario_config
 
 # Language parameters for load test config generation.
+
 LanguageConfig = NamedTuple('LanguageConfig', [('category', str),
                                                ('language', str),
                                                ('client_language', str),
                                                ('server_language', str)])
 
 
-def as_dict_no_empty_values(self):
-    """Returns the parameters as a dictionary, ignoring empty values."""
-    return dict((item for item in self._asdict().items() if item[1]))
-
-
 def category_string(categories: Iterable[str], category: str) -> str:
     """Converts a list of categories into a single string for counting."""
     if category != 'all':
index fb1d633..01084ac 100644 (file)
@@ -14,246 +14,330 @@ metadata:
   name: basic_all_languages
 spec:
   clients:
-    - build:
-        command:
-          - bash
-          - /build_scripts/build_qps_worker.sh
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc.git
-      language: csharp
-      pool: ${client_pool}
-      run:
-        args:
-          - exec
-          - qps_worker/Grpc.IntegrationTesting.QpsWorker.dll
-        command:
-          - dotnet
-    - build:
-        args:
-          - build
-          - //test/cpp/qps:qps_worker
-        command:
-          - bazel
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc.git
-      language: cxx
-      pool: ${client_pool}
-      run:
-        command:
-          - bazel-bin/test/cpp/qps/qps_worker
-    - build:
-        args:
-          - build
-          - -o
-          - /src/workspace/bin/worker
-          - ./benchmark/worker
-        command:
-          - go
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc-go.git
-      language: go
-      pool: ${client_pool}
-      run:
-        command:
-          - /src/workspace/bin/worker
-    - build:
-        args:
-          - -PskipAndroid=true
-          - -PskipCodegen=true
-          - :grpc-benchmarks:installDist
-        command:
-          - gradle
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc-java.git
-      language: java
-      pool: ${client_pool}
-      run:
-        command:
-          - benchmarks/build/install/grpc-benchmarks/bin/benchmark_worker
-    - build:
-        command:
-          - bash
-          - /build_scripts/build_qps_worker.sh
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc-node.git
-      language: node
-      pool: ${client_pool}
-      run:
-        args:
-          - -r
-          - ./test/fixtures/native_native.js
-          - test/performance/worker.js
-          - --benchmark_impl=grpc
-        command:
-          - node
-    - build:
-        command:
-          - bash
-          - /build_scripts/build_qps_worker.sh
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc.git
-      language: php
-      pool: ${client_pool}
-      run:
-        command:
-          - bash
-          - /run_scripts/run_worker.sh
-    - build:
-        args:
-          - build
-          - //src/python/grpcio_tests/tests/qps:qps_worker
-        command:
-          - bazel
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc.git
-      language: python
-      pool: ${client_pool}
-      run:
-        command:
-          - bazel-bin/src/python/grpcio_tests/tests/qps/qps_worker
-    - build:
-        command:
-          - bash
-          - /build_scripts/build_qps_worker.sh
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc
-      language: ruby
-      pool: ${client_pool}
-      run:
-        args:
-          - src/ruby/qps/worker.rb
-        command:
-          - ruby
+  - build:
+      command:
+      - bash
+      - /build_scripts/build_qps_worker.sh
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc.git
+    language: csharp
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" dotnet exec \
+            qps_worker/Grpc.IntegrationTesting.QpsWorker.dll \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      args:
+      - build
+      - //test/cpp/qps:qps_worker
+      command:
+      - bazel
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc.git
+    language: cxx
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            bazel-bin/test/cpp/qps/qps_worker --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      args:
+      - build
+      - -o
+      - /src/workspace/bin/worker
+      - ./benchmark/worker
+      command:
+      - go
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc-go.git
+    language: go
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /src/workspace/bin/worker --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      args:
+      - -PskipAndroid=true
+      - -PskipCodegen=true
+      - :grpc-benchmarks:installDist
+      command:
+      - gradle
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc-java.git
+    language: java
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            benchmarks/build/install/grpc-benchmarks/bin/benchmark_worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      command:
+      - bash
+      - /build_scripts/build_qps_worker.sh
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc-node.git
+    language: node
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" node -r \
+            ./test/fixtures/native_native.js test/performance/worker.js \
+            --benchmark_impl=grpc --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      command:
+      - bash
+      - /build_scripts/build_qps_worker.sh
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc.git
+    language: php7
+    pool: ${client_pool}
+    run:
+      command:
+      - bash
+      - /run_scripts/run_worker.sh
+  - build:
+      args:
+      - build
+      - //src/python/grpcio_tests/tests/qps:qps_worker
+      command:
+      - bazel
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc.git
+    language: python
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            bazel-bin/src/python/grpcio_tests/tests/qps/qps_worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      args:
+      - build
+      - //src/python/grpcio_tests/tests_aio/benchmark:worker
+      command:
+      - bazel
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc.git
+    language: python_asyncio
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            bazel-bin/src/python/grpcio_tests/tests_aio/benchmark/worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      command:
+      - bash
+      - /build_scripts/build_qps_worker.sh
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc
+    language: ruby
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" ruby \
+            src/ruby/qps/worker.rb --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
   results:
     bigQueryTable: ${big_query_table}
   servers:
-    - build:
-        command:
-          - bash
-          - /build_scripts/build_qps_worker.sh
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc.git
-      language: csharp
-      pool: ${server_pool}
-      run:
-        args:
-          - exec
-          - qps_worker/Grpc.IntegrationTesting.QpsWorker.dll
-        command:
-          - dotnet
-    - build:
-        args:
-          - build
-          - //test/cpp/qps:qps_worker
-        command:
-          - bazel
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc.git
-      language: cxx
-      pool: ${server_pool}
-      run:
-        args:
-          - --server_port=10010
-        command:
-          - bazel-bin/test/cpp/qps/qps_worker
-    - build:
-        args:
-          - build
-          - -o
-          - /src/workspace/bin/worker
-          - ./benchmark/worker
-        command:
-          - go
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc-go.git
-      language: go
-      pool: ${server_pool}
-      run:
-        command:
-          - /src/workspace/bin/worker
-    - build:
-        args:
-          - -PskipAndroid=true
-          - -PskipCodegen=true
-          - :grpc-benchmarks:installDist
-        command:
-          - gradle
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc-java.git
-      language: java
-      pool: ${server_pool}
-      run:
-        command:
-          - benchmarks/build/install/grpc-benchmarks/bin/benchmark_worker
-    - build:
-        command:
-          - bash
-          - /build_scripts/build_qps_worker.sh
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc-node.git
-      language: node
-      pool: ${server_pool}
-      run:
-        args:
-          - -r
-          - ./test/fixtures/native_native.js
-          - test/performance/worker.js
-          - --benchmark_impl=grpc
-        command:
-          - node
-    - build:
-        command:
-          - bash
-          - /build_scripts/build_qps_worker.sh
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc.git
-      language: php
-      pool: ${server_pool}
-      run:
-        command:
-          - bash
-          - /run_scripts/run_worker.sh
-    - build:
-        args:
-          - build
-          - //src/python/grpcio_tests/tests/qps:qps_worker
-        command:
-          - bazel
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc.git
-      language: python
-      pool: ${server_pool}
-      run:
-        command:
-          - bazel-bin/src/python/grpcio_tests/tests/qps/qps_worker
-    - build:
-        command:
-          - bash
-          - /build_scripts/build_qps_worker.sh
-      clone:
-        gitRef: master
-        repo: https://github.com/grpc/grpc
-      language: ruby
-      pool: ${server_pool}
-      run:
-        args:
-          - src/ruby/qps/worker.rb
-        command:
-          - ruby
+  - build:
+      command:
+      - bash
+      - /build_scripts/build_qps_worker.sh
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc.git
+    language: csharp
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" dotnet exec \
+            qps_worker/Grpc.IntegrationTesting.QpsWorker.dll \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      args:
+      - build
+      - //test/cpp/qps:qps_worker
+      command:
+      - bazel
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc.git
+    language: cxx
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            bazel-bin/test/cpp/qps/qps_worker --driver_port="${DRIVER_PORT}" \
+            --server_port=10010
+      command:
+      - bash
+  - build:
+      args:
+      - build
+      - -o
+      - /src/workspace/bin/worker
+      - ./benchmark/worker
+      command:
+      - go
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc-go.git
+    language: go
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /src/workspace/bin/worker --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      args:
+      - -PskipAndroid=true
+      - -PskipCodegen=true
+      - :grpc-benchmarks:installDist
+      command:
+      - gradle
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc-java.git
+    language: java
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            benchmarks/build/install/grpc-benchmarks/bin/benchmark_worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      command:
+      - bash
+      - /build_scripts/build_qps_worker.sh
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc-node.git
+    language: node
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" node -r \
+            ./test/fixtures/native_native.js test/performance/worker.js \
+            --benchmark_impl=grpc --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      args:
+      - build
+      - //src/python/grpcio_tests/tests/qps:qps_worker
+      command:
+      - bazel
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc.git
+    language: python
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            bazel-bin/src/python/grpcio_tests/tests/qps/qps_worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      args:
+      - build
+      - //src/python/grpcio_tests/tests_aio/benchmark:worker
+      command:
+      - bazel
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc.git
+    language: python_asyncio
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            bazel-bin/src/python/grpcio_tests/tests_aio/benchmark/worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+  - build:
+      command:
+      - bash
+      - /build_scripts/build_qps_worker.sh
+    clone:
+      gitRef: master
+      repo: https://github.com/grpc/grpc
+    language: ruby
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" ruby \
+            src/ruby/qps/worker.rb --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
   timeoutSeconds: ${timeout_seconds}
   ttlSeconds: 86400
diff --git a/tools/run_tests/performance/templates/loadtest_template_prebuilt_all_languages.yaml b/tools/run_tests/performance/templates/loadtest_template_prebuilt_all_languages.yaml
new file mode 100644 (file)
index 0000000..b97490e
--- /dev/null
@@ -0,0 +1,225 @@
+# Template generated from load test configurations by loadtest_template.py.
+#
+# Configuration templates contain client and server configurations for multiple
+# languages, and may contain template substitution keys. These templates are
+# used to generate load test configurations by selecting clients and servers for
+# the required languages. The source files for template generation may be load
+# test configurations or load test configuration templates. Load test
+# configuration generation is performed by loadtest_config.py. See documentation
+# below:
+# https://github.com/grpc/grpc/blob/master/tools/run_tests/performance/README.md
+apiVersion: e2etest.grpc.io/v1
+kind: LoadTest
+metadata:
+  name: prebuilt_all_languages
+spec:
+  clients:
+  - language: csharp
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" dotnet exec \
+            /execute/qps_worker/Grpc.IntegrationTesting.QpsWorker.dll \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/csharp:${prebuilt_image_tag}
+  - language: cxx
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /source/code/bazel-bin/test/cpp/qps/qps_worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/cxx:${prebuilt_image_tag}
+  - language: go
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /executable/bin/worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/go:${prebuilt_image_tag}
+  - language: java
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /execute/bin/benchmark_worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/java:${prebuilt_image_tag}
+  - language: node
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /execute/worker-linux --benchmark_impl=grpc \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/node:${prebuilt_image_tag}
+  - language: php7
+    pool: ${client_pool}
+    run:
+      command:
+      - bash
+      - /run_scripts/run_worker.sh
+      image: ${prebuilt_image_prefix}/php:${prebuilt_image_tag}
+  - language: python
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /execute/qps_worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/python:${prebuilt_image_tag}
+  - language: python_asyncio
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /execute/benchmark_worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/python:${prebuilt_image_tag}
+  - language: ruby
+    pool: ${client_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /execute/src/ruby/qps/worker.rb \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/ruby:${prebuilt_image_tag}
+  driver:
+    language: cxx
+    pool: ${driver_pool}
+    run:
+      image: ${driver_image}
+  results:
+    bigQueryTable: ${big_query_table}
+  servers:
+  - language: csharp
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" dotnet exec \
+            /execute/qps_worker/Grpc.IntegrationTesting.QpsWorker.dll \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/csharp:${prebuilt_image_tag}
+  - language: cxx
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /source/code/bazel-bin/test/cpp/qps/qps_worker \
+            --driver_port="${DRIVER_PORT}" --server_port=10010
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/cxx:${prebuilt_image_tag}
+  - language: go
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /executable/bin/worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/go:${prebuilt_image_tag}
+  - language: java
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /execute/bin/benchmark_worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/java:${prebuilt_image_tag}
+  - language: node
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /execute/worker-linux --benchmark_impl=grpc \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/node:${prebuilt_image_tag}
+  - language: python
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /execute/qps_worker \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/python:${prebuilt_image_tag}
+  - language: python_asyncio
+    pool: ${server_pool}
+    run:
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/python:${prebuilt_image_tag}
+      rgs:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /execute/benchmark_worker \
+            --driver_port="${DRIVER_PORT}"
+  - language: ruby
+    pool: ${server_pool}
+    run:
+      args:
+      - -c
+      - |
+        timeout --kill-after="${KILL_AFTER}" "${POD_TIMEOUT}" \
+            /execute/src/ruby/qps/worker.rb \
+            --driver_port="${DRIVER_PORT}"
+      command:
+      - bash
+      image: ${prebuilt_image_prefix}/ruby:${prebuilt_image_tag}
+  timeoutSeconds: ${timeout_seconds}
+  ttlSeconds: 86400
index c53749e..7940e5a 100755 (executable)
@@ -485,6 +485,10 @@ class CLanguage(object):
             return ('ubuntu1804', [])
         elif compiler == 'gcc8.3':
             return ('buster', [])
+        elif compiler == 'gcc8.3_openssl102':
+            return ('buster_openssl102', [
+                "-DgRPC_SSL_PROVIDER=package",
+            ])
         elif compiler == 'gcc_musl':
             return ('alpine', [])
         elif compiler == 'clang4.0':
@@ -622,13 +626,16 @@ class PythonConfig(
 class PythonLanguage(object):
 
     _TEST_SPECS_FILE = {
-        'native': 'src/python/grpcio_tests/tests/tests.json',
-        'gevent': 'src/python/grpcio_tests/tests/tests.json',
-        'asyncio': 'src/python/grpcio_tests/tests_aio/tests.json',
+        'native': ['src/python/grpcio_tests/tests/tests.json'],
+        'gevent': [
+            'src/python/grpcio_tests/tests/tests.json',
+            'src/python/grpcio_tests/tests_gevent/tests.json',
+        ],
+        'asyncio': ['src/python/grpcio_tests/tests_aio/tests.json'],
     }
     _TEST_FOLDER = {
         'native': 'test',
-        'gevent': 'test',
+        'gevent': 'test_gevent',
         'asyncio': 'test_aio',
     }
 
@@ -639,9 +646,11 @@ class PythonLanguage(object):
 
     def test_specs(self):
         # load list of known test suites
-        with open(self._TEST_SPECS_FILE[
-                self.args.iomgr_platform]) as tests_json_file:
-            tests_json = json.load(tests_json_file)
+        tests_json = []
+        for tests_json_file_name in self._TEST_SPECS_FILE[
+                self.args.iomgr_platform]:
+            with open(tests_json_file_name) as tests_json_file:
+                tests_json.extend(json.load(tests_json_file))
         environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)
         # TODO(https://github.com/grpc/grpc/issues/21401) Fork handlers is not
         # designed for non-native IO manager. It has a side-effect that
@@ -773,7 +782,7 @@ class PythonLanguage(object):
                                                major='3',
                                                config_vars=config_vars)
 
-        if args.iomgr_platform == 'asyncio':
+        if args.iomgr_platform in ('asyncio', 'gevent'):
             if args.compiler not in ('default', 'python3.6', 'python3.7',
                                      'python3.8'):
                 raise Exception(
@@ -789,7 +798,7 @@ class PythonLanguage(object):
                 else:
                     return (python38_config,)
             else:
-                if args.iomgr_platform == 'asyncio':
+                if args.iomgr_platform in ('asyncio', 'gevent'):
                     return (python36_config, python38_config)
                 elif os.uname()[0] == 'Darwin':
                     # NOTE(rbellevi): Testing takes significantly longer on
@@ -1424,6 +1433,7 @@ argp.add_argument(
         'gcc5.3',
         'gcc7.4',
         'gcc8.3',
+        'gcc8.3_openssl102',
         'gcc_musl',
         'clang4.0',
         'clang5.0',
index 951229d..ff051c7 100755 (executable)
@@ -266,8 +266,8 @@ def _create_portability_test_jobs(extra_args=[],
 
     # portability C and C++ on x64
     for compiler in [
-            'gcc4.9', 'gcc5.3', 'gcc7.4', 'gcc8.3', 'gcc_musl', 'clang4.0',
-            'clang5.0'
+            'gcc4.9', 'gcc5.3', 'gcc7.4', 'gcc8.3', 'gcc8.3_openssl102',
+            'gcc_musl', 'clang4.0', 'clang5.0'
     ]:
         test_jobs += _generate_jobs(languages=['c', 'c++'],
                                     configs=['dbg'],
index 423f57e..ea30e2c 100755 (executable)
@@ -84,6 +84,7 @@ _TEST_CASES = [
     'traffic_splitting',
     'path_matching',
     'header_matching',
+    'api_listener',
     'forwarding_rule_port_match',
     'forwarding_rule_default_port',
     'metadata_filter',
@@ -98,7 +99,6 @@ _ADDITIONAL_TEST_CASES = [
     'timeout',
     'fault_injection',
     'csds',
-    'api_listener',  # TODO(b/187352987) Relieve quota pressure
 ]
 
 # Test cases that require the V3 API.  Skipped in older runs.
@@ -202,6 +202,9 @@ argp.add_argument(
     help=
     'Leave GCP VMs and configuration running after test. Default behavior is '
     'to delete when tests complete.')
+argp.add_argument('--halt_after_fail',
+                  action='store_true',
+                  help='Halt and save the resources when test failed.')
 argp.add_argument(
     '--compute_discovery_document',
     default=None,
@@ -577,7 +580,7 @@ def compare_distributions(actual_distribution, expected_distribution,
       The similarity between the distributions as a boolean. Returns true if the
       actual distribution lies within the threshold of the expected
       distribution, false otherwise.
-    
+
     Raises:
       ValueError: if threshold is not with in [0,100].
       Exception: containing detailed error messages.
@@ -655,13 +658,18 @@ def test_change_backend_service(gcp, original_backend_service, instance_group,
                               same_zone_instance_group)
     wait_until_all_rpcs_go_to_given_backends(original_backend_instances,
                                              _WAIT_FOR_STATS_SEC)
+    passed = True
     try:
         patch_url_map_backend_service(gcp, alternate_backend_service)
         wait_until_all_rpcs_go_to_given_backends(alternate_backend_instances,
                                                  _WAIT_FOR_URL_MAP_PATCH_SEC)
+    except Exception:
+        passed = False
+        raise
     finally:
-        patch_url_map_backend_service(gcp, original_backend_service)
-        patch_backend_service(gcp, alternate_backend_service, [])
+        if passed or not args.halt_after_fail:
+            patch_url_map_backend_service(gcp, original_backend_service)
+            patch_backend_service(gcp, alternate_backend_service, [])
 
 
 def test_gentle_failover(gcp,
@@ -672,6 +680,7 @@ def test_gentle_failover(gcp,
     logger.info('Running test_gentle_failover')
     num_primary_instances = len(get_instance_names(gcp, primary_instance_group))
     min_instances_for_gentle_failover = 3  # Need >50% failure to start failover
+    passed = True
     try:
         if num_primary_instances < min_instances_for_gentle_failover:
             resize_instance_group(gcp, primary_instance_group,
@@ -710,20 +719,27 @@ def test_gentle_failover(gcp,
                                  primary_instance_group,
                                  swapped_primary_and_secondary=True)
         else:
+            passed = False
             raise e
+    except Exception:
+        passed = False
+        raise
     finally:
-        patch_backend_service(gcp, backend_service, [primary_instance_group])
-        resize_instance_group(gcp, primary_instance_group,
-                              num_primary_instances)
-        instance_names = get_instance_names(gcp, primary_instance_group)
-        wait_until_all_rpcs_go_to_given_backends(instance_names,
-                                                 _WAIT_FOR_BACKEND_SEC)
+        if passed or not args.halt_after_fail:
+            patch_backend_service(gcp, backend_service,
+                                  [primary_instance_group])
+            resize_instance_group(gcp, primary_instance_group,
+                                  num_primary_instances)
+            instance_names = get_instance_names(gcp, primary_instance_group)
+            wait_until_all_rpcs_go_to_given_backends(instance_names,
+                                                     _WAIT_FOR_BACKEND_SEC)
 
 
 def test_load_report_based_failover(gcp, backend_service,
                                     primary_instance_group,
                                     secondary_instance_group):
     logger.info('Running test_load_report_based_failover')
+    passed = True
     try:
         patch_backend_service(
             gcp, backend_service,
@@ -763,11 +779,16 @@ def test_load_report_based_failover(gcp, backend_service,
         wait_until_all_rpcs_go_to_given_backends(primary_instance_names,
                                                  _WAIT_FOR_BACKEND_SEC)
         logger.info("success")
+    except Exception:
+        passed = False
+        raise
     finally:
-        patch_backend_service(gcp, backend_service, [primary_instance_group])
-        instance_names = get_instance_names(gcp, primary_instance_group)
-        wait_until_all_rpcs_go_to_given_backends(instance_names,
-                                                 _WAIT_FOR_BACKEND_SEC)
+        if passed or not args.halt_after_fail:
+            patch_backend_service(gcp, backend_service,
+                                  [primary_instance_group])
+            instance_names = get_instance_names(gcp, primary_instance_group)
+            wait_until_all_rpcs_go_to_given_backends(instance_names,
+                                                     _WAIT_FOR_BACKEND_SEC)
 
 
 def test_ping_pong(gcp, backend_service, instance_group):
@@ -781,6 +802,7 @@ def test_ping_pong(gcp, backend_service, instance_group):
 def test_remove_instance_group(gcp, backend_service, instance_group,
                                same_zone_instance_group):
     logger.info('Running test_remove_instance_group')
+    passed = True
     try:
         patch_backend_service(gcp,
                               backend_service,
@@ -817,10 +839,14 @@ def test_remove_instance_group(gcp, backend_service, instance_group,
                               balancing_mode='RATE')
         wait_until_all_rpcs_go_to_given_backends(remaining_instance_names,
                                                  _WAIT_FOR_BACKEND_SEC)
+    except Exception:
+        passed = False
+        raise
     finally:
-        patch_backend_service(gcp, backend_service, [instance_group])
-        wait_until_all_rpcs_go_to_given_backends(instance_names,
-                                                 _WAIT_FOR_BACKEND_SEC)
+        if passed or not args.halt_after_fail:
+            patch_backend_service(gcp, backend_service, [instance_group])
+            wait_until_all_rpcs_go_to_given_backends(instance_names,
+                                                     _WAIT_FOR_BACKEND_SEC)
 
 
 def test_round_robin(gcp, backend_service, instance_group):
@@ -864,6 +890,7 @@ def test_secondary_locality_gets_no_requests_on_partial_primary_failure(
     logger.info(
         'Running secondary_locality_gets_no_requests_on_partial_primary_failure'
     )
+    passed = True
     try:
         patch_backend_service(
             gcp, backend_service,
@@ -897,9 +924,12 @@ def test_secondary_locality_gets_no_requests_on_partial_primary_failure(
                 primary_instance_group,
                 swapped_primary_and_secondary=True)
         else:
+            passed = False
             raise e
     finally:
-        patch_backend_service(gcp, backend_service, [primary_instance_group])
+        if passed or not args.halt_after_fail:
+            patch_backend_service(gcp, backend_service,
+                                  [primary_instance_group])
 
 
 def test_secondary_locality_gets_requests_on_primary_failure(
@@ -909,6 +939,7 @@ def test_secondary_locality_gets_requests_on_primary_failure(
         secondary_instance_group,
         swapped_primary_and_secondary=False):
     logger.info('Running secondary_locality_gets_requests_on_primary_failure')
+    passed = True
     try:
         patch_backend_service(
             gcp, backend_service,
@@ -942,9 +973,12 @@ def test_secondary_locality_gets_requests_on_primary_failure(
                 primary_instance_group,
                 swapped_primary_and_secondary=True)
         else:
+            passed = False
             raise e
     finally:
-        patch_backend_service(gcp, backend_service, [primary_instance_group])
+        if passed or not args.halt_after_fail:
+            patch_backend_service(gcp, backend_service,
+                                  [primary_instance_group])
 
 
 def prepare_services_for_urlmap_tests(gcp, original_backend_service,
@@ -991,6 +1025,7 @@ def test_metadata_filter(gcp, original_backend_service, instance_group,
                           [same_zone_instance_group])
     wait_for_healthy_backends(gcp, alternate_backend_service,
                               same_zone_instance_group)
+    passed = True
     try:
         with open(bootstrap_path) as f:
             md = json.load(f)['node']['metadata']
@@ -1122,13 +1157,18 @@ def test_metadata_filter(gcp, original_backend_service, instance_group,
             wait_until_all_rpcs_go_to_given_backends(
                 alternate_backend_instances, _WAIT_FOR_STATS_SEC)
             patch_url_map_backend_service(gcp, original_backend_service)
+    except Exception:
+        passed = False
+        raise
     finally:
-        patch_backend_service(gcp, alternate_backend_service, [])
+        if passed or not args.halt_after_fail:
+            patch_backend_service(gcp, alternate_backend_service, [])
 
 
 def test_api_listener(gcp, backend_service, instance_group,
                       alternate_backend_service):
     logger.info("Running api_listener")
+    passed = True
     try:
         wait_for_healthy_backends(gcp, backend_service, instance_group)
         backend_instances = get_instance_names(gcp, instance_group)
@@ -1175,27 +1215,33 @@ def test_api_listener(gcp, backend_service, instance_group,
         wait_until_no_rpcs_go_to_given_backends(backend_instances,
                                                 _WAIT_FOR_STATS_SEC)
 
+    except Exception:
+        passed = False
+        raise
     finally:
-        delete_global_forwarding_rule(gcp,
-                                      forwarding_rule_name + new_config_suffix)
-        delete_target_proxy(gcp, target_proxy_name + new_config_suffix)
-        delete_url_map(gcp, url_map_name + new_config_suffix)
-        create_url_map(gcp, url_map_name, backend_service, service_host_name)
-        create_target_proxy(gcp, target_proxy_name)
-        create_global_forwarding_rule(gcp, forwarding_rule_name,
-                                      potential_service_ports)
-        if gcp.service_port != _DEFAULT_SERVICE_PORT:
-            patch_url_map_host_rule_with_port(gcp, url_map_name,
-                                              backend_service,
-                                              service_host_name)
-            server_uri = service_host_name + ':' + str(gcp.service_port)
-        else:
-            server_uri = service_host_name
-        return server_uri
+        if passed or not args.halt_after_fail:
+            delete_global_forwarding_rule(
+                gcp, forwarding_rule_name + new_config_suffix)
+            delete_target_proxy(gcp, target_proxy_name + new_config_suffix)
+            delete_url_map(gcp, url_map_name + new_config_suffix)
+            create_url_map(gcp, url_map_name, backend_service,
+                           service_host_name)
+            create_target_proxy(gcp, target_proxy_name)
+            create_global_forwarding_rule(gcp, forwarding_rule_name,
+                                          potential_service_ports)
+            if gcp.service_port != _DEFAULT_SERVICE_PORT:
+                patch_url_map_host_rule_with_port(gcp, url_map_name,
+                                                  backend_service,
+                                                  service_host_name)
+                server_uri = service_host_name + ':' + str(gcp.service_port)
+            else:
+                server_uri = service_host_name
+            return server_uri
 
 
 def test_forwarding_rule_port_match(gcp, backend_service, instance_group):
     logger.info("Running test_forwarding_rule_port_match")
+    passed = True
     try:
         wait_for_healthy_backends(gcp, backend_service, instance_group)
         backend_instances = get_instance_names(gcp, instance_group)
@@ -1208,22 +1254,27 @@ def test_forwarding_rule_port_match(gcp, backend_service, instance_group):
         ])
         wait_until_no_rpcs_go_to_given_backends(backend_instances,
                                                 _WAIT_FOR_STATS_SEC)
+    except Exception:
+        passed = False
+        raise
     finally:
-        delete_global_forwarding_rule(gcp)
-        create_global_forwarding_rule(gcp, forwarding_rule_name,
-                                      potential_service_ports)
-        if gcp.service_port != _DEFAULT_SERVICE_PORT:
-            patch_url_map_host_rule_with_port(gcp, url_map_name,
-                                              backend_service,
-                                              service_host_name)
-            server_uri = service_host_name + ':' + str(gcp.service_port)
-        else:
-            server_uri = service_host_name
-        return server_uri
+        if passed or not args.halt_after_fail:
+            delete_global_forwarding_rule(gcp)
+            create_global_forwarding_rule(gcp, forwarding_rule_name,
+                                          potential_service_ports)
+            if gcp.service_port != _DEFAULT_SERVICE_PORT:
+                patch_url_map_host_rule_with_port(gcp, url_map_name,
+                                                  backend_service,
+                                                  service_host_name)
+                server_uri = service_host_name + ':' + str(gcp.service_port)
+            else:
+                server_uri = service_host_name
+            return server_uri
 
 
 def test_forwarding_rule_default_port(gcp, backend_service, instance_group):
     logger.info("Running test_forwarding_rule_default_port")
+    passed = True
     try:
         wait_for_healthy_backends(gcp, backend_service, instance_group)
         backend_instances = get_instance_names(gcp, instance_group)
@@ -1259,22 +1310,27 @@ def test_forwarding_rule_default_port(gcp, backend_service, instance_group):
                                           service_host_name)
         wait_until_no_rpcs_go_to_given_backends(backend_instances,
                                                 _WAIT_FOR_STATS_SEC)
+    except Exception:
+        passed = False
+        raise
     finally:
-        delete_global_forwarding_rule(gcp)
-        delete_target_proxy(gcp)
-        delete_url_map(gcp)
-        create_url_map(gcp, url_map_name, backend_service, service_host_name)
-        create_target_proxy(gcp, target_proxy_name)
-        create_global_forwarding_rule(gcp, forwarding_rule_name,
-                                      potential_service_ports)
-        if gcp.service_port != _DEFAULT_SERVICE_PORT:
-            patch_url_map_host_rule_with_port(gcp, url_map_name,
-                                              backend_service,
-                                              service_host_name)
-            server_uri = service_host_name + ':' + str(gcp.service_port)
-        else:
-            server_uri = service_host_name
-        return server_uri
+        if passed or not args.halt_after_fail:
+            delete_global_forwarding_rule(gcp)
+            delete_target_proxy(gcp)
+            delete_url_map(gcp)
+            create_url_map(gcp, url_map_name, backend_service,
+                           service_host_name)
+            create_target_proxy(gcp, target_proxy_name)
+            create_global_forwarding_rule(gcp, forwarding_rule_name,
+                                          potential_service_ports)
+            if gcp.service_port != _DEFAULT_SERVICE_PORT:
+                patch_url_map_host_rule_with_port(gcp, url_map_name,
+                                                  backend_service,
+                                                  service_host_name)
+                server_uri = service_host_name + ':' + str(gcp.service_port)
+            else:
+                server_uri = service_host_name
+            return server_uri
 
 
 def test_traffic_splitting(gcp, original_backend_service, instance_group,
@@ -1289,6 +1345,7 @@ def test_traffic_splitting(gcp, original_backend_service, instance_group,
         gcp, original_backend_service, instance_group,
         alternate_backend_service, same_zone_instance_group)
 
+    passed = True
     try:
         # Patch urlmap, change route action to traffic splitting between
         # original and alternate.
@@ -1345,9 +1402,13 @@ def test_traffic_splitting(gcp, original_backend_service, instance_group,
             else:
                 logger.info("success")
                 break
+    except Exception:
+        passed = False
+        raise
     finally:
-        patch_url_map_backend_service(gcp, original_backend_service)
-        patch_backend_service(gcp, alternate_backend_service, [])
+        if passed or not args.halt_after_fail:
+            patch_url_map_backend_service(gcp, original_backend_service)
+            patch_backend_service(gcp, alternate_backend_service, [])
 
 
 def test_path_matching(gcp, original_backend_service, instance_group,
@@ -1365,6 +1426,7 @@ def test_path_matching(gcp, original_backend_service, instance_group,
         gcp, original_backend_service, instance_group,
         alternate_backend_service, same_zone_instance_group)
 
+    passed = True
     try:
         # A list of tuples (route_rules, expected_instances).
         test_cases = [
@@ -1485,9 +1547,13 @@ def test_path_matching(gcp, original_backend_service, instance_group,
                     raise Exception(
                         'timeout waiting for RPCs to the expected instances: %s'
                         % expected_instances)
+    except Exception:
+        passed = False
+        raise
     finally:
-        patch_url_map_backend_service(gcp, original_backend_service)
-        patch_backend_service(gcp, alternate_backend_service, [])
+        if passed or not args.halt_after_fail:
+            patch_url_map_backend_service(gcp, original_backend_service)
+            patch_backend_service(gcp, alternate_backend_service, [])
 
 
 def test_header_matching(gcp, original_backend_service, instance_group,
@@ -1505,6 +1571,7 @@ def test_header_matching(gcp, original_backend_service, instance_group,
         gcp, original_backend_service, instance_group,
         alternate_backend_service, same_zone_instance_group)
 
+    passed = True
     try:
         # A list of tuples (route_rules, expected_instances).
         test_cases = [
@@ -1683,9 +1750,13 @@ def test_header_matching(gcp, original_backend_service, instance_group,
                     raise Exception(
                         'timeout waiting for RPCs to the expected instances: %s'
                         % expected_instances)
+    except Exception:
+        passed = False
+        raise
     finally:
-        patch_url_map_backend_service(gcp, original_backend_service)
-        patch_backend_service(gcp, alternate_backend_service, [])
+        if passed or not args.halt_after_fail:
+            patch_url_map_backend_service(gcp, original_backend_service)
+            patch_backend_service(gcp, alternate_backend_service, [])
 
 
 def test_circuit_breaking(gcp, original_backend_service, instance_group,
@@ -1718,6 +1789,7 @@ def test_circuit_breaking(gcp, original_backend_service, instance_group,
     '''
     logger.info('Running test_circuit_breaking')
     additional_backend_services = []
+    passed = True
     try:
         # TODO(chengyuanzhang): Dedicated backend services created for circuit
         # breaking test. Once the issue for unsetting backend service circuit
@@ -1835,12 +1907,17 @@ def test_circuit_breaking(gcp, original_backend_service, instance_group,
         # for sending RPCs) after restoring backend services.
         configure_client(
             [messages_pb2.ClientConfigureRequest.RpcType.UNARY_CALL])
+    except Exception:
+        passed = False
+        raise
     finally:
-        patch_url_map_backend_service(gcp, original_backend_service)
-        patch_backend_service(gcp, original_backend_service, [instance_group])
-        for backend_service in additional_backend_services:
-            delete_backend_service(gcp, backend_service)
-        set_validate_for_proxyless(gcp, True)
+        if passed or not args.halt_after_fail:
+            patch_url_map_backend_service(gcp, original_backend_service)
+            patch_backend_service(gcp, original_backend_service,
+                                  [instance_group])
+            for backend_service in additional_backend_services:
+                delete_backend_service(gcp, backend_service)
+            set_validate_for_proxyless(gcp, True)
 
 
 def test_timeout(gcp, original_backend_service, instance_group):
@@ -1919,6 +1996,7 @@ def test_timeout(gcp, original_backend_service, instance_group):
         )
     ]
 
+    passed = True
     try:
         first_case = True
         for (testcase_name, client_config, expected_results) in test_cases:
@@ -1967,8 +2045,12 @@ def test_timeout(gcp, original_backend_service, instance_group):
                     '%s: timeout waiting for expected results: %s; got %s' %
                     (testcase_name, expected_results,
                      after_stats.stats_per_method))
+    except Exception:
+        passed = False
+        raise
     finally:
-        patch_url_map_backend_service(gcp, original_backend_service)
+        if passed or not args.halt_after_fail:
+            patch_url_map_backend_service(gcp, original_backend_service)
 
 
 def test_fault_injection(gcp, original_backend_service, instance_group):
@@ -2088,6 +2170,7 @@ def test_fault_injection(gcp, original_backend_service, instance_group):
         )
     ]
 
+    passed = True
     try:
         first_case = True
         for (testcase_name, client_config, expected_results) in test_cases:
@@ -2146,9 +2229,13 @@ def test_fault_injection(gcp, original_backend_service, instance_group):
                     '%s: timeout waiting for expected results: %s; got %s' %
                     (testcase_name, expected_results,
                      after_stats.stats_per_method))
+    except Exception:
+        passed = False
+        raise
     finally:
-        patch_url_map_backend_service(gcp, original_backend_service)
-        set_validate_for_proxyless(gcp, True)
+        if passed or not args.halt_after_fail:
+            patch_url_map_backend_service(gcp, original_backend_service)
+            set_validate_for_proxyless(gcp, True)
 
 
 def test_csds(gcp, original_backend_service, instance_group, server_uri):
@@ -2541,62 +2628,96 @@ def create_global_forwarding_rule(gcp,
 
 
 def get_health_check(gcp, health_check_name):
-    result = gcp.compute.healthChecks().get(
-        project=gcp.project, healthCheck=health_check_name).execute()
-    gcp.health_check = GcpResource(health_check_name, result['selfLink'])
+    try:
+        result = gcp.compute.healthChecks().get(
+            project=gcp.project, healthCheck=health_check_name).execute()
+        gcp.health_check = GcpResource(health_check_name, result['selfLink'])
+    except Exception as e:
+        gcp.errors.append(e)
+        gcp.health_check = GcpResource(health_check_name, None)
 
 
 def get_health_check_firewall_rule(gcp, firewall_name):
-    result = gcp.compute.firewalls().get(project=gcp.project,
-                                         firewall=firewall_name).execute()
-    gcp.health_check_firewall_rule = GcpResource(firewall_name,
-                                                 result['selfLink'])
+    try:
+        result = gcp.compute.firewalls().get(project=gcp.project,
+                                             firewall=firewall_name).execute()
+        gcp.health_check_firewall_rule = GcpResource(firewall_name,
+                                                     result['selfLink'])
+    except Exception as e:
+        gcp.errors.append(e)
+        gcp.health_check_firewall_rule = GcpResource(firewall_name, None)
 
 
 def get_backend_service(gcp, backend_service_name):
-    result = gcp.compute.backendServices().get(
-        project=gcp.project, backendService=backend_service_name).execute()
-    backend_service = GcpResource(backend_service_name, result['selfLink'])
+    try:
+        result = gcp.compute.backendServices().get(
+            project=gcp.project, backendService=backend_service_name).execute()
+        backend_service = GcpResource(backend_service_name, result['selfLink'])
+    except Exception as e:
+        gcp.errors.append(e)
+        backend_service = GcpResource(backend_service_name, None)
     gcp.backend_services.append(backend_service)
     return backend_service
 
 
 def get_url_map(gcp, url_map_name):
-    result = gcp.compute.urlMaps().get(project=gcp.project,
-                                       urlMap=url_map_name).execute()
-    gcp.url_map = GcpResource(url_map_name, result['selfLink'])
+    try:
+        result = gcp.compute.urlMaps().get(project=gcp.project,
+                                           urlMap=url_map_name).execute()
+        gcp.url_map = GcpResource(url_map_name, result['selfLink'])
+    except Exception as e:
+        gcp.errors.append(e)
+        gcp.url_map = GcpResource(url_map_name, None)
 
 
 def get_target_proxy(gcp, target_proxy_name):
-    if gcp.alpha_compute:
-        result = gcp.alpha_compute.targetGrpcProxies().get(
-            project=gcp.project, targetGrpcProxy=target_proxy_name).execute()
-    else:
-        result = gcp.compute.targetHttpProxies().get(
-            project=gcp.project, targetHttpProxy=target_proxy_name).execute()
-    gcp.target_proxy = GcpResource(target_proxy_name, result['selfLink'])
+    try:
+        if gcp.alpha_compute:
+            result = gcp.alpha_compute.targetGrpcProxies().get(
+                project=gcp.project,
+                targetGrpcProxy=target_proxy_name).execute()
+        else:
+            result = gcp.compute.targetHttpProxies().get(
+                project=gcp.project,
+                targetHttpProxy=target_proxy_name).execute()
+        gcp.target_proxy = GcpResource(target_proxy_name, result['selfLink'])
+    except Exception as e:
+        gcp.errors.append(e)
+        gcp.target_proxy = GcpResource(target_proxy_name, None)
 
 
 def get_global_forwarding_rule(gcp, forwarding_rule_name):
-    result = gcp.compute.globalForwardingRules().get(
-        project=gcp.project, forwardingRule=forwarding_rule_name).execute()
-    gcp.global_forwarding_rule = GcpResource(forwarding_rule_name,
-                                             result['selfLink'])
+    try:
+        result = gcp.compute.globalForwardingRules().get(
+            project=gcp.project, forwardingRule=forwarding_rule_name).execute()
+        gcp.global_forwarding_rule = GcpResource(forwarding_rule_name,
+                                                 result['selfLink'])
+    except Exception as e:
+        gcp.errors.append(e)
+        gcp.global_forwarding_rule = GcpResource(forwarding_rule_name, None)
 
 
 def get_instance_template(gcp, template_name):
-    result = gcp.compute.instanceTemplates().get(
-        project=gcp.project, instanceTemplate=template_name).execute()
-    gcp.instance_template = GcpResource(template_name, result['selfLink'])
+    try:
+        result = gcp.compute.instanceTemplates().get(
+            project=gcp.project, instanceTemplate=template_name).execute()
+        gcp.instance_template = GcpResource(template_name, result['selfLink'])
+    except Exception as e:
+        gcp.errors.append(e)
+        gcp.instance_template = GcpResource(template_name, None)
 
 
 def get_instance_group(gcp, zone, instance_group_name):
-    result = gcp.compute.instanceGroups().get(
-        project=gcp.project, zone=zone,
-        instanceGroup=instance_group_name).execute()
-    gcp.service_port = result['namedPorts'][0]['port']
-    instance_group = InstanceGroup(instance_group_name, result['selfLink'],
-                                   zone)
+    try:
+        result = gcp.compute.instanceGroups().get(
+            project=gcp.project, zone=zone,
+            instanceGroup=instance_group_name).execute()
+        gcp.service_port = result['namedPorts'][0]['port']
+        instance_group = InstanceGroup(instance_group_name, result['selfLink'],
+                                       zone)
+    except Exception as e:
+        gcp.errors.append(e)
+        instance_group = InstanceGroup(instance_group_name, None, zone)
     gcp.instance_groups.append(instance_group)
     return instance_group
 
@@ -2607,6 +2728,7 @@ def delete_global_forwarding_rule(gcp, name=None):
     else:
         forwarding_rule_to_delete = gcp.global_forwarding_rule.name
     try:
+        logger.debug('Deleting forwarding rule %s', forwarding_rule_to_delete)
         result = gcp.compute.globalForwardingRules().delete(
             project=gcp.project,
             forwardingRule=forwarding_rule_to_delete).execute(
@@ -2623,10 +2745,12 @@ def delete_target_proxy(gcp, name=None):
         proxy_to_delete = gcp.target_proxy.name
     try:
         if gcp.alpha_compute:
+            logger.debug('Deleting grpc proxy %s', proxy_to_delete)
             result = gcp.alpha_compute.targetGrpcProxies().delete(
                 project=gcp.project, targetGrpcProxy=proxy_to_delete).execute(
                     num_retries=_GCP_API_RETRIES)
         else:
+            logger.debug('Deleting http proxy %s', proxy_to_delete)
             result = gcp.compute.targetHttpProxies().delete(
                 project=gcp.project, targetHttpProxy=proxy_to_delete).execute(
                     num_retries=_GCP_API_RETRIES)
@@ -2641,6 +2765,7 @@ def delete_url_map(gcp, name=None):
     else:
         url_map_to_delete = gcp.url_map.name
     try:
+        logger.debug('Deleting url map %s', url_map_to_delete)
         result = gcp.compute.urlMaps().delete(
             project=gcp.project,
             urlMap=url_map_to_delete).execute(num_retries=_GCP_API_RETRIES)
@@ -2651,6 +2776,7 @@ def delete_url_map(gcp, name=None):
 
 def delete_backend_service(gcp, backend_service):
     try:
+        logger.debug('Deleting backend service %s', backend_service.name)
         result = gcp.compute.backendServices().delete(
             project=gcp.project, backendService=backend_service.name).execute(
                 num_retries=_GCP_API_RETRIES)
@@ -2666,6 +2792,8 @@ def delete_backend_services(gcp):
 
 def delete_firewall(gcp):
     try:
+        logger.debug('Deleting firewall %s',
+                     gcp.health_check_firewall_rule.name)
         result = gcp.compute.firewalls().delete(
             project=gcp.project,
             firewall=gcp.health_check_firewall_rule.name).execute(
@@ -2677,6 +2805,7 @@ def delete_firewall(gcp):
 
 def delete_health_check(gcp):
     try:
+        logger.debug('Deleting health check %s', gcp.health_check.name)
         result = gcp.compute.healthChecks().delete(
             project=gcp.project, healthCheck=gcp.health_check.name).execute(
                 num_retries=_GCP_API_RETRIES)
@@ -2688,6 +2817,8 @@ def delete_health_check(gcp):
 def delete_instance_groups(gcp):
     for instance_group in gcp.instance_groups:
         try:
+            logger.debug('Deleting instance group %s %s', instance_group.name,
+                         instance_group.zone)
             result = gcp.compute.instanceGroupManagers().delete(
                 project=gcp.project,
                 zone=instance_group.zone,
@@ -2703,6 +2834,8 @@ def delete_instance_groups(gcp):
 
 def delete_instance_template(gcp):
     try:
+        logger.debug('Deleting instance template %s',
+                     gcp.instance_template.name)
         result = gcp.compute.instanceTemplates().delete(
             project=gcp.project,
             instanceTemplate=gcp.instance_template.name).execute(
@@ -2952,6 +3085,7 @@ class GcpState(object):
         self.service_port = None
         self.instance_template = None
         self.instance_groups = []
+        self.errors = []
 
 
 alpha_compute = None
@@ -3006,13 +3140,7 @@ try:
     if args.use_existing_gcp_resources:
         logger.info('Reusing existing GCP resources')
         get_health_check(gcp, health_check_name)
-        try:
-            get_health_check_firewall_rule(gcp, firewall_name)
-        except googleapiclient.errors.HttpError as http_error:
-            # Firewall rule may be auto-deleted periodically depending on GCP
-            # project settings.
-            logger.exception('Failed to find firewall rule, recreating')
-            create_health_check_firewall_rule(gcp, firewall_name)
+        get_health_check_firewall_rule(gcp, firewall_name)
         backend_service = get_backend_service(gcp, backend_service_name)
         alternate_backend_service = get_backend_service(
             gcp, alternate_backend_service_name)
@@ -3025,6 +3153,8 @@ try:
             gcp, args.zone, same_zone_instance_group_name)
         secondary_zone_instance_group = get_instance_group(
             gcp, args.secondary_zone, secondary_zone_instance_group_name)
+        if gcp.errors:
+            raise Exception(gcp.errors)
     else:
         create_health_check_firewall_rule(gcp, firewall_name)
         backend_service = add_backend_service(gcp, backend_service_name)
@@ -3250,6 +3380,9 @@ try:
                 failed_tests.append(test_case)
                 result.state = 'FAILED'
                 result.message = str(e)
+                if args.halt_after_fail:
+                    # Stop the test suite if one case failed.
+                    raise
             finally:
                 if client_process:
                     if client_process.returncode:
@@ -3278,6 +3411,11 @@ try:
             logger.error('Test case(s) %s failed', failed_tests)
             sys.exit(1)
 finally:
-    if not args.keep_gcp_resources:
+    keep_resources = args.keep_gcp_resources
+    if args.halt_after_fail and failed_tests:
+        logger.info(
+            'Halt after fail triggered, exiting without cleaning up resources')
+        keep_resources = True
+    if not keep_resources:
         logger.info('Cleaning up GCP resources. This may take some time.')
         clean_up(gcp)
index 530c5bf..a0ba3a6 100755 (executable)
@@ -29,7 +29,7 @@ cat << EOF | sort > "$want_submodules"
 third_party/abseil-cpp 997aaf3a28308eba1b9156aa35ab7bca9688e9f6
 third_party/benchmark 73d4d5e8d6d449fc8663765a42aa8aeeee844489
 third_party/bloaty 73594cde8c9a52a102c4341c244c833aa61b9c06
-third_party/boringssl-with-bazel 688fc5cf5428868679d2ae1072cad81055752068
+third_party/boringssl-with-bazel bcc01b6c66b1c6fa2816b108e50a544b757fbd7b
 third_party/cares/cares e982924acee7f7313b4baa4ee5ec000c5e373c30
 third_party/envoy-api 18b54850c9b7ba29a4ab67cbd7ed7eab7b0bbdb2
 third_party/googleapis 82944da21578a53b74e547774cf62ed31a05b841
index ebacd4b..1bf932a 100644 (file)
@@ -14,41 +14,132 @@ changes to this codebase at the moment.
 - [ ] Make framework.infrastructure.gcp resources [first-class
       citizen](https://en.wikipedia.org/wiki/First-class_citizen), support
       simpler CRUD
-- [ ] Security: manage `roles/iam.workloadIdentityUser` role grant lifecycle for
+- [x] Security: manage `roles/iam.workloadIdentityUser` role grant lifecycle for
       dynamically-named namespaces 
 - [ ] Restructure `framework.test_app` and `framework.xds_k8s*` into a module
       containing xDS-interop-specific logic
 - [ ] Address inline TODOs in code
-- [ ] Improve README.md documentation, explain helpers in bin/ folder
+- [x] Improve README.md documentation, explain helpers in bin/ folder
 
 ## Installation
 
 #### Requirements
 1. Python v3.6+
 2. [Google Cloud SDK](https://cloud.google.com/sdk/docs/install)
-3. A GKE cluster (must enable "Enable VPC-native traffic routing" to use it with
-   the Traffic Director)
-    * Otherwise, you will see error logs when you inspect Kubernetes virtual
-      service
-    * (In `grpc-testing`, you will need a metadata tag
-      `--tags=allow-health-checks` to allow UHC to reach your resources.)
+3. Configured GKE cluster
 
-#### Configure GKE cluster access
-
-```sh
-# Update gloud sdk
+#### Configure GKE cluster
+This is an example outlining minimal requirements to run `tests.baseline_test`.  
+For more details, and for the setup for security tests, see
+["Setting up Traffic Director service security with proxyless gRPC"](https://cloud.google.com/traffic-director/docs/security-proxyless-setup)
+ user guide.
+Update gloud sdk:
+```shell
 gcloud -q components update
+```
+
+Pre-populate environment variables for convenience. To find project id, refer to
+[Identifying projects](https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects).
+```shell
+export PROJECT_ID="your-project-id"
+export PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}" --format="value(projectNumber)")
+# Compute Engine default service account
+export GCE_SA="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
+# The prefix to name GCP resources used by the framework
+export RESOURCE_PREFIX="xds-k8s-interop-tests"
+
+# The zone name your cluster, f.e. xds-k8s-test-cluster
+export CLUSTER_NAME="${RESOURCE_PREFIX}-cluster"
+# The zone of your cluster, f.e. us-central1-a
+export ZONE="us-central1-a" 
+# Dedicated GCP Service Account to use with workload identity.
+export WORKLOAD_SA_NAME="${RESOURCE_PREFIX}"
+export WORKLOAD_SA_EMAIL="${WORKLOAD_SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
+```
+
+##### Create the cluster 
+Minimal requirements: [VPC-native](https://cloud.google.com/traffic-director/docs/security-proxyless-setup)
+cluster with [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) enabled. 
+```shell
+gcloud beta container clusters create "${CLUSTER_NAME}" \
+ --zone="${ZONE}" \
+ --enable-ip-alias \
+ --workload-pool="${PROJECT_ID}.svc.id.goog" \
+ --workload-metadata=GKE_METADATA \
+ --tags=allow-health-checks
+```
+
+##### Create the firewall rule
+Allow [health checking mechanisms](https://cloud.google.com/traffic-director/docs/set-up-proxyless-gke#creating_the_health_check_firewall_rule_and_backend_service)
+to query the workloads health.  
+This step can be skipped, if the driver is executed with `--ensure_firewall`.
+```shell
+gcloud compute firewall-rules create "${RESOURCE_PREFIX}-allow-health-checks" \
+  --network=default --action=allow --direction=INGRESS \
+  --source-ranges="35.191.0.0/16,130.211.0.0/22" \
+  --target-tags=allow-health-checks \
+  --rules=tcp:8080-8100
+```
+
+##### Setup GCP Service Account
+
+Create dedicated GCP Service Account to use
+with [workload identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity).
+
+```shell
+gcloud iam service-accounts create "${WORKLOAD_SA_NAME}" \
+  --display-name="xDS K8S Interop Tests Workload Identity Service Account"
+```
+
+Enable the service account to [access the Traffic Director API](https://cloud.google.com/traffic-director/docs/prepare-for-envoy-setup#enable-service-account).
+```shell
+gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
+   --member="serviceAccount:${WORKLOAD_SERVICE_ACCOUNT}" \
+   --role="roles/trafficdirector.client"
+```
 
+##### Allow test driver to configure workload identity automatically
+Test driver will automatically grant `roles/iam.workloadIdentityUser` to 
+allow the Kubernetes service account to impersonate the dedicated GCP workload
+service account (corresponds to the step 5
+of [Authenticating to Google Cloud](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#authenticating_to)).
+This action requires the test framework to have `iam.serviceAccounts.create`
+permission on the project.
+
+If you're running test framework locally, and you have `roles/owner` to your
+project, **you can skip this step**.  
+If you're configuring the test framework to run on a CI: use `roles/owner`
+account once to allow test framework to grant `roles/iam.workloadIdentityUser`.
+
+```shell
+# Assuming CI is using Compute Engine default service account.
+gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
+  --member="serviceAccount:${GCE_SA}" \
+  --role="roles/iam.serviceAccountAdmin" \
+  --condition-from-file=<(cat <<-END
+---
+title: allow_workload_identity_only
+description: Restrict serviceAccountAdmin to granting role iam.workloadIdentityUser
+expression: |-
+  api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', [])
+        .hasOnly(['roles/iam.workloadIdentityUser'])
+END
+)
+```
+
+##### Configure GKE cluster access
+```shell
 # Configuring GKE cluster access for kubectl
 gcloud container clusters get-credentials "your_gke_cluster_name" --zone "your_gke_cluster_zone"
 
 # Save generated kube context name
-KUBE_CONTEXT="$(kubectl config current-context)"
+export KUBE_CONTEXT="$(kubectl config current-context)"
 ``` 
 
 #### Install python dependencies
 
-```sh
+```shell
 # Create python virtual environment
 python3.6 -m venv venv
 
@@ -80,10 +171,10 @@ every single one of them. It is triggered nightly and per-release. For example,
 the commit we are using below (`d22f93e1ade22a1e026b57210f6fc21f7a3ca0cf`) comes
 from branch `v1.37.x` in `grpc-java` repo.
 
-```sh
+```shell
 # Help
 python -m tests.baseline_test --help
-python -m tests.baseline_test --helpful
+python -m tests.baseline_test --helpfull
 
 # Run on grpc-testing cluster
 python -m tests.baseline_test \
@@ -94,10 +185,10 @@ python -m tests.baseline_test \
 ```
 
 ### xDS Security Tests
-```sh
+```shell
 # Help
 python -m tests.security_test --help
-python -m tests.security_test --helpful
+python -m tests.security_test --helpfull
 
 # Run on grpc-testing cluster
 python -m tests.security_test \
@@ -108,7 +199,6 @@ python -m tests.security_test \
 ```
 
 ### Test namespace
-
 It's possible to run multiple xDS interop test workloads in the same project.
 But we need to ensure the name of the global resources won't conflict. This can
 be solved by supplying `--namespace` and `--server_xds_port`. The xDS port needs
@@ -125,22 +215,197 @@ python3 -m tests.baseline_test \
   --server_xds_port="$(($RANDOM%1000 + 34567))"
 ```
 
+## Local development
+This test driver allows running tests locally against remote GKE clusters, right
+from your dev environment. You need:
+
+1. Follow [installation](#installation) instructions
+2. Authenticated `gcloud`
+3. `kubectl` context (see [Configure GKE cluster access](#configure-gke-cluster-access))
+4. Run tests with `--debug_use_port_forwarding` argument. The test driver 
+   will automatically start and stop port forwarding using
+   `kubectl` subprocesses. (experimental)
+
+### Making changes to the driver
+1. Install additional dev packages: `pip install -r requirements-dev.txt`
+2. Use `./bin/yapf.sh` and `./bin/isort.sh` helpers to auto-format code.
+
 ### Setup test configuration
 
 There are many arguments to be passed into the test run. You can save the
-arguments to a config file for your development environment. Please take a look
-at
-https://github.com/grpc/grpc/blob/master/tools/run_tests/xds_k8s_test_driver/config/local-dev.cfg.example.
-You can create your own config by:
+arguments to a config file ("flagfile") for your development environment.
+Use [`config/local-dev.cfg.example`](https://github.com/grpc/grpc/blob/master/tools/run_tests/xds_k8s_test_driver/config/local-dev.cfg.example)
+as a starting point:
 
 ```shell
 cp config/local-dev.cfg.example config/local-dev.cfg
 ```
 
-### Clean-up resources
+Learn more about flagfiles in [abseil documentation](https://abseil.io/docs/python/guides/flags#a-note-about---flagfile).
+
+### Helper scripts
+You can use interop xds-k8s [`bin/`](https://github.com/grpc/grpc/tree/master/tools/run_tests/xds_k8s_test_driver/bin)
+scripts to configure TD, start k8s instances step-by-step, and keep them alive
+for as long as you need. 
+
+* To run helper scripts using local config:
+  * `python -m bin.script_name --flagfile=config/local-dev.cfg`
+  * `./run.sh bin/script_name.py` automatically appends the flagfile
+* Use `--help` to see script-specific argument
+* Use `--helpfull` to see all available argument
+
+#### Overview
+```shell
+# Helper tool to configure Traffic Director with different security options
+python -m bin.run_td_setup --help
+
+# Helper tools to run the test server, client (with or without security)
+python -m bin.run_test_server --help
+python -m bin.run_test_client --help
+
+# Helper tool to verify different security configurations via channelz
+python -m bin.run_channelz --help
+```
+
+#### `./run.sh` helper
+Use `./run.sh` to execute helper scripts and tests with `config/local-dev.cfg`.
+
+```sh
+USAGE: ./run.sh script_path [arguments]
+   script_path: path to python script to execute, relative to driver root folder
+   arguments ...: arguments passed to program in sys.argv
+
+ENVIRONMENT:
+   XDS_K8S_CONFIG: file path to the config flagfile, relative to
+                   driver root folder. Default: config/local-dev.cfg
+                   Will be appended as --flagfile="config_absolute_path" argument
+   XDS_K8S_DRIVER_VENV_DIR: the path to python virtual environment directory
+                            Default: $XDS_K8S_DRIVER_DIR/venv
+DESCRIPTION:
+This tool performs the following:
+1) Ensures python virtual env installed and activated
+2) Exports test driver root in PYTHONPATH
+3) Automatically appends --flagfile="\$XDS_K8S_CONFIG" argument
+
+EXAMPLES:
+./run.sh bin/run_td_setup.py --help
+./run.sh bin/run_td_setup.py --helpfull
+XDS_K8S_CONFIG=./path-to-flagfile.cfg ./run.sh bin/run_td_setup.py --namespace=override-namespace
+./run.sh tests/baseline_test.py
+./run.sh tests/security_test.py --verbosity=1 --logger_levels=__main__:DEBUG,framework:DEBUG
+./run.sh tests/security_test.py SecurityTest.test_mtls --nocheck_local_certs
+```
+
+### Regular workflow
+```shell
+# Setup Traffic Director
+./run.sh bin/run_td_setup.py
+
+# Start test server
+./run.sh bin/run_test_server.py
+
+# Add test server to the backend service
+./run.sh bin/run_td_setup.py --cmd=backends-add
+
+# Start test client
+./run.sh bin/run_test_client.py
+```
+
+### Secure workflow
+```shell
+# Setup Traffic Director in mtls. See --help for all options
+./run.sh bin/run_td_setup.py --security=mtls
+
+# Start test server in a secure mode
+./run.sh bin/run_test_server.py --secure
 
+# Add test server to the backend service
+./run.sh bin/run_td_setup.py --cmd=backends-add
+
+# Start test client in a secure more --secure
+./run.sh bin/run_test_client.py --secure
+```
+
+### Sending RPCs
+#### Start port forwarding
 ```shell
-python -m bin.run_td_setup --cmd=cleanup --flagfile=config/local-dev.cfg && \
-python -m bin.run_test_client --cmd=cleanup --flagfile=config/local-dev.cfg && \
-python -m bin.run_test_server --cmd=cleanup --cleanup_namespace --flagfile=config/local-dev.cfg
+# Client: all services always on port 8079
+kubectl port-forward deployment.apps/psm-grpc-client 8079
+
+# Server regular mode: all grpc services on port 8079
+kubectl port-forward deployment.apps/psm-grpc-server 8080
+# OR
+# Server secure mode: TestServiceImpl is on 8080, 
+kubectl port-forward deployment.apps/psm-grpc-server 8080
+# everything else (channelz, healthcheck, CSDS) on 8081
+kubectl port-forward deployment.apps/psm-grpc-server 8081
 ```
+
+#### Send RPCs with grpccurl
+```shell
+# 8081 if security enabled
+export SERVER_ADMIN_PORT=8080
+
+# List server services using reflection
+grpcurl --plaintext 127.0.0.1:$SERVER_ADMIN_PORT list
+# List client services using reflection
+grpcurl --plaintext 127.0.0.1:8079 list
+
+# List channels via channelz
+grpcurl --plaintext 127.0.0.1:$SERVER_ADMIN_PORT grpc.channelz.v1.Channelz.GetTopChannels
+grpcurl --plaintext 127.0.0.1:8079 grpc.channelz.v1.Channelz.GetTopChannels
+
+# Send GetClientStats to the client
+grpcurl --plaintext -d '{"num_rpcs": 10, "timeout_sec": 30}' 127.0.0.1:8079 \
+  grpc.testing.LoadBalancerStatsService.GetClientStats
+```
+
+### Cleanup
+* First, make sure to stop port forwarding, if any
+* Run `./bin/cleanup.sh`
+
+##### Partial cleanup
+You can run commands below to stop/start, create/delete resources however you want.  
+Generally, it's better to remove resources in the opposite order of their creation.
+
+Cleanup regular resources:
+```shell
+# Cleanup TD resources
+./run.sh bin/run_td_setup.py --cmd=cleanup
+# Stop test client
+./run.sh bin/run_test_client.py --cmd=cleanup
+# Stop test server, and remove the namespace
+./run.sh bin/run_test_server.py --cmd=cleanup --cleanup_namespace
+```
+
+Cleanup regular and security-specific resources:
+```shell
+# Cleanup TD resources, with security
+./run.sh bin/run_td_setup.py --cmd=cleanup --security=mtls
+# Stop test client (secure)
+./run.sh bin/run_test_client.py --cmd=cleanup --secure
+# Stop test server (secure), and remove the namespace
+./run.sh bin/run_test_server.py --cmd=cleanup --cleanup_namespace --secure
+```
+
+In addition, here's some other helpful partial cleanup commands:
+```shell
+# Remove all backends from the backend services
+./run.sh bin/run_td_setup.py --cmd=backends-cleanup
+
+# Stop the server, but keep the namespace
+./run.sh bin/run_test_server.py --cmd=cleanup --nocleanup_namespace
+```
+
+### Known errors
+#### Error forwarding port
+If you stopped a test with `ctrl+c`, while using `--debug_use_port_forwarding`,
+you might see an error like this:
+
+> `framework.infrastructure.k8s.PortForwardingError: Error forwarding port, unexpected output Unable to listen on port 8081: Listeners failed to create with the following errors: [unable to create listener: Error listen tcp4 127.0.0.1:8081: bind: address already in use]`
+
+Unless you're running `kubectl port-forward` manually, it's likely that `ctrl+c`
+interrupted python before it could clean up subprocesses.
+
+You can do `ps aux | grep port-forward` and then kill the processes by id,
+or with `killall kubectl`
diff --git a/tools/run_tests/xds_k8s_test_driver/bin/cleanup.sh b/tools/run_tests/xds_k8s_test_driver/bin/cleanup.sh
new file mode 100755 (executable)
index 0000000..4ee29eb
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+# 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.
+
+set -eo pipefail
+
+display_usage() {
+  cat <<EOF >/dev/stderr
+Performs full TD and K8S resource cleanup
+
+USAGE: $0 [--nosecure] [arguments]
+   --nosecure: Skip cleanup for the resources specific for PSM Security
+   arguments ...: additional arguments passed to ./run.sh
+
+ENVIRONMENT:
+   XDS_K8S_CONFIG: file path to the config flagfile, relative to
+                   driver root folder. Default: config/local-dev.cfg
+                   Will be appended as --flagfile="config_absolute_path" argument
+   XDS_K8S_DRIVER_VENV_DIR: the path to python virtual environment directory
+                            Default: $XDS_K8S_DRIVER_DIR/venv
+EXAMPLES:
+$0
+$0 --secure
+XDS_K8S_CONFIG=./path-to-flagfile.cfg $0 --namespace=override-namespace
+EOF
+  exit 1
+}
+
+if [[ "$1" == "-h" || "$1" == "--help" ]]; then
+  display_usage
+fi
+
+SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
+readonly SCRIPT_DIR
+readonly XDS_K8S_DRIVER_DIR="${SCRIPT_DIR}/.."
+
+cd "${XDS_K8S_DRIVER_DIR}"
+
+if [[ "$1" == "--nosecure" ]]; then
+  shift
+  ./run.sh bin/run_td_setup.py --cmd=cleanup "$@" && \
+  ./run.sh bin/run_test_client.py --cmd=cleanup "$@" && \
+  ./run.sh bin/run_test_server.py --cmd=cleanup --cleanup_namespace "$@"
+else
+  ./run.sh bin/run_td_setup.py --cmd=cleanup --security=mtls "$@" && \
+  ./run.sh bin/run_test_client.py --cmd=cleanup --secure "$@" && \
+  ./run.sh bin/run_test_server.py --cmd=cleanup --cleanup_namespace --secure "$@"
+fi
diff --git a/tools/run_tests/xds_k8s_test_driver/bin/ensure_venv.sh b/tools/run_tests/xds_k8s_test_driver/bin/ensure_venv.sh
new file mode 100755 (executable)
index 0000000..288c3fa
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+# 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.
+
+# Expected $XDS_K8S_DRIVER_DIR to be set by the file sourcing this.
+readonly XDS_K8S_DRIVER_VENV_DIR="${XDS_K8S_DRIVER_VENV_DIR:-$XDS_K8S_DRIVER_DIR/venv}"
+
+if [[ -z "${VIRTUAL_ENV}" ]]; then
+  if [[ -d "${XDS_K8S_DRIVER_VENV_DIR}" ]]; then
+    # Intentional: No need to check python venv activate script.
+    # shellcheck source=/dev/null
+    source "${XDS_K8S_DRIVER_VENV_DIR}/bin/activate"
+  else
+    echo "Missing python virtual environment directory: ${XDS_K8S_DRIVER_VENV_DIR}" >&2
+    echo "Follow README.md installation steps first." >&2
+    exit 1
+  fi
+fi
diff --git a/tools/run_tests/xds_k8s_test_driver/bin/isort.sh b/tools/run_tests/xds_k8s_test_driver/bin/isort.sh
new file mode 100755 (executable)
index 0000000..5fe812d
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+# 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.
+
+set -eo pipefail
+
+display_usage() {
+  cat <<EOF >/dev/stderr
+A helper to run isort import sorter.
+
+USAGE: $0 [--diff]
+   --diff: Do not apply changes, only show the diff
+
+ENVIRONMENT:
+   XDS_K8S_DRIVER_VENV_DIR: the path to python virtual environment directory
+                            Default: $XDS_K8S_DRIVER_DIR/venv
+EXAMPLES:
+$0
+$0 --diff
+EOF
+  exit 1
+}
+
+if [[ "$1" == "-h" || "$1" == "--help" ]]; then
+  display_usage
+fi
+
+SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
+readonly SCRIPT_DIR
+readonly XDS_K8S_DRIVER_DIR="${SCRIPT_DIR}/.."
+
+cd "${XDS_K8S_DRIVER_DIR}"
+
+# Relative paths not yet supported by shellcheck.
+# shellcheck source=/dev/null
+source "${XDS_K8S_DRIVER_DIR}/bin/ensure_venv.sh"
+
+if [[ "$1" == "--diff" ]]; then
+  readonly MODE="--diff"
+else
+  readonly MODE="--overwrite-in-place"
+fi
+
+# typing is the only module allowed to put imports on the same line:
+# https://google.github.io/styleguide/pyguide.html#313-imports-formatting
+exec python -m isort "${MODE}" \
+  --force-sort-within-sections \
+  --force-single-line-imports --single-line-exclusions=typing \
+  framework bin tests
+
index a3a341a..a3ccb3a 100755 (executable)
@@ -26,7 +26,7 @@ Typical usage examples:
     python -m bin.run_channelz --flagfile=config/local-dev.cfg --security=mtls_error
 
     # More information and usage options
-    python -m bin.run_channelz --helpful
+    python -m bin.run_channelz --helpfull
 """
 import hashlib
 import logging
index 049493a..d866986 100755 (executable)
@@ -28,7 +28,7 @@ Typical usage examples:
     python -m bin.run_td_setup --flagfile=config/local-dev.cfg --security=mtls
 
     # More information and usage options
-    python -m bin.run_td_setup --helpful
+    python -m bin.run_td_setup --helpfull
 """
 import logging
 import uuid
index 18faa87..f351127 100755 (executable)
@@ -18,6 +18,7 @@ from absl import flags
 
 from framework import xds_flags
 from framework import xds_k8s_flags
+from framework.infrastructure import gcp
 from framework.infrastructure import k8s
 from framework.test_app import client_app
 
@@ -49,6 +50,10 @@ def main(argv):
     if len(argv) > 1:
         raise app.UsageError('Too many command-line arguments.')
 
+    # Flag shortcuts.
+    project: str = xds_flags.PROJECT.value
+    # GCP Service Account email
+    gcp_service_account: str = xds_k8s_flags.GCP_SERVICE_ACCOUNT.value
     # Base namespace
     namespace = xds_flags.NAMESPACE.value
     client_namespace = namespace
@@ -56,8 +61,10 @@ def main(argv):
     runner_kwargs = dict(
         deployment_name=xds_flags.CLIENT_NAME.value,
         image_name=xds_k8s_flags.CLIENT_IMAGE.value,
-        gcp_service_account=xds_k8s_flags.GCP_SERVICE_ACCOUNT.value,
         td_bootstrap_image=xds_k8s_flags.TD_BOOTSTRAP_IMAGE.value,
+        gcp_project=project,
+        gcp_api_manager=gcp.api.GcpApiManager(),
+        gcp_service_account=gcp_service_account,
         xds_server_uri=xds_flags.XDS_SERVER_URI.value,
         network=xds_flags.NETWORK.value,
         stats_port=xds_flags.CLIENT_PORT.value,
index a1e45b3..8085b2d 100755 (executable)
@@ -18,6 +18,7 @@ from absl import flags
 
 from framework import xds_flags
 from framework import xds_k8s_flags
+from framework.infrastructure import gcp
 from framework.infrastructure import k8s
 from framework.test_app import server_app
 
@@ -45,6 +46,10 @@ def main(argv):
     if len(argv) > 1:
         raise app.UsageError('Too many command-line arguments.')
 
+    # Flag shortcuts.
+    project: str = xds_flags.PROJECT.value
+    # GCP Service Account email
+    gcp_service_account: str = xds_k8s_flags.GCP_SERVICE_ACCOUNT.value
     # Base namespace
     namespace = xds_flags.NAMESPACE.value
     server_namespace = namespace
@@ -52,13 +57,15 @@ def main(argv):
     runner_kwargs = dict(
         deployment_name=xds_flags.SERVER_NAME.value,
         image_name=xds_k8s_flags.SERVER_IMAGE.value,
-        gcp_service_account=xds_k8s_flags.GCP_SERVICE_ACCOUNT.value,
+        td_bootstrap_image=xds_k8s_flags.TD_BOOTSTRAP_IMAGE.value,
+        gcp_project=project,
+        gcp_api_manager=gcp.api.GcpApiManager(),
+        gcp_service_account=gcp_service_account,
         network=xds_flags.NETWORK.value,
         reuse_namespace=_REUSE_NAMESPACE.value)
 
     if _SECURE.value:
         runner_kwargs.update(
-            td_bootstrap_image=xds_k8s_flags.TD_BOOTSTRAP_IMAGE.value,
             xds_server_uri=xds_flags.XDS_SERVER_URI.value,
             deployment_template='server-secure.deployment.yaml')
 
diff --git a/tools/run_tests/xds_k8s_test_driver/bin/yapf.sh b/tools/run_tests/xds_k8s_test_driver/bin/yapf.sh
new file mode 100755 (executable)
index 0000000..0b6d2ce
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+# 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.
+
+set -eo pipefail
+
+display_usage() {
+  cat <<EOF >/dev/stderr
+A helper to run yapf formatter.
+
+USAGE: $0 [--diff]
+   --diff: Do not apply changes, only show the diff
+
+ENVIRONMENT:
+   XDS_K8S_DRIVER_VENV_DIR: the path to python virtual environment directory
+                            Default: $XDS_K8S_DRIVER_DIR/venv
+EXAMPLES:
+$0
+$0 --diff
+EOF
+  exit 1
+}
+
+if [[ "$1" == "-h" || "$1" == "--help" ]]; then
+  display_usage
+fi
+
+SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
+readonly SCRIPT_DIR
+readonly XDS_K8S_DRIVER_DIR="${SCRIPT_DIR}/.."
+
+cd "${XDS_K8S_DRIVER_DIR}"
+
+# Relative paths not yet supported by shellcheck.
+# shellcheck source=/dev/null
+source "${XDS_K8S_DRIVER_DIR}/bin/ensure_venv.sh"
+
+if [[ "$1" == "--diff" ]]; then
+  readonly MODE="--diff"
+else
+  readonly MODE="--in-place"
+  readonly VERBOSE="--verbose" # print out file names while processing
+fi
+
+exec python -m yapf "${MODE}" ${VERBOSE:-} \
+  --parallel --recursive --style=../../../setup.cfg \
+  framework bin tests
index 7a362a8..ddd70a4 100644 (file)
@@ -1,5 +1,5 @@
 --flagfile=config/common.cfg
 --project=grpc-testing
 --network=default-vpc
---gcp_service_account=830293263384-compute@developer.gserviceaccount.com
+--gcp_service_account=xds-k8s-interop-tests@grpc-testing.iam.gserviceaccount.com
 --private_api_key_secret_name=projects/830293263384/secrets/xds-interop-tests-private-api-access-key
index 2b31024..9a7f908 100644 (file)
@@ -8,6 +8,8 @@
 --project=your_project_id
 --gcp_service_account=project_number-compute@developer.gserviceaccount.com
 --private_api_key_secret_name=projects/project_number/secrets/xds-interop-tests-private-api-access-key
+# Uncomment to ensure the allow health check firewall exists before test case runs
+# --ensure_firewall
 
 # The name of kube context to use. See `gcloud container clusters get-credentials` and `kubectl config`
 --kube_context=context_name
index 4ea4e90..94c7cf1 100644 (file)
@@ -22,7 +22,7 @@ We use tenacity as a general-purpose retrying library.
 """
 import datetime
 import logging
-from typing import Any, List, Optional
+from typing import Any, Optional, Sequence
 
 import tenacity
 
@@ -41,7 +41,7 @@ _wait_exponential = tenacity.wait_exponential
 _wait_fixed = tenacity.wait_fixed
 
 
-def _retry_on_exceptions(retry_on_exceptions: Optional[List[Any]] = None):
+def _retry_on_exceptions(retry_on_exceptions: Optional[Sequence[Any]] = None):
     # Retry on all exceptions by default
     if retry_on_exceptions is None:
         retry_on_exceptions = (Exception,)
@@ -53,7 +53,7 @@ def exponential_retryer_with_timeout(
         wait_min: timedelta,
         wait_max: timedelta,
         timeout: timedelta,
-        retry_on_exceptions: Optional[List[Any]] = None,
+        retry_on_exceptions: Optional[Sequence[Any]] = None,
         logger: Optional[logging.Logger] = None,
         log_level: Optional[int] = logging.DEBUG) -> Retrying:
     if logger is None:
@@ -71,7 +71,7 @@ def constant_retryer(*,
                      wait_fixed: timedelta,
                      attempts: int = 0,
                      timeout: timedelta = None,
-                     retry_on_exceptions: Optional[List[Any]] = None,
+                     retry_on_exceptions: Optional[Sequence[Any]] = None,
                      logger: Optional[logging.Logger] = None,
                      log_level: Optional[int] = logging.DEBUG) -> Retrying:
     if logger is None:
@@ -84,7 +84,7 @@ def constant_retryer(*,
     if attempts > 0:
         stops.append(_stop_after_attempt(attempts))
     if timeout is not None:
-        stops.append(_stop_after_delay.total_seconds())
+        stops.append(_stop_after_delay(timeout.total_seconds()))
 
     return Retrying(retry=_retry_on_exceptions(retry_on_exceptions),
                     wait=_wait_fixed(wait_fixed.total_seconds()),
index 02d8fad..127ce04 100644 (file)
@@ -13,5 +13,6 @@
 # limitations under the License.
 from framework.infrastructure.gcp import api
 from framework.infrastructure.gcp import compute
+from framework.infrastructure.gcp import iam
 from framework.infrastructure.gcp import network_security
 from framework.infrastructure.gcp import network_services
index 25458bc..2a0bae8 100644 (file)
@@ -15,12 +15,8 @@ import abc
 import contextlib
 import functools
 import logging
-from typing import Optional, List
+from typing import Any, Dict, List, Optional
 
-# Workaround: `grpc` must be imported before `google.protobuf.json_format`,
-# to prevent "Segmentation fault". Ref https://github.com/grpc/grpc/issues/24897
-# TODO(sergiitk): Remove after #24897 is solved
-import grpc  # noqa pylint: disable=unused-import
 from absl import flags
 from google.cloud import secretmanager_v1
 from google.longrunning import operations_pb2
@@ -28,6 +24,7 @@ from google.protobuf import json_format
 from google.rpc import code_pb2
 from googleapiclient import discovery
 import googleapiclient.errors
+import googleapiclient.http
 import tenacity
 import yaml
 
@@ -49,7 +46,10 @@ COMPUTE_V1_DISCOVERY_FILE = flags.DEFINE_string(
     help="Load compute v1 from discovery file")
 
 # Type aliases
+_HttpError = googleapiclient.errors.HttpError
+_HttpLib2Error = googleapiclient.http.httplib2.HttpLib2Error
 Operation = operations_pb2.Operation
+HttpRequest = googleapiclient.http.HttpRequest
 
 
 class GcpApiManager:
@@ -139,6 +139,20 @@ class GcpApiManager:
 
         raise NotImplementedError(f'Secret Manager {version} not supported')
 
+    @functools.lru_cache(None)
+    def iam(self, version: str) -> discovery.Resource:
+        """Identity and Access Management (IAM) API.
+
+        https://cloud.google.com/iam/docs/reference/rest
+        https://googleapis.github.io/google-api-python-client/docs/dyn/iam_v1.html
+        """
+        api_name = 'iam'
+        if version == 'v1':
+            return self._build_from_discovery_v1(api_name, version)
+
+        raise NotImplementedError(
+            f'Identity and Access Management (IAM) {version} not supported')
+
     def _build_from_discovery_v1(self, api_name, version):
         api = discovery.build(api_name,
                               version,
@@ -180,7 +194,46 @@ class GcpApiManager:
 
 
 class Error(Exception):
-    """Base error class for GCP API errors"""
+    """Base error class for GCP API errors."""
+
+
+class ResponseError(Error):
+    """The response was not a 2xx."""
+    reason: str
+    uri: str
+    error_details: Optional[str]
+    status: Optional[int]
+    cause: _HttpError
+
+    def __init__(self, cause: _HttpError):
+        # TODO(sergiitk): cleanup when we upgrade googleapiclient:
+        #  - remove _get_reason()
+        #  - remove error_details note
+        #  - use status_code()
+        self.reason = cause._get_reason().strip()  # noqa
+        self.uri = cause.uri
+        self.error_details = cause.error_details  # NOTE: Must after _get_reason
+        self.status = None
+        if cause.resp and cause.resp.status:
+            self.status = cause.resp.status
+        self.cause = cause
+        super().__init__()
+
+    def __repr__(self):
+        return (f'<ResponseError {self.status} when requesting {self.uri} '
+                f'returned "{self.reason}". Details: "{self.error_details}">')
+
+
+class TransportError(Error):
+    """A transport error has occurred."""
+    cause: _HttpLib2Error
+
+    def __init__(self, cause: _HttpLib2Error):
+        self.cause = cause
+        super().__init__()
+
+    def __repr__(self):
+        return f'<TransportError cause: {self.cause!r}>'
 
 
 class OperationError(Error):
@@ -216,6 +269,31 @@ class GcpProjectApiResource:
         self.api: discovery.Resource = api
         self.project: str = project
 
+    # TODO(sergiitk): in upcoming GCP refactoring, differentiate between
+    #   _execute for LRO (Long Running Operations), and immediate operations.
+    def _execute(
+            self,
+            request: HttpRequest,
+            *,
+            num_retries: Optional[int] = _GCP_API_RETRIES) -> Dict[str, Any]:
+        """Execute the immediate request.
+
+        Returns:
+          Unmarshalled response as a dictionary.
+
+        Raises:
+          ResponseError if the response was not a 2xx.
+          TransportError if a transport error has occurred.
+        """
+        if num_retries is None:
+            num_retries = self._GCP_API_RETRIES
+        try:
+            return request.execute(num_retries=num_retries)
+        except _HttpError as error:
+            raise ResponseError(error)
+        except _HttpLib2Error as error:
+            raise TransportError(error)
+
     @staticmethod
     def wait_for_operation(operation_request,
                            test_success_fn,
@@ -231,7 +309,7 @@ class GcpProjectApiResource:
         return retryer(operation_request.execute)
 
     @staticmethod
-    def _resource_pretty_format(body: dict) -> str:
+    def resource_pretty_format(body: dict) -> str:
         """Return a string with pretty-printed resource body."""
         return yaml.dump(body, explicit_start=True, explicit_end=True)
 
@@ -250,7 +328,7 @@ class GcpStandardCloudApiResource(GcpProjectApiResource, metaclass=abc.ABCMeta):
     def _create_resource(self, collection: discovery.Resource, body: dict,
                          **kwargs):
         logger.info("Creating %s resource:\n%s", self.api_name,
-                    self._resource_pretty_format(body))
+                    self.resource_pretty_format(body))
         create_req = collection.create(parent=self.parent(),
                                        body=body,
                                        **kwargs)
@@ -269,7 +347,7 @@ class GcpStandardCloudApiResource(GcpProjectApiResource, metaclass=abc.ABCMeta):
     def _get_resource(self, collection: discovery.Resource, full_name):
         resource = collection.get(name=full_name).execute()
         logger.info('Loaded %s:\n%s', full_name,
-                    self._resource_pretty_format(resource))
+                    self.resource_pretty_format(resource))
         return resource
 
     def _delete_resource(self, collection: discovery.Resource,
@@ -278,15 +356,16 @@ class GcpStandardCloudApiResource(GcpProjectApiResource, metaclass=abc.ABCMeta):
         try:
             self._execute(collection.delete(name=full_name))
             return True
-        except googleapiclient.errors.HttpError as error:
+        except _HttpError as error:
             if error.resp and error.resp.status == 404:
                 logger.info('%s not deleted since it does not exist', full_name)
             else:
                 logger.warning('Failed to delete %s, %r', full_name, error)
         return False
 
+    # TODO(sergiitk): Use ResponseError and TransportError
     def _execute(self,
-                 request,
+                 request: HttpRequest,
                  timeout_sec=GcpProjectApiResource._WAIT_FOR_OPERATION_SEC):
         operation = request.execute(num_retries=self._GCP_API_RETRIES)
         self._wait(operation, timeout_sec)
index 838aed0..ac6eef6 100644 (file)
@@ -14,7 +14,7 @@
 import dataclasses
 import enum
 import logging
-from typing import Any, Dict, Optional
+from typing import Any, Dict, List, Optional
 
 from googleapiclient import discovery
 import googleapiclient.errors
@@ -80,6 +80,34 @@ class ComputeV1(gcp.api.GcpProjectApiResource):
     def delete_health_check(self, name):
         self._delete_resource(self.api.healthChecks(), 'healthCheck', name)
 
+    def create_firewall_rule(self, name: str, network_url: str,
+                             source_ranges: List[str],
+                             ports: List[str]) -> Optional[GcpResource]:
+        try:
+            return self._insert_resource(
+                self.api.firewalls(), {
+                    "allowed": [{
+                        "IPProtocol": "tcp",
+                        "ports": ports
+                    }],
+                    "direction": "INGRESS",
+                    "name": name,
+                    "network": network_url,
+                    "priority": 1000,
+                    "sourceRanges": source_ranges,
+                    "targetTags": ["allow-health-checks"]
+                })
+        except googleapiclient.errors.HttpError as http_error:
+            # TODO(lidiz) use status_code() when we upgrade googleapiclient
+            if http_error.resp.status == 409:
+                logger.debug('Firewall rule %s already existed', name)
+                return
+            else:
+                raise
+
+    def delete_firewall_rule(self, name):
+        self._delete_resource(self.api.firewalls(), 'firewall', name)
+
     def create_backend_service_traffic_director(
             self,
             name: str,
@@ -153,6 +181,9 @@ class ComputeV1(gcp.api.GcpProjectApiResource):
                 }],
             })
 
+    def create_url_map_with_content(self, url_map_body: Any) -> GcpResource:
+        return self._insert_resource(self.api.urlMaps(), url_map_body)
+
     def delete_url_map(self, name):
         self._delete_resource(self.api.urlMaps(), 'urlMap', name)
 
@@ -295,19 +326,19 @@ class ComputeV1(gcp.api.GcpProjectApiResource):
                       **kwargs) -> GcpResource:
         resp = collection.get(project=self.project, **kwargs).execute()
         logger.info('Loaded compute resource:\n%s',
-                    self._resource_pretty_format(resp))
+                    self.resource_pretty_format(resp))
         return self.GcpResource(resp['name'], resp['selfLink'])
 
     def _insert_resource(self, collection: discovery.Resource,
                          body: Dict[str, Any]) -> GcpResource:
         logger.info('Creating compute resource:\n%s',
-                    self._resource_pretty_format(body))
+                    self.resource_pretty_format(body))
         resp = self._execute(collection.insert(project=self.project, body=body))
         return self.GcpResource(body['name'], resp['targetLink'])
 
     def _patch_resource(self, collection, body, **kwargs):
         logger.info('Patching compute resource:\n%s',
-                    self._resource_pretty_format(body))
+                    self.resource_pretty_format(body))
         self._execute(
             collection.patch(project=self.project, body=body, **kwargs))
 
diff --git a/tools/run_tests/xds_k8s_test_driver/framework/infrastructure/gcp/iam.py b/tools/run_tests/xds_k8s_test_driver/framework/infrastructure/gcp/iam.py
new file mode 100644 (file)
index 0000000..b1f6ed4
--- /dev/null
@@ -0,0 +1,312 @@
+# 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.
+import dataclasses
+import datetime
+import functools
+import logging
+from typing import Any, Dict, FrozenSet, Optional
+
+from framework.helpers import retryers
+from framework.infrastructure import gcp
+
+logger = logging.getLogger(__name__)
+
+# Type aliases
+_timedelta = datetime.timedelta
+_HttpRequest = gcp.api.HttpRequest
+
+
+class EtagConflict(gcp.api.Error):
+    """
+    Indicates concurrent policy changes.
+
+    https://cloud.google.com/iam/docs/policies#etag
+    """
+    pass
+
+
+def handle_etag_conflict(func):
+
+    def wrap_retry_on_etag_conflict(*args, **kwargs):
+        retryer = retryers.exponential_retryer_with_timeout(
+            retry_on_exceptions=(EtagConflict, gcp.api.TransportError),
+            wait_min=_timedelta(seconds=1),
+            wait_max=_timedelta(seconds=10),
+            timeout=_timedelta(minutes=2))
+        return retryer(func, *args, **kwargs)
+
+    return wrap_retry_on_etag_conflict
+
+
+def _replace_binding(policy: 'Policy', binding: 'Policy.Binding',
+                     new_binding: 'Policy.Binding') -> 'Policy':
+    new_bindings = set(policy.bindings)
+    new_bindings.discard(binding)
+    new_bindings.add(new_binding)
+    return dataclasses.replace(policy, bindings=frozenset(new_bindings))
+
+
+@dataclasses.dataclass(frozen=True)
+class ServiceAccount:
+    """An IAM service account.
+
+    https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts
+    Note: "etag" field is skipped because it's deprecated
+    """
+    name: str
+    projectId: str
+    uniqueId: str
+    email: str
+    oauth2ClientId: str
+    displayName: str = ''
+    description: str = ''
+    disabled: bool = False
+
+    @classmethod
+    def from_response(cls, response: Dict[str, Any]) -> 'ServiceAccount':
+        return cls(name=response['name'],
+                   projectId=response['projectId'],
+                   uniqueId=response['uniqueId'],
+                   email=response['email'],
+                   oauth2ClientId=response['oauth2ClientId'],
+                   description=response.get('description', ''),
+                   displayName=response.get('displayName', ''),
+                   disabled=response.get('disabled', False))
+
+    def as_dict(self) -> Dict[str, Any]:
+        return dataclasses.asdict(self)
+
+
+@dataclasses.dataclass(frozen=True)
+class Expr:
+    """
+    Represents a textual expression in the Common Expression Language syntax.
+
+    https://cloud.google.com/iam/docs/reference/rest/v1/Expr
+    """
+    expression: str
+    title: str = ''
+    description: str = ''
+    location: str = ''
+
+    @classmethod
+    def from_response(cls, response: Dict[str, Any]) -> 'Expr':
+        return cls(**response)
+
+    def as_dict(self) -> Dict[str, Any]:
+        return dataclasses.asdict(self)
+
+
+@dataclasses.dataclass(frozen=True)
+class Policy:
+    """An Identity and Access Management (IAM) policy, which specifies
+    access controls for Google Cloud resources.
+
+    https://cloud.google.com/iam/docs/reference/rest/v1/Policy
+    Note: auditConfigs not supported by this implementation.
+    """
+
+    @dataclasses.dataclass(frozen=True)
+    class Binding:
+        """Policy Binding. Associates members with a role.
+
+        https://cloud.google.com/iam/docs/reference/rest/v1/Policy#binding
+        """
+        role: str
+        members: FrozenSet[str]
+        condition: Optional[Expr] = None
+
+        @classmethod
+        def from_response(cls, response: Dict[str, Any]) -> 'Policy.Binding':
+            fields = {
+                'role': response['role'],
+                'members': frozenset(response.get('members', [])),
+            }
+            if 'condition' in response:
+                fields['condition'] = Expr.from_response(response['condition'])
+
+            return cls(**fields)
+
+        def as_dict(self) -> Dict[str, Any]:
+            result = {
+                'role': self.role,
+                'members': list(self.members),
+            }
+            if self.condition is not None:
+                result['condition'] = self.condition.as_dict()
+            return result
+
+    bindings: FrozenSet[Binding]
+    etag: str
+    version: Optional[int] = None
+
+    @functools.lru_cache(maxsize=128)
+    def find_binding_for_role(
+            self,
+            role: str,
+            condition: Optional[Expr] = None) -> Optional['Policy.Binding']:
+        results = (binding for binding in self.bindings
+                   if binding.role == role and binding.condition == condition)
+        return next(results, None)
+
+    @classmethod
+    def from_response(cls, response: Dict[str, Any]) -> 'Policy':
+        bindings = frozenset(
+            cls.Binding.from_response(b) for b in response.get('bindings', []))
+        return cls(bindings=bindings,
+                   etag=response['etag'],
+                   version=response.get('version'))
+
+    def as_dict(self) -> Dict[str, Any]:
+        result = {
+            'bindings': [binding.as_dict() for binding in self.bindings],
+            'etag': self.etag,
+        }
+        if self.version is not None:
+            result['version'] = self.version
+        return result
+
+
+class IamV1(gcp.api.GcpProjectApiResource):
+    """
+    Identity and Access Management (IAM) API.
+
+    https://cloud.google.com/iam/docs/reference/rest
+    """
+    _service_accounts: gcp.api.discovery.Resource
+
+    # Operations that affect conditional role bindings must specify version 3.
+    # Otherwise conditions are omitted, and role names returned with a suffix,
+    # f.e. roles/iam.workloadIdentityUser_withcond_f1ec33c9beb41857dbf0
+    # https://cloud.google.com/iam/docs/reference/rest/v1/Policy#FIELDS.version
+    POLICY_VERSION: str = 3
+
+    def __init__(self, api_manager: gcp.api.GcpApiManager, project: str):
+        super().__init__(api_manager.iam('v1'), project)
+        # Shortcut to projects/*/serviceAccounts/ endpoints
+        self._service_accounts = self.api.projects().serviceAccounts()
+
+    def service_account_resource_name(self, account) -> str:
+        """
+        Returns full resource name of the service account.
+
+        The resource name of the service account in the following format:
+        projects/{PROJECT_ID}/serviceAccounts/{ACCOUNT}.
+        The ACCOUNT value can be the email address or the uniqueId of the
+        service account.
+        Ref https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts/get
+
+        Args:
+            account: The ACCOUNT value
+        """
+        return f'projects/{self.project}/serviceAccounts/{account}'
+
+    def get_service_account(self, account: str) -> ServiceAccount:
+        resource_name = self.service_account_resource_name(account)
+        request: _HttpRequest = self._service_accounts.get(name=resource_name)
+        response: Dict[str, Any] = self._execute(request)
+        logger.debug('Loaded Service Account:\n%s',
+                     self.resource_pretty_format(response))
+        return ServiceAccount.from_response(response)
+
+    def get_service_account_iam_policy(self, account: str) -> Policy:
+        resource_name = self.service_account_resource_name(account)
+        request: _HttpRequest = self._service_accounts.getIamPolicy(
+            resource=resource_name,
+            options_requestedPolicyVersion=self.POLICY_VERSION)
+        response: Dict[str, Any] = self._execute(request)
+        logger.debug('Loaded Service Account Policy:\n%s',
+                     self.resource_pretty_format(response))
+        return Policy.from_response(response)
+
+    def set_service_account_iam_policy(self, account: str,
+                                       policy: Policy) -> Policy:
+        """Sets the IAM policy that is attached to a service account.
+
+        https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts/setIamPolicy
+        """
+        resource_name = self.service_account_resource_name(account)
+        body = {'policy': policy.as_dict()}
+        logger.debug('Updating Service Account %s policy:\n%s', account,
+                     self.resource_pretty_format(body))
+        try:
+            request: _HttpRequest = self._service_accounts.setIamPolicy(
+                resource=resource_name, body=body)
+            response: Dict[str, Any] = self._execute(request)
+            return Policy.from_response(response)
+        except gcp.api.ResponseError as error:
+            if error.status == 409:
+                # https://cloud.google.com/iam/docs/policies#etag
+                logger.debug(error)
+                raise EtagConflict from error
+            raise
+
+    @handle_etag_conflict
+    def add_service_account_iam_policy_binding(self, account: str, role: str,
+                                               member: str) -> None:
+        """Add an IAM policy binding to an IAM service account.
+
+        See for details on updating policy bindings:
+        https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts/setIamPolicy
+        """
+        policy: Policy = self.get_service_account_iam_policy(account)
+        binding: Optional[Policy.Binding] = policy.find_binding_for_role(role)
+        if binding and member in binding.members:
+            logger.debug('Member %s already has role %s for Service Account %s',
+                         member, role, account)
+            return
+
+        if binding is None:
+            updated_binding = Policy.Binding(role, frozenset([member]))
+        else:
+            updated_members: FrozenSet[str] = binding.members.union({member})
+            updated_binding: Policy.Binding = dataclasses.replace(
+                binding, members=updated_members)
+
+        updated_policy: Policy = _replace_binding(policy, binding,
+                                                  updated_binding)
+        self.set_service_account_iam_policy(account, updated_policy)
+        logger.debug('Role %s granted to member %s for Service Account %s',
+                     role, member, account)
+
+    @handle_etag_conflict
+    def remove_service_account_iam_policy_binding(self, account: str, role: str,
+                                                  member: str) -> None:
+        """Remove an IAM policy binding from the IAM policy of a service
+        account.
+
+        See for details on updating policy bindings:
+        https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts/setIamPolicy
+        """
+        policy: Policy = self.get_service_account_iam_policy(account)
+        binding: Optional[Policy.Binding] = policy.find_binding_for_role(role)
+
+        if binding is None:
+            logger.debug('Noop: Service Account %s has no bindings for role %s',
+                         account, role)
+            return
+        if member not in binding.members:
+            logger.debug(
+                'Noop: Service Account %s binding for role %s has no member %s',
+                account, role, member)
+            return
+
+        updated_members: FrozenSet[str] = binding.members.difference({member})
+        updated_binding: Policy.Binding = dataclasses.replace(
+            binding, members=updated_members)
+        updated_policy: Policy = _replace_binding(policy, binding,
+                                                  updated_binding)
+        self.set_service_account_iam_policy(account, updated_policy)
+        logger.debug('Role %s revoked from member %s for Service Account %s',
+                     role, member, account)
index cb60840..9d93cc0 100644 (file)
@@ -11,9 +11,9 @@
 # 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.
+import dataclasses
 import logging
 
-import dataclasses
 from google.rpc import code_pb2
 import tenacity
 
index b331ade..cede020 100644 (file)
 # 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.
+import dataclasses
 import logging
 from typing import Optional
 
-import dataclasses
 from google.rpc import code_pb2
 import tenacity
 
index 557c424..2569825 100644 (file)
@@ -16,13 +16,13 @@ import json
 import logging
 import subprocess
 import time
-from typing import Optional, List, Tuple
+from typing import List, Optional, Tuple
 
-# TODO(sergiitk): replace with tenacity
-import retrying
-import kubernetes.config
 from kubernetes import client
 from kubernetes import utils
+import kubernetes.config
+# TODO(sergiitk): replace with tenacity
+import retrying
 
 logger = logging.getLogger(__name__)
 # Type aliases
index 531c0c2..871f3c0 100644 (file)
@@ -12,8 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 import logging
-from typing import Optional, Set
+from typing import Any, List, Optional, Set
 
+from framework import xds_flags
 from framework.infrastructure import gcp
 
 logger = logging.getLogger(__name__)
@@ -41,11 +42,13 @@ EndpointConfigSelector = _NetworkServicesV1Alpha1.EndpointConfigSelector
 class TrafficDirectorManager:
     compute: _ComputeV1
     BACKEND_SERVICE_NAME = "backend-service"
+    ALTERNATIVE_BACKEND_SERVICE_NAME = "alternative-backend-service"
     HEALTH_CHECK_NAME = "health-check"
     URL_MAP_NAME = "url-map"
     URL_MAP_PATH_MATCHER_NAME = "path-matcher"
     TARGET_PROXY_NAME = "target-proxy"
     FORWARDING_RULE_NAME = "forwarding-rule"
+    FIREWALL_RULE_NAME = "allow-health-checks"
 
     def __init__(
         self,
@@ -69,11 +72,17 @@ class TrafficDirectorManager:
         # TODO(sergiitk): remove this flag once backend service resource loaded
         self.backend_service_protocol: Optional[BackendServiceProtocol] = None
         self.url_map: Optional[GcpResource] = None
+        self.firewall_rule: Optional[GcpResource] = None
         self.target_proxy: Optional[GcpResource] = None
         # TODO(sergiitk): remove this flag once target proxy resource loaded
         self.target_proxy_is_http: bool = False
         self.forwarding_rule: Optional[GcpResource] = None
         self.backends: Set[ZonalGcpResource] = set()
+        self.alternative_backend_service: Optional[GcpResource] = None
+        # TODO(sergiitk): remove this flag once backend service resource loaded
+        self.alternative_backend_service_protocol: Optional[
+            BackendServiceProtocol] = None
+        self.alternative_backends: Set[ZonalGcpResource] = set()
 
     @property
     def network_url(self):
@@ -112,6 +121,7 @@ class TrafficDirectorManager:
             self.delete_target_grpc_proxy(force=force)
         self.delete_url_map(force=force)
         self.delete_backend_service(force=force)
+        self.delete_alternative_backend_service(force=force)
         self.delete_health_check(force=force)
 
     def _ns_name(self, name):
@@ -199,6 +209,63 @@ class TrafficDirectorManager:
         self.compute.wait_for_backends_healthy_status(self.backend_service,
                                                       self.backends)
 
+    def create_alternative_backend_service(
+            self, protocol: Optional[BackendServiceProtocol] = _BackendGRPC):
+        if protocol is None:
+            protocol = _BackendGRPC
+        name = self._ns_name(self.ALTERNATIVE_BACKEND_SERVICE_NAME)
+        logger.info('Creating %s Alternative Backend Service "%s"',
+                    protocol.name, name)
+        resource = self.compute.create_backend_service_traffic_director(
+            name, health_check=self.health_check, protocol=protocol)
+        self.alternative_backend_service = resource
+        self.alternative_backend_service_protocol = protocol
+
+    def load_alternative_backend_service(self):
+        name = self._ns_name(self.ALTERNATIVE_BACKEND_SERVICE_NAME)
+        resource = self.compute.get_backend_service_traffic_director(name)
+        self.alternative_backend_service = resource
+
+    def delete_alternative_backend_service(self, force=False):
+        if force:
+            name = self._ns_name(self.ALTERNATIVE_BACKEND_SERVICE_NAME)
+        elif self.alternative_backend_service:
+            name = self.alternative_backend_service.name
+        else:
+            return
+        logger.info('Deleting Alternative Backend Service "%s"', name)
+        self.compute.delete_backend_service(name)
+        self.alternative_backend_service = None
+
+    def alternative_backend_service_add_neg_backends(self, name, zones):
+        logger.info('Waiting for Network Endpoint Groups to load endpoints.')
+        for zone in zones:
+            backend = self.compute.wait_for_network_endpoint_group(name, zone)
+            logger.info('Loaded NEG "%s" in zone %s', backend.name,
+                        backend.zone)
+            self.alternative_backends.add(backend)
+        self.alternative_backend_service_add_backends()
+
+    def alternative_backend_service_add_backends(self):
+        logging.info('Adding backends to Backend Service %s: %r',
+                     self.alternative_backend_service.name,
+                     self.alternative_backends)
+        self.compute.backend_service_add_backends(
+            self.alternative_backend_service, self.alternative_backends)
+
+    def alternative_backend_service_remove_all_backends(self):
+        logging.info('Removing backends from Backend Service %s',
+                     self.alternative_backend_service.name)
+        self.compute.backend_service_remove_all_backends(
+            self.alternative_backend_service)
+
+    def wait_for_alternative_backends_healthy_status(self):
+        logger.debug(
+            "Waiting for Backend Service %s to report all backends healthy %r",
+            self.alternative_backend_service, self.alternative_backends)
+        self.compute.wait_for_backends_healthy_status(
+            self.alternative_backend_service, self.alternative_backends)
+
     def create_url_map(
         self,
         src_host: str,
@@ -215,6 +282,12 @@ class TrafficDirectorManager:
         self.url_map = resource
         return resource
 
+    def create_url_map_with_content(self, url_map_body: Any) -> GcpResource:
+        logger.info('Creating URL map: %s', url_map_body)
+        resource = self.compute.create_url_map_with_content(url_map_body)
+        self.url_map = resource
+        return resource
+
     def delete_url_map(self, force=False):
         if force:
             name = self._ns_name(self.URL_MAP_NAME)
@@ -290,6 +363,28 @@ class TrafficDirectorManager:
         self.compute.delete_forwarding_rule(name)
         self.forwarding_rule = None
 
+    def create_firewall_rule(self, allowed_ports: List[str]):
+        name = self._ns_name(self.FIREWALL_RULE_NAME)
+        logging.info(
+            'Creating firewall rule "%s" in network "%s" with allowed ports %s',
+            name, self.network, allowed_ports)
+        resource = self.compute.create_firewall_rule(
+            name, self.network_url, xds_flags.FIREWALL_SOURCE_RANGE.value,
+            allowed_ports)
+        self.firewall_rule = resource
+
+    def delete_firewall_rule(self, force=False):
+        """The firewall rule won't be automatically removed."""
+        if force:
+            name = self._ns_name(self.FIREWALL_RULE_NAME)
+        elif self.firewall_rule:
+            name = self.firewall_rule.name
+        else:
+            return
+        logger.info('Deleting Firewall Rule "%s"', name)
+        self.compute.delete_firewall_rule(name)
+        self.firewall_rule = None
+
 
 class TrafficDirectorSecureManager(TrafficDirectorManager):
     netsec: Optional[_NetworkSecurityV1Alpha1]
index 3e15553..c93a8f4 100644 (file)
@@ -15,11 +15,9 @@ import logging
 import re
 from typing import ClassVar, Dict, Optional
 
-# Workaround: `grpc` must be imported before `google.protobuf.json_format`,
-# to prevent "Segmentation fault". Ref https://github.com/grpc/grpc/issues/24897
-import grpc
 from google.protobuf import json_format
 import google.protobuf.message
+import grpc
 
 logger = logging.getLogger(__name__)
 
diff --git a/tools/run_tests/xds_k8s_test_driver/framework/rpc/grpc_csds.py b/tools/run_tests/xds_k8s_test_driver/framework/rpc/grpc_csds.py
new file mode 100644 (file)
index 0000000..2278c5b
--- /dev/null
@@ -0,0 +1,59 @@
+# Copyright 2021 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.
+"""
+This contains helpers for gRPC services defined in
+https://github.com/envoyproxy/envoy/blob/main/api/envoy/service/status/v3/csds.proto
+"""
+
+import logging
+import queue
+from typing import Callable, Optional
+
+from envoy.extensions.filters.common.fault.v3 import fault_pb2
+from envoy.extensions.filters.http.fault.v3 import fault_pb2
+from envoy.extensions.filters.http.router.v3 import router_pb2
+# Envoy protos provided by PyPI package xds-protos
+# Needs to import the generated Python file to load descriptors
+from envoy.extensions.filters.network.http_connection_manager.v3 import \
+    http_connection_manager_pb2
+from envoy.service.status.v3 import csds_pb2
+from envoy.service.status.v3 import csds_pb2_grpc
+import grpc
+
+import framework.rpc
+
+logger = logging.getLogger(__name__)
+
+# Type aliases
+ClientConfig = csds_pb2.ClientConfig
+_ClientStatusRequest = csds_pb2.ClientStatusRequest
+
+
+class CsdsClient(framework.rpc.grpc.GrpcClientHelper):
+    stub: csds_pb2_grpc.ClientStatusDiscoveryServiceStub
+
+    def __init__(self, channel: grpc.Channel):
+        super().__init__(channel,
+                         csds_pb2_grpc.ClientStatusDiscoveryServiceStub)
+
+    def fetch_client_status(self, **kwargs) -> Optional[ClientConfig]:
+        """Fetches the active xDS configurations."""
+        response = self.call_unary_with_deadline(rpc='FetchClientStatus',
+                                                 req=_ClientStatusRequest(),
+                                                 **kwargs)
+        if len(response.config) != 1:
+            logger.debug('Unexpected number of client configs: %s',
+                         len(response.config))
+            return None
+        return response.config[0]
index 31485f9..ac32d51 100644 (file)
@@ -16,22 +16,25 @@ This contains helpers for gRPC services defined in
 https://github.com/grpc/grpc/blob/master/src/proto/grpc/testing/test.proto
 """
 import logging
-from typing import Optional
+from typing import Iterable, Optional, Tuple
 
 import grpc
 
 import framework.rpc
-from src.proto.grpc.testing import test_pb2_grpc
 from src.proto.grpc.testing import messages_pb2
+from src.proto.grpc.testing import test_pb2_grpc
 
 # Type aliases
 _LoadBalancerStatsRequest = messages_pb2.LoadBalancerStatsRequest
 LoadBalancerStatsResponse = messages_pb2.LoadBalancerStatsResponse
+_LoadBalancerAccumulatedStatsRequest = messages_pb2.LoadBalancerAccumulatedStatsRequest
+LoadBalancerAccumulatedStatsResponse = messages_pb2.LoadBalancerAccumulatedStatsResponse
 
 
 class LoadBalancerStatsServiceClient(framework.rpc.grpc.GrpcClientHelper):
     stub: test_pb2_grpc.LoadBalancerStatsServiceStub
     STATS_PARTIAL_RESULTS_TIMEOUT_SEC = 1200
+    STATS_ACCUMULATED_RESULTS_TIMEOUT_SEC = 600
 
     def __init__(self, channel: grpc.Channel):
         super().__init__(channel, test_pb2_grpc.LoadBalancerStatsServiceStub)
@@ -51,3 +54,56 @@ class LoadBalancerStatsServiceClient(framework.rpc.grpc.GrpcClientHelper):
                                                  timeout_sec=timeout_sec),
                                              deadline_sec=timeout_sec,
                                              log_level=logging.INFO)
+
+    def get_client_accumulated_stats(
+        self,
+        *,
+        timeout_sec: Optional[int] = None
+    ) -> LoadBalancerAccumulatedStatsResponse:
+        if timeout_sec is None:
+            timeout_sec = self.STATS_ACCUMULATED_RESULTS_TIMEOUT_SEC
+
+        return self.call_unary_with_deadline(
+            rpc='GetClientAccumulatedStats',
+            req=_LoadBalancerAccumulatedStatsRequest(),
+            deadline_sec=timeout_sec,
+            log_level=logging.INFO)
+
+
+class XdsUpdateClientConfigureServiceClient(framework.rpc.grpc.GrpcClientHelper
+                                           ):
+    stub: test_pb2_grpc.XdsUpdateClientConfigureServiceStub
+    CONFIGURE_TIMEOUT_SEC: int = 5
+
+    def __init__(self, channel: grpc.Channel):
+        super().__init__(channel,
+                         test_pb2_grpc.XdsUpdateClientConfigureServiceStub)
+
+    def configure(
+        self,
+        *,
+        rpc_types: Iterable[str],
+        metadata: Optional[Iterable[Tuple[str, str, str]]] = None,
+        app_timeout: Optional[int] = None,
+        timeout_sec: int = CONFIGURE_TIMEOUT_SEC,
+    ) -> None:
+        request = messages_pb2.ClientConfigureRequest()
+        for rpc_type in rpc_types:
+            request.types.append(
+                messages_pb2.ClientConfigureRequest.RpcType.Value(rpc_type))
+        if metadata:
+            for entry in metadata:
+                request.metadata.append(
+                    messages_pb2.ClientConfigureRequest.Metadata(
+                        type=messages_pb2.ClientConfigureRequest.RpcType.Value(
+                            entry[0]),
+                        key=entry[1],
+                        value=entry[2],
+                    ))
+        if app_timeout:
+            request.timeout_sec = app_timeout
+        # Configure's response is empty
+        self.call_unary_with_deadline(rpc='Configure',
+                                      req=request,
+                                      deadline_sec=timeout_sec,
+                                      log_level=logging.INFO)
index 58a73d4..504dc18 100644 (file)
@@ -19,6 +19,7 @@ from typing import Optional
 import mako.template
 import yaml
 
+from framework.infrastructure import gcp
 from framework.infrastructure import k8s
 
 logger = logging.getLogger(__name__)
@@ -31,6 +32,7 @@ class RunnerError(Exception):
 class KubernetesBaseRunner:
     TEMPLATE_DIR_NAME = 'kubernetes-manifests'
     TEMPLATE_DIR_RELATIVE_PATH = f'../../{TEMPLATE_DIR_NAME}'
+    ROLE_WORKLOAD_IDENTITY_USER = 'roles/iam.workloadIdentityUser'
 
     def __init__(self,
                  k8s_namespace,
@@ -53,7 +55,7 @@ class KubernetesBaseRunner:
 
     def cleanup(self, *, force=False):
         if (self.namespace and not self.reuse_namespace) or force:
-            self._delete_namespace()
+            self.delete_namespace()
             self.namespace = None
 
     @staticmethod
@@ -129,6 +131,46 @@ class KubernetesBaseRunner:
                      namespace.metadata.creation_timestamp)
         return namespace
 
+    @staticmethod
+    def _get_workload_identity_member_name(project, namespace_name,
+                                           service_account_name):
+        """
+        Returns workload identity member name used to authenticate Kubernetes
+        service accounts.
+
+        https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity
+        """
+        return (f'serviceAccount:{project}.svc.id.goog'
+                f'[{namespace_name}/{service_account_name}]')
+
+    def _grant_workload_identity_user(self, *, gcp_iam, gcp_service_account,
+                                      service_account_name):
+        workload_identity_member = self._get_workload_identity_member_name(
+            gcp_iam.project, self.k8s_namespace.name, service_account_name)
+        logger.info('Granting %s to %s for GCP Service Account %s',
+                    self.ROLE_WORKLOAD_IDENTITY_USER, workload_identity_member,
+                    gcp_service_account)
+
+        gcp_iam.add_service_account_iam_policy_binding(
+            gcp_service_account, self.ROLE_WORKLOAD_IDENTITY_USER,
+            workload_identity_member)
+
+    def _revoke_workload_identity_user(self, *, gcp_iam, gcp_service_account,
+                                       service_account_name):
+        workload_identity_member = self._get_workload_identity_member_name(
+            gcp_iam.project, self.k8s_namespace.name, service_account_name)
+        logger.info('Revoking %s from %s for GCP Service Account %s',
+                    self.ROLE_WORKLOAD_IDENTITY_USER, workload_identity_member,
+                    gcp_service_account)
+        try:
+            gcp_iam.remove_service_account_iam_policy_binding(
+                gcp_service_account, self.ROLE_WORKLOAD_IDENTITY_USER,
+                workload_identity_member)
+        except gcp.api.Error as error:
+            logger.warning('Failed  %s from %s for Service Account %s: %r',
+                           self.ROLE_WORKLOAD_IDENTITY_USER,
+                           workload_identity_member, gcp_service_account, error)
+
     def _create_service_account(self, template,
                                 **kwargs) -> k8s.V1ServiceAccount:
         resource = self._create_from_template(template, **kwargs)
@@ -207,7 +249,7 @@ class KubernetesBaseRunner:
             self.k8s_namespace.wait_for_service_account_deleted(name)
         logger.debug('Service account %s deleted', name)
 
-    def _delete_namespace(self, wait_for_deletion=True):
+    def delete_namespace(self, wait_for_deletion=True):
         logger.info('Deleting namespace %s', self.k8s_namespace.name)
         try:
             self.k8s_namespace.delete()
index 23dddf9..b0b5602 100644 (file)
@@ -20,12 +20,14 @@ modules.
 import datetime
 import functools
 import logging
-from typing import Iterator, Optional
+from typing import Iterable, Optional, Tuple
 
 from framework.helpers import retryers
+from framework.infrastructure import gcp
 from framework.infrastructure import k8s
 import framework.rpc
 from framework.rpc import grpc_channelz
+from framework.rpc import grpc_csds
 from framework.rpc import grpc_testing
 from framework.test_app import base_runner
 
@@ -34,11 +36,13 @@ logger = logging.getLogger(__name__)
 # Type aliases
 _timedelta = datetime.timedelta
 _LoadBalancerStatsServiceClient = grpc_testing.LoadBalancerStatsServiceClient
+_XdsUpdateClientConfigureServiceClient = grpc_testing.XdsUpdateClientConfigureServiceClient
 _ChannelzServiceClient = grpc_channelz.ChannelzServiceClient
 _ChannelzChannel = grpc_channelz.Channel
 _ChannelzChannelState = grpc_channelz.ChannelState
 _ChannelzSubchannel = grpc_channelz.Subchannel
 _ChannelzSocket = grpc_channelz.Socket
+_CsdsClient = grpc_csds.CsdsClient
 
 
 class XdsTestClient(framework.rpc.grpc.GrpcApp):
@@ -68,9 +72,20 @@ class XdsTestClient(framework.rpc.grpc.GrpcApp):
 
     @property
     @functools.lru_cache(None)
+    def update_config(self):
+        return _XdsUpdateClientConfigureServiceClient(
+            self._make_channel(self.rpc_port))
+
+    @property
+    @functools.lru_cache(None)
     def channelz(self) -> _ChannelzServiceClient:
         return _ChannelzServiceClient(self._make_channel(self.maintenance_port))
 
+    @property
+    @functools.lru_cache(None)
+    def csds(self) -> _CsdsClient:
+        return _CsdsClient(self._make_channel(self.maintenance_port))
+
     def get_load_balancer_stats(
         self,
         *,
@@ -83,6 +98,15 @@ class XdsTestClient(framework.rpc.grpc.GrpcApp):
         return self.load_balancer_stats.get_client_stats(
             num_rpcs=num_rpcs, timeout_sec=timeout_sec)
 
+    def get_load_balancer_accumulated_stats(
+        self,
+        *,
+        timeout_sec: Optional[int] = None,
+    ) -> grpc_testing.LoadBalancerAccumulatedStatsResponse:
+        """Shortcut to LoadBalancerStatsServiceClient.get_client_accumulated_stats()"""
+        return self.load_balancer_stats.get_client_accumulated_stats(
+            timeout_sec=timeout_sec)
+
     def wait_for_active_server_channel(self) -> _ChannelzChannel:
         """Wait for the channel to the server to transition to READY.
 
@@ -173,7 +197,7 @@ class XdsTestClient(framework.rpc.grpc.GrpcApp):
             f'Client has no {_ChannelzChannelState.Name(state)} channel with '
             'the server')
 
-    def get_server_channels(self, **kwargs) -> Iterator[_ChannelzChannel]:
+    def get_server_channels(self, **kwargs) -> Iterable[_ChannelzChannel]:
         return self.channelz.find_channels_for_target(self.server_target,
                                                       **kwargs)
 
@@ -197,8 +221,10 @@ class KubernetesClientRunner(base_runner.KubernetesBaseRunner):
                  *,
                  deployment_name,
                  image_name,
-                 gcp_service_account,
                  td_bootstrap_image,
+                 gcp_api_manager: gcp.api.GcpApiManager,
+                 gcp_project: str,
+                 gcp_service_account: str,
                  xds_server_uri=None,
                  network='default',
                  service_account_name=None,
@@ -213,16 +239,22 @@ class KubernetesClientRunner(base_runner.KubernetesBaseRunner):
         # Settings
         self.deployment_name = deployment_name
         self.image_name = image_name
-        self.gcp_service_account = gcp_service_account
-        self.service_account_name = service_account_name or deployment_name
         self.stats_port = stats_port
         # xDS bootstrap generator
         self.td_bootstrap_image = td_bootstrap_image
         self.xds_server_uri = xds_server_uri
         self.network = network
         self.deployment_template = deployment_template
-        self.service_account_template = service_account_template
         self.debug_use_port_forwarding = debug_use_port_forwarding
+        # Service account settings:
+        # Kubernetes service account
+        self.service_account_name = service_account_name or deployment_name
+        self.service_account_template = service_account_template
+        # GCP service account to map to Kubernetes service account
+        self.gcp_service_account = gcp_service_account
+        # GCP IAM API used to grant allow workload service accounts permission
+        # to use GCP service account identity.
+        self.gcp_iam = gcp.iam.IamV1(gcp_api_manager, gcp_project)
 
         # Mutable state
         self.deployment: Optional[k8s.V1Deployment] = None
@@ -239,6 +271,13 @@ class KubernetesClientRunner(base_runner.KubernetesBaseRunner):
         super().run()
         # TODO(sergiitk): make rpc UnaryCall enum or get it from proto
 
+        # Allow Kubernetes service account to use the GCP service account
+        # identity.
+        self._grant_workload_identity_user(
+            gcp_iam=self.gcp_iam,
+            gcp_service_account=self.gcp_service_account,
+            service_account_name=self.service_account_name)
+
         # Create service account
         self.service_account = self._create_service_account(
             self.service_account_template,
@@ -292,6 +331,10 @@ class KubernetesClientRunner(base_runner.KubernetesBaseRunner):
             self._delete_deployment(self.deployment_name)
             self.deployment = None
         if self.service_account or force:
+            self._revoke_workload_identity_user(
+                gcp_iam=self.gcp_iam,
+                gcp_service_account=self.gcp_service_account,
+                service_account_name=self.service_account_name)
             self._delete_service_account(self.service_account_name)
             self.service_account = None
         super().cleanup(force=force_namespace and force)
index 2b258af..aba2bb1 100644 (file)
@@ -21,6 +21,7 @@ import functools
 import logging
 from typing import Iterator, Optional
 
+from framework.infrastructure import gcp
 from framework.infrastructure import k8s
 import framework.rpc
 from framework.rpc import grpc_channelz
@@ -135,11 +136,13 @@ class KubernetesServerRunner(base_runner.KubernetesBaseRunner):
                  *,
                  deployment_name,
                  image_name,
-                 gcp_service_account,
+                 td_bootstrap_image,
+                 gcp_api_manager: gcp.api.GcpApiManager,
+                 gcp_project: str,
+                 gcp_service_account: str,
                  service_account_name=None,
                  service_name=None,
                  neg_name=None,
-                 td_bootstrap_image=None,
                  xds_server_uri=None,
                  network='default',
                  deployment_template='server.deployment.yaml',
@@ -154,8 +157,6 @@ class KubernetesServerRunner(base_runner.KubernetesBaseRunner):
         # Settings
         self.deployment_name = deployment_name
         self.image_name = image_name
-        self.gcp_service_account = gcp_service_account
-        self.service_account_name = service_account_name or deployment_name
         self.service_name = service_name or deployment_name
         # xDS bootstrap generator
         self.td_bootstrap_image = td_bootstrap_image
@@ -166,10 +167,18 @@ class KubernetesServerRunner(base_runner.KubernetesBaseRunner):
                                      f'{self.service_name}')
         self.network = network
         self.deployment_template = deployment_template
-        self.service_account_template = service_account_template
         self.service_template = service_template
         self.reuse_service = reuse_service
         self.debug_use_port_forwarding = debug_use_port_forwarding
+        # Service account settings:
+        # Kubernetes service account
+        self.service_account_name = service_account_name or deployment_name
+        self.service_account_template = service_account_template
+        # GCP service account to map to Kubernetes service account
+        self.gcp_service_account = gcp_service_account
+        # GCP IAM API used to grant allow workload service accounts permission
+        # to use GCP service account identity.
+        self.gcp_iam = gcp.iam.IamV1(gcp_api_manager, gcp_project)
 
         # Mutable state
         self.deployment: Optional[k8s.V1Deployment] = None
@@ -223,6 +232,13 @@ class KubernetesServerRunner(base_runner.KubernetesBaseRunner):
                 test_port=test_port)
         self._wait_service_neg(self.service_name, test_port)
 
+        # Allow Kubernetes service account to use the GCP service account
+        # identity.
+        self._grant_workload_identity_user(
+            gcp_iam=self.gcp_iam,
+            gcp_service_account=self.gcp_service_account,
+            service_account_name=self.service_account_name)
+
         # Create service account
         self.service_account = self._create_service_account(
             self.service_account_template,
@@ -284,6 +300,10 @@ class KubernetesServerRunner(base_runner.KubernetesBaseRunner):
             self._delete_service(self.service_name)
             self.service = None
         if self.service_account or force:
+            self._revoke_workload_identity_user(
+                gcp_iam=self.gcp_iam,
+                gcp_service_account=self.gcp_service_account,
+                service_account_name=self.service_account_name)
             self._delete_service_account(self.service_account_name)
             self.service_account = None
         super().cleanup(force=(force_namespace and force))
index 65ebc84..6b1ecdc 100644 (file)
@@ -30,6 +30,18 @@ XDS_SERVER_URI = flags.DEFINE_string(
     "xds_server_uri",
     default=None,
     help="Override Traffic Director server uri, for testing")
+ENSURE_FIREWALL = flags.DEFINE_bool(
+    "ensure_firewall",
+    default=False,
+    help="Ensure the allow-health-check firewall exists before each test case")
+FIREWALL_SOURCE_RANGE = flags.DEFINE_list(
+    "firewall_source_range",
+    default=['35.191.0.0/16', '130.211.0.0/22'],
+    help="Update the source range of the firewall rule.")
+FIREWALL_ALLOWED_PORTS = flags.DEFINE_list(
+    "firewall_allowed_ports",
+    default=['8080-8100'],
+    help="Update the allowed ports of the firewall rule.")
 
 # Test server
 SERVER_NAME = flags.DEFINE_string("server_name",
index 0606ba9..909ecd8 100644 (file)
@@ -20,6 +20,7 @@ from typing import Optional, Tuple
 
 from absl import flags
 from absl.testing import absltest
+from google.protobuf import json_format
 
 from framework import xds_flags
 from framework import xds_k8s_flags
@@ -68,6 +69,8 @@ class XdsKubernetesTestCase(absltest.TestCase):
         cls.gcp_service_account: str = xds_k8s_flags.GCP_SERVICE_ACCOUNT.value
         cls.td_bootstrap_image = xds_k8s_flags.TD_BOOTSTRAP_IMAGE.value
         cls.xds_server_uri = xds_flags.XDS_SERVER_URI.value
+        cls.ensure_firewall = xds_flags.ENSURE_FIREWALL.value
+        cls.firewall_allowed_ports = xds_flags.FIREWALL_ALLOWED_PORTS.value
 
         # Base namespace
         # TODO(sergiitk): generate for each test
@@ -155,6 +158,22 @@ class XdsKubernetesTestCase(absltest.TestCase):
             0,
             msg=f'Expected all RPCs to succeed: {failed} of {num_rpcs} failed')
 
+    def assertXdsConfigExists(self, test_client: XdsTestClient):
+        config = test_client.csds.fetch_client_status(log_level=logging.INFO)
+        self.assertIsNotNone(config)
+        seen = set()
+        want = frozenset([
+            'listener_config',
+            'cluster_config',
+            'route_config',
+            'endpoint_config',
+        ])
+        for xds_config in config.xds_config:
+            seen.add(xds_config.WhichOneof('per_xds_config'))
+        logger.debug('Received xDS config dump: %s',
+                     json_format.MessageToJson(config, indent=2))
+        self.assertSameElements(want, seen)
+
     def assertFailedRpcs(self,
                          test_client: XdsTestClient,
                          num_rpcs: Optional[int] = 100):
@@ -185,6 +204,13 @@ class XdsKubernetesTestCase(absltest.TestCase):
 
 class RegularXdsKubernetesTestCase(XdsKubernetesTestCase):
 
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        if cls.server_maintenance_port is None:
+            cls.server_maintenance_port = \
+                server_app.KubernetesServerRunner.DEFAULT_MAINTENANCE_PORT
+
     def setUp(self):
         super().setUp()
 
@@ -195,14 +221,21 @@ class RegularXdsKubernetesTestCase(XdsKubernetesTestCase):
             resource_prefix=self.namespace,
             network=self.network)
 
+        # Ensures the firewall exist
+        if self.ensure_firewall:
+            self.td.create_firewall_rule(
+                allowed_ports=self.firewall_allowed_ports)
+
         # Test Server Runner
         self.server_runner = server_app.KubernetesServerRunner(
             k8s.KubernetesNamespace(self.k8s_api_manager,
                                     self.server_namespace),
             deployment_name=self.server_name,
             image_name=self.server_image,
-            gcp_service_account=self.gcp_service_account,
             td_bootstrap_image=self.td_bootstrap_image,
+            gcp_project=self.project,
+            gcp_api_manager=self.gcp_api_manager,
+            gcp_service_account=self.gcp_service_account,
             xds_server_uri=self.xds_server_uri,
             network=self.network)
 
@@ -212,8 +245,10 @@ class RegularXdsKubernetesTestCase(XdsKubernetesTestCase):
                                     self.client_namespace),
             deployment_name=self.client_name,
             image_name=self.client_image,
-            gcp_service_account=self.gcp_service_account,
             td_bootstrap_image=self.td_bootstrap_image,
+            gcp_project=self.project,
+            gcp_api_manager=self.gcp_api_manager,
+            gcp_service_account=self.gcp_service_account,
             xds_server_uri=self.xds_server_uri,
             network=self.network,
             debug_use_port_forwarding=self.debug_use_port_forwarding,
@@ -265,15 +300,22 @@ class SecurityXdsKubernetesTestCase(XdsKubernetesTestCase):
             resource_prefix=self.namespace,
             network=self.network)
 
+        # Ensures the firewall exist
+        if self.ensure_firewall:
+            self.td.create_firewall_rule(
+                allowed_ports=self.firewall_allowed_ports)
+
         # Test Server Runner
         self.server_runner = server_app.KubernetesServerRunner(
             k8s.KubernetesNamespace(self.k8s_api_manager,
                                     self.server_namespace),
             deployment_name=self.server_name,
             image_name=self.server_image,
+            td_bootstrap_image=self.td_bootstrap_image,
+            gcp_project=self.project,
+            gcp_api_manager=self.gcp_api_manager,
             gcp_service_account=self.gcp_service_account,
             network=self.network,
-            td_bootstrap_image=self.td_bootstrap_image,
             xds_server_uri=self.xds_server_uri,
             deployment_template='server-secure.deployment.yaml',
             debug_use_port_forwarding=self.debug_use_port_forwarding)
@@ -284,8 +326,10 @@ class SecurityXdsKubernetesTestCase(XdsKubernetesTestCase):
                                     self.client_namespace),
             deployment_name=self.client_name,
             image_name=self.client_image,
-            gcp_service_account=self.gcp_service_account,
             td_bootstrap_image=self.td_bootstrap_image,
+            gcp_project=self.project,
+            gcp_api_manager=self.gcp_api_manager,
+            gcp_service_account=self.gcp_service_account,
             xds_server_uri=self.xds_server_uri,
             network=self.network,
             deployment_template='client-secure.deployment.yaml',
diff --git a/tools/run_tests/xds_k8s_test_driver/framework/xds_url_map_test_resources.py b/tools/run_tests/xds_k8s_test_driver/framework/xds_url_map_test_resources.py
new file mode 100644 (file)
index 0000000..693cd2a
--- /dev/null
@@ -0,0 +1,271 @@
+# Copyright 2021 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.
+"""A test framework built for urlMap related xDS test cases."""
+
+import inspect
+import functools
+from typing import Any, Iterable, List, Mapping, Tuple
+
+from absl import flags
+from absl import logging
+
+from framework import xds_flags
+from framework import xds_k8s_flags
+from framework.infrastructure import gcp
+from framework.infrastructure import k8s
+from framework.infrastructure import traffic_director
+from framework.test_app import client_app
+from framework.test_app import server_app
+
+flags.adopt_module_key_flags(xds_flags)
+flags.adopt_module_key_flags(xds_k8s_flags)
+
+STRATEGY = flags.DEFINE_enum('strategy',
+                             default='reuse',
+                             enum_values=['create', 'keep', 'reuse'],
+                             help='Strategy of GCP resources management')
+
+# Type alias
+UrlMapType = Any
+HostRule = Any
+PathMatcher = Any
+
+_COMPUTE_V1_URL_PREFIX = 'https://www.googleapis.com/compute/v1'
+
+
+class _UrlMapChangeAggregator:
+    """Where all the urlMap change happens."""
+
+    def __init__(self, url_map_name: str):
+        self._map = {
+            "name": url_map_name,
+            "defaultService": GcpResourceManager().default_backend_service(),
+            "hostRules": [],
+            "pathMatchers": [],
+        }
+
+    def get_map(self) -> UrlMapType:
+        return self._map
+
+    def apply_change(self, test_case: 'XdsUrlMapTestCase') -> None:
+        logging.info('Apply urlMap change for test case: %s.%s',
+                     test_case.short_module_name, test_case.__name__)
+        url_map_parts = test_case.url_map_change(
+            *self._get_test_case_url_map(test_case))
+        self._set_test_case_url_map(*url_map_parts)
+
+    def _get_test_case_url_map(
+            self,
+            test_case: 'XdsUrlMapTestCase') -> Tuple[HostRule, PathMatcher]:
+        host_rule = {
+            "hosts": [test_case.hostname()],
+            "pathMatcher": test_case.path_matcher_name(),
+        }
+        path_matcher = {
+            "name": test_case.path_matcher_name(),
+            "defaultService": GcpResourceManager().default_backend_service(),
+        }
+        return host_rule, path_matcher
+
+    def _set_test_case_url_map(self, host_rule: HostRule,
+                               path_matcher: PathMatcher) -> None:
+        self._map["hostRules"].append(host_rule)
+        self._map["pathMatchers"].append(path_matcher)
+
+
+def _package_flags() -> Mapping[str, Any]:
+    """Automatically parse Abseil flags into a dictionary.
+
+    Abseil flag is only available after the Abseil app initialization. If we use
+    __new__ in our metaclass, the flag value parse will happen during the
+    initialization of modules, hence will fail. That's why we are using __call__
+    to inject metaclass magics, and the flag parsing will be delayed until the
+    class is about to be instantiated.
+    """
+    res = {}
+    for flag_module in [xds_flags, xds_k8s_flags]:
+        for key, value in inspect.getmembers(flag_module):
+            if isinstance(value, flags.FlagHolder):
+                res[key.lower()] = value.value
+    res['strategy'] = STRATEGY.value
+    return res
+
+
+class _MetaSingletonAndAbslFlags(type):
+    """Ensures singleton and injects flag values."""
+
+    # Allow different subclasses to create different singletons.
+    _instances = {}
+    # But we only parse Abseil flags once.
+    _flags = None
+
+    def __call__(cls, *args, **kwargs):
+        if cls not in cls._instances:
+            if cls._flags is None:
+                cls._flags = _package_flags()
+            obj = super().__call__(cls._flags, *args, **kwargs)
+            cls._instances[cls] = obj
+            return obj
+        return cls._instances[cls]
+
+
+class GcpResourceManager(metaclass=_MetaSingletonAndAbslFlags):
+    """Manages the lifecycle of GCP resources.
+
+    The GCP resources including:
+        - 3 K8s deployment (client, default backends, alternative backends)
+        - Full set of the Traffic Director stuff
+        - Merged gigantic urlMap from all imported test cases
+
+    All resources are intended to be used across test cases and multiple runs
+    (except the client K8s deployment).
+    """
+
+    def __init__(self, absl_flags: Mapping[str, Any]):
+        for key in absl_flags:
+            setattr(self, key, absl_flags[key])
+        # API managers
+        self.k8s_api_manager = k8s.KubernetesApiManager(self.kube_context)
+        self.gcp_api_manager = gcp.api.GcpApiManager()
+        self.td = traffic_director.TrafficDirectorManager(
+            self.gcp_api_manager,
+            self.project,
+            resource_prefix=self.namespace,
+            network=self.network,
+        )
+        # Kubernetes namespace
+        self.k8s_namespace = k8s.KubernetesNamespace(self.k8s_api_manager,
+                                                     self.namespace)
+        # Kubernetes Test Client
+        self.test_client_runner = client_app.KubernetesClientRunner(
+            self.k8s_namespace,
+            deployment_name=self.client_name,
+            image_name=self.client_image,
+            gcp_project=self.project,
+            gcp_api_manager=self.gcp_api_manager,
+            gcp_service_account=self.gcp_service_account,
+            td_bootstrap_image=self.td_bootstrap_image,
+            network=self.network,
+            debug_use_port_forwarding=self.debug_use_port_forwarding,
+            stats_port=self.client_port,
+            reuse_namespace=True)
+        # Kubernetes Test Servers
+        self.test_server_runner = server_app.KubernetesServerRunner(
+            self.k8s_namespace,
+            deployment_name=self.server_name,
+            image_name=self.server_image,
+            gcp_project=self.project,
+            gcp_api_manager=self.gcp_api_manager,
+            gcp_service_account=self.gcp_service_account,
+            td_bootstrap_image=self.td_bootstrap_image,
+            network=self.network)
+        self.test_server_alternative_runner = server_app.KubernetesServerRunner(
+            self.k8s_namespace,
+            deployment_name=self.server_name + '-alternative',
+            image_name=self.server_image,
+            gcp_project=self.project,
+            gcp_api_manager=self.gcp_api_manager,
+            gcp_service_account=self.gcp_service_account,
+            td_bootstrap_image=self.td_bootstrap_image,
+            network=self.network,
+            reuse_namespace=True)
+        logging.info('Strategy of GCP resources management: %s', self.strategy)
+
+    def _pre_cleanup(self):
+        # Cleanup existing debris
+        logging.info('GcpResourceManager: pre clean-up')
+        self.td.cleanup(force=True)
+        self.test_client_runner.delete_namespace()
+
+    def setup(self, test_case_classes: 'Iterable[XdsUrlMapTestCase]') -> None:
+        if self.strategy not in ['create', 'keep']:
+            logging.info('GcpResourceManager: skipping setup for strategy [%s]',
+                         self.strategy)
+            return
+        # Construct UrlMap from test classes
+        # This is the step that mostly likely to go wrong. Lifting it to be the
+        # first task ensures fail fast.
+        aggregator = _UrlMapChangeAggregator(
+            url_map_name="%s-%s" % (self.namespace, self.td.URL_MAP_NAME))
+        for test_case_class in test_case_classes:
+            aggregator.apply_change(test_case_class)
+        final_url_map = aggregator.get_map()
+        # Clean up debris from previous runs
+        self._pre_cleanup()
+        # Start creating GCP resources
+        logging.info('GcpResourceManager: start setup')
+        # Firewall
+        if self.ensure_firewall:
+            self.td.create_firewall_rule(
+                allowed_ports=self.firewall_allowed_ports)
+        # Health Checks
+        self.td.create_health_check()
+        # Backend Services
+        self.td.create_backend_service()
+        self.td.create_alternative_backend_service()
+        # UrlMap
+        self.td.create_url_map_with_content(final_url_map)
+        # Target Proxy
+        self.td.create_target_proxy()
+        # Forwarding Rule
+        self.td.create_forwarding_rule(self.server_xds_port)
+        # Kubernetes Test Server
+        self.test_server_runner.run(
+            test_port=self.server_port,
+            maintenance_port=self.server_maintenance_port)
+        # Kubernetes Test Server Alternative
+        self.test_server_alternative_runner.run(
+            test_port=self.server_port,
+            maintenance_port=self.server_maintenance_port)
+        # Add backend to default backend service
+        neg_name, neg_zones = self.k8s_namespace.get_service_neg(
+            self.test_server_runner.service_name, self.server_port)
+        self.td.backend_service_add_neg_backends(neg_name, neg_zones)
+        # Add backend to alternative backend service
+        neg_name, neg_zones = self.k8s_namespace.get_service_neg(
+            self.test_server_alternative_runner.service_name, self.server_port)
+        self.td.alternative_backend_service_add_neg_backends(
+            neg_name, neg_zones)
+        # Wait for healthy backends
+        self.td.wait_for_backends_healthy_status()
+        self.td.wait_for_alternative_backends_healthy_status()
+
+    def cleanup(self) -> None:
+        if self.strategy not in ['create']:
+            logging.info(
+                'GcpResourceManager: skipping tear down for strategy [%s]',
+                self.strategy)
+            return
+        logging.info('GcpResourceManager: start tear down')
+        if hasattr(self, 'td'):
+            self.td.cleanup(force=True)
+        if hasattr(self, 'test_client_runner'):
+            self.test_client_runner.cleanup(force=True)
+        if hasattr(self, 'test_server_runner'):
+            self.test_server_runner.cleanup(force=True)
+        if hasattr(self, 'test_server_alternative_runner'):
+            self.test_server_alternative_runner.cleanup(force=True,
+                                                        force_namespace=True)
+
+    @functools.lru_cache(None)
+    def default_backend_service(self) -> str:
+        """Returns default backend service URL."""
+        self.td.load_backend_service()
+        return self.td.backend_service.url
+
+    @functools.lru_cache(None)
+    def alternative_backend_service(self) -> str:
+        """Returns alternative backend service URL."""
+        self.td.load_alternative_backend_service()
+        return self.td.alternative_backend_service.url
diff --git a/tools/run_tests/xds_k8s_test_driver/framework/xds_url_map_testcase.py b/tools/run_tests/xds_k8s_test_driver/framework/xds_url_map_testcase.py
new file mode 100644 (file)
index 0000000..dac0fd6
--- /dev/null
@@ -0,0 +1,415 @@
+# Copyright 2021 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.
+"""A test framework built for urlMap related xDS test cases."""
+
+import abc
+from dataclasses import dataclass
+import datetime
+import json
+import os
+import unittest
+import sys
+import time
+from typing import Any, Iterable, Mapping, Optional, Tuple, Union
+
+from absl import flags
+from absl import logging
+from absl.testing import absltest
+from google.protobuf import json_format
+import grpc
+
+from framework import xds_k8s_testcase
+from framework import xds_url_map_test_resources
+from framework.helpers import retryers
+from framework.rpc import grpc_testing
+from framework.test_app import client_app
+
+# Load existing flags
+flags.adopt_module_key_flags(xds_k8s_testcase)
+flags.adopt_module_key_flags(xds_url_map_test_resources)
+
+# Define urlMap specific flags
+QPS = flags.DEFINE_integer('qps', default=25, help='The QPS client is sending')
+
+# Test configs
+_URL_MAP_PROPAGATE_TIMEOUT_SEC = 600
+_URL_MAP_PROPAGATE_CHECK_INTERVAL_SEC = 2
+URL_MAP_TESTCASE_FILE_SUFFIX = '_test.py'
+_CLIENT_CONFIGURE_WAIT_SEC = 2
+
+# Type aliases
+XdsTestClient = client_app.XdsTestClient
+GcpResourceManager = xds_url_map_test_resources.GcpResourceManager
+HostRule = xds_url_map_test_resources.HostRule
+PathMatcher = xds_url_map_test_resources.PathMatcher
+JsonType = Any
+
+# ProtoBuf translatable RpcType enums
+RpcTypeUnaryCall = 'UNARY_CALL'
+RpcTypeEmptyCall = 'EMPTY_CALL'
+
+
+def _split_camel(s: str, delimiter: str = '-') -> str:
+    """Turn camel case name to snake-case-like name."""
+    return ''.join(delimiter + c.lower() if c.isupper() else c
+                   for c in s).lstrip(delimiter)
+
+
+class DumpedXdsConfig(dict):
+    """A convenience class to check xDS config.
+
+    Feel free to add more pre-compute fields.
+    """
+
+    def __init__(self, xds_json: JsonType):
+        super().__init__(xds_json)
+        self.json_config = xds_json
+        self.lds = None
+        self.rds = None
+        self.cds = []
+        self.eds = []
+        self.endpoints = []
+        for xds_config in self['xdsConfig']:
+            try:
+                if 'listenerConfig' in xds_config:
+                    self.lds = xds_config['listenerConfig']['dynamicListeners'][
+                        0]['activeState']['listener']
+                elif 'routeConfig' in xds_config:
+                    self.rds = xds_config['routeConfig']['dynamicRouteConfigs'][
+                        0]['routeConfig']
+                elif 'clusterConfig' in xds_config:
+                    for cluster in xds_config['clusterConfig'][
+                            'dynamicActiveClusters']:
+                        self.cds.append(cluster['cluster'])
+                elif 'endpointConfig' in xds_config:
+                    for endpoint in xds_config['endpointConfig'][
+                            'dynamicEndpointConfigs']:
+                        self.eds.append(endpoint['endpointConfig'])
+            except Exception as e:
+                logging.debug('Parse dumped xDS config failed with %s: %s',
+                              type(e), e)
+        for endpoint_config in self.eds:
+            for endpoint in endpoint_config.get('endpoints', {}):
+                for lb_endpoint in endpoint.get('lbEndpoints', {}):
+                    try:
+                        if lb_endpoint['healthStatus'] == 'HEALTHY':
+                            self.endpoints.append(
+                                '%s:%s' % (lb_endpoint['endpoint']['address']
+                                           ['socketAddress']['address'],
+                                           lb_endpoint['endpoint']['address']
+                                           ['socketAddress']['portValue']))
+                    except Exception as e:
+                        logging.debug('Parse endpoint failed with %s: %s',
+                                      type(e), e)
+
+    def __str__(self) -> str:
+        return json.dumps(self, indent=2)
+
+
+class RpcDistributionStats:
+    """A convenience class to check RPC distribution.
+
+    Feel free to add more pre-compute fields.
+    """
+    num_failures: int
+    num_oks: int
+    default_service_rpc_count: int
+    alternative_service_rpc_count: int
+    unary_call_default_service_rpc_count: int
+    empty_call_default_service_rpc_count: int
+    unary_call_alternative_service_rpc_count: int
+    empty_call_alternative_service_rpc_count: int
+
+    def __init__(self, json_lb_stats: JsonType):
+        self.num_failures = json_lb_stats.get('numFailures', 0)
+
+        self.num_oks = 0
+        self.default_service_rpc_count = 0
+        self.alternative_service_rpc_count = 0
+        self.unary_call_default_service_rpc_count = 0
+        self.empty_call_default_service_rpc_count = 0
+        self.unary_call_alternative_service_rpc_count = 0
+        self.empty_call_alternative_service_rpc_count = 0
+
+        if 'rpcsByMethod' in json_lb_stats:
+            for rpc_type in json_lb_stats['rpcsByMethod']:
+                for peer in json_lb_stats['rpcsByMethod'][rpc_type][
+                        'rpcsByPeer']:
+                    count = json_lb_stats['rpcsByMethod'][rpc_type][
+                        'rpcsByPeer'][peer]
+                    self.num_oks += count
+                    if rpc_type == 'UnaryCall':
+                        if 'alternative' in peer:
+                            self.unary_call_alternative_service_rpc_count = count
+                            self.alternative_service_rpc_count += count
+                        else:
+                            self.unary_call_default_service_rpc_count = count
+                            self.default_service_rpc_count += count
+                    else:
+                        if 'alternative' in peer:
+                            self.empty_call_alternative_service_rpc_count = count
+                            self.alternative_service_rpc_count += count
+                        else:
+                            self.empty_call_default_service_rpc_count = count
+                            self.default_service_rpc_count += count
+
+
+@dataclass
+class ExpectedResult:
+    """Describes the expected result of assertRpcStatusCode method below."""
+    rpc_type: str = RpcTypeUnaryCall
+    status_code: grpc.StatusCode = grpc.StatusCode.OK
+    ratio: float = 1
+
+
+class _MetaXdsUrlMapTestCase(type):
+    """Tracking test case subclasses."""
+
+    # Automatic discover of all subclasses
+    _test_case_classes = []
+    _test_case_names = set()
+    # Keep track of started and finished test cases, so we know when to setup
+    # and tear down GCP resources.
+    _started_test_cases = set()
+    _finished_test_cases = set()
+
+    def __new__(cls, name: str, bases: Iterable[Any],
+                attrs: Mapping[str, Any]) -> Any:
+        # Hand over the tracking objects
+        attrs['test_case_classes'] = cls._test_case_classes
+        attrs['test_case_names'] = cls._test_case_names
+        attrs['started_test_cases'] = cls._started_test_cases
+        attrs['finished_test_cases'] = cls._finished_test_cases
+        # Handle the test name reflection
+        module_name = os.path.split(
+            sys.modules[attrs['__module__']].__file__)[-1]
+        if module_name.endswith(URL_MAP_TESTCASE_FILE_SUFFIX):
+            module_name = module_name.replace(URL_MAP_TESTCASE_FILE_SUFFIX, '')
+        attrs['short_module_name'] = module_name.replace('_', '-')
+        # Create the class and track
+        new_class = type.__new__(cls, name, bases, attrs)
+        if name.startswith('Test'):
+            cls._test_case_names.add(name)
+            cls._test_case_classes.append(new_class)
+        else:
+            logging.debug('Skipping test case class: %s', name)
+        return new_class
+
+
+class XdsUrlMapTestCase(absltest.TestCase, metaclass=_MetaXdsUrlMapTestCase):
+    """XdsUrlMapTestCase is the base class for urlMap related tests.
+
+    The subclass is expected to implement 3 methods:
+
+    - url_map_change: Updates the urlMap components for this test case
+    - xds_config_validate: Validates if the client received legit xDS configs
+    - rpc_distribution_validate: Validates if the routing behavior is correct
+    """
+
+    @staticmethod
+    @abc.abstractmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        """Updates the dedicated urlMap components for this test case.
+
+        Each test case will have a dedicated HostRule, where the hostname is
+        generated from the test case name. The HostRule will be linked to a
+        PathMatcher, where stores the routing logic.
+
+        Args:
+            host_rule: A HostRule GCP resource as a JSON dict.
+            path_matcher: A PathMatcher GCP resource as a JSON dict.
+
+        Returns:
+            A tuple contains the updated version of given HostRule and
+            PathMatcher.
+        """
+        pass
+
+    @abc.abstractmethod
+    def xds_config_validate(self, xds_config: DumpedXdsConfig) -> None:
+        """Validates received xDS config, if anything is wrong, raise.
+
+        This stage only ends when the control plane failed to send a valid
+        config within a given time range, like 600s.
+
+        Args:
+            xds_config: A DumpedXdsConfig instance can be used as a JSON dict,
+              but also provides helper fields for commonly checked xDS config.
+        """
+        pass
+
+    @abc.abstractmethod
+    def rpc_distribution_validate(self, client: XdsTestClient) -> None:
+        """Validates the routing behavior, if any is wrong, raise.
+
+        Args:
+            client: A XdsTestClient instance for all sorts of end2end testing.
+        """
+        pass
+
+    @classmethod
+    def hostname(cls):
+        return "%s.%s:%s" % (cls.short_module_name, _split_camel(
+            cls.__name__), GcpResourceManager().server_xds_port)
+
+    @classmethod
+    def path_matcher_name(cls):
+        # Path matcher name must match r'(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)'
+        return "%s-%s-pm" % (cls.short_module_name, _split_camel(cls.__name__))
+
+    @classmethod
+    def setUpClass(cls):
+        if not cls.started_test_cases:
+            # Create the GCP resource once before the first test start
+            GcpResourceManager().setup(cls.test_case_classes)
+        cls.started_test_cases.add(cls.__name__)
+        # TODO(lidiz) concurrency is possible, pending multiple-instance change
+        GcpResourceManager().test_client_runner.cleanup(force=True)
+        # Sending both RPCs when starting.
+        cls.test_client = GcpResourceManager().test_client_runner.run(
+            server_target=f'xds:///{cls.hostname()}',
+            rpc='UnaryCall,EmptyCall',
+            qps=QPS.value,
+            print_response=True)
+
+    @classmethod
+    def tearDownClass(cls):
+        GcpResourceManager().test_client_runner.cleanup(force=True)
+        cls.finished_test_cases.add(cls.__name__)
+        if cls.finished_test_cases == cls.test_case_names:
+            # Tear down the GCP resource after all tests finished
+            GcpResourceManager().cleanup()
+
+    def _fetch_and_check_xds_config(self):
+        # Cleanup state for this attempt
+        self._xds_json_config = None
+        # Fetch client config
+        config = self.test_client.csds.fetch_client_status(
+            log_level=logging.INFO)
+        self.assertIsNotNone(config)
+        # Found client config, test it.
+        self._xds_json_config = json_format.MessageToDict(config)
+        try:
+            self.xds_config_validate(DumpedXdsConfig(self._xds_json_config))
+        except Exception as e:
+            # Log the exception for debugging purposes.
+            if type(self._last_xds_config_exception) != type(e) or str(
+                    self._last_xds_config_exception) != str(e):
+                # Suppress repetitive exception logs
+                logging.exception(e)
+                self._last_xds_config_exception = e
+            raise
+        return
+
+    def run(self, result: unittest.TestResult = None) -> None:
+        """Abort this test case if CSDS check is failed.
+
+        This prevents the test runner to waste time on RPC distribution test,
+        and yields clearer signal.
+        """
+        if result.failures or result.errors:
+            logging.info('Aborting %s', self.__class__.__name__)
+        else:
+            super().run(result)
+
+    def test_client_config(self):
+        self._last_xds_config_exception = None
+        retryer = retryers.constant_retryer(
+            wait_fixed=datetime.timedelta(
+                seconds=_URL_MAP_PROPAGATE_CHECK_INTERVAL_SEC),
+            timeout=datetime.timedelta(seconds=_URL_MAP_PROPAGATE_TIMEOUT_SEC),
+            logger=logging,
+            log_level=logging.INFO)
+        try:
+            retryer(self._fetch_and_check_xds_config)
+        finally:
+            logging.info(
+                'latest xDS config:\n%s',
+                GcpResourceManager().td.compute.resource_pretty_format(
+                    self._xds_json_config))
+
+    def test_rpc_distribution(self):
+        self.rpc_distribution_validate(self.test_client)
+
+    @staticmethod
+    def configure_and_send(test_client: XdsTestClient,
+                           *,
+                           rpc_types: Iterable[str],
+                           metadata: Optional[Iterable[Tuple[str, str,
+                                                             str]]] = None,
+                           app_timeout: Optional[int] = None,
+                           num_rpcs: int) -> RpcDistributionStats:
+        test_client.update_config.configure(rpc_types=rpc_types,
+                                            metadata=metadata,
+                                            app_timeout=app_timeout)
+        # Configure RPC might race with get stats RPC on slower machines.
+        time.sleep(_CLIENT_CONFIGURE_WAIT_SEC)
+        json_lb_stats = json_format.MessageToDict(
+            test_client.get_load_balancer_stats(num_rpcs=num_rpcs))
+        logging.info(
+            'Received LoadBalancerStatsResponse from test client %s:\n%s',
+            test_client.ip, json.dumps(json_lb_stats, indent=2))
+        return RpcDistributionStats(json_lb_stats)
+
+    def assertNumEndpoints(self, xds_config: DumpedXdsConfig, k: int) -> None:
+        self.assertLen(
+            xds_config.endpoints, k,
+            f'insufficient endpoints in EDS: want={k} seen={xds_config.endpoints}'
+        )
+
+    def assertRpcStatusCode(self, test_client: XdsTestClient, *,
+                            expected: Iterable[ExpectedResult], length: int,
+                            tolerance: float) -> None:
+        """Assert the distribution of RPC statuses over a period of time."""
+        # Sending with pre-set QPS for a period of time
+        before_stats = test_client.get_load_balancer_accumulated_stats()
+        logging.info(
+            'Received LoadBalancerAccumulatedStatsResponse from test client %s: before:\n%s',
+            test_client.ip, before_stats)
+        time.sleep(length)
+        after_stats = test_client.get_load_balancer_accumulated_stats()
+        logging.info(
+            'Received LoadBalancerAccumulatedStatsResponse from test client %s: after: \n%s',
+            test_client.ip, after_stats)
+
+        # Validate the diff
+        for expected_result in expected:
+            rpc = expected_result.rpc_type
+            status = expected_result.status_code.value[0]
+            # Compute observation
+            seen_after = after_stats.stats_per_method.get(rpc, {}).result.get(
+                status, 0)
+            seen_before = before_stats.stats_per_method.get(rpc, {}).result.get(
+                status, 0)
+            seen = seen_after - seen_before
+            # Compute total number of RPC started
+            stats_per_method_after = after_stats.stats_per_method.get(
+                rpc, {}).result.items()
+            total_after = sum(
+                x[1] for x in stats_per_method_after)  # (status_code, count)
+            stats_per_method_before = before_stats.stats_per_method.get(
+                rpc, {}).result.items()
+            total_before = sum(
+                x[1] for x in stats_per_method_before)  # (status_code, count)
+            total = total_after - total_before
+            # Compute and validate the number
+            want = total * expected_result.ratio
+            diff_ratio = abs(seen - want) / total
+            self.assertLessEqual(
+                diff_ratio, tolerance,
+                'Expect rpc [%s] to return [%s] at %.2f ratio: seen=%d want=%d total=%d diff_ratio=%.4f > %.2f'
+                % (rpc, expected_result.status_code, expected_result.ratio,
+                   seen, want, total, diff_ratio, tolerance))
index e3ace2e..dbf2fa5 100644 (file)
@@ -16,6 +16,7 @@ spec:
     metadata:
       labels:
         app: ${deployment_name}
+        owner: xds-k8s-interop-test
     spec:
       serviceAccountName: ${service_account_name}
       containers:
@@ -26,6 +27,15 @@ spec:
           - "--port=${test_port}"
         ports:
           - containerPort: ${test_port}
+        env:
+          - name: GRPC_XDS_BOOTSTRAP
+            value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
+          - name: GRPC_XDS_EXPERIMENTAL_V3_SUPPORT
+            value: "true"
+        volumeMounts:
+          - mountPath: /tmp/grpc-xds/
+            name: grpc-td-conf
+            readOnly: true
         resources:
           limits:
             cpu: 800m
@@ -33,4 +43,30 @@ spec:
           requests:
             cpu: 100m
             memory: 512Mi
+      initContainers:
+        - name: grpc-td-init
+          image: ${td_bootstrap_image}
+          imagePullPolicy: Always
+          args:
+            - "--output=/tmp/bootstrap/td-grpc-bootstrap.json"
+            - "--vpc-network-name=${network}"
+            % if xds_server_uri:
+            - "--xds-server-uri=${xds_server_uri}"
+            % endif
+            - "--include-v3-features-experimental"
+            - "--node-metadata-experimental=app=${namespace_name}-${deployment_name}"
+          resources:
+            limits:
+              cpu: 100m
+              memory: 100Mi
+            requests:
+              cpu: 10m
+              memory: 100Mi
+          volumeMounts:
+            - mountPath: /tmp/bootstrap/
+              name: grpc-td-conf
+      volumes:
+        - name: grpc-td-conf
+          emptyDir:
+            medium: Memory
 ...
diff --git a/tools/run_tests/xds_k8s_test_driver/requirements-dev.txt b/tools/run_tests/xds_k8s_test_driver/requirements-dev.txt
new file mode 100644 (file)
index 0000000..27dc27b
--- /dev/null
@@ -0,0 +1,4 @@
+-r requirements.txt
+yapf==0.30.0  # Mirrors yapf version set in https://github.com/grpc/grpc/blob/master/tools/distrib/yapf_code.sh
+isort~=5.9
+# TODO(https://github.com/grpc/grpc/pull/25872): mypy
index bf7fb6b..8f573ca 100644 (file)
@@ -1,7 +1,7 @@
 Mako~=1.1
 PyYAML~=5.3
 absl-py~=0.11
-dataclasses~=0.8
+dataclasses~=0.8; python_version < '3.7'
 google-api-python-client~=1.12
 google-cloud-secret-manager~=2.1
 grpcio~=1.34
@@ -13,3 +13,4 @@ kubernetes~=12.0
 retrying~=1.3
 tenacity~=6.2
 protobuf~=3.14
+xds-protos~=0.0.8
diff --git a/tools/run_tests/xds_k8s_test_driver/run.sh b/tools/run_tests/xds_k8s_test_driver/run.sh
new file mode 100755 (executable)
index 0000000..f549a48
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/env bash
+# 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.
+
+set -eo pipefail
+
+XDS_K8S_DRIVER_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
+readonly XDS_K8S_DRIVER_DIR
+readonly XDS_K8S_CONFIG="${XDS_K8S_CONFIG:-$XDS_K8S_DRIVER_DIR/config/local-dev.cfg}"
+
+display_usage() {
+  cat <<EOF >/dev/stderr
+Convenience script to execute tests/ and helper bin/ scripts.
+
+USAGE: $0 script_path [arguments]
+   script_path: path to python script to execute, relative to driver root folder
+   arguments ...: arguments passed to program in sys.argv
+
+ENVIRONMENT:
+   XDS_K8S_CONFIG: file path to the config flagfile, relative to
+                   driver root folder. Default: config/local-dev.cfg
+                   Will be appended as --flagfile="config_absolute_path" argument
+   XDS_K8S_DRIVER_VENV_DIR: the path to python virtual environment directory
+                            Default: $XDS_K8S_DRIVER_DIR/venv
+DESCRIPTION:
+This tool performs the following:
+1) Ensures python virtual env installed and activated
+2) Exports test driver root in PYTHONPATH
+3) Automatically appends --flagfile="\$XDS_K8S_CONFIG" argument
+
+EXAMPLES:
+$0 bin/run_td_setup.py --help      # list script-specific options
+$0 bin/run_td_setup.py --helpfull  # list all available options
+XDS_K8S_CONFIG=./path-to-flagfile.cfg $0 bin/run_td_setup.py --namespace=override-namespace
+$0 tests/baseline_test.py
+$0 tests/security_test.py --verbosity=1 --logger_levels=__main__:DEBUG,framework:DEBUG
+$0 tests/security_test.py SecurityTest.test_mtls --nocheck_local_certs
+EOF
+  exit 1
+}
+
+if [[ "$#" -eq 0 || "$1" = "-h" || "$1" = "--help" ]]; then
+  display_usage
+fi
+
+# Relative paths not yet supported by shellcheck.
+# shellcheck source=/dev/null
+source "${XDS_K8S_DRIVER_DIR}/bin/ensure_venv.sh"
+
+cd "${XDS_K8S_DRIVER_DIR}"
+export PYTHONPATH="${XDS_K8S_DRIVER_DIR}"
+# Split path to python file from the rest of the args.
+readonly PY_FILE="$1"
+shift
+# Append args after --flagfile, so they take higher priority.
+exec python "${PY_FILE}" --flagfile="${XDS_K8S_CONFIG}" "$@"
index 2a61e69..3a72312 100644 (file)
@@ -53,7 +53,10 @@ class BaselineTest(xds_k8s_testcase.RegularXdsKubernetesTestCase):
         with self.subTest('7_start_test_client'):
             test_client: _XdsTestClient = self.startTestClient(test_server)
 
-        with self.subTest('8_test_server_received_rpcs_from_test_client'):
+        with self.subTest('8_test_client_xds_config_exists'):
+            self.assertXdsConfigExists(test_client)
+
+        with self.subTest('9_test_server_received_rpcs_from_test_client'):
             self.assertSuccessfulRpcs(test_client)
 
 
diff --git a/tools/run_tests/xds_k8s_test_driver/tests/url_map/__init__.py b/tools/run_tests/xds_k8s_test_driver/tests/url_map/__init__.py
new file mode 100644 (file)
index 0000000..712a2e1
--- /dev/null
@@ -0,0 +1,13 @@
+# Copyright 2021 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.
diff --git a/tools/run_tests/xds_k8s_test_driver/tests/url_map/__main__.py b/tools/run_tests/xds_k8s_test_driver/tests/url_map/__main__.py
new file mode 100644 (file)
index 0000000..c89f6ec
--- /dev/null
@@ -0,0 +1,31 @@
+# Copyright 2021 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.
+
+import os
+
+from absl.testing import absltest
+
+from framework import xds_url_map_testcase  # Needed for xDS flags
+
+_TEST_CASE_FOLDER = os.path.dirname(__file__)
+
+
+def load_tests(loader: absltest.TestLoader, unused_tests, unused_pattern):
+    return loader.discover(_TEST_CASE_FOLDER,
+                           pattern='*' +
+                           xds_url_map_testcase.URL_MAP_TESTCASE_FILE_SUFFIX)
+
+
+if __name__ == '__main__':
+    absltest.main()
diff --git a/tools/run_tests/xds_k8s_test_driver/tests/url_map/csds_test.py b/tools/run_tests/xds_k8s_test_driver/tests/url_map/csds_test.py
new file mode 100644 (file)
index 0000000..15d15d1
--- /dev/null
@@ -0,0 +1,70 @@
+# Copyright 2021 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.
+import logging
+from typing import Tuple
+
+from absl import flags
+from absl.testing import absltest
+
+from framework import xds_url_map_testcase
+from framework.test_app import client_app
+
+# Type aliases
+HostRule = xds_url_map_testcase.HostRule
+PathMatcher = xds_url_map_testcase.PathMatcher
+GcpResourceManager = xds_url_map_testcase.GcpResourceManager
+DumpedXdsConfig = xds_url_map_testcase.DumpedXdsConfig
+RpcTypeUnaryCall = xds_url_map_testcase.RpcTypeUnaryCall
+RpcTypeEmptyCall = xds_url_map_testcase.RpcTypeEmptyCall
+XdsTestClient = client_app.XdsTestClient
+
+logger = logging.getLogger(__name__)
+flags.adopt_module_key_flags(xds_url_map_testcase)
+
+_NUM_RPCS = 50
+
+
+class TestBasicCsds(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        # Validate Endpoint Configs
+        self.assertNumEndpoints(xds_config, 1)
+        # Validate Node
+        self.assertEqual(self.test_client.ip,
+                         xds_config['node']['metadata']['INSTANCE_IP'])
+        # Validate Listeners
+        self.assertIsNotNone(xds_config.lds)
+        self.assertEqual(self.hostname(), xds_config.lds['name'])
+        # Validate Route Configs
+        self.assertTrue(xds_config.rds['virtualHosts'])
+        # Validate Clusters
+        self.assertEqual(1, len(xds_config.cds))
+        self.assertEqual('EDS', xds_config.cds[0]['type'])
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(
+            test_client,
+            rpc_types=[RpcTypeUnaryCall, RpcTypeEmptyCall],
+            num_rpcs=_NUM_RPCS)
+        self.assertEqual(_NUM_RPCS, rpc_distribution.num_oks)
+
+
+if __name__ == '__main__':
+    absltest.main()
diff --git a/tools/run_tests/xds_k8s_test_driver/tests/url_map/fault_injection_test.py b/tools/run_tests/xds_k8s_test_driver/tests/url_map/fault_injection_test.py
new file mode 100644 (file)
index 0000000..325fd74
--- /dev/null
@@ -0,0 +1,362 @@
+# Copyright 2021 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.
+import logging
+import time
+from typing import Tuple
+
+from absl import flags
+from absl.testing import absltest
+import grpc
+
+from framework import xds_url_map_testcase
+from framework.rpc import grpc_testing
+from framework.test_app import client_app
+
+# Type aliases
+HostRule = xds_url_map_testcase.HostRule
+PathMatcher = xds_url_map_testcase.PathMatcher
+GcpResourceManager = xds_url_map_testcase.GcpResourceManager
+DumpedXdsConfig = xds_url_map_testcase.DumpedXdsConfig
+RpcTypeUnaryCall = xds_url_map_testcase.RpcTypeUnaryCall
+RpcTypeEmptyCall = xds_url_map_testcase.RpcTypeEmptyCall
+XdsTestClient = client_app.XdsTestClient
+ExpectedResult = xds_url_map_testcase.ExpectedResult
+
+logger = logging.getLogger(__name__)
+flags.adopt_module_key_flags(xds_url_map_testcase)
+
+# The first batch of RPCs don't count towards the result of test case. They are
+# meant to prove the communication between driver and client is fine.
+_NUM_RPCS = 10
+_LENGTH_OF_RPC_SENDING_SEC = 16
+# We are using sleep to synchronize test driver and the client... Even though
+# the client is sending at QPS rate, we can't assert that exactly QPS *
+# SLEEP_DURATION number of RPC is finished. The final completed RPC might be
+# slightly more or less.
+_NON_RANDOM_ERROR_TOLERANCE = 0.01
+# For random generator involved test cases, we want to be more loose about the
+# final result. Otherwise, we will need more test duration (sleep duration) and
+# more accurate communication mechanism. The accurate of random number
+# generation is not the intention of this test.
+_ERROR_TOLERANCE = 0.2
+_DELAY_CASE_APPLICATION_TIMEOUT_SEC = 1
+_BACKLOG_WAIT_TIME_SEC = 20
+
+
+def _build_fault_injection_route_rule(abort_percentage: int = 0,
+                                      delay_percentage: int = 0):
+    return {
+        'priority': 0,
+        'matchRules': [{
+            'fullPathMatch': '/grpc.testing.TestService/UnaryCall'
+        }],
+        'service': GcpResourceManager().default_backend_service(),
+        'routeAction': {
+            'faultInjectionPolicy': {
+                'abort': {
+                    'httpStatus': 401,
+                    'percentage': abort_percentage,
+                },
+                'delay': {
+                    'fixedDelay': {
+                        'seconds': '20'
+                    },
+                    'percentage': delay_percentage,
+                }
+            }
+        },
+    }
+
+
+def _wait_until_backlog_cleared(test_client: XdsTestClient,
+                                timeout: int = _BACKLOG_WAIT_TIME_SEC):
+    """ Wait until the completed RPC is close to started RPC.
+
+    For delay injected test cases, there might be a backlog of RPCs due to slow
+    initialization of the client. E.g., if initialization took 20s and qps is
+    25, then there will be a backlog of 500 RPCs. In normal test cases, this is
+    fine, because RPCs will fail immediately. But for delay injected test cases,
+    the RPC might linger much longer and affect the stability of test results.
+    """
+    logger.info('Waiting for RPC backlog to clear for %d seconds', timeout)
+    deadline = time.time() + timeout
+    while time.time() < deadline:
+        stats = test_client.get_load_balancer_accumulated_stats()
+        ok = True
+        for rpc_type in [RpcTypeUnaryCall, RpcTypeEmptyCall]:
+            started = stats.num_rpcs_started_by_method.get(rpc_type, 0)
+            completed = stats.num_rpcs_succeeded_by_method.get(
+                rpc_type, 0) + stats.num_rpcs_failed_by_method.get(rpc_type, 0)
+            # We consider the backlog is healthy, if the diff between started
+            # RPCs and completed RPCs is less than 1.5 QPS.
+            if abs(started - completed) > xds_url_map_testcase.QPS.value * 1.1:
+                logger.info(
+                    'RPC backlog exist: rpc_type=%s started=%s completed=%s',
+                    rpc_type, started, completed)
+                time.sleep(_DELAY_CASE_APPLICATION_TIMEOUT_SEC)
+                ok = False
+            else:
+                logger.info(
+                    'RPC backlog clear: rpc_type=%s started=%s completed=%s',
+                    rpc_type, started, completed)
+        if ok:
+            # Both backlog of both types of RPCs is clear, success, return.
+            return
+
+    raise RuntimeError('failed to clear RPC backlog in %s seconds', timeout)
+
+
+class TestZeroPercentFaultInjection(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [
+            _build_fault_injection_route_rule(abort_percentage=0,
+                                              delay_percentage=0)
+        ]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 1)
+        filter_config = xds_config.rds['virtualHosts'][0]['routes'][0][
+            'typedPerFilterConfig']['envoy.filters.http.fault']
+        self.assertEqual('20s', filter_config['delay']['fixedDelay'])
+        self.assertEqual(
+            0, filter_config['delay']['percentage'].get('numerator', 0))
+        self.assertEqual('MILLION',
+                         filter_config['delay']['percentage']['denominator'])
+        self.assertEqual(401, filter_config['abort']['httpStatus'])
+        self.assertEqual(
+            0, filter_config['abort']['percentage'].get('numerator', 0))
+        self.assertEqual('MILLION',
+                         filter_config['abort']['percentage']['denominator'])
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeUnaryCall],
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertRpcStatusCode(test_client,
+                                 expected=(ExpectedResult(
+                                     rpc_type=RpcTypeUnaryCall,
+                                     status_code=grpc.StatusCode.OK,
+                                     ratio=1),),
+                                 length=_LENGTH_OF_RPC_SENDING_SEC,
+                                 tolerance=_NON_RANDOM_ERROR_TOLERANCE)
+
+
+class TestNonMatchingFaultInjection(xds_url_map_testcase.XdsUrlMapTestCase):
+    """EMPTY_CALL is not fault injected, so it should succeed."""
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [
+            _build_fault_injection_route_rule(abort_percentage=100,
+                                              delay_percentage=100)
+        ]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 1)
+        # The first route rule for UNARY_CALL is fault injected
+        self.assertEqual(
+            "/grpc.testing.TestService/UnaryCall",
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['path'])
+        filter_config = xds_config.rds['virtualHosts'][0]['routes'][0][
+            'typedPerFilterConfig']['envoy.filters.http.fault']
+        self.assertEqual('20s', filter_config['delay']['fixedDelay'])
+        self.assertEqual(1000000,
+                         filter_config['delay']['percentage']['numerator'])
+        self.assertEqual('MILLION',
+                         filter_config['delay']['percentage']['denominator'])
+        self.assertEqual(401, filter_config['abort']['httpStatus'])
+        self.assertEqual(1000000,
+                         filter_config['abort']['percentage']['numerator'])
+        self.assertEqual('MILLION',
+                         filter_config['abort']['percentage']['denominator'])
+        # The second route rule for all other RPCs is untouched
+        self.assertNotIn(
+            'envoy.filters.http.fault',
+            xds_config.rds['virtualHosts'][0]['routes'][1].get(
+                'typedPerFilterConfig', {}))
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeEmptyCall],
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertRpcStatusCode(test_client,
+                                 expected=(ExpectedResult(
+                                     rpc_type=RpcTypeEmptyCall,
+                                     status_code=grpc.StatusCode.OK,
+                                     ratio=1),),
+                                 length=_LENGTH_OF_RPC_SENDING_SEC,
+                                 tolerance=_NON_RANDOM_ERROR_TOLERANCE)
+
+
+@absltest.skip('20% RPC might pass immediately, reason unknown')
+class TestAlwaysDelay(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [
+            _build_fault_injection_route_rule(abort_percentage=0,
+                                              delay_percentage=100)
+        ]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 1)
+        filter_config = xds_config.rds['virtualHosts'][0]['routes'][0][
+            'typedPerFilterConfig']['envoy.filters.http.fault']
+        self.assertEqual('20s', filter_config['delay']['fixedDelay'])
+        self.assertEqual(1000000,
+                         filter_config['delay']['percentage']['numerator'])
+        self.assertEqual('MILLION',
+                         filter_config['delay']['percentage']['denominator'])
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(
+            test_client,
+            rpc_types=[RpcTypeUnaryCall],
+            num_rpcs=_NUM_RPCS,
+            app_timeout=_DELAY_CASE_APPLICATION_TIMEOUT_SEC)
+        _wait_until_backlog_cleared(test_client)
+        self.assertRpcStatusCode(
+            test_client,
+            expected=(ExpectedResult(
+                rpc_type=RpcTypeUnaryCall,
+                status_code=grpc.StatusCode.DEADLINE_EXCEEDED,
+                ratio=1),),
+            length=_LENGTH_OF_RPC_SENDING_SEC,
+            tolerance=_NON_RANDOM_ERROR_TOLERANCE)
+
+
+class TestAlwaysAbort(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [
+            _build_fault_injection_route_rule(abort_percentage=100,
+                                              delay_percentage=0)
+        ]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 1)
+        filter_config = xds_config.rds['virtualHosts'][0]['routes'][0][
+            'typedPerFilterConfig']['envoy.filters.http.fault']
+        self.assertEqual(401, filter_config['abort']['httpStatus'])
+        self.assertEqual(1000000,
+                         filter_config['abort']['percentage']['numerator'])
+        self.assertEqual('MILLION',
+                         filter_config['abort']['percentage']['denominator'])
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeUnaryCall],
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertRpcStatusCode(
+            test_client,
+            expected=(ExpectedResult(
+                rpc_type=RpcTypeUnaryCall,
+                status_code=grpc.StatusCode.UNAUTHENTICATED,
+                ratio=1),),
+            length=_LENGTH_OF_RPC_SENDING_SEC,
+            tolerance=_NON_RANDOM_ERROR_TOLERANCE)
+
+
+class TestDelayHalf(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [
+            _build_fault_injection_route_rule(abort_percentage=0,
+                                              delay_percentage=50)
+        ]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 1)
+        filter_config = xds_config.rds['virtualHosts'][0]['routes'][0][
+            'typedPerFilterConfig']['envoy.filters.http.fault']
+        self.assertEqual('20s', filter_config['delay']['fixedDelay'])
+        self.assertEqual(500000,
+                         filter_config['delay']['percentage']['numerator'])
+        self.assertEqual('MILLION',
+                         filter_config['delay']['percentage']['denominator'])
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(
+            test_client,
+            rpc_types=[RpcTypeUnaryCall],
+            num_rpcs=_NUM_RPCS,
+            app_timeout=_DELAY_CASE_APPLICATION_TIMEOUT_SEC)
+        _wait_until_backlog_cleared(test_client)
+        self.assertRpcStatusCode(
+            test_client,
+            expected=(ExpectedResult(
+                rpc_type=RpcTypeUnaryCall,
+                status_code=grpc.StatusCode.DEADLINE_EXCEEDED,
+                ratio=0.5),),
+            length=_LENGTH_OF_RPC_SENDING_SEC,
+            tolerance=_ERROR_TOLERANCE)
+
+
+class TestAbortHalf(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [
+            _build_fault_injection_route_rule(abort_percentage=50,
+                                              delay_percentage=0)
+        ]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 1)
+        filter_config = xds_config.rds['virtualHosts'][0]['routes'][0][
+            'typedPerFilterConfig']['envoy.filters.http.fault']
+        self.assertEqual(401, filter_config['abort']['httpStatus'])
+        self.assertEqual(500000,
+                         filter_config['abort']['percentage']['numerator'])
+        self.assertEqual('MILLION',
+                         filter_config['abort']['percentage']['denominator'])
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeUnaryCall],
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertRpcStatusCode(
+            test_client,
+            expected=(ExpectedResult(
+                rpc_type=RpcTypeUnaryCall,
+                status_code=grpc.StatusCode.UNAUTHENTICATED,
+                ratio=0.5),),
+            length=_LENGTH_OF_RPC_SENDING_SEC,
+            tolerance=_ERROR_TOLERANCE)
+
+
+if __name__ == '__main__':
+    absltest.main()
diff --git a/tools/run_tests/xds_k8s_test_driver/tests/url_map/header_matching_test.py b/tools/run_tests/xds_k8s_test_driver/tests/url_map/header_matching_test.py
new file mode 100644 (file)
index 0000000..9eb3439
--- /dev/null
@@ -0,0 +1,357 @@
+# Copyright 2021 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.
+import logging
+from typing import Tuple
+
+from absl import flags
+from absl.testing import absltest
+
+from framework import xds_url_map_testcase
+from framework.test_app import client_app
+
+# Type aliases
+HostRule = xds_url_map_testcase.HostRule
+PathMatcher = xds_url_map_testcase.PathMatcher
+GcpResourceManager = xds_url_map_testcase.GcpResourceManager
+DumpedXdsConfig = xds_url_map_testcase.DumpedXdsConfig
+RpcTypeUnaryCall = xds_url_map_testcase.RpcTypeUnaryCall
+RpcTypeEmptyCall = xds_url_map_testcase.RpcTypeEmptyCall
+XdsTestClient = client_app.XdsTestClient
+
+logger = logging.getLogger(__name__)
+flags.adopt_module_key_flags(xds_url_map_testcase)
+
+_NUM_RPCS = 150
+_TEST_METADATA_KEY = 'xds_md'
+_TEST_METADATA_VALUE_UNARY = 'unary_yranu'
+_TEST_METADATA_VALUE_EMPTY = 'empty_ytpme'
+_TEST_METADATA_NUMERIC_KEY = 'xds_md_numeric'
+_TEST_METADATA_NUMERIC_VALUE = '159'
+
+_TEST_METADATA = (
+    (RpcTypeUnaryCall, _TEST_METADATA_KEY, _TEST_METADATA_VALUE_UNARY),
+    (RpcTypeEmptyCall, _TEST_METADATA_KEY, _TEST_METADATA_VALUE_EMPTY),
+    (RpcTypeUnaryCall, _TEST_METADATA_NUMERIC_KEY,
+     _TEST_METADATA_NUMERIC_VALUE),
+)
+
+
+class TestExactMatch(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [{
+            'priority': 0,
+            # Header ExactMatch -> alternate_backend_service.
+            # EmptyCall is sent with the metadata.
+            'matchRules': [{
+                'prefixMatch':
+                    '/',
+                'headerMatches': [{
+                    'headerName': _TEST_METADATA_KEY,
+                    'exactMatch': _TEST_METADATA_VALUE_EMPTY
+                }]
+            }],
+            'service': GcpResourceManager().alternative_backend_service()
+        }]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 2)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['name'], _TEST_METADATA_KEY)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['exactMatch'], _TEST_METADATA_VALUE_EMPTY)
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeEmptyCall],
+                                                   metadata=_TEST_METADATA,
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertEqual(
+            _NUM_RPCS,
+            rpc_distribution.empty_call_alternative_service_rpc_count)
+
+
+@absltest.skip('the xDS config is good, but distribution is wrong.')
+class TestPrefixMatch(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [{
+            'priority': 0,
+            # Header PrefixMatch -> alternate_backend_service.
+            # UnaryCall is sent with the metadata.
+            'matchRules': [{
+                'prefixMatch':
+                    '/',
+                'headerMatches': [{
+                    'headerName': _TEST_METADATA_KEY,
+                    'prefixMatch': _TEST_METADATA_VALUE_UNARY[:2]
+                }]
+            }],
+            'service': GcpResourceManager().alternative_backend_service()
+        }]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 2)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['name'], _TEST_METADATA_KEY)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['prefixMatch'], _TEST_METADATA_VALUE_UNARY[:2])
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeUnaryCall],
+                                                   metadata=_TEST_METADATA,
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertEqual(
+            _NUM_RPCS,
+            rpc_distribution.unary_call_alternative_service_rpc_count)
+
+
+class TestSuffixMatch(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [{
+            'priority': 0,
+            # Header SuffixMatch -> alternate_backend_service.
+            # EmptyCall is sent with the metadata.
+            'matchRules': [{
+                'prefixMatch':
+                    '/',
+                'headerMatches': [{
+                    'headerName': _TEST_METADATA_KEY,
+                    'suffixMatch': _TEST_METADATA_VALUE_EMPTY[-2:]
+                }]
+            }],
+            'service': GcpResourceManager().alternative_backend_service()
+        }]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 2)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['name'], _TEST_METADATA_KEY)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['suffixMatch'], _TEST_METADATA_VALUE_EMPTY[-2:])
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeEmptyCall],
+                                                   metadata=_TEST_METADATA,
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertEqual(
+            _NUM_RPCS,
+            rpc_distribution.empty_call_alternative_service_rpc_count)
+
+
+class TestPresentMatch(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [{
+            'priority': 0,
+            # Header 'xds_md_numeric' present -> alternate_backend_service.
+            # UnaryCall is sent with the metadata, so will be sent to alternative.
+            'matchRules': [{
+                'prefixMatch':
+                    '/',
+                'headerMatches': [{
+                    'headerName': _TEST_METADATA_NUMERIC_KEY,
+                    'presentMatch': True
+                }]
+            }],
+            'service': GcpResourceManager().alternative_backend_service()
+        }]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 2)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['name'], _TEST_METADATA_NUMERIC_KEY)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['presentMatch'], True)
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeUnaryCall],
+                                                   metadata=_TEST_METADATA,
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertEqual(
+            _NUM_RPCS,
+            rpc_distribution.unary_call_alternative_service_rpc_count)
+
+
+class TestInvertMatch(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [{
+            'priority': 0,
+            # Header invert ExactMatch -> alternate_backend_service.
+            # UnaryCall is sent with the metadata, so will be sent to
+            # default. EmptyCall will be sent to alternative.
+            'matchRules': [{
+                'prefixMatch':
+                    '/',
+                'headerMatches': [{
+                    'headerName': _TEST_METADATA_KEY,
+                    'exactMatch': _TEST_METADATA_VALUE_UNARY,
+                    'invertMatch': True
+                }]
+            }],
+            'service': GcpResourceManager().alternative_backend_service()
+        }]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 2)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['name'], _TEST_METADATA_KEY)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['invertMatch'], True)
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(
+            test_client,
+            rpc_types=[RpcTypeUnaryCall, RpcTypeEmptyCall],
+            metadata=_TEST_METADATA,
+            num_rpcs=_NUM_RPCS)
+        self.assertEqual(_NUM_RPCS, rpc_distribution.num_oks)
+        self.assertEqual(
+            0, rpc_distribution.unary_call_alternative_service_rpc_count)
+        self.assertEqual(0,
+                         rpc_distribution.empty_call_default_service_rpc_count)
+
+
+class TestRangeMatch(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [{
+            'priority': 0,
+            # Header 'xds_md_numeric' range [100,200] -> alternate_backend_service.
+            # UnaryCall is sent with the metadata in range.
+            'matchRules': [{
+                'prefixMatch':
+                    '/',
+                'headerMatches': [{
+                    'headerName': _TEST_METADATA_NUMERIC_KEY,
+                    'rangeMatch': {
+                        'rangeStart': '100',
+                        'rangeEnd': '200'
+                    }
+                }]
+            }],
+            'service': GcpResourceManager().alternative_backend_service()
+        }]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 2)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['name'], _TEST_METADATA_NUMERIC_KEY)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['rangeMatch']['start'], '100')
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['rangeMatch']['end'], '200')
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(
+            test_client,
+            rpc_types=[RpcTypeUnaryCall, RpcTypeEmptyCall],
+            metadata=_TEST_METADATA,
+            num_rpcs=_NUM_RPCS)
+        self.assertEqual(_NUM_RPCS, rpc_distribution.num_oks)
+        self.assertEqual(0,
+                         rpc_distribution.unary_call_default_service_rpc_count)
+        self.assertEqual(
+            0, rpc_distribution.empty_call_alternative_service_rpc_count)
+
+
+class TestRegexMatch(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [{
+            'priority': 0,
+            # Header RegexMatch -> alternate_backend_service.
+            # EmptyCall is sent with the metadata.
+            'matchRules': [{
+                'prefixMatch':
+                    '/',
+                'headerMatches': [{
+                    'headerName':
+                        _TEST_METADATA_KEY,
+                    'regexMatch':
+                        "^%s.*%s$" % (_TEST_METADATA_VALUE_EMPTY[:2],
+                                      _TEST_METADATA_VALUE_EMPTY[-2:])
+                }]
+            }],
+            'service': GcpResourceManager().alternative_backend_service()
+        }],
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 2)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['name'], _TEST_METADATA_KEY)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['headers']
+            [0]['safeRegexMatch']['regex'], "^%s.*%s$" %
+            (_TEST_METADATA_VALUE_EMPTY[:2], _TEST_METADATA_VALUE_EMPTY[-2:]))
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeEmptyCall],
+                                                   metadata=_TEST_METADATA,
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertEqual(
+            _NUM_RPCS,
+            rpc_distribution.empty_call_alternative_service_rpc_count)
+
+
+if __name__ == '__main__':
+    absltest.main()
diff --git a/tools/run_tests/xds_k8s_test_driver/tests/url_map/path_matching_test.py b/tools/run_tests/xds_k8s_test_driver/tests/url_map/path_matching_test.py
new file mode 100644 (file)
index 0000000..9b64b32
--- /dev/null
@@ -0,0 +1,220 @@
+# Copyright 2021 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.
+import logging
+from typing import Tuple
+
+from absl import flags
+from absl.testing import absltest
+
+from framework import xds_url_map_testcase
+from framework.test_app import client_app
+
+# Type aliases
+HostRule = xds_url_map_testcase.HostRule
+PathMatcher = xds_url_map_testcase.PathMatcher
+GcpResourceManager = xds_url_map_testcase.GcpResourceManager
+DumpedXdsConfig = xds_url_map_testcase.DumpedXdsConfig
+RpcTypeUnaryCall = xds_url_map_testcase.RpcTypeUnaryCall
+RpcTypeEmptyCall = xds_url_map_testcase.RpcTypeEmptyCall
+XdsTestClient = client_app.XdsTestClient
+
+logger = logging.getLogger(__name__)
+flags.adopt_module_key_flags(xds_url_map_testcase)
+
+_NUM_RPCS = 150
+
+
+class TestFullPathMatchEmptyCall(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [{
+            'priority': 0,
+            # FullPath EmptyCall -> alternate_backend_service.
+            'matchRules': [{
+                'fullPathMatch': '/grpc.testing.TestService/EmptyCall'
+            }],
+            'service': GcpResourceManager().alternative_backend_service()
+        }]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 2)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['path'],
+            "/grpc.testing.TestService/EmptyCall")
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeEmptyCall],
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertEqual(
+            _NUM_RPCS,
+            rpc_distribution.empty_call_alternative_service_rpc_count)
+
+
+class TestFullPathMatchUnaryCall(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [{
+            'priority': 0,
+            # FullPath EmptyCall -> alternate_backend_service.
+            'matchRules': [{
+                'fullPathMatch': '/grpc.testing.TestService/UnaryCall'
+            }],
+            'service': GcpResourceManager().alternative_backend_service()
+        }]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 2)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['path'],
+            "/grpc.testing.TestService/UnaryCall")
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeUnaryCall],
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertEqual(
+            _NUM_RPCS,
+            rpc_distribution.unary_call_alternative_service_rpc_count)
+
+
+class TestTwoRoutesAndPrefixMatch(xds_url_map_testcase.XdsUrlMapTestCase):
+    """This test case is similar to the one above (but with route services
+    swapped). This test has two routes (full_path and the default) to match
+    EmptyCall, and both routes set alternative_backend_service as the action.
+    This forces the client to handle duplicate Clusters in the RDS response."""
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [
+            {
+                'priority': 0,
+                # Prefix UnaryCall -> default_backend_service.
+                'matchRules': [{
+                    'prefixMatch': '/grpc.testing.TestService/Unary'
+                }],
+                'service': GcpResourceManager().default_backend_service()
+            },
+            {
+                'priority': 1,
+                # FullPath EmptyCall -> alternate_backend_service.
+                'matchRules': [{
+                    'fullPathMatch': '/grpc.testing.TestService/EmptyCall'
+                }],
+                'service': GcpResourceManager().alternative_backend_service()
+            }
+        ]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 2)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['prefix'],
+            "/grpc.testing.TestService/Unary")
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][1]['match']['path'],
+            "/grpc.testing.TestService/EmptyCall")
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(
+            test_client,
+            rpc_types=[RpcTypeUnaryCall, RpcTypeEmptyCall],
+            num_rpcs=_NUM_RPCS)
+        self.assertEqual(0, rpc_distribution.num_failures)
+        self.assertEqual(
+            0, rpc_distribution.unary_call_alternative_service_rpc_count)
+        self.assertEqual(0,
+                         rpc_distribution.empty_call_default_service_rpc_count)
+
+
+class TestRegexMatch(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [{
+            'priority': 0,
+            # Regex UnaryCall -> alternate_backend_service.
+            'matchRules': [{
+                'regexMatch':
+                    '^\/.*\/UnaryCall$'  # Unary methods with any services.
+            }],
+            'service': GcpResourceManager().alternative_backend_service()
+        }]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 2)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['safeRegex']
+            ['regex'], '^\/.*\/UnaryCall$')
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeUnaryCall],
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertEqual(
+            _NUM_RPCS,
+            rpc_distribution.unary_call_alternative_service_rpc_count)
+
+
+class TestCaseInsensitiveMatch(xds_url_map_testcase.XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher["routeRules"] = [{
+            'priority': 0,
+            # ignoreCase EmptyCall -> alternate_backend_service.
+            'matchRules': [{
+                # Case insensitive matching.
+                'fullPathMatch': '/gRpC.tEsTinG.tEstseRvice/empTycaLl',
+                'ignoreCase': True,
+            }],
+            'service': GcpResourceManager().alternative_backend_service()
+        }]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 2)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']['path'],
+            '/gRpC.tEsTinG.tEstseRvice/empTycaLl')
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['match']
+            ['caseSensitive'], False)
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(test_client,
+                                                   rpc_types=[RpcTypeEmptyCall],
+                                                   num_rpcs=_NUM_RPCS)
+        self.assertEqual(
+            _NUM_RPCS,
+            rpc_distribution.empty_call_alternative_service_rpc_count)
+
+
+if __name__ == '__main__':
+    absltest.main()
diff --git a/tools/run_tests/xds_k8s_test_driver/tests/url_map/timeout_test.py b/tools/run_tests/xds_k8s_test_driver/tests/url_map/timeout_test.py
new file mode 100644 (file)
index 0000000..4f639bd
--- /dev/null
@@ -0,0 +1,159 @@
+# Copyright 2021 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.
+import logging
+import time
+from typing import Tuple
+import unittest
+
+from absl import flags
+from absl.testing import absltest
+import grpc
+
+from framework import xds_k8s_flags
+from framework import xds_url_map_testcase
+from framework.test_app import client_app
+
+# Type aliases
+HostRule = xds_url_map_testcase.HostRule
+PathMatcher = xds_url_map_testcase.PathMatcher
+GcpResourceManager = xds_url_map_testcase.GcpResourceManager
+DumpedXdsConfig = xds_url_map_testcase.DumpedXdsConfig
+RpcTypeUnaryCall = xds_url_map_testcase.RpcTypeUnaryCall
+RpcTypeEmptyCall = xds_url_map_testcase.RpcTypeEmptyCall
+ExpectedResult = xds_url_map_testcase.ExpectedResult
+XdsTestClient = client_app.XdsTestClient
+XdsUrlMapTestCase = xds_url_map_testcase.XdsUrlMapTestCase
+
+logger = logging.getLogger(__name__)
+flags.adopt_module_key_flags(xds_url_map_testcase)
+
+# The first batch of RPCs don't count towards the result of test case. They are
+# meant to prove the communication between driver and client is fine.
+_NUM_RPCS = 25
+_LENGTH_OF_RPC_SENDING_SEC = 10
+_ERROR_TOLERANCE = 0.1
+
+
+class _BaseXdsTimeOutTestCase(XdsUrlMapTestCase):
+
+    @staticmethod
+    def url_map_change(
+            host_rule: HostRule,
+            path_matcher: PathMatcher) -> Tuple[HostRule, PathMatcher]:
+        path_matcher['routeRules'] = [{
+            'priority': 0,
+            'matchRules': [{
+                'fullPathMatch': '/grpc.testing.TestService/UnaryCall'
+            }],
+            'service': GcpResourceManager().default_backend_service(),
+            'routeAction': {
+                'maxStreamDuration': {
+                    'seconds': 3,
+                },
+            },
+        }]
+        return host_rule, path_matcher
+
+    def xds_config_validate(self, xds_config: DumpedXdsConfig):
+        self.assertNumEndpoints(xds_config, 1)
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['route']
+            ['maxStreamDuration']['maxStreamDuration'], '3s')
+        self.assertEqual(
+            xds_config.rds['virtualHosts'][0]['routes'][0]['route']
+            ['maxStreamDuration']['grpcTimeoutHeaderMax'], '3s')
+
+    def rpc_distribution_validate(self, unused_test_client):
+        raise NotImplementedError()
+
+
+# TODO(lidiz) either add support for rpc-behavior to other languages, or we
+# should always use Java server as backend.
+@absltest.skipUnless('java-server' in xds_k8s_flags.SERVER_IMAGE.value,
+                     'Only Java server supports the rpc-behavior metadata.')
+class TestTimeoutInRouteRule(_BaseXdsTimeOutTestCase):
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(
+            test_client,
+            rpc_types=[RpcTypeUnaryCall, RpcTypeEmptyCall],
+            # UnaryCall and EmptyCall both sleep-4.
+            # UnaryCall timeouts, EmptyCall succeeds.
+            metadata=(
+                (RpcTypeUnaryCall, 'rpc-behavior', 'sleep-4'),
+                (RpcTypeEmptyCall, 'rpc-behavior', 'sleep-4'),
+            ),
+            num_rpcs=_NUM_RPCS)
+        self.assertRpcStatusCode(
+            test_client,
+            expected=(
+                ExpectedResult(rpc_type=RpcTypeUnaryCall,
+                               status_code=grpc.StatusCode.DEADLINE_EXCEEDED),
+                ExpectedResult(rpc_type=RpcTypeEmptyCall,
+                               status_code=grpc.StatusCode.OK),
+            ),
+            length=_LENGTH_OF_RPC_SENDING_SEC,
+            tolerance=_ERROR_TOLERANCE)
+
+
+@absltest.skipUnless('java-server' in xds_k8s_flags.SERVER_IMAGE.value,
+                     'Only Java server supports the rpc-behavior metadata.')
+class TestTimeoutInApplication(_BaseXdsTimeOutTestCase):
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(
+            test_client,
+            rpc_types=[RpcTypeUnaryCall],
+            # UnaryCall only with sleep-2; timeout=1s; calls timeout.
+            metadata=((RpcTypeUnaryCall, 'rpc-behavior', 'sleep-2'),),
+            app_timeout=1,
+            num_rpcs=_NUM_RPCS)
+        self.assertRpcStatusCode(
+            test_client,
+            expected=(ExpectedResult(
+                rpc_type=RpcTypeUnaryCall,
+                status_code=grpc.StatusCode.DEADLINE_EXCEEDED),),
+            length=_LENGTH_OF_RPC_SENDING_SEC,
+            tolerance=_ERROR_TOLERANCE)
+
+
+class TestTimeoutNotExceeded(_BaseXdsTimeOutTestCase):
+
+    def rpc_distribution_validate(self, test_client: XdsTestClient):
+        rpc_distribution = self.configure_and_send(
+            test_client,
+            # UnaryCall only with no sleep; calls succeed.
+            rpc_types=[RpcTypeUnaryCall],
+            num_rpcs=_NUM_RPCS)
+        self.assertRpcStatusCode(test_client,
+                                 expected=(ExpectedResult(
+                                     rpc_type=RpcTypeUnaryCall,
+                                     status_code=grpc.StatusCode.OK),),
+                                 length=_LENGTH_OF_RPC_SENDING_SEC,
+                                 tolerance=_ERROR_TOLERANCE)
+
+
+def load_tests(loader: absltest.TestLoader, unused_tests, unused_pattern):
+    suite = unittest.TestSuite()
+    test_cases = [
+        TestTimeoutInRouteRule, TestTimeoutInApplication, TestTimeoutNotExceeded
+    ]
+    for test_class in test_cases:
+        tests = loader.loadTestsFromTestCase(test_class)
+        suite.addTests(tests)
+    return suite
+
+
+if __name__ == '__main__':
+    absltest.main()