name: Report a bug
about: Create a report to help us improve
labels: kind/bug, priority/P2
-assignees: nicolasnoble
+assignees: yashykt
---
name: Request a cleanup
about: Suggest a cleanup in our repository
labels: kind/internal cleanup, priority/P2
-assignees: nicolasnoble
+assignees: yashykt
---
name: Request a feature
about: Suggest an idea for this project
labels: kind/enhancement, priority/P2
-assignees: nicolasnoble
+assignees: yashykt
---
name: Ask a question
about: Ask a question
labels: kind/question, priority/P3
-assignees: nicolasnoble
+assignees: yashykt
---
-->
-@nicolasnoble
+@yashykt
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",
]
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",
"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",
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",
language = "c++",
public_hdrs = GRPC_PUBLIC_HDRS,
standalone = True,
+ visibility = ["@grpc:public"],
deps = [
"grpc_common",
"grpc_lb_policy_grpclb",
],
},
standalone = True,
+ visibility = [
+ "@grpc:alt_grpc_legacy",
+ "@grpc:public",
+ ],
deps = [
"grpc_common",
"grpc_lb_policy_grpclb_secure",
"absl/synchronization",
"protobuf_headers",
],
+ visibility = ["@grpc:public"],
)
grpc_cc_library(
],
},
standalone = True,
+ visibility = [
+ "@grpc:alt_grpc++_legacy",
+ "@grpc:public",
+ ],
deps = [
"grpc++_internals",
],
],
language = "c++",
standalone = True,
+ visibility = ["@grpc:public"],
deps = [
"gpr",
"grpc++_base_unsecure",
"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",
"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/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",
"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",
external_deps = [
"madler_zlib",
"absl/container:inlined_vector",
+ "absl/functional:bind_front",
"absl/status",
"absl/status:statusor",
"absl/strings",
"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",
"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",
hdrs = [
"src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.h",
],
+ external_deps = [
+ "absl/strings",
+ "xxhash",
+ ],
language = "c++",
deps = [
"grpc_base",
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",
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",
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",
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",
],
language = "c++",
public_hdrs = GRPC_SECURE_PUBLIC_HDRS,
+ visibility = ["@grpc:public"],
deps = [
"alts_util",
"grpc_base",
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",
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",
"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",
"opencensus-context",
],
language = "c++",
+ visibility = ["@grpc:grpc_opencensus_plugin"],
deps = [
":census",
":grpc++",
"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",
"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",
"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",
"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",
"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",
"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",
"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",
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/")
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)
--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)
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)
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)
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)
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)
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)
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
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
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
${_gRPC_ALLTARGETS_LIBRARIES}
absl::flat_hash_map
absl::inlined_vector
+ absl::bind_front
absl::statusor
gpr
address_sorting
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
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
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
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
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}
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
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
)
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
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
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
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
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
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
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
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
-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}
-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}
-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}
-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}
-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}
-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}
-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}
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
"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")
"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")
- [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
## 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
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)
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)
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
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
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 \
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 \
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
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
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 \
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 \
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
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
# 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)
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)
include etc/roots.pem
include Makefile
include LICENSE
+include _metadata.py
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")
),
.unsafeFlags(["-Wno-module-import-in-extern-c"]),
]
),
+ .testTarget(
+ name: "build-test",
+ dependencies: [
+ "gRPC-cpp",
+ ],
+ path: "test/spm_build"
+ ),
],
cLanguageStandard: .gnu11,
cxxLanguageStandard: .cxx11
# 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
-# 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"""
+# 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")
+# 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
+# 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
outputs = out_files,
executable = ctx.executable._protoc,
arguments = arguments,
+ use_default_shell_env = True,
)
return struct(files = depset(out_files))
+# 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",
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 = [],
use_cfstream = False,
tags = [],
linkstatic = False):
+ visibility = _update_visibility(visibility)
copts = []
if use_cfstream:
copts = if_mac(["-DGRPC_CFSTREAM"])
+# 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")
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",
],
)
+# 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")
+# 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")
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():
+# 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"]
+# 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",
+# 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")
+# 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")
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
deps:
- absl/container:flat_hash_map
- absl/container:inlined_vector
+ - absl/functional:bind_front
- absl/status:statusor
- gpr
- address_sorting
- 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
- 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
- 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
- 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
- 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
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
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
- 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
- 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++
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
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
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
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
# 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
'#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
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/gpr/alloc.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/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 \
, $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)
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)
"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\\gpr\\alloc.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\\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 " +
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");
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
- 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)
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 | |
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
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
`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`.
)
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"],
)
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"],
# 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}
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 $@
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) $<
$(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.
--- /dev/null
+/*
+ *
+ * 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;
+}
--- /dev/null
+/*
+ *
+ * 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;
+}
"//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",
+ ],
+)
--- /dev/null
+/*
+ *
+ * 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(¬e);
+ 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;
+}
--- /dev/null
+/*
+ *
+ * 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(¬e_);
+ }
+ 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(¬e_);
+ }
+ 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;
+}
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'
'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',
'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',
'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',
'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',
'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',
'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',
'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',
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'
'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',
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
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
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'
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'
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'
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
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
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 )
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 )
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 )
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 )
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 )
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 )
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 )
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 )
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 )
'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',
--- /dev/null
+// 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
#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 {
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
/// 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.
/// 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.
///
/// 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;
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
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
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>
// 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:
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);
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
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(
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
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
#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"
#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
/** 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. */
/** 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
/* 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;
#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 */
/// 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)
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,
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));
}
/// 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);
}
/// \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_;
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.
grpc::Server* server_{nullptr};
};
-#ifndef GRPC_CALLBACK_API_NONEXPERIMENTAL
-} // namespace experimental
-#endif
} // namespace grpc
#endif // GRPCPP_IMPL_CODEGEN_ASYNC_GENERIC_SERVICE_H
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;
// 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) {
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) {
/// 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) {
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) {
/// 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
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);
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);
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);
start_tag_.Set(
call_.call(),
[this](bool ok) {
- reactor_->OnReadInitialMetadataDone(ok);
+ reactor_->OnReadInitialMetadataDone(
+ ok && !reactor_->InternalTrailersOnly(call_.call()));
MaybeFinish();
},
&start_ops_, /*can_inline=*/false);
namespace testing {
class InteropClientContextInspector;
+class ClientContextTestPeer;
} // namespace testing
namespace internal {
/// \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,
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;
/// \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}),
#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.
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
};
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_);
: 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)))
::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 {
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:
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),
::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,
read_tag_.Set(
call_.call(),
[this, reactor](bool ok) {
+ if (GPR_UNLIKELY(!ok)) {
+ ctx_->MaybeMarkCancelledOnRead();
+ }
reactor->OnReadDone(ok);
this->MaybeDone(/*inlineable_ondone=*/true);
},
read_tag_.Set(
call_.call(),
[this, reactor](bool ok) {
+ if (GPR_UNLIKELY(!ok)) {
+ ctx_->MaybeMarkCancelledOnRead();
+ }
reactor->OnReadDone(ok);
this->MaybeDone(/*inlineable_ondone=*/true);
},
} // 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
/// 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
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&);
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();
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 {
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_;
};
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
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;
/// 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.
///
}
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,
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);
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);
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_;
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_);
/// 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_);
--- /dev/null
+// 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
/// 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.
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;
/// 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_;
// 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_;
#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>
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:
/// \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
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
};
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
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;
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
/// 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.
--- /dev/null
+/*
+ *
+ * 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
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
<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>
</stability>
<license>Apache 2.0</license>
<notes>
-- gRPC Core 1.38.1 update
+- gRPC Core 1.39.0 update
</notes>
<contents>
<dir baseinstalldir="/" name="/">
<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" />
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
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'
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 = ''
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(
-// 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.
//
#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)
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",
// 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(
"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");
}
}
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();
}
"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");
}
}
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");
printer->Print("};\n");
printer->Print(
- "class experimental_async_interface* experimental_async() override { "
+ "class async* async() override { "
"return &async_stub_; }\n");
}
" 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(
" 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(
"}\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(
"}\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");
}
}
(*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);
(*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"
"{}\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);
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);
}
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) {
"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");
"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, "
"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("
"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("
"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("
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) {
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, "
out->Outdent();
out->Print("}\n\n");
+ GenerateGeneratedCodeAttribute(out);
out->Print(
"static class __Helper_MessageCache<T>\n"
"{\n");
out->Outdent();
out->Print("}\n\n");
+ GenerateGeneratedCodeAttribute(out);
out->Print(
"static T __Helper_DeserializeMessage<T>("
"grpc::DeserializationContext context, "
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, "
}
void GenerateStaticMethodField(Printer* out, const MethodDescriptor* method) {
+ GenerateGeneratedCodeAttribute(out);
out->Print(
"static readonly grpc::Method<$request$, $response$> $fieldname$ = new "
"grpc::Method<$request$, $response$>(\n",
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$, "
"/// <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");
"/// <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));
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");
"/// <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",
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 "
// 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",
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 "
// overload taking CallOptions as a param
GenerateDocCommentClientMethod(out, method, false, true);
+ GenerateGeneratedCodeAttribute(out);
out->Print(
"public virtual $returntype$ "
"$methodname$($request_maybe$grpc::CallOptions "
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",
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",
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$ "
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));
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;
}
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;
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;
// 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(
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;
}
}
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;
}
}
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(
// 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;
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_;
};
#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"
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,
*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,
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 =
#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
#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
}
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();
// 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();
#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"
};
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);
}
};
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.
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;
};
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_;
};
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));
}
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();
}
//
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>(
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(
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,
.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)));
}
}
}
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);
}
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);
}
}
}
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;
}
namespace {
-const char kDefaultPort[] = "https";
-
class AresDnsResolver : public Resolver {
public:
explicit AresDnsResolver(ResolverArgs args);
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,
--- /dev/null
+// 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) */
--- /dev/null
+// 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) */
namespace {
-const char kDefaultPort[] = "https";
-
class NativeDnsResolver : public Resolver {
public:
explicit NativeDnsResolver(ResolverArgs args);
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();
}
#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"
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));
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{
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.
}
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) {
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(
static void SetPollent(grpc_call_element* elem, grpc_polling_entity* pollent);
private:
- class Canceller;
class CallStackDestructionBarrier;
// Pending batches stored in call data.
};
// 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.
// 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
// 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);
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.
// 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.
// 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
// 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);
// 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.
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);
// 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_;
// 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.,
// 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;
// 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
// 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;
};
//
-// 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),
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() {
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
// 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());
}
!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();
!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 */);
!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 */);
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) {
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.
// 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;
}
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);
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);
}
}
// 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);
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;
// 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,
}
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_);
}
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::
}
}
-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(
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
!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
}
// 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);
}
//
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.
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
!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
}
// 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);
}
//
// 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.
}
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(
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();
}
}
}
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");
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;
&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));
}
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);
}
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;
}
// 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");
- }
}
//
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_];
++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 =
&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
//
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;
}
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_);
// 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.
}
// 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; "
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);
}
// 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();
}
}
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");
}
//
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_);
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];
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)) {
}
}
-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)) {
// 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");
}
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");
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"));
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"));
"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"));
}
}
}
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) {
}
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);
}
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
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_; }
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;
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 {
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();
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);
#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;
}
#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);
}
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");
// 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);
}
}
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 +
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);
}
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);
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;
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) {
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));
}
.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
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,
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");
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;
}
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
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;
}
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;
}
// 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
} 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(
// 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
}
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,
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));
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);
+++ /dev/null
-/* 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"
-
+++ /dev/null
-/* 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_ */
+++ /dev/null
-/* 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)
-};
+++ /dev/null
-/* 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_ */
//
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",
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, ", "), "}");
} // 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
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()) {
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);
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) {
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) {
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) &&
} 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*
// 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()));
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;
}
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());
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;
// 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;
}
upb::SymbolTable symtab_;
const std::string build_version_;
const std::string user_agent_name_;
+ const std::string user_agent_version_;
};
} // namespace grpc_core
#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"
}
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
#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
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 */
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.
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
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;
};
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(
// 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
--- /dev/null
+// 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
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);
#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"
--- /dev/null
+// 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
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;
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;
}
#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,
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
#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) {
}
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;
}
}
- 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;
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
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);
}
}
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));
}
// 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;
// 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;
{
grpc_timer_manager_shutdown();
grpc_iomgr_platform_flush();
- grpc_core::Executor::ShutdownAll();
gpr_mu_lock(&g_mu);
g_shutdown = 1;
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 */
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();
}
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();
}
static void iomgr_platform_shutdown(void) {
grpc_event_engine_shutdown();
grpc_wakeup_fd_global_destroy();
+ MaybeShutdownTcpPosix();
}
static void iomgr_platform_shutdown_background_closure(void) {
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);
// 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(
#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;
#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>
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);
uv_timer_stop(&g_handle->poll_timer);
}
}
+ return GRPC_ERROR_NONE;
}
static void kick() {
#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);
* 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
#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
* 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) {
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,
#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"
#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"
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,
#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;
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);
}
/* 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) {
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;
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.
#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"
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;
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(
#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);
"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);
}
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);
}
}
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
// 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) {
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;
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 */
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 */
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*>(
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",
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)),
#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"
#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 {
#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 {
std::string matching_policy_name;
};
- virtual ~AuthorizationEngine() = default;
virtual Decision Evaluate(const EvaluateArgs& args) const = 0;
};
--- /dev/null
+// 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
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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;
+}
#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 {
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,
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,
#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
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));
}
}
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 {
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 "";
#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"
// 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)
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:
// 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;
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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
#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;
}
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 {
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.
// 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
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
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:
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:
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;
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 "";
}
//
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:
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:
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;
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 "";
}
enum class RuleType {
kAnd,
kOr,
+ kNot,
kAny,
kHeader,
kPath,
};
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;
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,
};
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;
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 {
#include <string.h>
+#include "absl/strings/match.h"
+
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
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;
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
#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
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
#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"
#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:"
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)
}
}
}
- 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.");
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)) {
} 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);
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;
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;
&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;
}
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);
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:
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;
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);
};
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() {
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
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);
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;
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();
}
}
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();
}
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);
}
}
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);
}
// 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()) {
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;
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 */
}
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};
}
void grpc_init(void) {
- int i;
gpr_once_init(&g_basic_init, do_basic_init);
grpc_core::MutexLock lock(g_init_mu);
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();
}
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();
* 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);
#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";
// 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 =
// 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;
// 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;
" 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)));
}
namespace grpc_core {
void FaultInjectionFilterInit(void);
void FaultInjectionFilterShutdown(void);
+void GrpcLbPolicyRingHashInit(void);
+void GrpcLbPolicyRingHashShutdown(void);
} // namespace grpc_core
#ifndef GRPC_NO_XDS
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,
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);
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,
#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.
# 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.
}
namespace {
-class ShutdownCallback : public grpc_experimental_completion_queue_functor {
+class ShutdownCallback : public grpc_completion_queue_functor {
public:
ShutdownCallback() {
functor_run = &ShutdownCallback::Run;
// 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;
#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 {
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
}
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);
}
*/
#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 {
#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>
}
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);
}
}
// 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);
}
},
#include <grpcpp/grpcpp.h>
namespace grpc {
-std::string Version() { return "1.38.1"; }
+std::string Version() { return "1.39.0"; }
} // namespace grpc
--- /dev/null
+// 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
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>
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));
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;
}
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)) {
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(
}
namespace {
-class ShutdownCallback : public grpc_experimental_completion_queue_functor {
+class ShutdownCallback : public grpc_completion_queue_functor {
public:
ShutdownCallback() {
functor_run = &ShutdownCallback::Run;
// 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;
: 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);
// 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) {
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) {
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);
#include <grpcpp/impl/codegen/server_context.h>
#include <algorithm>
+#include <atomic>
#include <utility>
#include <grpc/compression.h>
}
}
+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
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_) {
public abstract class ChannelCredentials
{
static readonly ChannelCredentials InsecureInstance = new InsecureCredentials();
+ static readonly ChannelCredentials SecureSslInstance = new SslCredentials();
/// <summary>
/// Creates a new instance of channel credentials
}
/// <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>
/// <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";
}
}
}
[Test]
+ public void SecureCredentials_IsComposable()
+ {
+ Assert.IsTrue(ChannelCredentials.SecureSsl.IsComposable);
+ }
+
+ [Test]
public void ChannelCredentials_CreateComposite()
{
var composite = ChannelCredentials.Create(new FakeChannelCredentials(true), new FakeCallCredentials());
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);
}
}
}
<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
{
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
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
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,
__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,
__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,
__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,
/// <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, ""));
/// <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, ""));
/// <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, ""));
/// <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, ""));
{
/// <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)
{
}
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// </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);
/// <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));
/// <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);
/// <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));
/// </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);
/// <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()
/// 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));
{
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
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
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,
__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,
/// <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, ""));
/// <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, ""));
{
/// <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)
{
}
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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()
/// 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));
{
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
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
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,
__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,
__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,
__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,
__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,
/// <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, ""));
/// <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, ""));
/// <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, ""));
/// <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, ""));
/// <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, ""));
{
/// <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)
{
}
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// </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);
/// <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));
/// </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);
/// <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));
/// <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);
/// <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));
/// </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);
/// <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()
/// 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));
{
/// <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);
/// <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();
/// 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)
{
}
{
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
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
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,
__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,
/// <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, ""));
/// <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, ""));
{
/// <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)
{
}
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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()
/// 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));
{
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
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
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,
/// <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, ""));
{
/// <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)
{
}
/// <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));
/// <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);
/// <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));
/// <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);
/// <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()
/// 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));
{
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
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
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,
__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,
__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,
__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,
__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,
__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,
__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,
__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,
/// <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, ""));
/// <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, ""));
/// <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, ""));
/// <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, ""));
/// <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, ""));
/// <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, ""));
/// <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, ""));
/// <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, ""));
{
/// <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)
{
}
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// </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);
/// <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));
/// </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);
/// <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));
/// </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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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()
/// 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));
{
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
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
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,
/// <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, ""));
{
/// <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)
{
}
/// <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));
/// <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);
/// <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));
/// <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);
/// <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()
/// 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));
{
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
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
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,
__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,
[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, ""));
{
/// <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);
/// <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()
/// 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));
{
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
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
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,
__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,
/// <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, ""));
/// <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, ""));
{
/// <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)
{
}
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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()
/// 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));
{
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
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
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,
__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,
[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, ""));
{
/// <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);
/// <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()
/// 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));
{
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
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
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,
/// <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, ""));
{
/// <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)
{
}
/// <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));
/// <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);
/// <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));
/// <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);
/// <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()
/// 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));
{
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
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
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,
__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,
__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,
__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,
/// <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, ""));
/// <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, ""));
/// <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, ""));
/// <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, ""));
{
/// <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)
{
}
/// <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));
/// </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);
/// <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));
/// </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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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));
/// <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);
/// <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()
/// 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));
{
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
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
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,
/// <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, ""));
{
/// <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)
{
}
/// <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));
/// </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);
/// <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()
/// 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));
<!-- This file is generated -->
<Project>
<PropertyGroup>
- <GrpcCsharpVersion>2.38.1</GrpcCsharpVersion>
+ <GrpcCsharpVersion>2.39.0</GrpcCsharpVersion>
<GoogleProtobufVersion>3.15.8</GoogleProtobufVersion>
</PropertyGroup>
</Project>
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
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
+```
-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
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" \
# 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
# 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
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:
s.source = {
:git => 'https://github.com/google/boringssl.git',
- :commit => "688fc5cf5428868679d2ae1072cad81055752068",
+ :commit => "bcc01b6c66b1c6fa2816b108e50a544b757fbd7b",
}
s.ios.deployment_target = '9.0'
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
- ywy8nP+b/uNs9R/gR8lM/vR5+4Wgw/ziWXr27/9+lqj/s/q3nprdJ9tE1jLwNfR/bP/wjx74D8shsoZq
- 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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
+ 9bFT2qc9e0xKhNPhUZbpJt9lqmWjeQ3Ssz7JFq7ImGIb9tysREB+fUyehXNM1RV5mTd5Whx/SbI5dDUv
+ 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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
// 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"
// 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"
$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
"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"
#include "channel.h"
#include "server.h"
#include "timeval.h"
+#include "version.h"
#include "channel_credentials.h"
#include "call_credentials.h"
#include "server_credentials.h"
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);
#ifndef VERSION_H
#define VERSION_H
-#define PHP_GRPC_VERSION "1.38.1"
+#define PHP_GRPC_VERSION "1.39.0"
#endif /* VERSION_H */
}
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 '.
},
));
list($response, $status) = $call->wait();
- $this->assertSame(\Grpc\STATUS_UNAUTHENTICATED, $status->code);
+ $this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testInvalidMethodName()
--- /dev/null
+<?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
+ ),
+ ];
+ }
+
+}
--- /dev/null
+<?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();
+++ /dev/null
-// 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;
-}
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 {
+# 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")
deps = [
"base_proto",
"config_source_proto",
+ "endpoint_proto",
],
)
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
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";
// 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`.
// 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.
// The regex match string. The string must be supported by the configured engine.
string regex = 2;
}
+
+message RegexMatchAndSubstitute {
+ RegexMatcher pattern = 1;
+ string substitution = 2;
+}
// 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;
}
+# 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(
+# 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")
# 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.
# 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
@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:
@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
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
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:
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,
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
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
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":
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(
ctypedef enum grpc_local_connect_type:
UDS
LOCAL_TCP
+
+
+cdef extern from "src/core/lib/iomgr/error.h":
+ const grpc_error* GRPC_ERROR_CANCELLED
from libc cimport string
import errno
+import sys
gevent_g = None
gevent_socket = None
gevent_hub = None
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 ###############
###############################
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)()
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc/_grpcio_metadata.py.template`!!!
-__version__ = """1.38.1"""
+__version__ = """1.39.0"""
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),
async def intercept_stream_unary(
self,
continuation: Callable[[ClientCallDetails, RequestType],
- UnaryStreamCall],
+ StreamUnaryCall],
client_call_details: ClientCallDetails,
request_iterator: RequestIterableType,
) -> StreamUnaryCall:
async def intercept_stream_stream(
self,
continuation: Callable[[ClientCallDetails, RequestType],
- UnaryStreamCall],
+ StreamStreamCall],
client_call_details: ClientCallDetails,
request_iterator: RequestIterableType,
) -> Union[ResponseIterableType, StreamStreamCall]:
+# 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(
+# 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(
+# 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(
+# 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(
+# 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(
+# 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(
+# 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(
'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/gpr/alloc.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/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',
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!!
-VERSION = '1.38.1'
+VERSION = '1.39.0'
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_admin/grpc_version.py.template`!!!
-VERSION = '1.38.1'
+VERSION = '1.39.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.
load("//bazel:python_rules.bzl", "py_grpc_library", "py_proto_library")
package(default_visibility = ["//visibility:public"])
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_channelz/grpc_version.py.template`!!!
-VERSION = '1.38.1'
+VERSION = '1.39.0'
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_csds/grpc_version.py.template`!!!
-VERSION = '1.38.1'
+VERSION = '1.39.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.
load("//bazel:python_rules.bzl", "py_grpc_library", "py_proto_library")
package(default_visibility = ["//visibility:public"])
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_health_checking/grpc_version.py.template`!!!
-VERSION = '1.38.1'
+VERSION = '1.39.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.
load("//bazel:python_rules.bzl", "py_grpc_library", "py_proto_library")
load("@grpc_python_dependencies//:requirements.bzl", "requirement")
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_reflection/grpc_version.py.template`!!!
-VERSION = '1.38.1'
+VERSION = '1.39.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.
load("@grpc_python_dependencies//:requirements.bzl", "requirement")
package(default_visibility = ["//visibility:public"])
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_status/grpc_version.py.template`!!!
-VERSION = '1.38.1'
+VERSION = '1.39.0'
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_testing/grpc_version.py.template`!!!
-VERSION = '1.38.1'
+VERSION = '1.39.0'
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)
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_tests/grpc_version.py.template`!!!
-VERSION = '1.38.1'
+VERSION = '1.39.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.
py_library(
name = "bazel_namespace_package_hack",
srcs = ["bazel_namespace_package_hack.py"],
+# 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")
+# 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")
+# 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")
+# 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(
+# 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")
+# 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")
+# 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")
+# 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(
+# 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"])
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"
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()
+# 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")
+# 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(
+# 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(
+# 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")
@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():
import asyncio
import logging
+import platform
import threading
import time
import unittest
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,
--- /dev/null
+# 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.
--- /dev/null
+["unit.close_channel_test.CloseChannelTest"]
--- /dev/null
+# 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.
--- /dev/null
+# 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)
--- /dev/null
+# 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)
+# 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"],
"//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",
],
)
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
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
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
_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:
# It currently only implements enough functionality to pass the xDS security
# tests.
-_LISTEN_HOST = "[::]"
+_LISTEN_HOST = "0.0.0.0"
_THREAD_POOL_SIZE = 256
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)
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;
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;
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");
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");
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);
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
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
# GRPC contains the General RPC module.
module GRPC
- VERSION = '1.38.1'
+ VERSION = '1.39.0'
end
$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 = {}
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
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|
module GRPC
module Tools
- VERSION = '1.38.1'
+ VERSION = '1.39.0'
end
end
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
--- /dev/null
+%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()}"""
, $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))
%>
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
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:
--- /dev/null
+%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
--- /dev/null
+%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"]
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"
GRPC_ERROR_UNREF(error);
}
-TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoff) {
+TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoffWrongType) {
const char* test_json =
"{\n"
" \"methodConfig\": [ {\n"
" { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
" ],\n"
" \"retryPolicy\": {\n"
- " \"maxAttempts\": 1,\n"
+ " \"maxAttempts\": 2,\n"
" \"initialBackoff\": \"1sec\",\n"
" \"maxBackoff\": \"120s\",\n"
" \"backoffMultiplier\": 1.6,\n"
GRPC_ERROR_UNREF(error);
}
-TEST_F(RetryParserTest, InvalidRetryPolicyMaxBackoff) {
+TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoffBadValue) {
const char* test_json =
"{\n"
" \"methodConfig\": [ {\n"
" { \"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"
GRPC_ERROR_UNREF(error);
}
-TEST_F(RetryParserTest, InvalidRetryPolicyBackoffMultiplier) {
+TEST_F(RetryParserTest, InvalidRetryPolicyMaxBackoffBadValue) {
const char* test_json =
"{\n"
" \"methodConfig\": [ {\n"
" { \"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"
GRPC_ERROR_UNREF(error);
}
-TEST_F(RetryParserTest, InvalidRetryPolicyRetryableStatusCodes) {
+TEST_F(RetryParserTest, InvalidRetryPolicyBackoffMultiplierBadValue) {
const char* test_json =
"{\n"
" \"methodConfig\": [ {\n"
" { \"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"
"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);
}
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);
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);
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();
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);
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;
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;
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;
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);
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);
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();
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);
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;
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;
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;
#else /* GRPC_POSIX_SOCKET */
-int main(int argc, char** argv) { return 1; }
+int main(int /* argc */, char** /* argv */) { return 1; }
#endif /* GRPC_POSIX_SOCKET */
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,
#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 {
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);
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;
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);
}
"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(),
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;
};
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;
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));
}
// 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);
} 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);
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");
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);
--- /dev/null
+//
+// 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) {}
--- /dev/null
+//
+// 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) {}
--- /dev/null
+//
+// 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);
+}
--- /dev/null
+/*
+ *
+ * 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);
+}
--- /dev/null
+# 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",
+ ],
+)
--- /dev/null
+// 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;
+}
],
)
-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",
+# ],
+#)
#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.
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.
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);
// 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";
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);
// 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) {
close(client);
close(sock);
SSL_CTX_free(ctx);
- EVP_cleanup();
}
// This test launches a minimal TLS server on a separate thread and then
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;
// 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;
}
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;
}
// 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;
}
SSL_free(ssl);
gpr_free(alpn_protos);
SSL_CTX_free(ctx);
- EVP_cleanup();
close(sock);
thd.Join();
return success;
}
+
+void CleanupSslLibrary() { EVP_cleanup(); }
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
srcs = ["endpoint_tests.cc"],
hdrs = ["endpoint_tests.h"],
language = "C++",
- visibility = ["//test:__subpackages__"],
+ visibility = [
+ "//test:__subpackages__",
+ "@grpc:endpoint_tests",
+ ],
deps = [
"//:gpr",
"//:grpc",
#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"
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);
}
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);
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);
}
// 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() {
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);
}
}
// 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;
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);
deps = [
"//:gpr",
"//:grpc",
- "//:grpc_rbac_engine",
+ "//:grpc_secure",
"//test/core/util:grpc_test_util",
- "//test/core/util:grpc_test_util_base",
],
)
"//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",
+ ],
+)
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");
.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) {
.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) {
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) {
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) {
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) {
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();
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();
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();
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,
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(
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) {
#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"
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());
}
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();
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;
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));
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;
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));
--- /dev/null
+// 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();
+}
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"};
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();
deps = [
"//:gpr",
"//:grpc",
+ "//:grpc_authorization_provider",
"//test/core/util:grpc_test_util",
],
)
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.
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;
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);
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);
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);
#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>
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;
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;
};
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
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
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
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
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:
}
};
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
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
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
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
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>
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;
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_;
};
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
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:
}
};
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
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 {
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(
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;
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(
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);
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();
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);
}
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
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) {
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());
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,
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_);
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) {
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);
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) {
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());
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);
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);
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,
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();
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 {
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) {
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 {
}
}
-class ReadClient : public grpc::experimental::ClientReadReactor<EchoResponse> {
+class ReadClient : public grpc::ClientReadReactor<EchoResponse> {
public:
ReadClient(grpc::testing::EchoTestService::Stub* stub,
ServerTryCancelRequestPhase server_try_cancel,
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();
}
}
-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,
}
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();
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();
}
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());
}
}
+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_);
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
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)
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();
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());
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");
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) {
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) {
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);
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);
}
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_;
};
~MessageAllocatorEnd2endTestBase() override = default;
- void CreateServer(
- experimental::MessageAllocator<EchoRequest, EchoResponse>* allocator) {
+ void CreateServer(MessageAllocator<EchoRequest, EchoResponse>* allocator) {
ServerBuilder builder;
auto server_creds = GetCredentialsProvider()->GetServerCredentials(
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());
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)
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);
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());
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());
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(
private:
google::protobuf::Arena arena_;
};
- experimental::MessageHolder<EchoRequest, EchoResponse>* AllocateMessages()
- override {
+ MessageHolder<EchoRequest, EchoResponse>* AllocateMessages() override {
allocation_count++;
return new MessageHolderImpl;
}
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
};
TEST_F(MockCallbackTest, MockedCallSucceedsWithWait) {
- experimental::CallbackServerContext ctx;
+ CallbackServerContext ctx;
EchoRequest req;
EchoResponse resp;
grpc::internal::Mutex mu;
}
TEST_F(MockCallbackTest, MockedCallSucceeds) {
- experimental::CallbackServerContext ctx;
+ CallbackServerContext ctx;
EchoRequest req;
EchoResponse resp;
DefaultReactorTestPeer peer(&ctx);
}
TEST_F(MockCallbackTest, MockedCallFails) {
- experimental::CallbackServerContext ctx;
+ CallbackServerContext ctx;
EchoRequest req;
EchoResponse resp;
DefaultReactorTestPeer peer(&ctx);
// 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()) {
}
}
-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();
}
}
-void ServerTryCancelNonblocking(experimental::CallbackServerContext* context) {
+void ServerTryCancelNonblocking(CallbackServerContext* context) {
EXPECT_FALSE(context->IsCancelled());
context->TryCancel();
gpr_log(GPR_INFO,
} // 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
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)),
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());
}
CallbackTestServiceImpl* const service_;
- experimental::CallbackServerContext* const ctx_;
+ CallbackServerContext* const ctx_;
const EchoRequest* const req_;
EchoResponse* const resp_;
Alarm alarm_;
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),
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:
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),
}
}
- experimental::CallbackServerContext* const ctx_;
+ CallbackServerContext* const ctx_;
EchoResponse* const response_;
EchoRequest request_;
int num_msgs_read_{0};
// 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:
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);
}
}
}
- experimental::CallbackServerContext* const ctx_;
+ CallbackServerContext* const ctx_;
const EchoRequest* const request_;
EchoResponse response_;
int num_msgs_sent_{0};
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:
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 {
return;
}
}
+ } else if (client_try_cancel_) {
+ EXPECT_TRUE(ctx_->IsCancelled());
}
if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
}
}
- experimental::CallbackServerContext* const ctx_;
+ CallbackServerContext* const ctx_;
EchoRequest request_;
EchoResponse response_;
int num_msgs_read_{0};
bool finished_{false};
bool setup_done_{false};
std::thread finish_thread_;
+ bool client_try_cancel_ = false;
};
return new Reactor(context);
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";
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);
};
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() {
#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"
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);
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) {
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,
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;
}
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();
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);
}
}
-// 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();
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();
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();
}
// 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;
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.
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);
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) {
// 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();
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));
// 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
{
"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");
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_;
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_;
::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);
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();
::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);
::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();
::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_ =
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(
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;
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();
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));
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(); }, {}, {});
}
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());
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());
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());
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());
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());
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();
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
+ // 3 receiving a request.
WaitForAllBackends(3, 4);
gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
// Send kNumRpcs RPCs.
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();
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();
{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;
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();
}
// 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();
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;
::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;
#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
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;
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
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));
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;
};
// 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);
// 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)
}
// 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(
->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;
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();
// 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)
}
// 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);
// 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_;
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;
* 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,
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();
}
}
} // 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) {
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_);
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
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());
#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
"--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"],
)
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);
}
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
};
class CallbackStreamingPingPongReactor final
- : public grpc::experimental::ClientBidiReactor<SimpleRequest,
- SimpleResponse> {
+ : public grpc::ClientBidiReactor<SimpleRequest, SimpleResponse> {
public:
CallbackStreamingPingPongReactor(
CallbackStreamingPingPongClient* client,
: 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();
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());
});
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();
}
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()
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')
+# 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}]}\''}
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()
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')
+# 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}]}\''}
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_); }
"//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",
+ ],
+)
--- /dev/null
+// 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();
+}
mock_socket_mutator_mutate_fd,
mock_socket_mutator_compare,
mock_socket_mutator_destroy,
+ nullptr,
};
class MockSocketMutator : public grpc_socket_mutator {
)
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 = [
--- /dev/null
+/*
+ *
+ * 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();
+}
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) {
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
"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" \
"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"
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);
--- /dev/null
+/*
+ *
+ * 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"
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
genrule(
name = "copy_six",
- srcs = ["six-1.12.0/six.py"],
+ srcs = ["six-1.16.0/six.py"],
outs = ["__init__.py"],
cmd = "cp $< $(@)",
)
"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" \
'.proto': r'//\s*',
'.cs': r'//\s*',
'.mak': r'#\s*',
+ '.bazel': r'#\s*',
+ '.bzl': r'#\s*',
'Makefile': r'#\s*',
'Dockerfile': r'#\s*',
'BUILD': r'#\s*',
TEST_DIRS=(
'src/python/grpcio_tests/tests'
+ 'src/python/grpcio_tests/tests_gevent'
)
VIRTUALENV=python_pylint_venv
# 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'
# 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'
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"]
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"]
--- /dev/null
+# 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"]
# 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
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 \
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 \
# 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
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 \
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 \
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 \
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 \
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 \
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 \
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 \
# 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
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 \
# 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
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 \
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 \
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 \
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 \
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 \
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 \
# 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
# 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
# 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
--- /dev/null
+#!/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."
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
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
--- /dev/null
+# 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"
+}
--- /dev/null
+# 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"
+}
--- /dev/null
+# 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"
+}
--- /dev/null
+# 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"
+}
--- /dev/null
+# 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"
+}
--- /dev/null
+# 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.*"
+ }
+}
--- /dev/null
+#!/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/...
--- /dev/null
+#!/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
--- /dev/null
+#!/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"
--- /dev/null
+#!/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/...
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+# 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/**"
+ }
+}
--- /dev/null
+#!/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"
# 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.*"
# 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
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"
# 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 \
# 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 \
# --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 \
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"
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 \
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 \
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 \
--- /dev/null
+# 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"
+ }
+}
--- /dev/null
+#!/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 "$@"
# 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.*"
('v1.35.0', ReleaseInfo()),
('v1.36.3', ReleaseInfo()),
('v1.37.0', ReleaseInfo()),
+ ('v1.38.0', ReleaseInfo()),
]),
'go':
OrderedDict([
('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([
('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([
('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([
('v1.35.0', ReleaseInfo()),
('v1.36.3', ReleaseInfo()),
('v1.37.0', ReleaseInfo()),
+ ('v1.38.0', ReleaseInfo()),
]),
'php':
OrderedDict([
('v1.35.0', ReleaseInfo()),
('v1.36.3', ReleaseInfo()),
('v1.37.0', ReleaseInfo()),
+ ('v1.38.0', ReleaseInfo()),
]),
'csharp':
OrderedDict([
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}.
"grpcio-reflection",
"grpcio-channelz",
"grpcio-testing",
+ "grpcio-admin",
+ "grpcio-csds",
]
Artifact = collections.namedtuple("Artifact", ("filename", "checksum"))
)
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 ..\..\..
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
"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",
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
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
### 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.
$ 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
```
$ ./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
- `-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.
- `--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.
$ 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
```
$ ./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
- `-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,
- `-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
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
# 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
# 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:
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,
"""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,
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.
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.
"""
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,
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()
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
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."""
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',
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,
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:
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']
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))
--- /dev/null
+#!/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
import argparse
import sys
-from typing import Any, Dict, Iterable, Mapping, Type
+from typing import Any, Dict, Iterable, List, Mapping, Type
import yaml
"""
+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',
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']
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()
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
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}\'.')
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,
SCALABLE = 'scalable'
INPROC = 'inproc'
SWEEP = 'sweep'
-DEFAULT_CATEGORIES = [SCALABLE, SMOKETEST]
+DEFAULT_CATEGORIES = (SCALABLE, SMOKETEST)
SECURE_SECARGS = {
'use_test_ca': True,
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."""
'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
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']
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']
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']
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']
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']
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:
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']
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']
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'
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':
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
--- /dev/null
+# 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
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':
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',
}
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
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(
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
'gcc5.3',
'gcc7.4',
'gcc8.3',
+ 'gcc8.3_openssl102',
'gcc_musl',
'clang4.0',
'clang5.0',
# 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'],
'traffic_splitting',
'path_matching',
'header_matching',
+ 'api_listener',
'forwarding_rule_port_match',
'forwarding_rule_default_port',
'metadata_filter',
'timeout',
'fault_injection',
'csds',
- 'api_listener', # TODO(b/187352987) Relieve quota pressure
]
# Test cases that require the V3 API. Skipped in older runs.
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,
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.
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,
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,
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,
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):
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,
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):
logger.info(
'Running secondary_locality_gets_no_requests_on_partial_primary_failure'
)
+ passed = True
try:
patch_backend_service(
gcp, backend_service,
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(
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,
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,
[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']
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)
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)
])
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)
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,
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.
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,
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 = [
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,
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 = [
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,
'''
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
# 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):
)
]
+ passed = True
try:
first_case = True
for (testcase_name, client_config, expected_results) in test_cases:
'%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):
)
]
+ passed = True
try:
first_case = True
for (testcase_name, client_config, expected_results) in test_cases:
'%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):
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
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(
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)
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)
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)
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(
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)
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,
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(
self.service_port = None
self.instance_template = None
self.instance_groups = []
+ self.errors = []
alpha_compute = None
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)
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)
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:
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)
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
- [ ] 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
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 \
```
### 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 \
```
### 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
--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`
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
+
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
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
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
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
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,
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
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
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')
--- /dev/null
+#!/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
--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
--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
"""
import datetime
import logging
-from typing import Any, List, Optional
+from typing import Any, Optional, Sequence
import tenacity
_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,)
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:
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:
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()),
# 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
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
from google.rpc import code_pb2
from googleapiclient import discovery
import googleapiclient.errors
+import googleapiclient.http
import tenacity
import yaml
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:
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,
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):
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,
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)
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)
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,
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)
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
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,
}],
})
+ 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)
**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))
--- /dev/null
+# 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)
# 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
# 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
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
# 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__)
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,
# 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):
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):
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,
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)
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]
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__)
--- /dev/null
+# 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]
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)
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)
import mako.template
import yaml
+from framework.infrastructure import gcp
from framework.infrastructure import k8s
logger = logging.getLogger(__name__)
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,
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
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)
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()
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
# 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):
@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,
*,
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.
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)
*,
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,
# 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
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,
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)
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
*,
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',
# 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
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
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,
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))
"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",
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
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
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):
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()
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)
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,
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)
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',
--- /dev/null
+# 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
--- /dev/null
+# 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))
metadata:
labels:
app: ${deployment_name}
+ owner: xds-k8s-interop-test
spec:
serviceAccountName: ${service_account_name}
containers:
- "--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
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
...
--- /dev/null
+-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
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
retrying~=1.3
tenacity~=6.2
protobuf~=3.14
+xds-protos~=0.0.8
--- /dev/null
+#!/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}" "$@"
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)
--- /dev/null
+# 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.
--- /dev/null
+# 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()
--- /dev/null
+# 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()
--- /dev/null
+# 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()
--- /dev/null
+# 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()
--- /dev/null
+# 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()
--- /dev/null
+# 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()