aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--BUILD7
-rw-r--r--CMakeLists.txt117
-rw-r--r--Makefile129
-rw-r--r--bazel/grpc_deps.bzl10
-rw-r--r--build.yaml39
-rw-r--r--doc/connectivity-semantics-and-api.md11
-rw-r--r--gRPC-C++.podspec10
-rw-r--r--grpc.def1
-rw-r--r--include/grpc/grpc.h3
-rw-r--r--include/grpcpp/channel.h6
-rw-r--r--include/grpcpp/impl/codegen/async_stream.h34
-rw-r--r--include/grpcpp/impl/codegen/async_unary_call.h15
-rw-r--r--include/grpcpp/impl/codegen/byte_buffer.h14
-rw-r--r--include/grpcpp/impl/codegen/call.h687
-rw-r--r--include/grpcpp/impl/codegen/call_op_set.h920
-rw-r--r--include/grpcpp/impl/codegen/call_op_set_interface.h59
-rw-r--r--include/grpcpp/impl/codegen/callback_common.h31
-rw-r--r--include/grpcpp/impl/codegen/channel_interface.h20
-rw-r--r--include/grpcpp/impl/codegen/client_callback.h4
-rw-r--r--include/grpcpp/impl/codegen/client_context.h14
-rw-r--r--include/grpcpp/impl/codegen/client_interceptor.h68
-rw-r--r--include/grpcpp/impl/codegen/client_unary_call.h2
-rw-r--r--include/grpcpp/impl/codegen/completion_queue.h55
-rw-r--r--include/grpcpp/impl/codegen/config_protobuf.h12
-rw-r--r--include/grpcpp/impl/codegen/core_codegen.h3
-rw-r--r--include/grpcpp/impl/codegen/core_codegen_interface.h3
-rw-r--r--include/grpcpp/impl/codegen/intercepted_channel.h80
-rw-r--r--include/grpcpp/impl/codegen/interceptor.h59
-rw-r--r--include/grpcpp/impl/codegen/interceptor_common.h383
-rw-r--r--include/grpcpp/impl/codegen/metadata_map.h2
-rw-r--r--include/grpcpp/impl/codegen/method_handler_impl.h83
-rw-r--r--include/grpcpp/impl/codegen/rpc_service_method.h74
-rw-r--r--include/grpcpp/impl/codegen/server_callback.h200
-rw-r--r--include/grpcpp/impl/codegen/server_context.h48
-rw-r--r--include/grpcpp/impl/codegen/server_interceptor.h99
-rw-r--r--include/grpcpp/impl/codegen/server_interface.h94
-rw-r--r--include/grpcpp/impl/codegen/service_type.h58
-rw-r--r--include/grpcpp/impl/codegen/sync_stream.h20
-rw-r--r--include/grpcpp/server.h32
-rw-r--r--include/grpcpp/server_builder.h26
-rw-r--r--include/grpcpp/support/server_callback.h24
-rw-r--r--setup.py8
-rw-r--r--src/compiler/cpp_generator.cc204
-rw-r--r--src/core/ext/filters/client_channel/client_channel.cc34
-rw-r--r--src/core/ext/filters/client_channel/health/health_check_client.cc7
-rw-r--r--src/core/ext/transport/chttp2/client/chttp2_connector.cc12
-rw-r--r--src/core/ext/transport/chttp2/server/chttp2_server.cc42
-rw-r--r--src/core/ext/transport/chttp2/transport/chttp2_transport.cc29
-rw-r--r--src/core/ext/transport/chttp2/transport/chttp2_transport.h3
-rw-r--r--src/core/ext/transport/chttp2/transport/frame_rst_stream.cc2
-rw-r--r--src/core/ext/transport/chttp2/transport/internal.h2
-rw-r--r--src/core/lib/channel/channel_stack_builder.cc16
-rw-r--r--src/core/lib/channel/channel_stack_builder.h8
-rw-r--r--src/core/lib/channel/channelz.cc18
-rw-r--r--src/core/lib/channel/channelz.h7
-rw-r--r--src/core/lib/channel/channelz_registry.cc18
-rw-r--r--src/core/lib/iomgr/resource_quota.cc74
-rw-r--r--src/core/lib/iomgr/resource_quota.h27
-rw-r--r--src/core/lib/iomgr/tcp_client_custom.cc17
-rw-r--r--src/core/lib/security/credentials/google_default/google_default_credentials.cc93
-rw-r--r--src/core/lib/security/credentials/google_default/google_default_credentials.h5
-rw-r--r--src/core/lib/surface/channel.cc24
-rw-r--r--src/core/lib/surface/channel.h3
-rw-r--r--src/core/lib/surface/server.cc28
-rw-r--r--src/core/lib/surface/server.h5
-rw-r--r--src/cpp/client/channel_cc.cc36
-rw-r--r--src/cpp/common/completion_queue_cc.cc10
-rw-r--r--src/cpp/common/core_codegen.cc7
-rw-r--r--src/cpp/server/channelz/channelz_service.cc84
-rw-r--r--src/cpp/server/channelz/channelz_service.h4
-rw-r--r--src/cpp/server/server_builder.cc38
-rw-r--r--src/cpp/server/server_cc.cc467
-rw-r--r--src/cpp/server/server_context.cc211
-rw-r--r--src/proto/grpc/channelz/channelz.proto13
-rw-r--r--src/python/grpcio/commands.py6
-rw-r--r--src/ruby/ext/grpc/rb_grpc_imports.generated.c2
-rw-r--r--src/ruby/ext/grpc/rb_grpc_imports.generated.h3
-rw-r--r--templates/gRPC-C++.podspec.template3
-rw-r--r--templates/tools/dockerfile/bazel.include7
-rw-r--r--test/core/channel/channelz_test.cc5
-rw-r--r--test/core/iomgr/tcp_client_uv_test.cc27
-rw-r--r--test/core/iomgr/tcp_server_uv_test.cc40
-rw-r--r--test/core/memory_usage/BUILD60
-rw-r--r--test/core/memory_usage/server.cc2
-rw-r--r--test/core/security/credentials_test.cc13
-rw-r--r--test/core/surface/public_headers_must_be_c89.c1
-rw-r--r--test/core/util/ubsan_suppressions.txt14
-rw-r--r--test/cpp/codegen/compiler_test_golden199
-rw-r--r--test/cpp/end2end/BUILD53
-rw-r--r--test/cpp/end2end/channelz_service_test.cc25
-rw-r--r--test/cpp/end2end/client_callback_end2end_test.cc53
-rw-r--r--test/cpp/end2end/client_interceptors_end2end_test.cc612
-rw-r--r--test/cpp/end2end/client_lb_end2end_test.cc30
-rw-r--r--test/cpp/end2end/grpclb_end2end_test.cc8
-rw-r--r--test/cpp/end2end/interceptors_util.h308
-rw-r--r--test/cpp/end2end/server_interceptors_end2end_test.cc625
-rw-r--r--test/cpp/end2end/test_service_impl.cc150
-rw-r--r--test/cpp/end2end/test_service_impl.h33
-rw-r--r--test/cpp/interop/client_helper.h2
-rw-r--r--test/cpp/util/channel_trace_proto_helper.cc5
-rw-r--r--test/cpp/util/channel_trace_proto_helper.h1
-rw-r--r--test/cpp/util/config_grpc_cli.h7
m---------third_party/boringssl-with-bazel0
-rw-r--r--tools/bazel.rc3
-rw-r--r--tools/dockerfile/test/bazel/Dockerfile7
-rw-r--r--tools/doxygen/Doxyfile.c++7
-rw-r--r--tools/doxygen/Doxyfile.c++.internal7
-rw-r--r--tools/internal_ci/helper_scripts/prepare_build_macos_rc3
-rwxr-xr-xtools/internal_ci/linux/grpc_asan_on_foundry.sh2
-rwxr-xr-xtools/internal_ci/linux/grpc_bazel_on_foundry_base.sh17
-rw-r--r--tools/internal_ci/linux/grpc_bazel_on_foundry_dbg.sh2
-rw-r--r--tools/internal_ci/linux/grpc_bazel_on_foundry_opt.sh2
-rwxr-xr-xtools/internal_ci/linux/grpc_e2e_performance_singlevm.sh2
-rwxr-xr-xtools/internal_ci/linux/grpc_full_performance_master.sh2
-rw-r--r--tools/internal_ci/linux/grpc_interop_matrix.cfg6
-rwxr-xr-xtools/internal_ci/linux/grpc_interop_matrix.sh2
-rw-r--r--tools/internal_ci/linux/grpc_msan_on_foundry.sh66
-rw-r--r--tools/internal_ci/linux/grpc_tsan_on_foundry.sh2
-rw-r--r--tools/internal_ci/linux/grpc_ubsan_on_foundry.sh2
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_asan_on_foundry.sh2
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_bazel_on_foundry_dbg.sh2
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_bazel_on_foundry_opt.sh2
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_interop_matrix_adhoc.cfg30
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_tsan_on_foundry.sh2
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_ubsan_on_foundry.sh2
-rw-r--r--tools/internal_ci/macos/grpc_basictests_objc_dbg.cfg31
-rw-r--r--tools/internal_ci/macos/grpc_basictests_objc_opt.cfg31
-rw-r--r--tools/internal_ci/macos/pull_request/grpc_basictests_objc_dbg.cfg31
-rw-r--r--tools/internal_ci/macos/pull_request/grpc_basictests_objc_opt.cfg31
-rw-r--r--tools/interop_matrix/client_matrix.py8
-rwxr-xr-xtools/interop_matrix/run_interop_matrix_tests.py222
-rw-r--r--tools/remote_build/README.md3
-rw-r--r--tools/remote_build/kokoro.bazelrc21
-rw-r--r--tools/remote_build/manual.bazelrc3
-rw-r--r--tools/remote_build/rbe_common.bazelrc33
-rw-r--r--tools/run_tests/generated/sources_and_headers.json58
-rw-r--r--tools/run_tests/generated/tests.json48
-rw-r--r--tools/run_tests/python_utils/upload_rbe_results.py2
-rwxr-xr-xtools/run_tests/sanity/check_submodules.sh2
-rwxr-xr-xtools/run_tests/sanity/core_banned_functions.py3
140 files changed, 6703 insertions, 1402 deletions
diff --git a/BUILD b/BUILD
index 65cd4715f1..08948e32e1 100644
--- a/BUILD
+++ b/BUILD
@@ -245,6 +245,7 @@ GRPCXX_PUBLIC_HDRS = [
"include/grpcpp/support/config.h",
"include/grpcpp/support/proto_buffer_reader.h",
"include/grpcpp/support/proto_buffer_writer.h",
+ "include/grpcpp/support/server_callback.h",
"include/grpcpp/support/slice.h",
"include/grpcpp/support/status.h",
"include/grpcpp/support/status_code_enum.h",
@@ -2065,6 +2066,8 @@ grpc_cc_library(
"include/grpcpp/impl/codegen/byte_buffer.h",
"include/grpcpp/impl/codegen/call.h",
"include/grpcpp/impl/codegen/call_hook.h",
+ "include/grpcpp/impl/codegen/call_op_set.h",
+ "include/grpcpp/impl/codegen/call_op_set_interface.h",
"include/grpcpp/impl/codegen/callback_common.h",
"include/grpcpp/impl/codegen/channel_interface.h",
"include/grpcpp/impl/codegen/client_callback.h",
@@ -2077,14 +2080,18 @@ grpc_cc_library(
"include/grpcpp/impl/codegen/core_codegen_interface.h",
"include/grpcpp/impl/codegen/create_auth_context.h",
"include/grpcpp/impl/codegen/grpc_library.h",
+ "include/grpcpp/impl/codegen/intercepted_channel.h",
"include/grpcpp/impl/codegen/interceptor.h",
+ "include/grpcpp/impl/codegen/interceptor_common.h",
"include/grpcpp/impl/codegen/metadata_map.h",
"include/grpcpp/impl/codegen/method_handler_impl.h",
"include/grpcpp/impl/codegen/rpc_method.h",
"include/grpcpp/impl/codegen/rpc_service_method.h",
"include/grpcpp/impl/codegen/security/auth_context.h",
"include/grpcpp/impl/codegen/serialization_traits.h",
+ "include/grpcpp/impl/codegen/server_callback.h",
"include/grpcpp/impl/codegen/server_context.h",
+ "include/grpcpp/impl/codegen/server_interceptor.h",
"include/grpcpp/impl/codegen/server_interface.h",
"include/grpcpp/impl/codegen/service_type.h",
"include/grpcpp/impl/codegen/slice.h",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 23ef8b3b64..cfb163d6b5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -584,6 +584,7 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx client_crash_test)
endif()
add_dependencies(buildtests_cxx client_crash_test_server)
+add_dependencies(buildtests_cxx client_interceptors_end2end_test)
add_dependencies(buildtests_cxx client_lb_end2end_test)
add_dependencies(buildtests_cxx codegen_test_full)
add_dependencies(buildtests_cxx codegen_test_minimal)
@@ -664,6 +665,7 @@ add_dependencies(buildtests_cxx server_crash_test)
endif()
add_dependencies(buildtests_cxx server_crash_test_client)
add_dependencies(buildtests_cxx server_early_return_test)
+add_dependencies(buildtests_cxx server_interceptors_end2end_test)
add_dependencies(buildtests_cxx server_request_call_test)
add_dependencies(buildtests_cxx shutdown_test)
add_dependencies(buildtests_cxx slice_hash_table_test)
@@ -3018,6 +3020,7 @@ foreach(_hdr
include/grpcpp/support/config.h
include/grpcpp/support/proto_buffer_reader.h
include/grpcpp/support/proto_buffer_writer.h
+ include/grpcpp/support/server_callback.h
include/grpcpp/support/slice.h
include/grpcpp/support/status.h
include/grpcpp/support/status_code_enum.h
@@ -3112,6 +3115,8 @@ foreach(_hdr
include/grpcpp/impl/codegen/byte_buffer.h
include/grpcpp/impl/codegen/call.h
include/grpcpp/impl/codegen/call_hook.h
+ include/grpcpp/impl/codegen/call_op_set.h
+ include/grpcpp/impl/codegen/call_op_set_interface.h
include/grpcpp/impl/codegen/callback_common.h
include/grpcpp/impl/codegen/channel_interface.h
include/grpcpp/impl/codegen/client_callback.h
@@ -3124,14 +3129,18 @@ foreach(_hdr
include/grpcpp/impl/codegen/core_codegen_interface.h
include/grpcpp/impl/codegen/create_auth_context.h
include/grpcpp/impl/codegen/grpc_library.h
+ include/grpcpp/impl/codegen/intercepted_channel.h
include/grpcpp/impl/codegen/interceptor.h
+ include/grpcpp/impl/codegen/interceptor_common.h
include/grpcpp/impl/codegen/metadata_map.h
include/grpcpp/impl/codegen/method_handler_impl.h
include/grpcpp/impl/codegen/rpc_method.h
include/grpcpp/impl/codegen/rpc_service_method.h
include/grpcpp/impl/codegen/security/auth_context.h
include/grpcpp/impl/codegen/serialization_traits.h
+ include/grpcpp/impl/codegen/server_callback.h
include/grpcpp/impl/codegen/server_context.h
+ include/grpcpp/impl/codegen/server_interceptor.h
include/grpcpp/impl/codegen/server_interface.h
include/grpcpp/impl/codegen/service_type.h
include/grpcpp/impl/codegen/slice.h
@@ -3593,6 +3602,7 @@ foreach(_hdr
include/grpcpp/support/config.h
include/grpcpp/support/proto_buffer_reader.h
include/grpcpp/support/proto_buffer_writer.h
+ include/grpcpp/support/server_callback.h
include/grpcpp/support/slice.h
include/grpcpp/support/status.h
include/grpcpp/support/status_code_enum.h
@@ -3687,6 +3697,8 @@ foreach(_hdr
include/grpcpp/impl/codegen/byte_buffer.h
include/grpcpp/impl/codegen/call.h
include/grpcpp/impl/codegen/call_hook.h
+ include/grpcpp/impl/codegen/call_op_set.h
+ include/grpcpp/impl/codegen/call_op_set_interface.h
include/grpcpp/impl/codegen/callback_common.h
include/grpcpp/impl/codegen/channel_interface.h
include/grpcpp/impl/codegen/client_callback.h
@@ -3699,14 +3711,18 @@ foreach(_hdr
include/grpcpp/impl/codegen/core_codegen_interface.h
include/grpcpp/impl/codegen/create_auth_context.h
include/grpcpp/impl/codegen/grpc_library.h
+ include/grpcpp/impl/codegen/intercepted_channel.h
include/grpcpp/impl/codegen/interceptor.h
+ include/grpcpp/impl/codegen/interceptor_common.h
include/grpcpp/impl/codegen/metadata_map.h
include/grpcpp/impl/codegen/method_handler_impl.h
include/grpcpp/impl/codegen/rpc_method.h
include/grpcpp/impl/codegen/rpc_service_method.h
include/grpcpp/impl/codegen/security/auth_context.h
include/grpcpp/impl/codegen/serialization_traits.h
+ include/grpcpp/impl/codegen/server_callback.h
include/grpcpp/impl/codegen/server_context.h
+ include/grpcpp/impl/codegen/server_interceptor.h
include/grpcpp/impl/codegen/server_interface.h
include/grpcpp/impl/codegen/service_type.h
include/grpcpp/impl/codegen/slice.h
@@ -4096,6 +4112,8 @@ foreach(_hdr
include/grpcpp/impl/codegen/byte_buffer.h
include/grpcpp/impl/codegen/call.h
include/grpcpp/impl/codegen/call_hook.h
+ include/grpcpp/impl/codegen/call_op_set.h
+ include/grpcpp/impl/codegen/call_op_set_interface.h
include/grpcpp/impl/codegen/callback_common.h
include/grpcpp/impl/codegen/channel_interface.h
include/grpcpp/impl/codegen/client_callback.h
@@ -4108,14 +4126,18 @@ foreach(_hdr
include/grpcpp/impl/codegen/core_codegen_interface.h
include/grpcpp/impl/codegen/create_auth_context.h
include/grpcpp/impl/codegen/grpc_library.h
+ include/grpcpp/impl/codegen/intercepted_channel.h
include/grpcpp/impl/codegen/interceptor.h
+ include/grpcpp/impl/codegen/interceptor_common.h
include/grpcpp/impl/codegen/metadata_map.h
include/grpcpp/impl/codegen/method_handler_impl.h
include/grpcpp/impl/codegen/rpc_method.h
include/grpcpp/impl/codegen/rpc_service_method.h
include/grpcpp/impl/codegen/security/auth_context.h
include/grpcpp/impl/codegen/serialization_traits.h
+ include/grpcpp/impl/codegen/server_callback.h
include/grpcpp/impl/codegen/server_context.h
+ include/grpcpp/impl/codegen/server_interceptor.h
include/grpcpp/impl/codegen/server_interface.h
include/grpcpp/impl/codegen/service_type.h
include/grpcpp/impl/codegen/slice.h
@@ -4277,6 +4299,8 @@ foreach(_hdr
include/grpcpp/impl/codegen/byte_buffer.h
include/grpcpp/impl/codegen/call.h
include/grpcpp/impl/codegen/call_hook.h
+ include/grpcpp/impl/codegen/call_op_set.h
+ include/grpcpp/impl/codegen/call_op_set_interface.h
include/grpcpp/impl/codegen/callback_common.h
include/grpcpp/impl/codegen/channel_interface.h
include/grpcpp/impl/codegen/client_callback.h
@@ -4289,14 +4313,18 @@ foreach(_hdr
include/grpcpp/impl/codegen/core_codegen_interface.h
include/grpcpp/impl/codegen/create_auth_context.h
include/grpcpp/impl/codegen/grpc_library.h
+ include/grpcpp/impl/codegen/intercepted_channel.h
include/grpcpp/impl/codegen/interceptor.h
+ include/grpcpp/impl/codegen/interceptor_common.h
include/grpcpp/impl/codegen/metadata_map.h
include/grpcpp/impl/codegen/method_handler_impl.h
include/grpcpp/impl/codegen/rpc_method.h
include/grpcpp/impl/codegen/rpc_service_method.h
include/grpcpp/impl/codegen/security/auth_context.h
include/grpcpp/impl/codegen/serialization_traits.h
+ include/grpcpp/impl/codegen/server_callback.h
include/grpcpp/impl/codegen/server_context.h
+ include/grpcpp/impl/codegen/server_interceptor.h
include/grpcpp/impl/codegen/server_interface.h
include/grpcpp/impl/codegen/service_type.h
include/grpcpp/impl/codegen/slice.h
@@ -4508,6 +4536,7 @@ foreach(_hdr
include/grpcpp/support/config.h
include/grpcpp/support/proto_buffer_reader.h
include/grpcpp/support/proto_buffer_writer.h
+ include/grpcpp/support/server_callback.h
include/grpcpp/support/slice.h
include/grpcpp/support/status.h
include/grpcpp/support/status_code_enum.h
@@ -4602,6 +4631,8 @@ foreach(_hdr
include/grpcpp/impl/codegen/byte_buffer.h
include/grpcpp/impl/codegen/call.h
include/grpcpp/impl/codegen/call_hook.h
+ include/grpcpp/impl/codegen/call_op_set.h
+ include/grpcpp/impl/codegen/call_op_set_interface.h
include/grpcpp/impl/codegen/callback_common.h
include/grpcpp/impl/codegen/channel_interface.h
include/grpcpp/impl/codegen/client_callback.h
@@ -4614,14 +4645,18 @@ foreach(_hdr
include/grpcpp/impl/codegen/core_codegen_interface.h
include/grpcpp/impl/codegen/create_auth_context.h
include/grpcpp/impl/codegen/grpc_library.h
+ include/grpcpp/impl/codegen/intercepted_channel.h
include/grpcpp/impl/codegen/interceptor.h
+ include/grpcpp/impl/codegen/interceptor_common.h
include/grpcpp/impl/codegen/metadata_map.h
include/grpcpp/impl/codegen/method_handler_impl.h
include/grpcpp/impl/codegen/rpc_method.h
include/grpcpp/impl/codegen/rpc_service_method.h
include/grpcpp/impl/codegen/security/auth_context.h
include/grpcpp/impl/codegen/serialization_traits.h
+ include/grpcpp/impl/codegen/server_callback.h
include/grpcpp/impl/codegen/server_context.h
+ include/grpcpp/impl/codegen/server_interceptor.h
include/grpcpp/impl/codegen/server_interface.h
include/grpcpp/impl/codegen/service_type.h
include/grpcpp/impl/codegen/slice.h
@@ -12405,6 +12440,47 @@ target_link_libraries(client_crash_test_server
endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS)
+add_executable(client_interceptors_end2end_test
+ test/cpp/end2end/client_interceptors_end2end_test.cc
+ third_party/googletest/googletest/src/gtest-all.cc
+ third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(client_interceptors_end2end_test
+ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+ PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+ PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+ PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+ PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+ PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+ PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+ PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+ PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
+ PRIVATE third_party/googletest/googletest/include
+ PRIVATE third_party/googletest/googletest
+ PRIVATE third_party/googletest/googlemock/include
+ PRIVATE third_party/googletest/googlemock
+ PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(client_interceptors_end2end_test
+ ${_gRPC_PROTOBUF_LIBRARIES}
+ ${_gRPC_ALLTARGETS_LIBRARIES}
+ grpc++_test_util
+ grpc_test_util
+ grpc++
+ grpc
+ gpr_test_util
+ gpr
+ ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
add_executable(client_lb_end2end_test
test/cpp/end2end/client_lb_end2end_test.cc
third_party/googletest/googletest/src/gtest-all.cc
@@ -15275,6 +15351,47 @@ target_link_libraries(server_early_return_test
endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS)
+add_executable(server_interceptors_end2end_test
+ test/cpp/end2end/server_interceptors_end2end_test.cc
+ third_party/googletest/googletest/src/gtest-all.cc
+ third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(server_interceptors_end2end_test
+ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+ PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+ PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+ PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+ PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+ PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+ PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+ PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+ PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
+ PRIVATE third_party/googletest/googletest/include
+ PRIVATE third_party/googletest/googletest
+ PRIVATE third_party/googletest/googlemock/include
+ PRIVATE third_party/googletest/googlemock
+ PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(server_interceptors_end2end_test
+ ${_gRPC_PROTOBUF_LIBRARIES}
+ ${_gRPC_ALLTARGETS_LIBRARIES}
+ grpc++_test_util
+ grpc_test_util
+ grpc++
+ grpc
+ gpr_test_util
+ gpr
+ ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
add_executable(server_request_call_test
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo_messages.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo_messages.grpc.pb.cc
diff --git a/Makefile b/Makefile
index d0a3f86383..d1f8762018 100644
--- a/Makefile
+++ b/Makefile
@@ -1164,6 +1164,7 @@ client_callback_end2end_test: $(BINDIR)/$(CONFIG)/client_callback_end2end_test
client_channel_stress_test: $(BINDIR)/$(CONFIG)/client_channel_stress_test
client_crash_test: $(BINDIR)/$(CONFIG)/client_crash_test
client_crash_test_server: $(BINDIR)/$(CONFIG)/client_crash_test_server
+client_interceptors_end2end_test: $(BINDIR)/$(CONFIG)/client_interceptors_end2end_test
client_lb_end2end_test: $(BINDIR)/$(CONFIG)/client_lb_end2end_test
codegen_test_full: $(BINDIR)/$(CONFIG)/codegen_test_full
codegen_test_minimal: $(BINDIR)/$(CONFIG)/codegen_test_minimal
@@ -1229,6 +1230,7 @@ server_context_test_spouse_test: $(BINDIR)/$(CONFIG)/server_context_test_spouse_
server_crash_test: $(BINDIR)/$(CONFIG)/server_crash_test
server_crash_test_client: $(BINDIR)/$(CONFIG)/server_crash_test_client
server_early_return_test: $(BINDIR)/$(CONFIG)/server_early_return_test
+server_interceptors_end2end_test: $(BINDIR)/$(CONFIG)/server_interceptors_end2end_test
server_request_call_test: $(BINDIR)/$(CONFIG)/server_request_call_test
shutdown_test: $(BINDIR)/$(CONFIG)/shutdown_test
slice_hash_table_test: $(BINDIR)/$(CONFIG)/slice_hash_table_test
@@ -1667,6 +1669,7 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/client_channel_stress_test \
$(BINDIR)/$(CONFIG)/client_crash_test \
$(BINDIR)/$(CONFIG)/client_crash_test_server \
+ $(BINDIR)/$(CONFIG)/client_interceptors_end2end_test \
$(BINDIR)/$(CONFIG)/client_lb_end2end_test \
$(BINDIR)/$(CONFIG)/codegen_test_full \
$(BINDIR)/$(CONFIG)/codegen_test_minimal \
@@ -1725,6 +1728,7 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/server_crash_test \
$(BINDIR)/$(CONFIG)/server_crash_test_client \
$(BINDIR)/$(CONFIG)/server_early_return_test \
+ $(BINDIR)/$(CONFIG)/server_interceptors_end2end_test \
$(BINDIR)/$(CONFIG)/server_request_call_test \
$(BINDIR)/$(CONFIG)/shutdown_test \
$(BINDIR)/$(CONFIG)/slice_hash_table_test \
@@ -1848,6 +1852,7 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/client_channel_stress_test \
$(BINDIR)/$(CONFIG)/client_crash_test \
$(BINDIR)/$(CONFIG)/client_crash_test_server \
+ $(BINDIR)/$(CONFIG)/client_interceptors_end2end_test \
$(BINDIR)/$(CONFIG)/client_lb_end2end_test \
$(BINDIR)/$(CONFIG)/codegen_test_full \
$(BINDIR)/$(CONFIG)/codegen_test_minimal \
@@ -1906,6 +1911,7 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/server_crash_test \
$(BINDIR)/$(CONFIG)/server_crash_test_client \
$(BINDIR)/$(CONFIG)/server_early_return_test \
+ $(BINDIR)/$(CONFIG)/server_interceptors_end2end_test \
$(BINDIR)/$(CONFIG)/server_request_call_test \
$(BINDIR)/$(CONFIG)/shutdown_test \
$(BINDIR)/$(CONFIG)/slice_hash_table_test \
@@ -2306,6 +2312,8 @@ test_cxx: buildtests_cxx
$(Q) $(BINDIR)/$(CONFIG)/client_channel_stress_test || ( echo test client_channel_stress_test failed ; exit 1 )
$(E) "[RUN] Testing client_crash_test"
$(Q) $(BINDIR)/$(CONFIG)/client_crash_test || ( echo test client_crash_test failed ; exit 1 )
+ $(E) "[RUN] Testing client_interceptors_end2end_test"
+ $(Q) $(BINDIR)/$(CONFIG)/client_interceptors_end2end_test || ( echo test client_interceptors_end2end_test failed ; exit 1 )
$(E) "[RUN] Testing client_lb_end2end_test"
$(Q) $(BINDIR)/$(CONFIG)/client_lb_end2end_test || ( echo test client_lb_end2end_test failed ; exit 1 )
$(E) "[RUN] Testing codegen_test_full"
@@ -2396,6 +2404,8 @@ test_cxx: buildtests_cxx
$(Q) $(BINDIR)/$(CONFIG)/server_crash_test || ( echo test server_crash_test failed ; exit 1 )
$(E) "[RUN] Testing server_early_return_test"
$(Q) $(BINDIR)/$(CONFIG)/server_early_return_test || ( echo test server_early_return_test failed ; exit 1 )
+ $(E) "[RUN] Testing server_interceptors_end2end_test"
+ $(Q) $(BINDIR)/$(CONFIG)/server_interceptors_end2end_test || ( echo test server_interceptors_end2end_test failed ; exit 1 )
$(E) "[RUN] Testing server_request_call_test"
$(Q) $(BINDIR)/$(CONFIG)/server_request_call_test || ( echo test server_request_call_test failed ; exit 1 )
$(E) "[RUN] Testing shutdown_test"
@@ -5361,6 +5371,7 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/support/config.h \
include/grpcpp/support/proto_buffer_reader.h \
include/grpcpp/support/proto_buffer_writer.h \
+ include/grpcpp/support/server_callback.h \
include/grpcpp/support/slice.h \
include/grpcpp/support/status.h \
include/grpcpp/support/status_code_enum.h \
@@ -5455,6 +5466,8 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/impl/codegen/byte_buffer.h \
include/grpcpp/impl/codegen/call.h \
include/grpcpp/impl/codegen/call_hook.h \
+ include/grpcpp/impl/codegen/call_op_set.h \
+ include/grpcpp/impl/codegen/call_op_set_interface.h \
include/grpcpp/impl/codegen/callback_common.h \
include/grpcpp/impl/codegen/channel_interface.h \
include/grpcpp/impl/codegen/client_callback.h \
@@ -5467,14 +5480,18 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/impl/codegen/core_codegen_interface.h \
include/grpcpp/impl/codegen/create_auth_context.h \
include/grpcpp/impl/codegen/grpc_library.h \
+ include/grpcpp/impl/codegen/intercepted_channel.h \
include/grpcpp/impl/codegen/interceptor.h \
+ include/grpcpp/impl/codegen/interceptor_common.h \
include/grpcpp/impl/codegen/metadata_map.h \
include/grpcpp/impl/codegen/method_handler_impl.h \
include/grpcpp/impl/codegen/rpc_method.h \
include/grpcpp/impl/codegen/rpc_service_method.h \
include/grpcpp/impl/codegen/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \
+ include/grpcpp/impl/codegen/server_callback.h \
include/grpcpp/impl/codegen/server_context.h \
+ include/grpcpp/impl/codegen/server_interceptor.h \
include/grpcpp/impl/codegen/server_interface.h \
include/grpcpp/impl/codegen/service_type.h \
include/grpcpp/impl/codegen/slice.h \
@@ -5945,6 +5962,7 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/support/config.h \
include/grpcpp/support/proto_buffer_reader.h \
include/grpcpp/support/proto_buffer_writer.h \
+ include/grpcpp/support/server_callback.h \
include/grpcpp/support/slice.h \
include/grpcpp/support/status.h \
include/grpcpp/support/status_code_enum.h \
@@ -6039,6 +6057,8 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/impl/codegen/byte_buffer.h \
include/grpcpp/impl/codegen/call.h \
include/grpcpp/impl/codegen/call_hook.h \
+ include/grpcpp/impl/codegen/call_op_set.h \
+ include/grpcpp/impl/codegen/call_op_set_interface.h \
include/grpcpp/impl/codegen/callback_common.h \
include/grpcpp/impl/codegen/channel_interface.h \
include/grpcpp/impl/codegen/client_callback.h \
@@ -6051,14 +6071,18 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/impl/codegen/core_codegen_interface.h \
include/grpcpp/impl/codegen/create_auth_context.h \
include/grpcpp/impl/codegen/grpc_library.h \
+ include/grpcpp/impl/codegen/intercepted_channel.h \
include/grpcpp/impl/codegen/interceptor.h \
+ include/grpcpp/impl/codegen/interceptor_common.h \
include/grpcpp/impl/codegen/metadata_map.h \
include/grpcpp/impl/codegen/method_handler_impl.h \
include/grpcpp/impl/codegen/rpc_method.h \
include/grpcpp/impl/codegen/rpc_service_method.h \
include/grpcpp/impl/codegen/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \
+ include/grpcpp/impl/codegen/server_callback.h \
include/grpcpp/impl/codegen/server_context.h \
+ include/grpcpp/impl/codegen/server_interceptor.h \
include/grpcpp/impl/codegen/server_interface.h \
include/grpcpp/impl/codegen/service_type.h \
include/grpcpp/impl/codegen/slice.h \
@@ -6433,6 +6457,8 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/impl/codegen/byte_buffer.h \
include/grpcpp/impl/codegen/call.h \
include/grpcpp/impl/codegen/call_hook.h \
+ include/grpcpp/impl/codegen/call_op_set.h \
+ include/grpcpp/impl/codegen/call_op_set_interface.h \
include/grpcpp/impl/codegen/callback_common.h \
include/grpcpp/impl/codegen/channel_interface.h \
include/grpcpp/impl/codegen/client_callback.h \
@@ -6445,14 +6471,18 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/impl/codegen/core_codegen_interface.h \
include/grpcpp/impl/codegen/create_auth_context.h \
include/grpcpp/impl/codegen/grpc_library.h \
+ include/grpcpp/impl/codegen/intercepted_channel.h \
include/grpcpp/impl/codegen/interceptor.h \
+ include/grpcpp/impl/codegen/interceptor_common.h \
include/grpcpp/impl/codegen/metadata_map.h \
include/grpcpp/impl/codegen/method_handler_impl.h \
include/grpcpp/impl/codegen/rpc_method.h \
include/grpcpp/impl/codegen/rpc_service_method.h \
include/grpcpp/impl/codegen/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \
+ include/grpcpp/impl/codegen/server_callback.h \
include/grpcpp/impl/codegen/server_context.h \
+ include/grpcpp/impl/codegen/server_interceptor.h \
include/grpcpp/impl/codegen/server_interface.h \
include/grpcpp/impl/codegen/service_type.h \
include/grpcpp/impl/codegen/slice.h \
@@ -6591,6 +6621,8 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/impl/codegen/byte_buffer.h \
include/grpcpp/impl/codegen/call.h \
include/grpcpp/impl/codegen/call_hook.h \
+ include/grpcpp/impl/codegen/call_op_set.h \
+ include/grpcpp/impl/codegen/call_op_set_interface.h \
include/grpcpp/impl/codegen/callback_common.h \
include/grpcpp/impl/codegen/channel_interface.h \
include/grpcpp/impl/codegen/client_callback.h \
@@ -6603,14 +6635,18 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/impl/codegen/core_codegen_interface.h \
include/grpcpp/impl/codegen/create_auth_context.h \
include/grpcpp/impl/codegen/grpc_library.h \
+ include/grpcpp/impl/codegen/intercepted_channel.h \
include/grpcpp/impl/codegen/interceptor.h \
+ include/grpcpp/impl/codegen/interceptor_common.h \
include/grpcpp/impl/codegen/metadata_map.h \
include/grpcpp/impl/codegen/method_handler_impl.h \
include/grpcpp/impl/codegen/rpc_method.h \
include/grpcpp/impl/codegen/rpc_service_method.h \
include/grpcpp/impl/codegen/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \
+ include/grpcpp/impl/codegen/server_callback.h \
include/grpcpp/impl/codegen/server_context.h \
+ include/grpcpp/impl/codegen/server_interceptor.h \
include/grpcpp/impl/codegen/server_interface.h \
include/grpcpp/impl/codegen/service_type.h \
include/grpcpp/impl/codegen/slice.h \
@@ -6827,6 +6863,7 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/support/config.h \
include/grpcpp/support/proto_buffer_reader.h \
include/grpcpp/support/proto_buffer_writer.h \
+ include/grpcpp/support/server_callback.h \
include/grpcpp/support/slice.h \
include/grpcpp/support/status.h \
include/grpcpp/support/status_code_enum.h \
@@ -6921,6 +6958,8 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/impl/codegen/byte_buffer.h \
include/grpcpp/impl/codegen/call.h \
include/grpcpp/impl/codegen/call_hook.h \
+ include/grpcpp/impl/codegen/call_op_set.h \
+ include/grpcpp/impl/codegen/call_op_set_interface.h \
include/grpcpp/impl/codegen/callback_common.h \
include/grpcpp/impl/codegen/channel_interface.h \
include/grpcpp/impl/codegen/client_callback.h \
@@ -6933,14 +6972,18 @@ PUBLIC_HEADERS_CXX += \
include/grpcpp/impl/codegen/core_codegen_interface.h \
include/grpcpp/impl/codegen/create_auth_context.h \
include/grpcpp/impl/codegen/grpc_library.h \
+ include/grpcpp/impl/codegen/intercepted_channel.h \
include/grpcpp/impl/codegen/interceptor.h \
+ include/grpcpp/impl/codegen/interceptor_common.h \
include/grpcpp/impl/codegen/metadata_map.h \
include/grpcpp/impl/codegen/method_handler_impl.h \
include/grpcpp/impl/codegen/rpc_method.h \
include/grpcpp/impl/codegen/rpc_service_method.h \
include/grpcpp/impl/codegen/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \
+ include/grpcpp/impl/codegen/server_callback.h \
include/grpcpp/impl/codegen/server_context.h \
+ include/grpcpp/impl/codegen/server_interceptor.h \
include/grpcpp/impl/codegen/server_interface.h \
include/grpcpp/impl/codegen/service_type.h \
include/grpcpp/impl/codegen/slice.h \
@@ -17279,6 +17322,49 @@ endif
endif
+CLIENT_INTERCEPTORS_END2END_TEST_SRC = \
+ test/cpp/end2end/client_interceptors_end2end_test.cc \
+
+CLIENT_INTERCEPTORS_END2END_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CLIENT_INTERCEPTORS_END2END_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/client_interceptors_end2end_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/client_interceptors_end2end_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/client_interceptors_end2end_test: $(PROTOBUF_DEP) $(CLIENT_INTERCEPTORS_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+ $(E) "[LD] Linking $@"
+ $(Q) mkdir -p `dirname $@`
+ $(Q) $(LDXX) $(LDFLAGS) $(CLIENT_INTERCEPTORS_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/client_interceptors_end2end_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/client_interceptors_end2end_test.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_client_interceptors_end2end_test: $(CLIENT_INTERCEPTORS_END2END_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(CLIENT_INTERCEPTORS_END2END_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
CLIENT_LB_END2END_TEST_SRC = \
test/cpp/end2end/client_lb_end2end_test.cc \
@@ -20064,6 +20150,49 @@ endif
endif
+SERVER_INTERCEPTORS_END2END_TEST_SRC = \
+ test/cpp/end2end/server_interceptors_end2end_test.cc \
+
+SERVER_INTERCEPTORS_END2END_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SERVER_INTERCEPTORS_END2END_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/server_interceptors_end2end_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
+
+$(BINDIR)/$(CONFIG)/server_interceptors_end2end_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/server_interceptors_end2end_test: $(PROTOBUF_DEP) $(SERVER_INTERCEPTORS_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+ $(E) "[LD] Linking $@"
+ $(Q) mkdir -p `dirname $@`
+ $(Q) $(LDXX) $(LDFLAGS) $(SERVER_INTERCEPTORS_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/server_interceptors_end2end_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/server_interceptors_end2end_test.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_server_interceptors_end2end_test: $(SERVER_INTERCEPTORS_END2END_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(SERVER_INTERCEPTORS_END2END_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
SERVER_REQUEST_CALL_TEST_SRC = \
$(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc \
$(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc \
diff --git a/bazel/grpc_deps.bzl b/bazel/grpc_deps.bzl
index 4096720569..8626817855 100644
--- a/bazel/grpc_deps.bzl
+++ b/bazel/grpc_deps.bzl
@@ -102,7 +102,7 @@ def grpc_deps():
native.http_archive(
name = "boringssl",
# on the chromium-stable-with-bazel branch
- url = "https://boringssl.googlesource.com/boringssl/+archive/dcd3e6e6ecddf059adb48fca45bc7346a108bdd9.tar.gz",
+ url = "https://boringssl.googlesource.com/boringssl/+archive/afc30d43eef92979b05776ec0963c9cede5fb80f.tar.gz",
)
if "com_github_madler_zlib" not in native.existing_rules():
@@ -169,12 +169,12 @@ def grpc_deps():
if "com_github_bazelbuild_bazeltoolchains" not in native.existing_rules():
native.http_archive(
name = "com_github_bazelbuild_bazeltoolchains",
- strip_prefix = "bazel-toolchains-cdea5b8675914d0a354d89f108de5d28e54e0edc",
+ strip_prefix = "bazel-toolchains-280edaa6f93623074513d2b426068de42e62ea4d",
urls = [
- "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/cdea5b8675914d0a354d89f108de5d28e54e0edc.tar.gz",
- "https://github.com/bazelbuild/bazel-toolchains/archive/cdea5b8675914d0a354d89f108de5d28e54e0edc.tar.gz",
+ "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/280edaa6f93623074513d2b426068de42e62ea4d.tar.gz",
+ "https://github.com/bazelbuild/bazel-toolchains/archive/280edaa6f93623074513d2b426068de42e62ea4d.tar.gz",
],
- sha256 = "cefb6ccf86ca592baaa029bcef04148593c0efe8f734542f10293ea58f170715",
+ sha256 = "50c9df51f80cdf9ff8f2bc27620c155526b9ba67be95e8a686f32ff8898a06e2",
)
if "io_opencensus_cpp" not in native.existing_rules():
diff --git a/build.yaml b/build.yaml
index d8d51c1b94..75860076bd 100644
--- a/build.yaml
+++ b/build.yaml
@@ -1222,6 +1222,8 @@ filegroups:
- include/grpcpp/impl/codegen/byte_buffer.h
- include/grpcpp/impl/codegen/call.h
- include/grpcpp/impl/codegen/call_hook.h
+ - include/grpcpp/impl/codegen/call_op_set.h
+ - include/grpcpp/impl/codegen/call_op_set_interface.h
- include/grpcpp/impl/codegen/callback_common.h
- include/grpcpp/impl/codegen/channel_interface.h
- include/grpcpp/impl/codegen/client_callback.h
@@ -1234,14 +1236,18 @@ filegroups:
- include/grpcpp/impl/codegen/core_codegen_interface.h
- include/grpcpp/impl/codegen/create_auth_context.h
- include/grpcpp/impl/codegen/grpc_library.h
+ - include/grpcpp/impl/codegen/intercepted_channel.h
- include/grpcpp/impl/codegen/interceptor.h
+ - include/grpcpp/impl/codegen/interceptor_common.h
- include/grpcpp/impl/codegen/metadata_map.h
- include/grpcpp/impl/codegen/method_handler_impl.h
- include/grpcpp/impl/codegen/rpc_method.h
- include/grpcpp/impl/codegen/rpc_service_method.h
- include/grpcpp/impl/codegen/security/auth_context.h
- include/grpcpp/impl/codegen/serialization_traits.h
+ - include/grpcpp/impl/codegen/server_callback.h
- include/grpcpp/impl/codegen/server_context.h
+ - include/grpcpp/impl/codegen/server_interceptor.h
- include/grpcpp/impl/codegen/server_interface.h
- include/grpcpp/impl/codegen/service_type.h
- include/grpcpp/impl/codegen/slice.h
@@ -1358,6 +1364,7 @@ filegroups:
- include/grpcpp/support/config.h
- include/grpcpp/support/proto_buffer_reader.h
- include/grpcpp/support/proto_buffer_writer.h
+ - include/grpcpp/support/server_callback.h
- include/grpcpp/support/slice.h
- include/grpcpp/support/status.h
- include/grpcpp/support/status_code_enum.h
@@ -4523,6 +4530,22 @@ targets:
- grpc
- gpr_test_util
- gpr
+- name: client_interceptors_end2end_test
+ gtest: true
+ cpu_cost: 0.5
+ build: test
+ language: c++
+ headers:
+ - test/cpp/end2end/interceptors_util.h
+ src:
+ - test/cpp/end2end/client_interceptors_end2end_test.cc
+ deps:
+ - grpc++_test_util
+ - grpc_test_util
+ - grpc++
+ - grpc
+ - gpr_test_util
+ - gpr
- name: client_lb_end2end_test
gtest: true
build: test
@@ -5435,6 +5458,22 @@ targets:
- grpc
- gpr_test_util
- gpr
+- name: server_interceptors_end2end_test
+ gtest: true
+ cpu_cost: 0.5
+ build: test
+ language: c++
+ headers:
+ - test/cpp/end2end/interceptors_util.h
+ src:
+ - test/cpp/end2end/server_interceptors_end2end_test.cc
+ deps:
+ - grpc++_test_util
+ - grpc_test_util
+ - grpc++
+ - grpc
+ - gpr_test_util
+ - gpr
- name: server_request_call_test
gtest: true
build: test
diff --git a/doc/connectivity-semantics-and-api.md b/doc/connectivity-semantics-and-api.md
index dc30fe5348..44fdf050c6 100644
--- a/doc/connectivity-semantics-and-api.md
+++ b/doc/connectivity-semantics-and-api.md
@@ -23,9 +23,10 @@ make progress on one of the steps involved in name resolution, TCP connection
establishment or TLS handshake. This may be used as the initial state for channels upon
creation.
-READY: The channel has successfully established a connection all the way
-through TLS handshake (or equivalent) and all subsequent attempt to communicate
-have succeeded (or are pending without any known failure ).
+READY: The channel has successfully established a connection all the way through
+TLS handshake (or equivalent) and protocol-level (HTTP/2, etc) handshaking, and
+all subsequent attempt to communicate have succeeded (or are pending without any
+known failure).
TRANSIENT_FAILURE: There has been some transient failure (such as a TCP 3-way
handshake timing out or a socket error). Channels in this state will eventually
@@ -52,8 +53,8 @@ immediately. Pending RPCs may continue running till the application cancels them
Channels may enter this state either because the application explicitly requested
a shutdown or if a non-recoverable error has happened during attempts to connect
communicate . (As of 6/12/2015, there are no known errors (while connecting or
-communicating) that are classified as non-recoverable)
-Channels that enter this state never leave this state.
+communicating) that are classified as non-recoverable.) Channels that enter this
+state never leave this state.
The following table lists the legal transitions from one state to another and
corresponding reasons. Empty cells denote disallowed transitions.
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
index 187551da87..df2618e760 100644
--- a/gRPC-C++.podspec
+++ b/gRPC-C++.podspec
@@ -69,6 +69,9 @@ Pod::Spec.new do |s|
s.default_subspecs = 'Interface', 'Implementation'
+ # Certificates, to be able to establish TLS connections:
+ s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] }
+
s.header_mappings_dir = 'include/grpcpp'
s.subspec 'Interface' do |ss|
@@ -115,6 +118,7 @@ Pod::Spec.new do |s|
'include/grpcpp/support/config.h',
'include/grpcpp/support/proto_buffer_reader.h',
'include/grpcpp/support/proto_buffer_writer.h',
+ 'include/grpcpp/support/server_callback.h',
'include/grpcpp/support/slice.h',
'include/grpcpp/support/status.h',
'include/grpcpp/support/status_code_enum.h',
@@ -128,6 +132,8 @@ Pod::Spec.new do |s|
'include/grpcpp/impl/codegen/byte_buffer.h',
'include/grpcpp/impl/codegen/call.h',
'include/grpcpp/impl/codegen/call_hook.h',
+ 'include/grpcpp/impl/codegen/call_op_set.h',
+ 'include/grpcpp/impl/codegen/call_op_set_interface.h',
'include/grpcpp/impl/codegen/callback_common.h',
'include/grpcpp/impl/codegen/channel_interface.h',
'include/grpcpp/impl/codegen/client_callback.h',
@@ -140,14 +146,18 @@ Pod::Spec.new do |s|
'include/grpcpp/impl/codegen/core_codegen_interface.h',
'include/grpcpp/impl/codegen/create_auth_context.h',
'include/grpcpp/impl/codegen/grpc_library.h',
+ 'include/grpcpp/impl/codegen/intercepted_channel.h',
'include/grpcpp/impl/codegen/interceptor.h',
+ 'include/grpcpp/impl/codegen/interceptor_common.h',
'include/grpcpp/impl/codegen/metadata_map.h',
'include/grpcpp/impl/codegen/method_handler_impl.h',
'include/grpcpp/impl/codegen/rpc_method.h',
'include/grpcpp/impl/codegen/rpc_service_method.h',
'include/grpcpp/impl/codegen/security/auth_context.h',
'include/grpcpp/impl/codegen/serialization_traits.h',
+ 'include/grpcpp/impl/codegen/server_callback.h',
'include/grpcpp/impl/codegen/server_context.h',
+ 'include/grpcpp/impl/codegen/server_interceptor.h',
'include/grpcpp/impl/codegen/server_interface.h',
'include/grpcpp/impl/codegen/service_type.h',
'include/grpcpp/impl/codegen/slice.h',
diff --git a/grpc.def b/grpc.def
index 2379cf17e6..b3466c004d 100644
--- a/grpc.def
+++ b/grpc.def
@@ -75,6 +75,7 @@ EXPORTS
grpc_resource_quota_arg_vtable
grpc_channelz_get_top_channels
grpc_channelz_get_servers
+ grpc_channelz_get_server
grpc_channelz_get_server_sockets
grpc_channelz_get_channel
grpc_channelz_get_subchannel
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index 514d9ddc4b..d3b74cabab 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -506,6 +506,9 @@ GRPCAPI char* grpc_channelz_get_top_channels(intptr_t start_channel_id);
/* Gets all servers that exist in the process. */
GRPCAPI char* grpc_channelz_get_servers(intptr_t start_server_id);
+/* Returns a single Server, or else a NOT_FOUND code. */
+GRPCAPI char* grpc_channelz_get_server(intptr_t server_id);
+
/* Gets all server sockets that exist in the server. */
GRPCAPI char* grpc_channelz_get_server_sockets(intptr_t server_id,
intptr_t start_socket_id);
diff --git a/include/grpcpp/channel.h b/include/grpcpp/channel.h
index b7c9e354de..14209b85ee 100644
--- a/include/grpcpp/channel.h
+++ b/include/grpcpp/channel.h
@@ -20,6 +20,7 @@
#define GRPCPP_CHANNEL_H
#include <memory>
+#include <mutex>
#include <grpc/grpc.h>
#include <grpcpp/impl/call.h>
@@ -67,6 +68,7 @@ class Channel final : public ChannelInterface,
std::unique_ptr<std::vector<
std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>
interceptor_creators);
+ friend class internal::InterceptedChannel;
Channel(const grpc::string& host, grpc_channel* c_channel,
std::unique_ptr<std::vector<
std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>
@@ -87,6 +89,10 @@ class Channel final : public ChannelInterface,
CompletionQueue* CallbackCQ() override;
+ internal::Call CreateCallInternal(const internal::RpcMethod& method,
+ ClientContext* context, CompletionQueue* cq,
+ int interceptor_pos) override;
+
const grpc::string host_;
grpc_channel* const c_channel_; // owned
diff --git a/include/grpcpp/impl/codegen/async_stream.h b/include/grpcpp/impl/codegen/async_stream.h
index 6e58fd0eef..bfb2df4f23 100644
--- a/include/grpcpp/impl/codegen/async_stream.h
+++ b/include/grpcpp/impl/codegen/async_stream.h
@@ -276,7 +276,7 @@ class ClientAsyncReader final : public ClientAsyncReaderInterface<R> {
}
void StartCallInternal(void* tag) {
- init_ops_.SendInitialMetadata(context_->send_initial_metadata_,
+ init_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
init_ops_.set_output_tag(tag);
call_.PerformOps(&init_ops_);
@@ -441,7 +441,7 @@ class ClientAsyncWriter final : public ClientAsyncWriterInterface<W> {
}
void StartCallInternal(void* tag) {
- write_ops_.SendInitialMetadata(context_->send_initial_metadata_,
+ write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
// if corked bit is set in context, we just keep the initial metadata
// buffered up to coalesce with later message send. No op is performed.
@@ -612,7 +612,7 @@ class ClientAsyncReaderWriter final
}
void StartCallInternal(void* tag) {
- write_ops_.SendInitialMetadata(context_->send_initial_metadata_,
+ write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
// if corked bit is set in context, we just keep the initial metadata
// buffered up to coalesce with later message send. No op is performed.
@@ -710,7 +710,7 @@ class ServerAsyncReader final : public ServerAsyncReaderInterface<W, R> {
GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
meta_ops_.set_output_tag(tag);
- meta_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+ meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
meta_ops_.set_compression_level(ctx_->compression_level());
@@ -739,7 +739,7 @@ class ServerAsyncReader final : public ServerAsyncReaderInterface<W, R> {
void Finish(const W& msg, const Status& status, void* tag) override {
finish_ops_.set_output_tag(tag);
if (!ctx_->sent_initial_metadata_) {
- finish_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+ finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
finish_ops_.set_compression_level(ctx_->compression_level());
@@ -748,10 +748,10 @@ class ServerAsyncReader final : public ServerAsyncReaderInterface<W, R> {
}
// The response is dropped if the status is not OK.
if (status.ok()) {
- finish_ops_.ServerSendStatus(ctx_->trailing_metadata_,
+ finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_,
finish_ops_.SendMessage(msg));
} else {
- finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
+ finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
}
call_.PerformOps(&finish_ops_);
}
@@ -769,14 +769,14 @@ class ServerAsyncReader final : public ServerAsyncReaderInterface<W, R> {
GPR_CODEGEN_ASSERT(!status.ok());
finish_ops_.set_output_tag(tag);
if (!ctx_->sent_initial_metadata_) {
- finish_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+ finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
finish_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
- finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
+ finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
call_.PerformOps(&finish_ops_);
}
@@ -859,7 +859,7 @@ class ServerAsyncWriter final : public ServerAsyncWriterInterface<W> {
GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
meta_ops_.set_output_tag(tag);
- meta_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+ meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
meta_ops_.set_compression_level(ctx_->compression_level());
@@ -904,7 +904,7 @@ class ServerAsyncWriter final : public ServerAsyncWriterInterface<W> {
EnsureInitialMetadataSent(&write_ops_);
options.set_buffer_hint();
GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
- write_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
+ write_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
call_.PerformOps(&write_ops_);
}
@@ -922,7 +922,7 @@ class ServerAsyncWriter final : public ServerAsyncWriterInterface<W> {
void Finish(const Status& status, void* tag) override {
finish_ops_.set_output_tag(tag);
EnsureInitialMetadataSent(&finish_ops_);
- finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
+ finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
call_.PerformOps(&finish_ops_);
}
@@ -932,7 +932,7 @@ class ServerAsyncWriter final : public ServerAsyncWriterInterface<W> {
template <class T>
void EnsureInitialMetadataSent(T* ops) {
if (!ctx_->sent_initial_metadata_) {
- ops->SendInitialMetadata(ctx_->initial_metadata_,
+ ops->SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ops->set_compression_level(ctx_->compression_level());
@@ -1025,7 +1025,7 @@ class ServerAsyncReaderWriter final
GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
meta_ops_.set_output_tag(tag);
- meta_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+ meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
meta_ops_.set_compression_level(ctx_->compression_level());
@@ -1075,7 +1075,7 @@ class ServerAsyncReaderWriter final
EnsureInitialMetadataSent(&write_ops_);
options.set_buffer_hint();
GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
- write_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
+ write_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
call_.PerformOps(&write_ops_);
}
@@ -1094,7 +1094,7 @@ class ServerAsyncReaderWriter final
finish_ops_.set_output_tag(tag);
EnsureInitialMetadataSent(&finish_ops_);
- finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
+ finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
call_.PerformOps(&finish_ops_);
}
@@ -1106,7 +1106,7 @@ class ServerAsyncReaderWriter final
template <class T>
void EnsureInitialMetadataSent(T* ops) {
if (!ctx_->sent_initial_metadata_) {
- ops->SendInitialMetadata(ctx_->initial_metadata_,
+ ops->SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ops->set_compression_level(ctx_->compression_level());
diff --git a/include/grpcpp/impl/codegen/async_unary_call.h b/include/grpcpp/impl/codegen/async_unary_call.h
index 60ff8e2f05..89dcb12418 100644
--- a/include/grpcpp/impl/codegen/async_unary_call.h
+++ b/include/grpcpp/impl/codegen/async_unary_call.h
@@ -174,7 +174,7 @@ class ClientAsyncResponseReader final
}
void StartCallInternal() {
- single_buf.SendInitialMetadata(context_->send_initial_metadata_,
+ single_buf.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
}
@@ -214,7 +214,7 @@ class ServerAsyncResponseWriter final
GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
meta_buf_.set_output_tag(tag);
- meta_buf_.SendInitialMetadata(ctx_->initial_metadata_,
+ meta_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
meta_buf_.set_compression_level(ctx_->compression_level());
@@ -240,8 +240,9 @@ class ServerAsyncResponseWriter final
/// metadata.
void Finish(const W& msg, const Status& status, void* tag) {
finish_buf_.set_output_tag(tag);
+ finish_buf_.set_core_cq_tag(&finish_buf_);
if (!ctx_->sent_initial_metadata_) {
- finish_buf_.SendInitialMetadata(ctx_->initial_metadata_,
+ finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
finish_buf_.set_compression_level(ctx_->compression_level());
@@ -250,10 +251,10 @@ class ServerAsyncResponseWriter final
}
// The response is dropped if the status is not OK.
if (status.ok()) {
- finish_buf_.ServerSendStatus(ctx_->trailing_metadata_,
+ finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_,
finish_buf_.SendMessage(msg));
} else {
- finish_buf_.ServerSendStatus(ctx_->trailing_metadata_, status);
+ finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, status);
}
call_.PerformOps(&finish_buf_);
}
@@ -274,14 +275,14 @@ class ServerAsyncResponseWriter final
GPR_CODEGEN_ASSERT(!status.ok());
finish_buf_.set_output_tag(tag);
if (!ctx_->sent_initial_metadata_) {
- finish_buf_.SendInitialMetadata(ctx_->initial_metadata_,
+ finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
finish_buf_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
- finish_buf_.ServerSendStatus(ctx_->trailing_metadata_, status);
+ finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, status);
call_.PerformOps(&finish_buf_);
}
diff --git a/include/grpcpp/impl/codegen/byte_buffer.h b/include/grpcpp/impl/codegen/byte_buffer.h
index 8cc5158115..abba5549b8 100644
--- a/include/grpcpp/impl/codegen/byte_buffer.h
+++ b/include/grpcpp/impl/codegen/byte_buffer.h
@@ -45,11 +45,18 @@ template <class ServiceType, class RequestType, class ResponseType>
class RpcMethodHandler;
template <class ServiceType, class RequestType, class ResponseType>
class ServerStreamingHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class CallbackUnaryHandler;
template <StatusCode code>
class ErrorMethodHandler;
template <class R>
class DeserializeFuncType;
class GrpcByteBufferPeer;
+template <class ServiceType, class RequestType, class ResponseType>
+class RpcMethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler;
+
} // namespace internal
/// A sequence of bytes.
class ByteBuffer final {
@@ -141,11 +148,16 @@ class ByteBuffer final {
template <class R>
friend class internal::CallOpRecvMessage;
friend class internal::CallOpGenericRecvMessage;
- friend class internal::MethodHandler;
+ template <class ServiceType, class RequestType, class ResponseType>
+ friend class RpcMethodHandler;
+ template <class ServiceType, class RequestType, class ResponseType>
+ friend class ServerStreamingHandler;
template <class ServiceType, class RequestType, class ResponseType>
friend class internal::RpcMethodHandler;
template <class ServiceType, class RequestType, class ResponseType>
friend class internal::ServerStreamingHandler;
+ template <class ServiceType, class RequestType, class ResponseType>
+ friend class internal::CallbackUnaryHandler;
template <StatusCode code>
friend class internal::ErrorMethodHandler;
template <class R>
diff --git a/include/grpcpp/impl/codegen/call.h b/include/grpcpp/impl/codegen/call.h
index 789ea805a3..c040c30dd9 100644
--- a/include/grpcpp/impl/codegen/call.h
+++ b/include/grpcpp/impl/codegen/call.h
@@ -1,6 +1,6 @@
/*
*
- * Copyright 2015 gRPC authors.
+ * 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.
@@ -15,671 +15,31 @@
* limitations under the License.
*
*/
-
#ifndef GRPCPP_IMPL_CODEGEN_CALL_H
#define GRPCPP_IMPL_CODEGEN_CALL_H
-#include <assert.h>
-#include <cstring>
-#include <functional>
-#include <map>
-#include <memory>
-
-#include <grpcpp/impl/codegen/byte_buffer.h>
-#include <grpcpp/impl/codegen/call_hook.h>
-#include <grpcpp/impl/codegen/client_context.h>
-#include <grpcpp/impl/codegen/completion_queue_tag.h>
-#include <grpcpp/impl/codegen/config.h>
-#include <grpcpp/impl/codegen/core_codegen_interface.h>
-#include <grpcpp/impl/codegen/serialization_traits.h>
-#include <grpcpp/impl/codegen/slice.h>
-#include <grpcpp/impl/codegen/status.h>
-#include <grpcpp/impl/codegen/string_ref.h>
-
-#include <grpc/impl/codegen/atm.h>
-#include <grpc/impl/codegen/compression_types.h>
#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/call_hook.h>
namespace grpc {
-
-class ByteBuffer;
class CompletionQueue;
-extern CoreCodegenInterface* g_core_codegen_interface;
+namespace experimental {
+class ClientRpcInfo;
+class ServerRpcInfo;
+} // namespace experimental
namespace internal {
-class Call;
class CallHook;
-
-// TODO(yangg) if the map is changed before we send, the pointers will be a
-// mess. Make sure it does not happen.
-inline grpc_metadata* FillMetadataArray(
- const std::multimap<grpc::string, grpc::string>& metadata,
- size_t* metadata_count, const grpc::string& optional_error_details) {
- *metadata_count = metadata.size() + (optional_error_details.empty() ? 0 : 1);
- if (*metadata_count == 0) {
- return nullptr;
- }
- grpc_metadata* metadata_array =
- (grpc_metadata*)(g_core_codegen_interface->gpr_malloc(
- (*metadata_count) * sizeof(grpc_metadata)));
- size_t i = 0;
- for (auto iter = metadata.cbegin(); iter != metadata.cend(); ++iter, ++i) {
- metadata_array[i].key = SliceReferencingString(iter->first);
- metadata_array[i].value = SliceReferencingString(iter->second);
- }
- if (!optional_error_details.empty()) {
- metadata_array[i].key =
- g_core_codegen_interface->grpc_slice_from_static_buffer(
- kBinaryErrorDetailsKey, sizeof(kBinaryErrorDetailsKey) - 1);
- metadata_array[i].value = SliceReferencingString(optional_error_details);
- }
- return metadata_array;
-}
-} // namespace internal
-
-/// Per-message write options.
-class WriteOptions {
- public:
- WriteOptions() : flags_(0), last_message_(false) {}
- WriteOptions(const WriteOptions& other)
- : flags_(other.flags_), last_message_(other.last_message_) {}
-
- /// Clear all flags.
- inline void Clear() { flags_ = 0; }
-
- /// Returns raw flags bitset.
- inline uint32_t flags() const { return flags_; }
-
- /// Sets flag for the disabling of compression for the next message write.
- ///
- /// \sa GRPC_WRITE_NO_COMPRESS
- inline WriteOptions& set_no_compression() {
- SetBit(GRPC_WRITE_NO_COMPRESS);
- return *this;
- }
-
- /// Clears flag for the disabling of compression for the next message write.
- ///
- /// \sa GRPC_WRITE_NO_COMPRESS
- inline WriteOptions& clear_no_compression() {
- ClearBit(GRPC_WRITE_NO_COMPRESS);
- return *this;
- }
-
- /// Get value for the flag indicating whether compression for the next
- /// message write is forcefully disabled.
- ///
- /// \sa GRPC_WRITE_NO_COMPRESS
- inline bool get_no_compression() const {
- return GetBit(GRPC_WRITE_NO_COMPRESS);
- }
-
- /// Sets flag indicating that the write may be buffered and need not go out on
- /// the wire immediately.
- ///
- /// \sa GRPC_WRITE_BUFFER_HINT
- inline WriteOptions& set_buffer_hint() {
- SetBit(GRPC_WRITE_BUFFER_HINT);
- return *this;
- }
-
- /// Clears flag indicating that the write may be buffered and need not go out
- /// on the wire immediately.
- ///
- /// \sa GRPC_WRITE_BUFFER_HINT
- inline WriteOptions& clear_buffer_hint() {
- ClearBit(GRPC_WRITE_BUFFER_HINT);
- return *this;
- }
-
- /// Get value for the flag indicating that the write may be buffered and need
- /// not go out on the wire immediately.
- ///
- /// \sa GRPC_WRITE_BUFFER_HINT
- inline bool get_buffer_hint() const { return GetBit(GRPC_WRITE_BUFFER_HINT); }
-
- /// corked bit: aliases set_buffer_hint currently, with the intent that
- /// set_buffer_hint will be removed in the future
- inline WriteOptions& set_corked() {
- SetBit(GRPC_WRITE_BUFFER_HINT);
- return *this;
- }
-
- inline WriteOptions& clear_corked() {
- ClearBit(GRPC_WRITE_BUFFER_HINT);
- return *this;
- }
-
- inline bool is_corked() const { return GetBit(GRPC_WRITE_BUFFER_HINT); }
-
- /// last-message bit: indicates this is the last message in a stream
- /// client-side: makes Write the equivalent of performing Write, WritesDone
- /// in a single step
- /// server-side: hold the Write until the service handler returns (sync api)
- /// or until Finish is called (async api)
- inline WriteOptions& set_last_message() {
- last_message_ = true;
- return *this;
- }
-
- /// Clears flag indicating that this is the last message in a stream,
- /// disabling coalescing.
- inline WriteOptions& clear_last_message() {
- last_message_ = false;
- return *this;
- }
-
- /// Guarantee that all bytes have been written to the socket before completing
- /// this write (usually writes are completed when they pass flow control).
- inline WriteOptions& set_write_through() {
- SetBit(GRPC_WRITE_THROUGH);
- return *this;
- }
-
- inline bool is_write_through() const { return GetBit(GRPC_WRITE_THROUGH); }
-
- /// Get value for the flag indicating that this is the last message, and
- /// should be coalesced with trailing metadata.
- ///
- /// \sa GRPC_WRITE_LAST_MESSAGE
- bool is_last_message() const { return last_message_; }
-
- WriteOptions& operator=(const WriteOptions& rhs) {
- flags_ = rhs.flags_;
- return *this;
- }
-
- private:
- void SetBit(const uint32_t mask) { flags_ |= mask; }
-
- void ClearBit(const uint32_t mask) { flags_ &= ~mask; }
-
- bool GetBit(const uint32_t mask) const { return (flags_ & mask) != 0; }
-
- uint32_t flags_;
- bool last_message_;
-};
-
-namespace internal {
-/// Default argument for CallOpSet. I is unused by the class, but can be
-/// used for generating multiple names for the same thing.
-template <int I>
-class CallNoOp {
- protected:
- void AddOp(grpc_op* ops, size_t* nops) {}
- void FinishOp(bool* status) {}
-};
-
-class CallOpSendInitialMetadata {
- public:
- CallOpSendInitialMetadata() : send_(false) {
- maybe_compression_level_.is_set = false;
- }
-
- void SendInitialMetadata(
- const std::multimap<grpc::string, grpc::string>& metadata,
- uint32_t flags) {
- maybe_compression_level_.is_set = false;
- send_ = true;
- flags_ = flags;
- initial_metadata_ =
- FillMetadataArray(metadata, &initial_metadata_count_, "");
- }
-
- void set_compression_level(grpc_compression_level level) {
- maybe_compression_level_.is_set = true;
- maybe_compression_level_.level = level;
- }
-
- protected:
- void AddOp(grpc_op* ops, size_t* nops) {
- if (!send_) return;
- grpc_op* op = &ops[(*nops)++];
- op->op = GRPC_OP_SEND_INITIAL_METADATA;
- op->flags = flags_;
- op->reserved = NULL;
- op->data.send_initial_metadata.count = initial_metadata_count_;
- op->data.send_initial_metadata.metadata = initial_metadata_;
- op->data.send_initial_metadata.maybe_compression_level.is_set =
- maybe_compression_level_.is_set;
- if (maybe_compression_level_.is_set) {
- op->data.send_initial_metadata.maybe_compression_level.level =
- maybe_compression_level_.level;
- }
- }
- void FinishOp(bool* status) {
- if (!send_) return;
- g_core_codegen_interface->gpr_free(initial_metadata_);
- send_ = false;
- }
-
- bool send_;
- uint32_t flags_;
- size_t initial_metadata_count_;
- grpc_metadata* initial_metadata_;
- struct {
- bool is_set;
- grpc_compression_level level;
- } maybe_compression_level_;
-};
-
-class CallOpSendMessage {
- public:
- CallOpSendMessage() : send_buf_() {}
-
- /// Send \a message using \a options for the write. The \a options are cleared
- /// after use.
- template <class M>
- Status SendMessage(const M& message,
- WriteOptions options) GRPC_MUST_USE_RESULT;
-
- template <class M>
- Status SendMessage(const M& message) GRPC_MUST_USE_RESULT;
-
- protected:
- void AddOp(grpc_op* ops, size_t* nops) {
- if (!send_buf_.Valid()) return;
- grpc_op* op = &ops[(*nops)++];
- op->op = GRPC_OP_SEND_MESSAGE;
- op->flags = write_options_.flags();
- op->reserved = NULL;
- op->data.send_message.send_message = send_buf_.c_buffer();
- // Flags are per-message: clear them after use.
- write_options_.Clear();
- }
- void FinishOp(bool* status) { send_buf_.Clear(); }
-
- private:
- ByteBuffer send_buf_;
- WriteOptions write_options_;
-};
-
-template <class M>
-Status CallOpSendMessage::SendMessage(const M& message, WriteOptions options) {
- write_options_ = options;
- bool own_buf;
- // TODO(vjpai): Remove the void below when possible
- // The void in the template parameter below should not be needed
- // (since it should be implicit) but is needed due to an observed
- // difference in behavior between clang and gcc for certain internal users
- Status result = SerializationTraits<M, void>::Serialize(
- message, send_buf_.bbuf_ptr(), &own_buf);
- if (!own_buf) {
- send_buf_.Duplicate();
- }
- return result;
-}
-
-template <class M>
-Status CallOpSendMessage::SendMessage(const M& message) {
- return SendMessage(message, WriteOptions());
-}
-
-template <class R>
-class CallOpRecvMessage {
- public:
- CallOpRecvMessage()
- : got_message(false),
- message_(nullptr),
- allow_not_getting_message_(false) {}
-
- void RecvMessage(R* message) { message_ = message; }
-
- // Do not change status if no message is received.
- void AllowNoMessage() { allow_not_getting_message_ = true; }
-
- bool got_message;
-
- protected:
- void AddOp(grpc_op* ops, size_t* nops) {
- if (message_ == nullptr) return;
- grpc_op* op = &ops[(*nops)++];
- op->op = GRPC_OP_RECV_MESSAGE;
- op->flags = 0;
- op->reserved = NULL;
- op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
- }
-
- void FinishOp(bool* status) {
- if (message_ == nullptr) return;
- if (recv_buf_.Valid()) {
- if (*status) {
- got_message = *status =
- SerializationTraits<R>::Deserialize(recv_buf_.bbuf_ptr(), message_)
- .ok();
- recv_buf_.Release();
- } else {
- got_message = false;
- recv_buf_.Clear();
- }
- } else {
- got_message = false;
- if (!allow_not_getting_message_) {
- *status = false;
- }
- }
- message_ = nullptr;
- }
-
- private:
- R* message_;
- ByteBuffer recv_buf_;
- bool allow_not_getting_message_;
-};
-
-class DeserializeFunc {
- public:
- virtual Status Deserialize(ByteBuffer* buf) = 0;
- virtual ~DeserializeFunc() {}
-};
-
-template <class R>
-class DeserializeFuncType final : public DeserializeFunc {
- public:
- DeserializeFuncType(R* message) : message_(message) {}
- Status Deserialize(ByteBuffer* buf) override {
- return SerializationTraits<R>::Deserialize(buf->bbuf_ptr(), message_);
- }
-
- ~DeserializeFuncType() override {}
-
- private:
- R* message_; // Not a managed pointer because management is external to this
-};
-
-class CallOpGenericRecvMessage {
- public:
- CallOpGenericRecvMessage()
- : got_message(false), allow_not_getting_message_(false) {}
-
- template <class R>
- void RecvMessage(R* message) {
- // Use an explicit base class pointer to avoid resolution error in the
- // following unique_ptr::reset for some old implementations.
- DeserializeFunc* func = new DeserializeFuncType<R>(message);
- deserialize_.reset(func);
- }
-
- // Do not change status if no message is received.
- void AllowNoMessage() { allow_not_getting_message_ = true; }
-
- bool got_message;
-
- protected:
- void AddOp(grpc_op* ops, size_t* nops) {
- if (!deserialize_) return;
- grpc_op* op = &ops[(*nops)++];
- op->op = GRPC_OP_RECV_MESSAGE;
- op->flags = 0;
- op->reserved = NULL;
- op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
- }
-
- void FinishOp(bool* status) {
- if (!deserialize_) return;
- if (recv_buf_.Valid()) {
- if (*status) {
- got_message = true;
- *status = deserialize_->Deserialize(&recv_buf_).ok();
- recv_buf_.Release();
- } else {
- got_message = false;
- recv_buf_.Clear();
- }
- } else {
- got_message = false;
- if (!allow_not_getting_message_) {
- *status = false;
- }
- }
- deserialize_.reset();
- }
-
- private:
- std::unique_ptr<DeserializeFunc> deserialize_;
- ByteBuffer recv_buf_;
- bool allow_not_getting_message_;
-};
-
-class CallOpClientSendClose {
- public:
- CallOpClientSendClose() : send_(false) {}
-
- void ClientSendClose() { send_ = true; }
-
- protected:
- void AddOp(grpc_op* ops, size_t* nops) {
- if (!send_) return;
- grpc_op* op = &ops[(*nops)++];
- op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
- op->flags = 0;
- op->reserved = NULL;
- }
- void FinishOp(bool* status) { send_ = false; }
-
- private:
- bool send_;
-};
-
-class CallOpServerSendStatus {
- public:
- CallOpServerSendStatus() : send_status_available_(false) {}
-
- void ServerSendStatus(
- const std::multimap<grpc::string, grpc::string>& trailing_metadata,
- const Status& status) {
- send_error_details_ = status.error_details();
- trailing_metadata_ = FillMetadataArray(
- trailing_metadata, &trailing_metadata_count_, send_error_details_);
- send_status_available_ = true;
- send_status_code_ = static_cast<grpc_status_code>(status.error_code());
- send_error_message_ = status.error_message();
- }
-
- protected:
- void AddOp(grpc_op* ops, size_t* nops) {
- if (!send_status_available_) return;
- grpc_op* op = &ops[(*nops)++];
- op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
- op->data.send_status_from_server.trailing_metadata_count =
- trailing_metadata_count_;
- op->data.send_status_from_server.trailing_metadata = trailing_metadata_;
- op->data.send_status_from_server.status = send_status_code_;
- error_message_slice_ = SliceReferencingString(send_error_message_);
- op->data.send_status_from_server.status_details =
- send_error_message_.empty() ? nullptr : &error_message_slice_;
- op->flags = 0;
- op->reserved = NULL;
- }
-
- void FinishOp(bool* status) {
- if (!send_status_available_) return;
- g_core_codegen_interface->gpr_free(trailing_metadata_);
- send_status_available_ = false;
- }
-
- private:
- bool send_status_available_;
- grpc_status_code send_status_code_;
- grpc::string send_error_details_;
- grpc::string send_error_message_;
- size_t trailing_metadata_count_;
- grpc_metadata* trailing_metadata_;
- grpc_slice error_message_slice_;
-};
-
-class CallOpRecvInitialMetadata {
- public:
- CallOpRecvInitialMetadata() : metadata_map_(nullptr) {}
-
- void RecvInitialMetadata(ClientContext* context) {
- context->initial_metadata_received_ = true;
- metadata_map_ = &context->recv_initial_metadata_;
- }
-
- protected:
- void AddOp(grpc_op* ops, size_t* nops) {
- if (metadata_map_ == nullptr) return;
- grpc_op* op = &ops[(*nops)++];
- op->op = GRPC_OP_RECV_INITIAL_METADATA;
- op->data.recv_initial_metadata.recv_initial_metadata = metadata_map_->arr();
- op->flags = 0;
- op->reserved = NULL;
- }
-
- void FinishOp(bool* status) {
- if (metadata_map_ == nullptr) return;
- metadata_map_ = nullptr;
- }
-
- private:
- MetadataMap* metadata_map_;
-};
-
-class CallOpClientRecvStatus {
- public:
- CallOpClientRecvStatus()
- : recv_status_(nullptr), debug_error_string_(nullptr) {}
-
- void ClientRecvStatus(ClientContext* context, Status* status) {
- client_context_ = context;
- metadata_map_ = &client_context_->trailing_metadata_;
- recv_status_ = status;
- error_message_ = g_core_codegen_interface->grpc_empty_slice();
- }
-
- protected:
- void AddOp(grpc_op* ops, size_t* nops) {
- if (recv_status_ == nullptr) return;
- grpc_op* op = &ops[(*nops)++];
- op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
- op->data.recv_status_on_client.trailing_metadata = metadata_map_->arr();
- op->data.recv_status_on_client.status = &status_code_;
- op->data.recv_status_on_client.status_details = &error_message_;
- op->data.recv_status_on_client.error_string = &debug_error_string_;
- op->flags = 0;
- op->reserved = NULL;
- }
-
- void FinishOp(bool* status) {
- if (recv_status_ == nullptr) return;
- grpc::string binary_error_details = metadata_map_->GetBinaryErrorDetails();
- *recv_status_ =
- Status(static_cast<StatusCode>(status_code_),
- GRPC_SLICE_IS_EMPTY(error_message_)
- ? grpc::string()
- : grpc::string(GRPC_SLICE_START_PTR(error_message_),
- GRPC_SLICE_END_PTR(error_message_)),
- binary_error_details);
- client_context_->set_debug_error_string(
- debug_error_string_ != nullptr ? debug_error_string_ : "");
- g_core_codegen_interface->grpc_slice_unref(error_message_);
- if (debug_error_string_ != nullptr) {
- g_core_codegen_interface->gpr_free((void*)debug_error_string_);
- }
- recv_status_ = nullptr;
- }
-
- private:
- ClientContext* client_context_;
- MetadataMap* metadata_map_;
- Status* recv_status_;
- const char* debug_error_string_;
- grpc_status_code status_code_;
- grpc_slice error_message_;
-};
-
-/// An abstract collection of call ops, used to generate the
-/// grpc_call_op structure to pass down to the lower layers,
-/// and as it is-a CompletionQueueTag, also massages the final
-/// completion into the correct form for consumption in the C++
-/// API.
-class CallOpSetInterface : public CompletionQueueTag {
- public:
- /// Fills in grpc_op, starting from ops[*nops] and moving
- /// upwards.
- virtual void FillOps(grpc_call* call, grpc_op* ops, size_t* nops) = 0;
-
- /// Get the tag to be used at the core completion queue. Generally, the
- /// value of cq_tag will be "this". However, it can be overridden if we
- /// want core to process the tag differently (e.g., as a core callback)
- virtual void* cq_tag() = 0;
-};
-
-/// Primary implementation of CallOpSetInterface.
-/// Since we cannot use variadic templates, we declare slots up to
-/// the maximum count of ops we'll need in a set. We leverage the
-/// empty base class optimization to slim this class (especially
-/// when there are many unused slots used). To avoid duplicate base classes,
-/// the template parmeter for CallNoOp is varied by argument position.
-template <class Op1 = CallNoOp<1>, class Op2 = CallNoOp<2>,
- class Op3 = CallNoOp<3>, class Op4 = CallNoOp<4>,
- class Op5 = CallNoOp<5>, class Op6 = CallNoOp<6>>
-class CallOpSet : public CallOpSetInterface,
- public Op1,
- public Op2,
- public Op3,
- public Op4,
- public Op5,
- public Op6 {
- public:
- CallOpSet() : cq_tag_(this), return_tag_(this), call_(nullptr) {}
-
- // The copy constructor and assignment operator reset the value of
- // cq_tag_ and return_tag_ since those are only meaningful on a specific
- // object, not across objects.
- CallOpSet(const CallOpSet& other)
- : cq_tag_(this), return_tag_(this), call_(other.call_) {}
- CallOpSet& operator=(const CallOpSet& other) {
- cq_tag_ = this;
- return_tag_ = this;
- call_ = other.call_;
- return *this;
- }
-
- void FillOps(grpc_call* call, grpc_op* ops, size_t* nops) override {
- this->Op1::AddOp(ops, nops);
- this->Op2::AddOp(ops, nops);
- this->Op3::AddOp(ops, nops);
- this->Op4::AddOp(ops, nops);
- this->Op5::AddOp(ops, nops);
- this->Op6::AddOp(ops, nops);
- g_core_codegen_interface->grpc_call_ref(call);
- call_ = call;
- }
-
- bool FinalizeResult(void** tag, bool* status) override {
- this->Op1::FinishOp(status);
- this->Op2::FinishOp(status);
- this->Op3::FinishOp(status);
- this->Op4::FinishOp(status);
- this->Op5::FinishOp(status);
- this->Op6::FinishOp(status);
- *tag = return_tag_;
-
- g_core_codegen_interface->grpc_call_unref(call_);
- return true;
- }
-
- void set_output_tag(void* return_tag) { return_tag_ = return_tag; }
-
- void* cq_tag() override { return cq_tag_; }
-
- /// set_cq_tag is used to provide a different core CQ tag than "this".
- /// This is used for callback-based tags, where the core tag is the core
- /// callback function. It does not change the use or behavior of any other
- /// function (such as FinalizeResult)
- void set_cq_tag(void* cq_tag) { cq_tag_ = cq_tag; }
-
- private:
- void* cq_tag_;
- void* return_tag_;
- grpc_call* call_;
-};
+class CallOpSetInterface;
/// Straightforward wrapping of the C call object
class Call final {
public:
+ Call()
+ : call_hook_(nullptr),
+ cq_(nullptr),
+ call_(nullptr),
+ max_receive_message_size_(-1) {}
/** call is owned by the caller */
Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq)
: call_hook_(call_hook),
@@ -688,11 +48,20 @@ class Call final {
max_receive_message_size_(-1) {}
Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq,
- int max_receive_message_size)
+ experimental::ClientRpcInfo* rpc_info)
: call_hook_(call_hook),
cq_(cq),
call_(call),
- max_receive_message_size_(max_receive_message_size) {}
+ max_receive_message_size_(-1),
+ client_rpc_info_(rpc_info) {}
+
+ Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq,
+ int max_receive_message_size, experimental::ServerRpcInfo* rpc_info)
+ : call_hook_(call_hook),
+ cq_(cq),
+ call_(call),
+ max_receive_message_size_(max_receive_message_size),
+ server_rpc_info_(rpc_info) {}
void PerformOps(CallOpSetInterface* ops) {
call_hook_->PerformOpsOnCall(ops, this);
@@ -703,11 +72,21 @@ class Call final {
int max_receive_message_size() const { return max_receive_message_size_; }
+ experimental::ClientRpcInfo* client_rpc_info() const {
+ return client_rpc_info_;
+ }
+
+ experimental::ServerRpcInfo* server_rpc_info() const {
+ return server_rpc_info_;
+ }
+
private:
CallHook* call_hook_;
CompletionQueue* cq_;
grpc_call* call_;
int max_receive_message_size_;
+ experimental::ClientRpcInfo* client_rpc_info_ = nullptr;
+ experimental::ServerRpcInfo* server_rpc_info_ = nullptr;
};
} // namespace internal
} // namespace grpc
diff --git a/include/grpcpp/impl/codegen/call_op_set.h b/include/grpcpp/impl/codegen/call_op_set.h
new file mode 100644
index 0000000000..5c52b027b2
--- /dev/null
+++ b/include/grpcpp/impl/codegen/call_op_set.h
@@ -0,0 +1,920 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H
+#define GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H
+
+#include <assert.h>
+#include <array>
+#include <cstring>
+#include <functional>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/call_hook.h>
+#include <grpcpp/impl/codegen/call_op_set_interface.h>
+#include <grpcpp/impl/codegen/client_context.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/intercepted_channel.h>
+#include <grpcpp/impl/codegen/interceptor_common.h>
+#include <grpcpp/impl/codegen/serialization_traits.h>
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+
+#include <grpc/impl/codegen/atm.h>
+#include <grpc/impl/codegen/compression_types.h>
+#include <grpc/impl/codegen/grpc_types.h>
+
+namespace grpc {
+
+class CompletionQueue;
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+namespace internal {
+class Call;
+class CallHook;
+
+// TODO(yangg) if the map is changed before we send, the pointers will be a
+// mess. Make sure it does not happen.
+inline grpc_metadata* FillMetadataArray(
+ const std::multimap<grpc::string, grpc::string>& metadata,
+ size_t* metadata_count, const grpc::string& optional_error_details) {
+ *metadata_count = metadata.size() + (optional_error_details.empty() ? 0 : 1);
+ if (*metadata_count == 0) {
+ return nullptr;
+ }
+ grpc_metadata* metadata_array =
+ (grpc_metadata*)(g_core_codegen_interface->gpr_malloc(
+ (*metadata_count) * sizeof(grpc_metadata)));
+ size_t i = 0;
+ for (auto iter = metadata.cbegin(); iter != metadata.cend(); ++iter, ++i) {
+ metadata_array[i].key = SliceReferencingString(iter->first);
+ metadata_array[i].value = SliceReferencingString(iter->second);
+ }
+ if (!optional_error_details.empty()) {
+ metadata_array[i].key =
+ g_core_codegen_interface->grpc_slice_from_static_buffer(
+ kBinaryErrorDetailsKey, sizeof(kBinaryErrorDetailsKey) - 1);
+ metadata_array[i].value = SliceReferencingString(optional_error_details);
+ }
+ return metadata_array;
+}
+} // namespace internal
+
+/// Per-message write options.
+class WriteOptions {
+ public:
+ WriteOptions() : flags_(0), last_message_(false) {}
+ WriteOptions(const WriteOptions& other)
+ : flags_(other.flags_), last_message_(other.last_message_) {}
+
+ /// Clear all flags.
+ inline void Clear() { flags_ = 0; }
+
+ /// Returns raw flags bitset.
+ inline uint32_t flags() const { return flags_; }
+
+ /// Sets flag for the disabling of compression for the next message write.
+ ///
+ /// \sa GRPC_WRITE_NO_COMPRESS
+ inline WriteOptions& set_no_compression() {
+ SetBit(GRPC_WRITE_NO_COMPRESS);
+ return *this;
+ }
+
+ /// Clears flag for the disabling of compression for the next message write.
+ ///
+ /// \sa GRPC_WRITE_NO_COMPRESS
+ inline WriteOptions& clear_no_compression() {
+ ClearBit(GRPC_WRITE_NO_COMPRESS);
+ return *this;
+ }
+
+ /// Get value for the flag indicating whether compression for the next
+ /// message write is forcefully disabled.
+ ///
+ /// \sa GRPC_WRITE_NO_COMPRESS
+ inline bool get_no_compression() const {
+ return GetBit(GRPC_WRITE_NO_COMPRESS);
+ }
+
+ /// Sets flag indicating that the write may be buffered and need not go out on
+ /// the wire immediately.
+ ///
+ /// \sa GRPC_WRITE_BUFFER_HINT
+ inline WriteOptions& set_buffer_hint() {
+ SetBit(GRPC_WRITE_BUFFER_HINT);
+ return *this;
+ }
+
+ /// Clears flag indicating that the write may be buffered and need not go out
+ /// on the wire immediately.
+ ///
+ /// \sa GRPC_WRITE_BUFFER_HINT
+ inline WriteOptions& clear_buffer_hint() {
+ ClearBit(GRPC_WRITE_BUFFER_HINT);
+ return *this;
+ }
+
+ /// Get value for the flag indicating that the write may be buffered and need
+ /// not go out on the wire immediately.
+ ///
+ /// \sa GRPC_WRITE_BUFFER_HINT
+ inline bool get_buffer_hint() const { return GetBit(GRPC_WRITE_BUFFER_HINT); }
+
+ /// corked bit: aliases set_buffer_hint currently, with the intent that
+ /// set_buffer_hint will be removed in the future
+ inline WriteOptions& set_corked() {
+ SetBit(GRPC_WRITE_BUFFER_HINT);
+ return *this;
+ }
+
+ inline WriteOptions& clear_corked() {
+ ClearBit(GRPC_WRITE_BUFFER_HINT);
+ return *this;
+ }
+
+ inline bool is_corked() const { return GetBit(GRPC_WRITE_BUFFER_HINT); }
+
+ /// last-message bit: indicates this is the last message in a stream
+ /// client-side: makes Write the equivalent of performing Write, WritesDone
+ /// in a single step
+ /// server-side: hold the Write until the service handler returns (sync api)
+ /// or until Finish is called (async api)
+ inline WriteOptions& set_last_message() {
+ last_message_ = true;
+ return *this;
+ }
+
+ /// Clears flag indicating that this is the last message in a stream,
+ /// disabling coalescing.
+ inline WriteOptions& clear_last_message() {
+ last_message_ = false;
+ return *this;
+ }
+
+ /// Guarantee that all bytes have been written to the socket before completing
+ /// this write (usually writes are completed when they pass flow control).
+ inline WriteOptions& set_write_through() {
+ SetBit(GRPC_WRITE_THROUGH);
+ return *this;
+ }
+
+ inline bool is_write_through() const { return GetBit(GRPC_WRITE_THROUGH); }
+
+ /// Get value for the flag indicating that this is the last message, and
+ /// should be coalesced with trailing metadata.
+ ///
+ /// \sa GRPC_WRITE_LAST_MESSAGE
+ bool is_last_message() const { return last_message_; }
+
+ WriteOptions& operator=(const WriteOptions& rhs) {
+ flags_ = rhs.flags_;
+ return *this;
+ }
+
+ private:
+ void SetBit(const uint32_t mask) { flags_ |= mask; }
+
+ void ClearBit(const uint32_t mask) { flags_ &= ~mask; }
+
+ bool GetBit(const uint32_t mask) const { return (flags_ & mask) != 0; }
+
+ uint32_t flags_;
+ bool last_message_;
+};
+
+namespace internal {
+
+/// Default argument for CallOpSet. I is unused by the class, but can be
+/// used for generating multiple names for the same thing.
+template <int I>
+class CallNoOp {
+ protected:
+ void AddOp(grpc_op* ops, size_t* nops) {}
+ void FinishOp(bool* status) {}
+ void SetInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {}
+ void SetFinishInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {}
+ void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
+ }
+};
+
+class CallOpSendInitialMetadata {
+ public:
+ CallOpSendInitialMetadata() : send_(false) {
+ maybe_compression_level_.is_set = false;
+ }
+
+ void SendInitialMetadata(std::multimap<grpc::string, grpc::string>* metadata,
+ uint32_t flags) {
+ maybe_compression_level_.is_set = false;
+ send_ = true;
+ flags_ = flags;
+ metadata_map_ = metadata;
+ }
+
+ void set_compression_level(grpc_compression_level level) {
+ maybe_compression_level_.is_set = true;
+ maybe_compression_level_.level = level;
+ }
+
+ protected:
+ void AddOp(grpc_op* ops, size_t* nops) {
+ if (!send_ || hijacked_) return;
+ grpc_op* op = &ops[(*nops)++];
+ op->op = GRPC_OP_SEND_INITIAL_METADATA;
+ op->flags = flags_;
+ op->reserved = NULL;
+ initial_metadata_ =
+ FillMetadataArray(*metadata_map_, &initial_metadata_count_, "");
+ op->data.send_initial_metadata.count = initial_metadata_count_;
+ op->data.send_initial_metadata.metadata = initial_metadata_;
+ op->data.send_initial_metadata.maybe_compression_level.is_set =
+ maybe_compression_level_.is_set;
+ if (maybe_compression_level_.is_set) {
+ op->data.send_initial_metadata.maybe_compression_level.level =
+ maybe_compression_level_.level;
+ }
+ }
+ void FinishOp(bool* status) {
+ if (!send_ || hijacked_) return;
+ g_core_codegen_interface->gpr_free(initial_metadata_);
+ send_ = false;
+ }
+
+ void SetInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {
+ if (!send_) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA);
+ interceptor_methods->SetSendInitialMetadata(metadata_map_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {}
+
+ void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
+ hijacked_ = true;
+ }
+
+ bool hijacked_ = false;
+ bool send_;
+ uint32_t flags_;
+ size_t initial_metadata_count_;
+ std::multimap<grpc::string, grpc::string>* metadata_map_;
+ grpc_metadata* initial_metadata_;
+ struct {
+ bool is_set;
+ grpc_compression_level level;
+ } maybe_compression_level_;
+};
+
+class CallOpSendMessage {
+ public:
+ CallOpSendMessage() : send_buf_() {}
+
+ /// Send \a message using \a options for the write. The \a options are cleared
+ /// after use.
+ template <class M>
+ Status SendMessage(const M& message,
+ WriteOptions options) GRPC_MUST_USE_RESULT;
+
+ template <class M>
+ Status SendMessage(const M& message) GRPC_MUST_USE_RESULT;
+
+ protected:
+ void AddOp(grpc_op* ops, size_t* nops) {
+ if (!send_buf_.Valid() || hijacked_) return;
+ grpc_op* op = &ops[(*nops)++];
+ op->op = GRPC_OP_SEND_MESSAGE;
+ op->flags = write_options_.flags();
+ op->reserved = NULL;
+ op->data.send_message.send_message = send_buf_.c_buffer();
+ // Flags are per-message: clear them after use.
+ write_options_.Clear();
+ }
+ void FinishOp(bool* status) { send_buf_.Clear(); }
+
+ void SetInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {
+ if (!send_buf_.Valid()) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_MESSAGE);
+ interceptor_methods->SetSendMessage(&send_buf_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {}
+
+ void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
+ hijacked_ = true;
+ }
+
+ private:
+ bool hijacked_ = false;
+ ByteBuffer send_buf_;
+ WriteOptions write_options_;
+};
+
+template <class M>
+Status CallOpSendMessage::SendMessage(const M& message, WriteOptions options) {
+ write_options_ = options;
+ bool own_buf;
+ // TODO(vjpai): Remove the void below when possible
+ // The void in the template parameter below should not be needed
+ // (since it should be implicit) but is needed due to an observed
+ // difference in behavior between clang and gcc for certain internal users
+ Status result = SerializationTraits<M, void>::Serialize(
+ message, send_buf_.bbuf_ptr(), &own_buf);
+ if (!own_buf) {
+ send_buf_.Duplicate();
+ }
+ return result;
+}
+
+template <class M>
+Status CallOpSendMessage::SendMessage(const M& message) {
+ return SendMessage(message, WriteOptions());
+}
+
+template <class R>
+class CallOpRecvMessage {
+ public:
+ CallOpRecvMessage()
+ : got_message(false),
+ message_(nullptr),
+ allow_not_getting_message_(false) {}
+
+ void RecvMessage(R* message) { message_ = message; }
+
+ // Do not change status if no message is received.
+ void AllowNoMessage() { allow_not_getting_message_ = true; }
+
+ bool got_message;
+
+ protected:
+ void AddOp(grpc_op* ops, size_t* nops) {
+ if (message_ == nullptr || hijacked_) return;
+ grpc_op* op = &ops[(*nops)++];
+ op->op = GRPC_OP_RECV_MESSAGE;
+ op->flags = 0;
+ op->reserved = NULL;
+ op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
+ }
+
+ void FinishOp(bool* status) {
+ if (message_ == nullptr || hijacked_) return;
+ if (recv_buf_.Valid()) {
+ if (*status) {
+ got_message = *status =
+ SerializationTraits<R>::Deserialize(recv_buf_.bbuf_ptr(), message_)
+ .ok();
+ recv_buf_.Release();
+ } else {
+ got_message = false;
+ recv_buf_.Clear();
+ }
+ } else {
+ got_message = false;
+ if (!allow_not_getting_message_) {
+ *status = false;
+ }
+ }
+ message_ = nullptr;
+ }
+
+ void SetInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {
+ interceptor_methods->SetRecvMessage(message_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {
+ if (!got_message) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+ }
+ void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
+ hijacked_ = true;
+ if (message_ == nullptr) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_RECV_MESSAGE);
+ got_message = true;
+ }
+
+ private:
+ R* message_;
+ ByteBuffer recv_buf_;
+ bool allow_not_getting_message_;
+ bool hijacked_ = false;
+};
+
+class DeserializeFunc {
+ public:
+ virtual Status Deserialize(ByteBuffer* buf) = 0;
+ virtual ~DeserializeFunc() {}
+};
+
+template <class R>
+class DeserializeFuncType final : public DeserializeFunc {
+ public:
+ DeserializeFuncType(R* message) : message_(message) {}
+ Status Deserialize(ByteBuffer* buf) override {
+ return SerializationTraits<R>::Deserialize(buf->bbuf_ptr(), message_);
+ }
+
+ ~DeserializeFuncType() override {}
+
+ private:
+ R* message_; // Not a managed pointer because management is external to this
+};
+
+class CallOpGenericRecvMessage {
+ public:
+ CallOpGenericRecvMessage()
+ : got_message(false), allow_not_getting_message_(false) {}
+
+ template <class R>
+ void RecvMessage(R* message) {
+ // Use an explicit base class pointer to avoid resolution error in the
+ // following unique_ptr::reset for some old implementations.
+ DeserializeFunc* func = new DeserializeFuncType<R>(message);
+ deserialize_.reset(func);
+ message_ = message;
+ }
+
+ // Do not change status if no message is received.
+ void AllowNoMessage() { allow_not_getting_message_ = true; }
+
+ bool got_message;
+
+ protected:
+ void AddOp(grpc_op* ops, size_t* nops) {
+ if (!deserialize_ || hijacked_) return;
+ grpc_op* op = &ops[(*nops)++];
+ op->op = GRPC_OP_RECV_MESSAGE;
+ op->flags = 0;
+ op->reserved = NULL;
+ op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
+ }
+
+ void FinishOp(bool* status) {
+ if (!deserialize_ || hijacked_) return;
+ if (recv_buf_.Valid()) {
+ if (*status) {
+ got_message = true;
+ *status = deserialize_->Deserialize(&recv_buf_).ok();
+ recv_buf_.Release();
+ } else {
+ got_message = false;
+ recv_buf_.Clear();
+ }
+ } else {
+ got_message = false;
+ if (!allow_not_getting_message_) {
+ *status = false;
+ }
+ }
+ deserialize_.reset();
+ }
+
+ void SetInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {
+ interceptor_methods->SetRecvMessage(message_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {
+ if (!got_message) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+ }
+ void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
+ hijacked_ = true;
+ if (!deserialize_) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_RECV_MESSAGE);
+ }
+
+ private:
+ void* message_;
+ bool hijacked_ = false;
+ std::unique_ptr<DeserializeFunc> deserialize_;
+ ByteBuffer recv_buf_;
+ bool allow_not_getting_message_;
+};
+
+class CallOpClientSendClose {
+ public:
+ CallOpClientSendClose() : send_(false) {}
+
+ void ClientSendClose() { send_ = true; }
+
+ protected:
+ void AddOp(grpc_op* ops, size_t* nops) {
+ if (!send_ || hijacked_) return;
+ grpc_op* op = &ops[(*nops)++];
+ op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+ op->flags = 0;
+ op->reserved = NULL;
+ }
+ void FinishOp(bool* status) { send_ = false; }
+
+ void SetInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {
+ if (!send_) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_CLOSE);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {}
+
+ void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
+ hijacked_ = true;
+ }
+
+ private:
+ bool hijacked_ = false;
+ bool send_;
+};
+
+class CallOpServerSendStatus {
+ public:
+ CallOpServerSendStatus() : send_status_available_(false) {}
+
+ void ServerSendStatus(
+ std::multimap<grpc::string, grpc::string>* trailing_metadata,
+ const Status& status) {
+ send_error_details_ = status.error_details();
+ metadata_map_ = trailing_metadata;
+ send_status_available_ = true;
+ send_status_code_ = static_cast<grpc_status_code>(status.error_code());
+ send_error_message_ = status.error_message();
+ }
+
+ protected:
+ void AddOp(grpc_op* ops, size_t* nops) {
+ if (!send_status_available_ || hijacked_) return;
+ trailing_metadata_ = FillMetadataArray(
+ *metadata_map_, &trailing_metadata_count_, send_error_details_);
+ grpc_op* op = &ops[(*nops)++];
+ op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+ op->data.send_status_from_server.trailing_metadata_count =
+ trailing_metadata_count_;
+ op->data.send_status_from_server.trailing_metadata = trailing_metadata_;
+ op->data.send_status_from_server.status = send_status_code_;
+ error_message_slice_ = SliceReferencingString(send_error_message_);
+ op->data.send_status_from_server.status_details =
+ send_error_message_.empty() ? nullptr : &error_message_slice_;
+ op->flags = 0;
+ op->reserved = NULL;
+ }
+
+ void FinishOp(bool* status) {
+ if (!send_status_available_ || hijacked_) return;
+ g_core_codegen_interface->gpr_free(trailing_metadata_);
+ send_status_available_ = false;
+ }
+
+ void SetInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {
+ if (!send_status_available_) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_STATUS);
+ interceptor_methods->SetSendTrailingMetadata(metadata_map_);
+ interceptor_methods->SetSendStatus(&send_status_code_, &send_error_details_,
+ &send_error_message_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {}
+
+ void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
+ hijacked_ = true;
+ }
+
+ private:
+ bool hijacked_ = false;
+ bool send_status_available_;
+ grpc_status_code send_status_code_;
+ grpc::string send_error_details_;
+ grpc::string send_error_message_;
+ size_t trailing_metadata_count_;
+ std::multimap<grpc::string, grpc::string>* metadata_map_;
+ grpc_metadata* trailing_metadata_;
+ grpc_slice error_message_slice_;
+};
+
+class CallOpRecvInitialMetadata {
+ public:
+ CallOpRecvInitialMetadata() : metadata_map_(nullptr) {}
+
+ void RecvInitialMetadata(ClientContext* context) {
+ context->initial_metadata_received_ = true;
+ metadata_map_ = &context->recv_initial_metadata_;
+ }
+
+ protected:
+ void AddOp(grpc_op* ops, size_t* nops) {
+ if (metadata_map_ == nullptr || hijacked_) return;
+ grpc_op* op = &ops[(*nops)++];
+ op->op = GRPC_OP_RECV_INITIAL_METADATA;
+ op->data.recv_initial_metadata.recv_initial_metadata = metadata_map_->arr();
+ op->flags = 0;
+ op->reserved = NULL;
+ }
+
+ void FinishOp(bool* status) {
+ if (metadata_map_ == nullptr || hijacked_) return;
+ }
+
+ void SetInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {
+ interceptor_methods->SetRecvInitialMetadata(metadata_map_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {
+ if (metadata_map_ == nullptr) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
+ metadata_map_ = nullptr;
+ }
+
+ void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
+ hijacked_ = true;
+ if (metadata_map_ == nullptr) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_RECV_INITIAL_METADATA);
+ }
+
+ private:
+ bool hijacked_ = false;
+ MetadataMap* metadata_map_;
+};
+
+class CallOpClientRecvStatus {
+ public:
+ CallOpClientRecvStatus()
+ : recv_status_(nullptr), debug_error_string_(nullptr) {}
+
+ void ClientRecvStatus(ClientContext* context, Status* status) {
+ client_context_ = context;
+ metadata_map_ = &client_context_->trailing_metadata_;
+ recv_status_ = status;
+ error_message_ = g_core_codegen_interface->grpc_empty_slice();
+ }
+
+ protected:
+ void AddOp(grpc_op* ops, size_t* nops) {
+ if (recv_status_ == nullptr || hijacked_) return;
+ grpc_op* op = &ops[(*nops)++];
+ op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+ op->data.recv_status_on_client.trailing_metadata = metadata_map_->arr();
+ op->data.recv_status_on_client.status = &status_code_;
+ op->data.recv_status_on_client.status_details = &error_message_;
+ op->data.recv_status_on_client.error_string = &debug_error_string_;
+ op->flags = 0;
+ op->reserved = NULL;
+ }
+
+ void FinishOp(bool* status) {
+ if (recv_status_ == nullptr || hijacked_) return;
+ grpc::string binary_error_details = metadata_map_->GetBinaryErrorDetails();
+ *recv_status_ =
+ Status(static_cast<StatusCode>(status_code_),
+ GRPC_SLICE_IS_EMPTY(error_message_)
+ ? grpc::string()
+ : grpc::string(GRPC_SLICE_START_PTR(error_message_),
+ GRPC_SLICE_END_PTR(error_message_)),
+ binary_error_details);
+ client_context_->set_debug_error_string(
+ debug_error_string_ != nullptr ? debug_error_string_ : "");
+ g_core_codegen_interface->grpc_slice_unref(error_message_);
+ if (debug_error_string_ != nullptr) {
+ g_core_codegen_interface->gpr_free((void*)debug_error_string_);
+ }
+ }
+
+ void SetInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {
+ interceptor_methods->SetRecvStatus(recv_status_);
+ interceptor_methods->SetRecvTrailingMetadata(metadata_map_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InternalInterceptorBatchMethods* interceptor_methods) {
+ if (recv_status_ == nullptr) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_STATUS);
+ recv_status_ = nullptr;
+ }
+
+ void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
+ hijacked_ = true;
+ if (recv_status_ == nullptr) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_RECV_STATUS);
+ }
+
+ private:
+ bool hijacked_ = false;
+ ClientContext* client_context_;
+ MetadataMap* metadata_map_;
+ Status* recv_status_;
+ const char* debug_error_string_;
+ grpc_status_code status_code_;
+ grpc_slice error_message_;
+};
+
+template <class Op1 = CallNoOp<1>, class Op2 = CallNoOp<2>,
+ class Op3 = CallNoOp<3>, class Op4 = CallNoOp<4>,
+ class Op5 = CallNoOp<5>, class Op6 = CallNoOp<6>>
+class CallOpSet;
+
+/// Primary implementation of CallOpSetInterface.
+/// Since we cannot use variadic templates, we declare slots up to
+/// the maximum count of ops we'll need in a set. We leverage the
+/// empty base class optimization to slim this class (especially
+/// when there are many unused slots used). To avoid duplicate base classes,
+/// the template parmeter for CallNoOp is varied by argument position.
+template <class Op1, class Op2, class Op3, class Op4, class Op5, class Op6>
+class CallOpSet : public CallOpSetInterface,
+ public Op1,
+ public Op2,
+ public Op3,
+ public Op4,
+ public Op5,
+ public Op6 {
+ public:
+ CallOpSet() : core_cq_tag_(this), return_tag_(this) {}
+ // The copy constructor and assignment operator reset the value of
+ // core_cq_tag_, return_tag_, done_intercepting_ and interceptor_methods_
+ // since those are only meaningful on a specific object, not across objects.
+ CallOpSet(const CallOpSet& other)
+ : core_cq_tag_(this),
+ return_tag_(this),
+ call_(other.call_),
+ done_intercepting_(false),
+ interceptor_methods_(InterceptorBatchMethodsImpl()) {}
+
+ CallOpSet& operator=(const CallOpSet& other) {
+ core_cq_tag_ = this;
+ return_tag_ = this;
+ call_ = other.call_;
+ done_intercepting_ = false;
+ interceptor_methods_ = InterceptorBatchMethodsImpl();
+ return *this;
+ }
+
+ void FillOps(Call* call) override {
+ done_intercepting_ = false;
+ g_core_codegen_interface->grpc_call_ref(call->call());
+ call_ =
+ *call; // It's fine to create a copy of call since it's just pointers
+
+ if (RunInterceptors()) {
+ ContinueFillOpsAfterInterception();
+ } else {
+ // After the interceptors are run, ContinueFillOpsAfterInterception will
+ // be run
+ }
+ }
+
+ bool FinalizeResult(void** tag, bool* status) override {
+ if (done_intercepting_) {
+ // We have already finished intercepting and filling in the results. This
+ // round trip from the core needed to be made because interceptors were
+ // run
+ *tag = return_tag_;
+ *status = saved_status_;
+ g_core_codegen_interface->grpc_call_unref(call_.call());
+ return true;
+ }
+
+ this->Op1::FinishOp(status);
+ this->Op2::FinishOp(status);
+ this->Op3::FinishOp(status);
+ this->Op4::FinishOp(status);
+ this->Op5::FinishOp(status);
+ this->Op6::FinishOp(status);
+ saved_status_ = *status;
+ if (RunInterceptorsPostRecv()) {
+ *tag = return_tag_;
+ g_core_codegen_interface->grpc_call_unref(call_.call());
+ return true;
+ }
+ // Interceptors are going to be run, so we can't return the tag just yet.
+ // After the interceptors are run, ContinueFinalizeResultAfterInterception
+ return false;
+ }
+
+ void set_output_tag(void* return_tag) { return_tag_ = return_tag; }
+
+ void* core_cq_tag() override { return core_cq_tag_; }
+
+ /// set_core_cq_tag is used to provide a different core CQ tag than "this".
+ /// This is used for callback-based tags, where the core tag is the core
+ /// callback function. It does not change the use or behavior of any other
+ /// function (such as FinalizeResult)
+ void set_core_cq_tag(void* core_cq_tag) { core_cq_tag_ = core_cq_tag; }
+
+ // This will be called while interceptors are run if the RPC is a hijacked
+ // RPC. This should set hijacking state for each of the ops.
+ void SetHijackingState() override {
+ this->Op1::SetHijackingState(&interceptor_methods_);
+ this->Op2::SetHijackingState(&interceptor_methods_);
+ this->Op3::SetHijackingState(&interceptor_methods_);
+ this->Op4::SetHijackingState(&interceptor_methods_);
+ this->Op5::SetHijackingState(&interceptor_methods_);
+ this->Op6::SetHijackingState(&interceptor_methods_);
+ }
+
+ // Should be called after interceptors are done running
+ void ContinueFillOpsAfterInterception() override {
+ static const size_t MAX_OPS = 6;
+ grpc_op ops[MAX_OPS];
+ size_t nops = 0;
+ this->Op1::AddOp(ops, &nops);
+ this->Op2::AddOp(ops, &nops);
+ this->Op3::AddOp(ops, &nops);
+ this->Op4::AddOp(ops, &nops);
+ this->Op5::AddOp(ops, &nops);
+ this->Op6::AddOp(ops, &nops);
+ GPR_CODEGEN_ASSERT(GRPC_CALL_OK ==
+ g_core_codegen_interface->grpc_call_start_batch(
+ call_.call(), ops, nops, core_cq_tag(), nullptr));
+ }
+
+ // Should be called after interceptors are done running on the finalize result
+ // path
+ void ContinueFinalizeResultAfterInterception() override {
+ done_intercepting_ = true;
+ GPR_CODEGEN_ASSERT(GRPC_CALL_OK ==
+ g_core_codegen_interface->grpc_call_start_batch(
+ call_.call(), nullptr, 0, core_cq_tag(), nullptr));
+ }
+
+ private:
+ // Returns true if no interceptors need to be run
+ bool RunInterceptors() {
+ interceptor_methods_.ClearState();
+ interceptor_methods_.SetCallOpSetInterface(this);
+ interceptor_methods_.SetCall(&call_);
+ this->Op1::SetInterceptionHookPoint(&interceptor_methods_);
+ this->Op2::SetInterceptionHookPoint(&interceptor_methods_);
+ this->Op3::SetInterceptionHookPoint(&interceptor_methods_);
+ this->Op4::SetInterceptionHookPoint(&interceptor_methods_);
+ this->Op5::SetInterceptionHookPoint(&interceptor_methods_);
+ this->Op6::SetInterceptionHookPoint(&interceptor_methods_);
+ return interceptor_methods_.RunInterceptors();
+ }
+ // Returns true if no interceptors need to be run
+ bool RunInterceptorsPostRecv() {
+ // Call and OpSet had already been set on the set state.
+ // SetReverse also clears previously set hook points
+ interceptor_methods_.SetReverse();
+ this->Op1::SetFinishInterceptionHookPoint(&interceptor_methods_);
+ this->Op2::SetFinishInterceptionHookPoint(&interceptor_methods_);
+ this->Op3::SetFinishInterceptionHookPoint(&interceptor_methods_);
+ this->Op4::SetFinishInterceptionHookPoint(&interceptor_methods_);
+ this->Op5::SetFinishInterceptionHookPoint(&interceptor_methods_);
+ this->Op6::SetFinishInterceptionHookPoint(&interceptor_methods_);
+ return interceptor_methods_.RunInterceptors();
+ }
+
+ void* core_cq_tag_;
+ void* return_tag_;
+ Call call_;
+ bool done_intercepting_ = false;
+ InterceptorBatchMethodsImpl interceptor_methods_;
+ bool saved_status_;
+};
+
+} // namespace internal
+} // namespace grpc
+
+#endif // GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H
diff --git a/include/grpcpp/impl/codegen/call_op_set_interface.h b/include/grpcpp/impl/codegen/call_op_set_interface.h
new file mode 100644
index 0000000000..3b74566a6d
--- /dev/null
+++ b/include/grpcpp/impl/codegen/call_op_set_interface.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CALL_OP_SET_INTERFACE_H
+#define GRPCPP_IMPL_CODEGEN_CALL_OP_SET_INTERFACE_H
+
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+
+namespace grpc {
+namespace internal {
+
+class Call;
+
+/// An abstract collection of call ops, used to generate the
+/// grpc_call_op structure to pass down to the lower layers,
+/// and as it is-a CompletionQueueTag, also massages the final
+/// completion into the correct form for consumption in the C++
+/// API.
+class CallOpSetInterface : public CompletionQueueTag {
+ public:
+ /// Fills in grpc_op, starting from ops[*nops] and moving
+ /// upwards.
+ virtual void FillOps(internal::Call* call) = 0;
+
+ /// Get the tag to be used at the core completion queue. Generally, the
+ /// value of core_cq_tag will be "this". However, it can be overridden if we
+ /// want core to process the tag differently (e.g., as a core callback)
+ virtual void* core_cq_tag() = 0;
+
+ // This will be called while interceptors are run if the RPC is a hijacked
+ // RPC. This should set hijacking state for each of the ops.
+ virtual void SetHijackingState() = 0;
+
+ // Should be called after interceptors are done running
+ virtual void ContinueFillOpsAfterInterception() = 0;
+
+ // Should be called after interceptors are done running on the finalize result
+ // path
+ virtual void ContinueFinalizeResultAfterInterception() = 0;
+};
+} // namespace internal
+} // namespace grpc
+
+#endif // GRPCPP_IMPL_CODEGEN_CALL_OP_SET_INTERFACE_H
diff --git a/include/grpcpp/impl/codegen/callback_common.h b/include/grpcpp/impl/codegen/callback_common.h
index a9835973ac..29deef658f 100644
--- a/include/grpcpp/impl/codegen/callback_common.h
+++ b/include/grpcpp/impl/codegen/callback_common.h
@@ -94,14 +94,18 @@ class CallbackWithStatusTag
void Run(bool ok) {
void* ignored = ops_;
- GPR_CODEGEN_ASSERT(ops_->FinalizeResult(&ignored, &ok));
+ if (!ops_->FinalizeResult(&ignored, &ok)) {
+ // The tag was swallowed
+ return;
+ }
GPR_CODEGEN_ASSERT(ignored == ops_);
// Last use of func_ or status_, so ok to move them out
- CatchingCallback(std::move(func_), std::move(status_));
-
+ auto func = std::move(func_);
+ auto status = std::move(status_);
func_ = nullptr; // reset to clear this out for sure
status_ = Status(); // reset to clear this out for sure
+ CatchingCallback(std::move(func), std::move(status));
g_core_codegen_interface->grpc_call_unref(call_);
}
};
@@ -121,6 +125,8 @@ class CallbackWithSuccessTag
// there are no tests catching the compiler warning.
static void operator delete(void*, void*) { assert(0); }
+ CallbackWithSuccessTag() : call_(nullptr), ops_(nullptr) {}
+
CallbackWithSuccessTag(grpc_call* call, std::function<void(bool)> f,
CompletionQueueTag* ops)
: call_(call), func_(std::move(f)), ops_(ops) {
@@ -135,6 +141,9 @@ class CallbackWithSuccessTag
// that are detected before the operations are internally processed.
void force_run(bool ok) { Run(ok); }
+ /// check if this tag has ever been set
+ operator bool() const { return call_ != nullptr; }
+
private:
grpc_call* call_;
std::function<void(bool)> func_;
@@ -147,13 +156,19 @@ class CallbackWithSuccessTag
void Run(bool ok) {
void* ignored = ops_;
bool new_ok = ok;
- GPR_CODEGEN_ASSERT(ops_->FinalizeResult(&ignored, &new_ok));
+ // Allow a "false" return value from FinalizeResult to silence the
+ // callback, just as it silences a CQ tag in the async cases
+ bool do_callback = ops_->FinalizeResult(&ignored, &new_ok);
GPR_CODEGEN_ASSERT(ignored == ops_);
- // Last use of func_, so ok to move it out for rvalue call above
- CatchingCallback(std::move(func_), ok);
-
- func_ = nullptr; // reset to clear this out for sure
+ if (do_callback) {
+ // Last use of func_, so ok to move it out for rvalue call above
+ auto func = std::move(func_);
+ func_ = nullptr; // reset to clear this out for sure
+ CatchingCallback(std::move(func), ok);
+ } else {
+ func_ = nullptr; // reset to clear this out for sure
+ }
g_core_codegen_interface->grpc_call_unref(call_);
}
};
diff --git a/include/grpcpp/impl/codegen/channel_interface.h b/include/grpcpp/impl/codegen/channel_interface.h
index b257acc1ab..39805a0ef5 100644
--- a/include/grpcpp/impl/codegen/channel_interface.h
+++ b/include/grpcpp/impl/codegen/channel_interface.h
@@ -20,6 +20,8 @@
#define GRPCPP_IMPL_CODEGEN_CHANNEL_INTERFACE_H
#include <grpc/impl/codegen/connectivity_state.h>
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/client_context.h>
#include <grpcpp/impl/codegen/status.h>
#include <grpcpp/impl/codegen/time.h>
@@ -51,6 +53,7 @@ template <class W, class R>
class ClientAsyncReaderWriterFactory;
template <class R>
class ClientAsyncResponseReaderFactory;
+class InterceptedChannel;
} // namespace internal
/// Codegen interface for \a grpc::Channel.
@@ -108,6 +111,7 @@ class ChannelInterface {
template <class InputMessage, class OutputMessage>
friend class ::grpc::internal::CallbackUnaryCallImpl;
friend class ::grpc::internal::RpcMethod;
+ friend class ::grpc::internal::InterceptedChannel;
virtual internal::Call CreateCall(const internal::RpcMethod& method,
ClientContext* context,
CompletionQueue* cq) = 0;
@@ -121,11 +125,25 @@ class ChannelInterface {
gpr_timespec deadline) = 0;
// EXPERIMENTAL
+ // This is needed to keep codegen_test_minimal happy. InterceptedChannel needs
+ // to make use of this but can't directly call Channel's implementation
+ // because of the test.
+ // Returns an empty Call object (rather than being pure) since this is a new
+ // method and adding a new pure method to an interface would be a breaking
+ // change (even though this is private and non-API)
+ virtual internal::Call CreateCallInternal(const internal::RpcMethod& method,
+ ClientContext* context,
+ CompletionQueue* cq,
+ int interceptor_pos) {
+ return internal::Call();
+ }
+
+ // EXPERIMENTAL
// A method to get the callbackable completion queue associated with this
// channel. If the return value is nullptr, this channel doesn't support
// callback operations.
// TODO(vjpai): Consider a better default like using a global CQ
- // Returns nullptr (rather than being pure) since this is a new method
+ // Returns nullptr (rather than being pure) since this is a post-1.0 method
// and adding a new pure method to an interface would be a breaking change
// (even though this is private and non-API)
virtual CompletionQueue* CallbackCQ() { return nullptr; }
diff --git a/include/grpcpp/impl/codegen/client_callback.h b/include/grpcpp/impl/codegen/client_callback.h
index 4d4faea063..4baa819091 100644
--- a/include/grpcpp/impl/codegen/client_callback.h
+++ b/include/grpcpp/impl/codegen/client_callback.h
@@ -77,14 +77,14 @@ class CallbackUnaryCallImpl {
tag->force_run(s);
return;
}
- ops->SendInitialMetadata(context->send_initial_metadata_,
+ ops->SendInitialMetadata(&context->send_initial_metadata_,
context->initial_metadata_flags());
ops->RecvInitialMetadata(context);
ops->RecvMessage(result);
ops->AllowNoMessage();
ops->ClientSendClose();
ops->ClientRecvStatus(context, tag->status_ptr());
- ops->set_cq_tag(tag);
+ ops->set_core_cq_tag(tag);
call.PerformOps(ops);
}
};
diff --git a/include/grpcpp/impl/codegen/client_context.h b/include/grpcpp/impl/codegen/client_context.h
index 24f5c431ce..f53b744dcf 100644
--- a/include/grpcpp/impl/codegen/client_context.h
+++ b/include/grpcpp/impl/codegen/client_context.h
@@ -41,6 +41,7 @@
#include <grpc/impl/codegen/compression_types.h>
#include <grpc/impl/codegen/propagation_bits.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
#include <grpcpp/impl/codegen/config.h>
#include <grpcpp/impl/codegen/core_codegen_interface.h>
#include <grpcpp/impl/codegen/create_auth_context.h>
@@ -402,6 +403,17 @@ class ClientContext {
grpc_call* call() const { return call_; }
void set_call(grpc_call* call, const std::shared_ptr<Channel>& channel);
+ experimental::ClientRpcInfo* set_client_rpc_info(
+ const char* method, grpc::ChannelInterface* channel,
+ const std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>&
+ creators,
+ size_t interceptor_pos) {
+ rpc_info_ = experimental::ClientRpcInfo(this, method, channel);
+ rpc_info_.RegisterInterceptors(creators, interceptor_pos);
+ return &rpc_info_;
+ }
+
uint32_t initial_metadata_flags() const {
return (idempotent_ ? GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST : 0) |
(wait_for_ready_ ? GRPC_INITIAL_METADATA_WAIT_FOR_READY : 0) |
@@ -439,6 +451,8 @@ class ClientContext {
bool initial_metadata_corked_;
grpc::string debug_error_string_;
+
+ experimental::ClientRpcInfo rpc_info_;
};
} // namespace grpc
diff --git a/include/grpcpp/impl/codegen/client_interceptor.h b/include/grpcpp/impl/codegen/client_interceptor.h
index f460c5ac0c..0e08a7ce01 100644
--- a/include/grpcpp/impl/codegen/client_interceptor.h
+++ b/include/grpcpp/impl/codegen/client_interceptor.h
@@ -19,23 +19,75 @@
#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_INTERCEPTOR_H
#define GRPCPP_IMPL_CODEGEN_CLIENT_INTERCEPTOR_H
+#include <vector>
+
#include <grpcpp/impl/codegen/interceptor.h>
+#include <grpcpp/impl/codegen/string_ref.h>
namespace grpc {
-namespace experimental {
-class ClientInterceptor {
- public:
- virtual ~ClientInterceptor() {}
- virtual void Intercept(InterceptorBatchMethods* methods) = 0;
-};
+class ClientContext;
+class Channel;
-class ClientRpcInfo {};
+namespace internal {
+class InterceptorBatchMethodsImpl;
+}
+
+namespace experimental {
+class ClientRpcInfo;
class ClientInterceptorFactoryInterface {
public:
virtual ~ClientInterceptorFactoryInterface() {}
- virtual ClientInterceptor* CreateClientInterceptor(ClientRpcInfo* info) = 0;
+ virtual Interceptor* CreateClientInterceptor(ClientRpcInfo* info) = 0;
+};
+
+class ClientRpcInfo {
+ public:
+ ClientRpcInfo() {}
+
+ ~ClientRpcInfo(){};
+
+ ClientRpcInfo(const ClientRpcInfo&) = delete;
+ ClientRpcInfo(ClientRpcInfo&&) = default;
+ ClientRpcInfo& operator=(ClientRpcInfo&&) = default;
+
+ // Getter methods
+ const char* method() { return method_; }
+ ChannelInterface* channel() { return channel_; }
+ grpc::ClientContext* client_context() { return ctx_; }
+
+ private:
+ ClientRpcInfo(grpc::ClientContext* ctx, const char* method,
+ grpc::ChannelInterface* channel)
+ : ctx_(ctx), method_(method), channel_(channel) {}
+ // Runs interceptor at pos \a pos.
+ void RunInterceptor(
+ experimental::InterceptorBatchMethods* interceptor_methods, size_t pos) {
+ GPR_CODEGEN_ASSERT(pos < interceptors_.size());
+ interceptors_[pos]->Intercept(interceptor_methods);
+ }
+
+ void RegisterInterceptors(
+ const std::vector<std::unique_ptr<
+ experimental::ClientInterceptorFactoryInterface>>& creators,
+ int interceptor_pos) {
+ for (auto it = creators.begin() + interceptor_pos; it != creators.end();
+ ++it) {
+ interceptors_.push_back(std::unique_ptr<experimental::Interceptor>(
+ (*it)->CreateClientInterceptor(this)));
+ }
+ }
+
+ grpc::ClientContext* ctx_ = nullptr;
+ const char* method_ = nullptr;
+ grpc::ChannelInterface* channel_ = nullptr;
+ std::vector<std::unique_ptr<experimental::Interceptor>> interceptors_;
+ bool hijacked_ = false;
+ size_t hijacked_interceptor_ = 0;
+
+ friend class internal::InterceptorBatchMethodsImpl;
+ friend class grpc::ClientContext;
};
} // namespace experimental
diff --git a/include/grpcpp/impl/codegen/client_unary_call.h b/include/grpcpp/impl/codegen/client_unary_call.h
index e4e8364e07..b1c80764f2 100644
--- a/include/grpcpp/impl/codegen/client_unary_call.h
+++ b/include/grpcpp/impl/codegen/client_unary_call.h
@@ -61,7 +61,7 @@ class BlockingUnaryCallImpl {
if (!status_.ok()) {
return;
}
- ops.SendInitialMetadata(context->send_initial_metadata_,
+ ops.SendInitialMetadata(&context->send_initial_metadata_,
context->initial_metadata_flags());
ops.RecvInitialMetadata(context);
ops.RecvMessage(result);
diff --git a/include/grpcpp/impl/codegen/completion_queue.h b/include/grpcpp/impl/codegen/completion_queue.h
index a62d48abe7..d603c7c700 100644
--- a/include/grpcpp/impl/codegen/completion_queue.h
+++ b/include/grpcpp/impl/codegen/completion_queue.h
@@ -122,8 +122,8 @@ class CompletionQueue : private GrpcLibraryCodegen {
/// Read from the queue, blocking until an event is available or the queue is
/// shutting down.
///
- /// \param tag[out] Updated to point to the read event's tag.
- /// \param ok[out] true if read a successful event, false otherwise.
+ /// \param tag [out] Updated to point to the read event's tag.
+ /// \param ok [out] true if read a successful event, false otherwise.
///
/// Note that each tag sent to the completion queue (through RPC operations
/// or alarms) will be delivered out of the completion queue by a call to
@@ -178,10 +178,10 @@ class CompletionQueue : private GrpcLibraryCodegen {
/// within the \a deadline). A \a tag points to an arbitrary location usually
/// employed to uniquely identify an event.
///
- /// \param tag[out] Upon sucess, updated to point to the event's tag.
- /// \param ok[out] Upon sucess, true if a successful event, false otherwise
+ /// \param tag [out] Upon sucess, updated to point to the event's tag.
+ /// \param ok [out] Upon sucess, true if a successful event, false otherwise
/// See documentation for CompletionQueue::Next for explanation of ok
- /// \param deadline[in] How long to block in wait for an event.
+ /// \param deadline [in] How long to block in wait for an event.
///
/// \return The type of event read.
template <typename T>
@@ -197,10 +197,11 @@ class CompletionQueue : private GrpcLibraryCodegen {
/// within the \a deadline). A \a tag points to an arbitrary location usually
/// employed to uniquely identify an event.
///
- /// \param F[in] Function to execute before calling AsyncNext on this queue.
- /// \param tag[out] Upon sucess, updated to point to the event's tag.
- /// \param ok[out] Upon sucess, true if read a regular event, false otherwise.
- /// \param deadline[in] How long to block in wait for an event.
+ /// \param f [in] Function to execute before calling AsyncNext on this queue.
+ /// \param tag [out] Upon sucess, updated to point to the event's tag.
+ /// \param ok [out] Upon sucess, true if read a regular event, false
+ /// otherwise.
+ /// \param deadline [in] How long to block in wait for an event.
///
/// \return The type of event read.
template <typename T, typename F>
@@ -299,14 +300,17 @@ class CompletionQueue : private GrpcLibraryCodegen {
bool Pluck(internal::CompletionQueueTag* tag) {
auto deadline =
g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_REALTIME);
- auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
- cq_, tag, deadline, nullptr);
- bool ok = ev.success != 0;
- void* ignored = tag;
- GPR_CODEGEN_ASSERT(tag->FinalizeResult(&ignored, &ok));
- GPR_CODEGEN_ASSERT(ignored == tag);
- // Ignore mutations by FinalizeResult: Pluck returns the C API status
- return ev.success != 0;
+ while (true) {
+ auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
+ cq_, tag, deadline, nullptr);
+ bool ok = ev.success != 0;
+ void* ignored = tag;
+ if (tag->FinalizeResult(&ignored, &ok)) {
+ GPR_CODEGEN_ASSERT(ignored == tag);
+ // Ignore mutations by FinalizeResult: Pluck returns the C API status
+ return ev.success != 0;
+ }
+ }
}
/// Performs a single polling pluck on \a tag.
@@ -376,13 +380,18 @@ class ServerCompletionQueue : public CompletionQueue {
ServerCompletionQueue() : polling_type_(GRPC_CQ_DEFAULT_POLLING) {}
private:
- /// \param is_frequently_polled Informs the GRPC library about whether the
- /// server completion queue would be actively polled (by calling Next() or
- /// AsyncNext()). By default all server completion queues are assumed to be
- /// frequently polled.
- ServerCompletionQueue(grpc_cq_polling_type polling_type)
+ /// \param completion_type indicates whether this is a NEXT or CALLBACK
+ /// completion queue.
+ /// \param polling_type Informs the GRPC library about the type of polling
+ /// allowed on this completion queue. See grpc_cq_polling_type's description
+ /// in grpc_types.h for more details.
+ /// \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)
: CompletionQueue(grpc_completion_queue_attributes{
- GRPC_CQ_CURRENT_VERSION, GRPC_CQ_NEXT, polling_type, nullptr}),
+ GRPC_CQ_CURRENT_VERSION, completion_type, polling_type,
+ shutdown_cb}),
polling_type_(polling_type) {}
grpc_cq_polling_type polling_type_;
diff --git a/include/grpcpp/impl/codegen/config_protobuf.h b/include/grpcpp/impl/codegen/config_protobuf.h
index 94e593d1ef..8c2e9e6792 100644
--- a/include/grpcpp/impl/codegen/config_protobuf.h
+++ b/include/grpcpp/impl/codegen/config_protobuf.h
@@ -66,6 +66,12 @@
#define GRPC_CUSTOM_CODEDINPUTSTREAM ::google::protobuf::io::CodedInputStream
#endif
+#ifndef GRPC_CUSTOM_JSONUTIL
+#include <google/protobuf/util/json_util.h>
+#define GRPC_CUSTOM_JSONUTIL ::google::protobuf::util
+#define GRPC_CUSTOM_UTIL_STATUS ::google::protobuf::util::Status
+#endif
+
namespace grpc {
namespace protobuf {
@@ -83,6 +89,12 @@ typedef GRPC_CUSTOM_SERVICEDESCRIPTOR ServiceDescriptor;
typedef GRPC_CUSTOM_SIMPLEDESCRIPTORDATABASE SimpleDescriptorDatabase;
typedef GRPC_CUSTOM_SOURCELOCATION SourceLocation;
+namespace util {
+typedef GRPC_CUSTOM_UTIL_STATUS Status;
+} // namespace util
+
+namespace json = GRPC_CUSTOM_JSONUTIL;
+
namespace io {
typedef GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM ZeroCopyOutputStream;
typedef GRPC_CUSTOM_ZEROCOPYINPUTSTREAM ZeroCopyInputStream;
diff --git a/include/grpcpp/impl/codegen/core_codegen.h b/include/grpcpp/impl/codegen/core_codegen.h
index e9df96bf04..6ef184d01a 100644
--- a/include/grpcpp/impl/codegen/core_codegen.h
+++ b/include/grpcpp/impl/codegen/core_codegen.h
@@ -63,6 +63,9 @@ class CoreCodegen final : public CoreCodegenInterface {
void gpr_cv_signal(gpr_cv* cv) override;
void gpr_cv_broadcast(gpr_cv* cv) override;
+ grpc_call_error grpc_call_start_batch(grpc_call* call, const grpc_op* ops,
+ size_t nops, void* tag,
+ void* reserved) override;
grpc_call_error grpc_call_cancel_with_status(grpc_call* call,
grpc_status_code status,
const char* description,
diff --git a/include/grpcpp/impl/codegen/core_codegen_interface.h b/include/grpcpp/impl/codegen/core_codegen_interface.h
index 1167a188a2..25e3abccca 100644
--- a/include/grpcpp/impl/codegen/core_codegen_interface.h
+++ b/include/grpcpp/impl/codegen/core_codegen_interface.h
@@ -100,6 +100,9 @@ class CoreCodegenInterface {
virtual grpc_slice grpc_slice_new_with_len(void* p, size_t len,
void (*destroy)(void*,
size_t)) = 0;
+ virtual grpc_call_error grpc_call_start_batch(grpc_call* call,
+ const grpc_op* ops, size_t nops,
+ void* tag, void* reserved) = 0;
virtual grpc_call_error grpc_call_cancel_with_status(grpc_call* call,
grpc_status_code status,
const char* description,
diff --git a/include/grpcpp/impl/codegen/intercepted_channel.h b/include/grpcpp/impl/codegen/intercepted_channel.h
new file mode 100644
index 0000000000..612e56d862
--- /dev/null
+++ b/include/grpcpp/impl/codegen/intercepted_channel.h
@@ -0,0 +1,80 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_INTERCEPTED_CHANNEL_H
+#define GRPCPP_IMPL_CODEGEN_INTERCEPTED_CHANNEL_H
+
+#include <grpcpp/impl/codegen/channel_interface.h>
+
+namespace grpc {
+
+namespace internal {
+
+class InterceptorBatchMethodsImpl;
+
+/// An InterceptedChannel is available to client Interceptors. An
+/// InterceptedChannel is unique to an interceptor, and when an RPC is started
+/// on this channel, only those interceptors that come after this interceptor
+/// see the RPC.
+class InterceptedChannel : public ChannelInterface {
+ public:
+ virtual ~InterceptedChannel() { channel_ = nullptr; }
+
+ /// Get the current channel state. If the channel is in IDLE and
+ /// \a try_to_connect is set to true, try to connect.
+ grpc_connectivity_state GetState(bool try_to_connect) override {
+ return channel_->GetState(try_to_connect);
+ }
+
+ private:
+ InterceptedChannel(ChannelInterface* channel, int pos)
+ : channel_(channel), interceptor_pos_(pos) {}
+
+ Call CreateCall(const RpcMethod& method, ClientContext* context,
+ CompletionQueue* cq) override {
+ return channel_->CreateCallInternal(method, context, cq, interceptor_pos_);
+ }
+
+ void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) override {
+ return channel_->PerformOpsOnCall(ops, call);
+ }
+ void* RegisterMethod(const char* method) override {
+ return channel_->RegisterMethod(method);
+ }
+
+ void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
+ gpr_timespec deadline, CompletionQueue* cq,
+ void* tag) override {
+ return channel_->NotifyOnStateChangeImpl(last_observed, deadline, cq, tag);
+ }
+ bool WaitForStateChangeImpl(grpc_connectivity_state last_observed,
+ gpr_timespec deadline) override {
+ return channel_->WaitForStateChangeImpl(last_observed, deadline);
+ }
+
+ CompletionQueue* CallbackCQ() override { return channel_->CallbackCQ(); }
+
+ ChannelInterface* channel_;
+ int interceptor_pos_;
+
+ friend class InterceptorBatchMethodsImpl;
+};
+} // namespace internal
+} // namespace grpc
+
+#endif // GRPCPP_IMPL_CODEGEN_INTERCEPTED_CHANNEL_H
diff --git a/include/grpcpp/impl/codegen/interceptor.h b/include/grpcpp/impl/codegen/interceptor.h
index 6402a3a946..15cab711e5 100644
--- a/include/grpcpp/impl/codegen/interceptor.h
+++ b/include/grpcpp/impl/codegen/interceptor.h
@@ -19,7 +19,17 @@
#ifndef GRPCPP_IMPL_CODEGEN_INTERCEPTOR_H
#define GRPCPP_IMPL_CODEGEN_INTERCEPTOR_H
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/metadata_map.h>
+
namespace grpc {
+
+class ChannelInterface;
+class Status;
+
namespace experimental {
class InterceptedMessage {
public:
@@ -35,6 +45,7 @@ enum class InterceptionHookPoints {
PRE_SEND_INITIAL_METADATA,
PRE_SEND_MESSAGE,
PRE_SEND_STATUS /* server only */,
+ PRE_SEND_CLOSE /* client only */,
/* The following three are for hijacked clients only and can only be
registered by the global interceptor */
PRE_RECV_INITIAL_METADATA,
@@ -50,7 +61,7 @@ enum class InterceptionHookPoints {
class InterceptorBatchMethods {
public:
- virtual ~InterceptorBatchMethods();
+ virtual ~InterceptorBatchMethods(){};
// Queries to check whether the current batch has an interception hook point
// of type \a type
virtual bool QueryInterceptionHookPoint(InterceptionHookPoints type) = 0;
@@ -60,7 +71,53 @@ class InterceptorBatchMethods {
// Calling this indicates that the interceptor has hijacked the RPC (only
// valid if the batch contains send_initial_metadata on the client side)
virtual void Hijack() = 0;
+
+ // Returns a modifable ByteBuffer holding serialized form of the message to be
+ // sent
+ virtual ByteBuffer* GetSendMessage() = 0;
+
+ // Returns a modifiable multimap of the initial metadata to be sent
+ virtual std::multimap<grpc::string, grpc::string>*
+ GetSendInitialMetadata() = 0;
+
+ // Returns the status to be sent
+ virtual Status GetSendStatus() = 0;
+
+ // Modifies the status with \a status
+ virtual void ModifySendStatus(const Status& status) = 0;
+
+ // Returns a modifiable multimap of the trailing metadata to be sent
+ virtual std::multimap<grpc::string, grpc::string>*
+ GetSendTrailingMetadata() = 0;
+
+ // Returns a pointer to the modifiable received message. Note that the message
+ // is already deserialized
+ virtual void* GetRecvMessage() = 0;
+
+ // Returns a modifiable multimap of the received initial metadata
+ virtual std::multimap<grpc::string_ref, grpc::string_ref>*
+ GetRecvInitialMetadata() = 0;
+
+ // Returns a modifiable view of the received status
+ virtual Status* GetRecvStatus() = 0;
+
+ // Returns a modifiable multimap of the received trailing metadata
+ virtual std::multimap<grpc::string_ref, grpc::string_ref>*
+ GetRecvTrailingMetadata() = 0;
+
+ // Gets an intercepted channel. When a call is started on this interceptor,
+ // only interceptors after the current interceptor are created from the
+ // factory objects registered with the channel.
+ virtual std::unique_ptr<ChannelInterface> GetInterceptedChannel() = 0;
};
+
+class Interceptor {
+ public:
+ virtual ~Interceptor() {}
+
+ virtual void Intercept(InterceptorBatchMethods* methods) = 0;
+};
+
} // namespace experimental
} // namespace grpc
diff --git a/include/grpcpp/impl/codegen/interceptor_common.h b/include/grpcpp/impl/codegen/interceptor_common.h
new file mode 100644
index 0000000000..cf564977f6
--- /dev/null
+++ b/include/grpcpp/impl/codegen/interceptor_common.h
@@ -0,0 +1,383 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_INTERCEPTOR_COMMON_H
+#define GRPCPP_IMPL_CODEGEN_INTERCEPTOR_COMMON_H
+
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/impl/codegen/server_interceptor.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+namespace grpc {
+namespace internal {
+
+/// Internal methods for setting the state
+class InternalInterceptorBatchMethods
+ : public experimental::InterceptorBatchMethods {
+ public:
+ virtual ~InternalInterceptorBatchMethods() {}
+
+ virtual void AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints type) = 0;
+
+ virtual void SetSendMessage(ByteBuffer* buf) = 0;
+
+ virtual void SetSendInitialMetadata(
+ std::multimap<grpc::string, grpc::string>* metadata) = 0;
+
+ virtual void SetSendStatus(grpc_status_code* code,
+ grpc::string* error_details,
+ grpc::string* error_message) = 0;
+
+ virtual void SetSendTrailingMetadata(
+ std::multimap<grpc::string, grpc::string>* metadata) = 0;
+
+ virtual void SetRecvMessage(void* message) = 0;
+
+ virtual void SetRecvInitialMetadata(MetadataMap* map) = 0;
+
+ virtual void SetRecvStatus(Status* status) = 0;
+
+ virtual void SetRecvTrailingMetadata(MetadataMap* map) = 0;
+};
+
+class InterceptorBatchMethodsImpl : public InternalInterceptorBatchMethods {
+ public:
+ InterceptorBatchMethodsImpl() {
+ for (auto i = static_cast<experimental::InterceptionHookPoints>(0);
+ i < experimental::InterceptionHookPoints::NUM_INTERCEPTION_HOOKS;
+ i = static_cast<experimental::InterceptionHookPoints>(
+ static_cast<size_t>(i) + 1)) {
+ hooks_[static_cast<size_t>(i)] = false;
+ }
+ }
+
+ ~InterceptorBatchMethodsImpl() {}
+
+ bool QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints type) override {
+ return hooks_[static_cast<size_t>(type)];
+ }
+
+ void Proceed() override { /* fill this */
+ if (call_->client_rpc_info() != nullptr) {
+ return ProceedClient();
+ }
+ GPR_CODEGEN_ASSERT(call_->server_rpc_info() != nullptr);
+ ProceedServer();
+ }
+
+ void Hijack() override {
+ // Only the client can hijack when sending down initial metadata
+ GPR_CODEGEN_ASSERT(!reverse_ && ops_ != nullptr &&
+ call_->client_rpc_info() != nullptr);
+ // It is illegal to call Hijack twice
+ GPR_CODEGEN_ASSERT(!ran_hijacking_interceptor_);
+ auto* rpc_info = call_->client_rpc_info();
+ rpc_info->hijacked_ = true;
+ rpc_info->hijacked_interceptor_ = current_interceptor_index_;
+ ClearHookPoints();
+ ops_->SetHijackingState();
+ ran_hijacking_interceptor_ = true;
+ rpc_info->RunInterceptor(this, current_interceptor_index_);
+ }
+
+ void AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints type) override {
+ hooks_[static_cast<size_t>(type)] = true;
+ }
+
+ ByteBuffer* GetSendMessage() override { return send_message_; }
+
+ std::multimap<grpc::string, grpc::string>* GetSendInitialMetadata() override {
+ return send_initial_metadata_;
+ }
+
+ Status GetSendStatus() override {
+ return Status(static_cast<StatusCode>(*code_), *error_message_,
+ *error_details_);
+ }
+
+ void ModifySendStatus(const Status& status) override {
+ *code_ = static_cast<grpc_status_code>(status.error_code());
+ *error_details_ = status.error_details();
+ *error_message_ = status.error_message();
+ }
+
+ std::multimap<grpc::string, grpc::string>* GetSendTrailingMetadata()
+ override {
+ return send_trailing_metadata_;
+ }
+
+ void* GetRecvMessage() override { return recv_message_; }
+
+ std::multimap<grpc::string_ref, grpc::string_ref>* GetRecvInitialMetadata()
+ override {
+ return recv_initial_metadata_->map();
+ }
+
+ Status* GetRecvStatus() override { return recv_status_; }
+
+ std::multimap<grpc::string_ref, grpc::string_ref>* GetRecvTrailingMetadata()
+ override {
+ return recv_trailing_metadata_->map();
+ }
+
+ void SetSendMessage(ByteBuffer* buf) override { send_message_ = buf; }
+
+ void SetSendInitialMetadata(
+ std::multimap<grpc::string, grpc::string>* metadata) override {
+ send_initial_metadata_ = metadata;
+ }
+
+ void SetSendStatus(grpc_status_code* code, grpc::string* error_details,
+ grpc::string* error_message) override {
+ code_ = code;
+ error_details_ = error_details;
+ error_message_ = error_message;
+ }
+
+ void SetSendTrailingMetadata(
+ std::multimap<grpc::string, grpc::string>* metadata) override {
+ send_trailing_metadata_ = metadata;
+ }
+
+ void SetRecvMessage(void* message) override { recv_message_ = message; }
+
+ void SetRecvInitialMetadata(MetadataMap* map) override {
+ recv_initial_metadata_ = map;
+ }
+
+ void SetRecvStatus(Status* status) override { recv_status_ = status; }
+
+ void SetRecvTrailingMetadata(MetadataMap* map) override {
+ recv_trailing_metadata_ = map;
+ }
+
+ std::unique_ptr<ChannelInterface> GetInterceptedChannel() override {
+ auto* info = call_->client_rpc_info();
+ if (info == nullptr) {
+ return std::unique_ptr<ChannelInterface>(nullptr);
+ }
+ // The intercepted channel starts from the interceptor just after the
+ // current interceptor
+ return std::unique_ptr<ChannelInterface>(new InterceptedChannel(
+ info->channel(), current_interceptor_index_ + 1));
+ }
+
+ // Clears all state
+ void ClearState() {
+ reverse_ = false;
+ ran_hijacking_interceptor_ = false;
+ ClearHookPoints();
+ }
+
+ // Prepares for Post_recv operations
+ void SetReverse() {
+ reverse_ = true;
+ ran_hijacking_interceptor_ = false;
+ ClearHookPoints();
+ }
+
+ // This needs to be set before interceptors are run
+ void SetCall(Call* call) { call_ = call; }
+
+ // This needs to be set before interceptors are run using RunInterceptors().
+ // Alternatively, RunInterceptors(std::function<void(void)> f) can be used.
+ void SetCallOpSetInterface(CallOpSetInterface* ops) { ops_ = ops; }
+
+ // Returns true if no interceptors are run. This should be used only by
+ // subclasses of CallOpSetInterface. SetCall and SetCallOpSetInterface should
+ // have been called before this. After all the interceptors are done running,
+ // either ContinueFillOpsAfterInterception or
+ // ContinueFinalizeOpsAfterInterception will be called. Note that neither of
+ // them is invoked if there were no interceptors registered.
+ bool RunInterceptors() {
+ GPR_CODEGEN_ASSERT(ops_);
+ auto* client_rpc_info = call_->client_rpc_info();
+ if (client_rpc_info != nullptr) {
+ if (client_rpc_info->interceptors_.size() == 0) {
+ return true;
+ } else {
+ RunClientInterceptors();
+ return false;
+ }
+ }
+
+ auto* server_rpc_info = call_->server_rpc_info();
+ if (server_rpc_info == nullptr ||
+ server_rpc_info->interceptors_.size() == 0) {
+ return true;
+ }
+ RunServerInterceptors();
+ return false;
+ }
+
+ // Returns true if no interceptors are run. Returns false otherwise if there
+ // are interceptors registered. After the interceptors are done running \a f
+ // will be invoked. This is to be used only by BaseAsyncRequest and
+ // SyncRequest.
+ bool RunInterceptors(std::function<void(void)> f) {
+ // This is used only by the server for initial call request
+ GPR_CODEGEN_ASSERT(reverse_ == true);
+ GPR_CODEGEN_ASSERT(call_->client_rpc_info() == nullptr);
+ auto* server_rpc_info = call_->server_rpc_info();
+ if (server_rpc_info == nullptr ||
+ server_rpc_info->interceptors_.size() == 0) {
+ return true;
+ }
+ callback_ = std::move(f);
+ RunServerInterceptors();
+ return false;
+ }
+
+ private:
+ void RunClientInterceptors() {
+ auto* rpc_info = call_->client_rpc_info();
+ if (!reverse_) {
+ current_interceptor_index_ = 0;
+ } else {
+ if (rpc_info->hijacked_) {
+ current_interceptor_index_ = rpc_info->hijacked_interceptor_;
+ } else {
+ current_interceptor_index_ = rpc_info->interceptors_.size() - 1;
+ }
+ }
+ rpc_info->RunInterceptor(this, current_interceptor_index_);
+ }
+
+ void RunServerInterceptors() {
+ auto* rpc_info = call_->server_rpc_info();
+ if (!reverse_) {
+ current_interceptor_index_ = 0;
+ } else {
+ current_interceptor_index_ = rpc_info->interceptors_.size() - 1;
+ }
+ rpc_info->RunInterceptor(this, current_interceptor_index_);
+ }
+
+ void ProceedClient() {
+ auto* rpc_info = call_->client_rpc_info();
+ if (rpc_info->hijacked_ && !reverse_ &&
+ current_interceptor_index_ == rpc_info->hijacked_interceptor_ &&
+ !ran_hijacking_interceptor_) {
+ // We now need to provide hijacked recv ops to this interceptor
+ ClearHookPoints();
+ ops_->SetHijackingState();
+ ran_hijacking_interceptor_ = true;
+ rpc_info->RunInterceptor(this, current_interceptor_index_);
+ return;
+ }
+ if (!reverse_) {
+ current_interceptor_index_++;
+ // We are going down the stack of interceptors
+ if (current_interceptor_index_ < rpc_info->interceptors_.size()) {
+ if (rpc_info->hijacked_ &&
+ current_interceptor_index_ > rpc_info->hijacked_interceptor_) {
+ // This is a hijacked RPC and we are done with hijacking
+ ops_->ContinueFillOpsAfterInterception();
+ } else {
+ rpc_info->RunInterceptor(this, current_interceptor_index_);
+ }
+ } else {
+ // we are done running all the interceptors without any hijacking
+ ops_->ContinueFillOpsAfterInterception();
+ }
+ } else {
+ // We are going up the stack of interceptors
+ if (current_interceptor_index_ > 0) {
+ // Continue running interceptors
+ current_interceptor_index_--;
+ rpc_info->RunInterceptor(this, current_interceptor_index_);
+ } else {
+ // we are done running all the interceptors without any hijacking
+ ops_->ContinueFinalizeResultAfterInterception();
+ }
+ }
+ }
+
+ void ProceedServer() {
+ auto* rpc_info = call_->server_rpc_info();
+ if (!reverse_) {
+ current_interceptor_index_++;
+ if (current_interceptor_index_ < rpc_info->interceptors_.size()) {
+ return rpc_info->RunInterceptor(this, current_interceptor_index_);
+ } else if (ops_) {
+ return ops_->ContinueFillOpsAfterInterception();
+ }
+ } else {
+ // We are going up the stack of interceptors
+ if (current_interceptor_index_ > 0) {
+ // Continue running interceptors
+ current_interceptor_index_--;
+ return rpc_info->RunInterceptor(this, current_interceptor_index_);
+ } else if (ops_) {
+ return ops_->ContinueFinalizeResultAfterInterception();
+ }
+ }
+ GPR_CODEGEN_ASSERT(callback_);
+ callback_();
+ }
+
+ void ClearHookPoints() {
+ for (auto i = static_cast<experimental::InterceptionHookPoints>(0);
+ i < experimental::InterceptionHookPoints::NUM_INTERCEPTION_HOOKS;
+ i = static_cast<experimental::InterceptionHookPoints>(
+ static_cast<size_t>(i) + 1)) {
+ hooks_[static_cast<size_t>(i)] = false;
+ }
+ }
+
+ std::array<bool,
+ static_cast<size_t>(
+ experimental::InterceptionHookPoints::NUM_INTERCEPTION_HOOKS)>
+ hooks_;
+
+ size_t current_interceptor_index_ = 0; // Current iterator
+ bool reverse_ = false;
+ bool ran_hijacking_interceptor_ = false;
+ Call* call_ = nullptr; // The Call object is present along with CallOpSet
+ // object/callback
+ CallOpSetInterface* ops_ = nullptr;
+ std::function<void(void)> callback_;
+
+ ByteBuffer* send_message_ = nullptr;
+
+ std::multimap<grpc::string, grpc::string>* send_initial_metadata_;
+
+ grpc_status_code* code_ = nullptr;
+ grpc::string* error_details_ = nullptr;
+ grpc::string* error_message_ = nullptr;
+ Status send_status_;
+
+ std::multimap<grpc::string, grpc::string>* send_trailing_metadata_ = nullptr;
+
+ void* recv_message_ = nullptr;
+
+ MetadataMap* recv_initial_metadata_ = nullptr;
+
+ Status* recv_status_ = nullptr;
+
+ MetadataMap* recv_trailing_metadata_ = nullptr;
+};
+
+} // namespace internal
+} // namespace grpc
+
+#endif // GRPCPP_IMPL_CODEGEN_INTERCEPTOR_COMMON_H
diff --git a/include/grpcpp/impl/codegen/metadata_map.h b/include/grpcpp/impl/codegen/metadata_map.h
index 5e062a50f8..0bba3ed4e3 100644
--- a/include/grpcpp/impl/codegen/metadata_map.h
+++ b/include/grpcpp/impl/codegen/metadata_map.h
@@ -19,6 +19,8 @@
#ifndef GRPCPP_IMPL_CODEGEN_METADATA_MAP_H
#define GRPCPP_IMPL_CODEGEN_METADATA_MAP_H
+#include <map>
+
#include <grpc/impl/codegen/log.h>
#include <grpcpp/impl/codegen/slice.h>
diff --git a/include/grpcpp/impl/codegen/method_handler_impl.h b/include/grpcpp/impl/codegen/method_handler_impl.h
index 53117f941b..dd53f975f6 100644
--- a/include/grpcpp/impl/codegen/method_handler_impl.h
+++ b/include/grpcpp/impl/codegen/method_handler_impl.h
@@ -59,21 +59,21 @@ class RpcMethodHandler : public MethodHandler {
: func_(func), service_(service) {}
void RunHandler(const HandlerParameter& param) final {
- RequestType req;
- Status status = SerializationTraits<RequestType>::Deserialize(
- param.request.bbuf_ptr(), &req);
ResponseType rsp;
+ Status status = param.status;
if (status.ok()) {
- status = CatchingFunctionHandler([this, &param, &req, &rsp] {
- return func_(service_, param.server_context, &req, &rsp);
+ status = CatchingFunctionHandler([this, &param, &rsp] {
+ return func_(service_, param.server_context,
+ static_cast<RequestType*>(param.request), &rsp);
});
+ static_cast<RequestType*>(param.request)->~RequestType();
}
GPR_CODEGEN_ASSERT(!param.server_context->sent_initial_metadata_);
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
CallOpServerSendStatus>
ops;
- ops.SendInitialMetadata(param.server_context->initial_metadata_,
+ ops.SendInitialMetadata(&param.server_context->initial_metadata_,
param.server_context->initial_metadata_flags());
if (param.server_context->compression_level_set()) {
ops.set_compression_level(param.server_context->compression_level());
@@ -81,11 +81,26 @@ class RpcMethodHandler : public MethodHandler {
if (status.ok()) {
status = ops.SendMessage(rsp);
}
- ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
+ ops.ServerSendStatus(&param.server_context->trailing_metadata_, status);
param.call->PerformOps(&ops);
param.call->cq()->Pluck(&ops);
}
+ void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
+ Status* status) final {
+ ByteBuffer buf;
+ buf.set_buffer(req);
+ auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc(
+ call, sizeof(RequestType))) RequestType();
+ *status = SerializationTraits<RequestType>::Deserialize(&buf, request);
+ buf.Release();
+ if (status->ok()) {
+ return request;
+ }
+ request->~RequestType();
+ return nullptr;
+ }
+
private:
/// Application provided rpc handler function.
std::function<Status(ServiceType*, ServerContext*, const RequestType*,
@@ -117,7 +132,7 @@ class ClientStreamingHandler : public MethodHandler {
CallOpServerSendStatus>
ops;
if (!param.server_context->sent_initial_metadata_) {
- ops.SendInitialMetadata(param.server_context->initial_metadata_,
+ ops.SendInitialMetadata(&param.server_context->initial_metadata_,
param.server_context->initial_metadata_flags());
if (param.server_context->compression_level_set()) {
ops.set_compression_level(param.server_context->compression_level());
@@ -126,7 +141,7 @@ class ClientStreamingHandler : public MethodHandler {
if (status.ok()) {
status = ops.SendMessage(rsp);
}
- ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
+ ops.ServerSendStatus(&param.server_context->trailing_metadata_, status);
param.call->PerformOps(&ops);
param.call->cq()->Pluck(&ops);
}
@@ -150,26 +165,25 @@ class ServerStreamingHandler : public MethodHandler {
: func_(func), service_(service) {}
void RunHandler(const HandlerParameter& param) final {
- RequestType req;
- Status status = SerializationTraits<RequestType>::Deserialize(
- param.request.bbuf_ptr(), &req);
-
+ Status status = param.status;
if (status.ok()) {
ServerWriter<ResponseType> writer(param.call, param.server_context);
- status = CatchingFunctionHandler([this, &param, &req, &writer] {
- return func_(service_, param.server_context, &req, &writer);
+ status = CatchingFunctionHandler([this, &param, &writer] {
+ return func_(service_, param.server_context,
+ static_cast<RequestType*>(param.request), &writer);
});
+ static_cast<RequestType*>(param.request)->~RequestType();
}
CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops;
if (!param.server_context->sent_initial_metadata_) {
- ops.SendInitialMetadata(param.server_context->initial_metadata_,
+ ops.SendInitialMetadata(&param.server_context->initial_metadata_,
param.server_context->initial_metadata_flags());
if (param.server_context->compression_level_set()) {
ops.set_compression_level(param.server_context->compression_level());
}
}
- ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
+ ops.ServerSendStatus(&param.server_context->trailing_metadata_, status);
param.call->PerformOps(&ops);
if (param.server_context->has_pending_ops_) {
param.call->cq()->Pluck(&param.server_context->pending_ops_);
@@ -177,6 +191,21 @@ class ServerStreamingHandler : public MethodHandler {
param.call->cq()->Pluck(&ops);
}
+ void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
+ Status* status) final {
+ ByteBuffer buf;
+ buf.set_buffer(req);
+ auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc(
+ call, sizeof(RequestType))) RequestType();
+ *status = SerializationTraits<RequestType>::Deserialize(&buf, request);
+ buf.Release();
+ if (status->ok()) {
+ return request;
+ }
+ request->~RequestType();
+ return nullptr;
+ }
+
private:
std::function<Status(ServiceType*, ServerContext*, const RequestType*,
ServerWriter<ResponseType>*)>
@@ -206,7 +235,7 @@ class TemplatedBidiStreamingHandler : public MethodHandler {
CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops;
if (!param.server_context->sent_initial_metadata_) {
- ops.SendInitialMetadata(param.server_context->initial_metadata_,
+ ops.SendInitialMetadata(&param.server_context->initial_metadata_,
param.server_context->initial_metadata_flags());
if (param.server_context->compression_level_set()) {
ops.set_compression_level(param.server_context->compression_level());
@@ -218,7 +247,7 @@ class TemplatedBidiStreamingHandler : public MethodHandler {
"Service did not provide response message");
}
}
- ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
+ ops.ServerSendStatus(&param.server_context->trailing_metadata_, status);
param.call->PerformOps(&ops);
if (param.server_context->has_pending_ops_) {
param.call->cq()->Pluck(&param.server_context->pending_ops_);
@@ -281,14 +310,14 @@ class ErrorMethodHandler : public MethodHandler {
static void FillOps(ServerContext* context, T* ops) {
Status status(code, "");
if (!context->sent_initial_metadata_) {
- ops->SendInitialMetadata(context->initial_metadata_,
+ ops->SendInitialMetadata(&context->initial_metadata_,
context->initial_metadata_flags());
if (context->compression_level_set()) {
ops->set_compression_level(context->compression_level());
}
context->sent_initial_metadata_ = true;
}
- ops->ServerSendStatus(context->trailing_metadata_, status);
+ ops->ServerSendStatus(&context->trailing_metadata_, status);
}
void RunHandler(const HandlerParameter& param) final {
@@ -296,11 +325,15 @@ class ErrorMethodHandler : public MethodHandler {
FillOps(param.server_context, &ops);
param.call->PerformOps(&ops);
param.call->cq()->Pluck(&ops);
- // We also have to destroy any request payload in the handler parameter
- ByteBuffer* payload = param.request.bbuf_ptr();
- if (payload != nullptr) {
- payload->Clear();
+ }
+
+ void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
+ Status* status) final {
+ // We have to destroy any request payload
+ if (req != nullptr) {
+ g_core_codegen_interface->grpc_byte_buffer_destroy(req);
}
+ return nullptr;
}
};
diff --git a/include/grpcpp/impl/codegen/rpc_service_method.h b/include/grpcpp/impl/codegen/rpc_service_method.h
index 5cf88e216f..f465c5fc2f 100644
--- a/include/grpcpp/impl/codegen/rpc_service_method.h
+++ b/include/grpcpp/impl/codegen/rpc_service_method.h
@@ -40,17 +40,41 @@ class MethodHandler {
public:
virtual ~MethodHandler() {}
struct HandlerParameter {
- HandlerParameter(Call* c, ServerContext* context, grpc_byte_buffer* req)
- : call(c), server_context(context) {
- request.set_buffer(req);
- }
- ~HandlerParameter() { request.Release(); }
+ /// Constructor for HandlerParameter
+ ///
+ /// \param c : the gRPC Call structure for this server call
+ /// \param context : the ServerContext structure for this server call
+ /// \param req : the request payload, if appropriate for this RPC
+ /// \param req_status : the request status after any interceptors have run
+ /// \param rpc_requester : used only by the callback API. It is a function
+ /// called by the RPC Controller to request another RPC (and also
+ /// to set up the state required to make that request possible)
+ HandlerParameter(Call* c, ServerContext* context, void* req,
+ Status req_status, std::function<void()> requester)
+ : call(c),
+ server_context(context),
+ request(req),
+ status(req_status),
+ call_requester(std::move(requester)) {}
+ ~HandlerParameter() {}
Call* call;
ServerContext* server_context;
- // Handler required to destroy these contents
- ByteBuffer request;
+ void* request;
+ Status status;
+ std::function<void()> call_requester;
};
virtual void RunHandler(const HandlerParameter& param) = 0;
+
+ /* Returns a pointer to the deserialized request. \a status reflects the
+ result of deserialization. This pointer and the status should be filled in
+ a HandlerParameter and passed to RunHandler. It is illegal to access the
+ pointer after calling RunHandler. Ownership of the deserialized request is
+ retained by the handler. Returns nullptr if deserialization failed. */
+ virtual void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
+ Status* status) {
+ GPR_CODEGEN_ASSERT(req == nullptr);
+ return nullptr;
+ }
};
/// Server side rpc method class
@@ -61,25 +85,29 @@ class RpcServiceMethod : public RpcMethod {
MethodHandler* handler)
: RpcMethod(name, type),
server_tag_(nullptr),
- async_type_(AsyncType::UNSET),
+ api_type_(ApiType::SYNC),
handler_(handler) {}
- enum class AsyncType {
- UNSET,
+ enum class ApiType {
+ SYNC,
ASYNC,
RAW,
+ CALL_BACK, // not CALLBACK because that is reserved in Windows
+ RAW_CALL_BACK,
};
void set_server_tag(void* tag) { server_tag_ = tag; }
void* server_tag() const { return server_tag_; }
/// if MethodHandler is nullptr, then this is an async method
MethodHandler* handler() const { return handler_.get(); }
+ ApiType api_type() const { return api_type_; }
void SetHandler(MethodHandler* handler) { handler_.reset(handler); }
- void SetServerAsyncType(RpcServiceMethod::AsyncType type) {
- if (async_type_ == AsyncType::UNSET) {
+ void SetServerApiType(RpcServiceMethod::ApiType type) {
+ if ((api_type_ == ApiType::SYNC) &&
+ (type == ApiType::ASYNC || type == ApiType::RAW)) {
// this marks this method as async
handler_.reset();
- } else {
+ } else if (api_type_ != ApiType::SYNC) {
// this is not an error condition, as it allows users to declare a server
// like WithRawMethod_foo<AsyncService>. However since it
// overwrites behavior, it should be logged.
@@ -88,24 +116,28 @@ class RpcServiceMethod : public RpcMethod {
"You are marking method %s as '%s', even though it was "
"previously marked '%s'. This behavior will overwrite the original "
"behavior. If you expected this then ignore this message.",
- name(), TypeToString(async_type_), TypeToString(type));
+ name(), TypeToString(api_type_), TypeToString(type));
}
- async_type_ = type;
+ api_type_ = type;
}
private:
void* server_tag_;
- AsyncType async_type_;
+ ApiType api_type_;
std::unique_ptr<MethodHandler> handler_;
- const char* TypeToString(RpcServiceMethod::AsyncType type) {
+ const char* TypeToString(RpcServiceMethod::ApiType type) {
switch (type) {
- case AsyncType::UNSET:
- return "unset";
- case AsyncType::ASYNC:
+ case ApiType::SYNC:
+ return "sync";
+ case ApiType::ASYNC:
return "async";
- case AsyncType::RAW:
+ case ApiType::RAW:
return "raw";
+ case ApiType::CALL_BACK:
+ return "callback";
+ case ApiType::RAW_CALL_BACK:
+ return "raw_callback";
default:
GPR_UNREACHABLE_CODE(return "unknown");
}
diff --git a/include/grpcpp/impl/codegen/server_callback.h b/include/grpcpp/impl/codegen/server_callback.h
new file mode 100644
index 0000000000..5d56cbf1df
--- /dev/null
+++ b/include/grpcpp/impl/codegen/server_callback.h
@@ -0,0 +1,200 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H
+#define GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H
+
+#include <functional>
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/callback_common.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/server_context.h>
+#include <grpcpp/impl/codegen/server_interface.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+// forward declarations
+namespace internal {
+template <class ServiceType, class RequestType, class ResponseType>
+class CallbackUnaryHandler;
+} // namespace internal
+
+namespace experimental {
+
+// For unary RPCs, the exposed controller class is only an interface
+// and the actual implementation is an internal class.
+class ServerCallbackRpcController {
+ public:
+ virtual ~ServerCallbackRpcController() {}
+
+ // The method handler must call this function when it is done so that
+ // the library knows to free its resources
+ virtual void Finish(Status s) = 0;
+
+ // Allow the method handler to push out the initial metadata before
+ // the response and status are ready
+ virtual void SendInitialMetadata(std::function<void(bool)>) = 0;
+};
+
+} // namespace experimental
+
+namespace internal {
+
+template <class ServiceType, class RequestType, class ResponseType>
+class CallbackUnaryHandler : public MethodHandler {
+ public:
+ CallbackUnaryHandler(
+ std::function<void(ServerContext*, const RequestType*, ResponseType*,
+ experimental::ServerCallbackRpcController*)>
+ func,
+ ServiceType* service)
+ : func_(func) {}
+ void RunHandler(const HandlerParameter& param) final {
+ // Arena allocate a controller structure (that includes request/response)
+ g_core_codegen_interface->grpc_call_ref(param.call->call());
+ auto* controller = new (g_core_codegen_interface->grpc_call_arena_alloc(
+ param.call->call(), sizeof(ServerCallbackRpcControllerImpl)))
+ ServerCallbackRpcControllerImpl(
+ param.server_context, param.call,
+ static_cast<RequestType*>(param.request),
+ std::move(param.call_requester));
+ Status status = param.status;
+
+ if (status.ok()) {
+ // Call the actual function handler and expect the user to call finish
+ CatchingCallback(std::move(func_), param.server_context,
+ controller->request(), controller->response(),
+ controller);
+ } else {
+ // if deserialization failed, we need to fail the call
+ controller->Finish(status);
+ }
+ }
+
+ void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
+ Status* status) final {
+ ByteBuffer buf;
+ buf.set_buffer(req);
+ auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc(
+ call, sizeof(RequestType))) RequestType();
+ *status = SerializationTraits<RequestType>::Deserialize(&buf, request);
+ buf.Release();
+ if (status->ok()) {
+ return request;
+ }
+ request->~RequestType();
+ return nullptr;
+ }
+
+ private:
+ std::function<void(ServerContext*, const RequestType*, ResponseType*,
+ experimental::ServerCallbackRpcController*)>
+ func_;
+
+ // The implementation class of ServerCallbackRpcController is a private member
+ // of CallbackUnaryHandler since it is never exposed anywhere, and this allows
+ // it to take advantage of CallbackUnaryHandler's friendships.
+ class ServerCallbackRpcControllerImpl
+ : public experimental::ServerCallbackRpcController {
+ public:
+ void Finish(Status s) override {
+ finish_tag_ = CallbackWithSuccessTag(
+ call_.call(),
+ [this](bool) {
+ grpc_call* call = call_.call();
+ auto call_requester = std::move(call_requester_);
+ this->~ServerCallbackRpcControllerImpl(); // explicitly call
+ // destructor
+ g_core_codegen_interface->grpc_call_unref(call);
+ call_requester();
+ },
+ &finish_buf_);
+ if (!ctx_->sent_initial_metadata_) {
+ finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
+ ctx_->initial_metadata_flags());
+ if (ctx_->compression_level_set()) {
+ finish_buf_.set_compression_level(ctx_->compression_level());
+ }
+ ctx_->sent_initial_metadata_ = true;
+ }
+ // The response is dropped if the status is not OK.
+ if (s.ok()) {
+ finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_,
+ finish_buf_.SendMessage(resp_));
+ } else {
+ finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, s);
+ }
+ finish_buf_.set_core_cq_tag(&finish_tag_);
+ call_.PerformOps(&finish_buf_);
+ }
+
+ void SendInitialMetadata(std::function<void(bool)> f) override {
+ GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+ meta_tag_ =
+ CallbackWithSuccessTag(call_.call(), std::move(f), &meta_buf_);
+ meta_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
+ ctx_->initial_metadata_flags());
+ if (ctx_->compression_level_set()) {
+ meta_buf_.set_compression_level(ctx_->compression_level());
+ }
+ ctx_->sent_initial_metadata_ = true;
+ meta_buf_.set_core_cq_tag(&meta_tag_);
+ call_.PerformOps(&meta_buf_);
+ }
+
+ private:
+ template <class SrvType, class ReqType, class RespType>
+ friend class CallbackUnaryHandler;
+
+ ServerCallbackRpcControllerImpl(ServerContext* ctx, Call* call,
+ RequestType* req,
+ std::function<void()> call_requester)
+ : ctx_(ctx),
+ call_(*call),
+ req_(req),
+ call_requester_(std::move(call_requester)) {}
+
+ ~ServerCallbackRpcControllerImpl() { req_->~RequestType(); }
+
+ RequestType* request() { return req_; }
+ ResponseType* response() { return &resp_; }
+
+ CallOpSet<CallOpSendInitialMetadata> meta_buf_;
+ CallbackWithSuccessTag meta_tag_;
+ CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
+ CallOpServerSendStatus>
+ finish_buf_;
+ CallbackWithSuccessTag finish_tag_;
+
+ ServerContext* ctx_;
+ Call call_;
+ RequestType* req_;
+ ResponseType resp_;
+ std::function<void()> call_requester_;
+ };
+};
+
+} // namespace internal
+
+} // namespace grpc
+
+#endif // GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H
diff --git a/include/grpcpp/impl/codegen/server_context.h b/include/grpcpp/impl/codegen/server_context.h
index b58f029de9..82ee862f61 100644
--- a/include/grpcpp/impl/codegen/server_context.h
+++ b/include/grpcpp/impl/codegen/server_context.h
@@ -26,11 +26,14 @@
#include <grpc/impl/codegen/compression_types.h>
#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/call_op_set.h>
+#include <grpcpp/impl/codegen/callback_common.h>
#include <grpcpp/impl/codegen/completion_queue_tag.h>
#include <grpcpp/impl/codegen/config.h>
#include <grpcpp/impl/codegen/create_auth_context.h>
#include <grpcpp/impl/codegen/metadata_map.h>
#include <grpcpp/impl/codegen/security/auth_context.h>
+#include <grpcpp/impl/codegen/server_interceptor.h>
#include <grpcpp/impl/codegen/string_ref.h>
#include <grpcpp/impl/codegen/time.h>
@@ -63,6 +66,8 @@ template <class ServiceType, class RequestType, class ResponseType>
class ServerStreamingHandler;
template <class ServiceType, class RequestType, class ResponseType>
class BidiStreamingHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class CallbackUnaryHandler;
template <class Streamer, bool WriteNeeded>
class TemplatedBidiStreamingHandler;
template <StatusCode code>
@@ -107,7 +112,7 @@ class ServerContext {
/// Return a \a gpr_timespec representation of the server call's deadline.
gpr_timespec raw_deadline() const { return deadline_; }
- /// Add the (\a meta_key, \a meta_value) pair to the initial metadata
+ /// Add the (\a key, \a value) pair to the initial metadata
/// associated with a server call. These are made available at the client side
/// by the \a grpc::ClientContext::GetServerInitialMetadata() method.
///
@@ -115,13 +120,13 @@ class ServerContext {
/// to the client (which can happen explicitly, or implicitly when sending a
/// a response message or status to the client).
///
- /// \param meta_key The metadata key. If \a meta_value is binary data, it must
+ /// \param key The metadata key. If \a value is binary data, it must
/// end in "-bin".
- /// \param meta_value The metadata value. If its value is binary, the key name
+ /// \param value The metadata value. If its value is binary, the key name
/// must end in "-bin".
void AddInitialMetadata(const grpc::string& key, const grpc::string& value);
- /// Add the (\a meta_key, \a meta_value) pair to the initial metadata
+ /// Add the (\a key, \a value) pair to the initial metadata
/// associated with a server call. These are made available at the client
/// side by the \a grpc::ClientContext::GetServerTrailingMetadata() method.
///
@@ -129,13 +134,13 @@ class ServerContext {
/// metadata to the client (which happens when the call is finished and a
/// status is sent to the client).
///
- /// \param meta_key The metadata key. If \a meta_value is binary data,
+ /// \param key The metadata key. If \a value is binary data,
/// it must end in "-bin".
- /// \param meta_value The metadata value. If its value is binary, the key name
+ /// \param value The metadata value. If its value is binary, the key name
/// must end in "-bin".
void AddTrailingMetadata(const grpc::string& key, const grpc::string& value);
- /// IsCancelled is always safe to call when using sync API.
+ /// IsCancelled is always safe to call when using sync or callback API.
/// When using async API, it is only safe to call IsCancelled after
/// the AsyncNotifyWhenDone tag has been delivered.
bool IsCancelled() const;
@@ -177,9 +182,9 @@ class ServerContext {
return compression_level_;
}
- /// Set \a algorithm to be the compression algorithm used for the server call.
+ /// Set \a level to be the compression level used for the server call.
///
- /// \param algorithm The compression algorithm used for the server call.
+ /// \param level The compression level used for the server call.
void set_compression_level(grpc_compression_level level) {
compression_level_set_ = true;
compression_level_ = level;
@@ -265,6 +270,8 @@ class ServerContext {
friend class ::grpc::internal::ServerStreamingHandler;
template <class Streamer, bool WriteNeeded>
friend class ::grpc::internal::TemplatedBidiStreamingHandler;
+ template <class ServiceType, class RequestType, class ResponseType>
+ friend class ::grpc::internal::CallbackUnaryHandler;
template <StatusCode code>
friend class internal::ErrorMethodHandler;
friend class ::grpc::ClientContext;
@@ -275,7 +282,7 @@ class ServerContext {
class CompletionOp;
- void BeginCompletionOp(internal::Call* call);
+ void BeginCompletionOp(internal::Call* call, bool callback);
/// Return the tag queued by BeginCompletionOp()
internal::CompletionQueueTag* GetCompletionOpTag();
@@ -283,11 +290,30 @@ class ServerContext {
void set_call(grpc_call* call) { call_ = call; }
+ void BindDeadlineAndMetadata(gpr_timespec deadline, grpc_metadata_array* arr);
+
+ void Clear();
+
+ void Setup(gpr_timespec deadline);
+
uint32_t initial_metadata_flags() const { return 0; }
+ experimental::ServerRpcInfo* set_server_rpc_info(
+ const char* method,
+ const std::vector<
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>&
+ creators) {
+ if (creators.size() != 0) {
+ rpc_info_ = new experimental::ServerRpcInfo(this, method);
+ rpc_info_->RegisterInterceptors(creators);
+ }
+ return rpc_info_;
+ }
+
CompletionOp* completion_op_;
bool has_notify_when_done_tag_;
void* async_notify_when_done_tag_;
+ internal::CallbackWithSuccessTag completion_tag_;
gpr_timespec deadline_;
grpc_call* call_;
@@ -306,6 +332,8 @@ class ServerContext {
internal::CallOpSendMessage>
pending_ops_;
bool has_pending_ops_;
+
+ experimental::ServerRpcInfo* rpc_info_;
};
} // namespace grpc
diff --git a/include/grpcpp/impl/codegen/server_interceptor.h b/include/grpcpp/impl/codegen/server_interceptor.h
new file mode 100644
index 0000000000..5fb5df28b7
--- /dev/null
+++ b/include/grpcpp/impl/codegen/server_interceptor.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_SERVER_INTERCEPTOR_H
+#define GRPCPP_IMPL_CODEGEN_SERVER_INTERCEPTOR_H
+
+#include <atomic>
+#include <vector>
+
+#include <grpcpp/impl/codegen/interceptor.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+
+namespace grpc {
+
+class ServerContext;
+
+namespace internal {
+class InterceptorBatchMethodsImpl;
+}
+
+namespace experimental {
+class ServerRpcInfo;
+
+class ServerInterceptorFactoryInterface {
+ public:
+ virtual ~ServerInterceptorFactoryInterface() {}
+ virtual Interceptor* CreateServerInterceptor(ServerRpcInfo* info) = 0;
+};
+
+class ServerRpcInfo {
+ public:
+ ~ServerRpcInfo(){};
+
+ ServerRpcInfo(const ServerRpcInfo&) = delete;
+ ServerRpcInfo(ServerRpcInfo&&) = default;
+ ServerRpcInfo& operator=(ServerRpcInfo&&) = default;
+
+ // Getter methods
+ const char* method() { return method_; }
+ grpc::ServerContext* server_context() { return ctx_; }
+
+ private:
+ ServerRpcInfo(grpc::ServerContext* ctx, const char* method)
+ : ctx_(ctx), method_(method) {
+ ref_.store(1);
+ }
+
+ // Runs interceptor at pos \a pos.
+ void RunInterceptor(
+ experimental::InterceptorBatchMethods* interceptor_methods, size_t pos) {
+ GPR_CODEGEN_ASSERT(pos < interceptors_.size());
+ interceptors_[pos]->Intercept(interceptor_methods);
+ }
+
+ void RegisterInterceptors(
+ const std::vector<
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>&
+ creators) {
+ for (const auto& creator : creators) {
+ interceptors_.push_back(std::unique_ptr<experimental::Interceptor>(
+ creator->CreateServerInterceptor(this)));
+ }
+ }
+
+ void Ref() { ref_++; }
+ void Unref() {
+ if (--ref_ == 0) {
+ delete this;
+ }
+ }
+
+ grpc::ServerContext* ctx_ = nullptr;
+ const char* method_ = nullptr;
+ std::atomic_int ref_;
+ std::vector<std::unique_ptr<experimental::Interceptor>> interceptors_;
+
+ friend class internal::InterceptorBatchMethodsImpl;
+ friend class grpc::ServerContext;
+};
+
+} // namespace experimental
+} // namespace grpc
+
+#endif // GRPCPP_IMPL_CODEGEN_SERVER_INTERCEPTOR_H
diff --git a/include/grpcpp/impl/codegen/server_interface.h b/include/grpcpp/impl/codegen/server_interface.h
index 237991cde6..8bfb10f704 100644
--- a/include/grpcpp/impl/codegen/server_interface.h
+++ b/include/grpcpp/impl/codegen/server_interface.h
@@ -21,10 +21,12 @@
#include <grpc/impl/codegen/grpc_types.h>
#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/call.h>
#include <grpcpp/impl/codegen/call_hook.h>
#include <grpcpp/impl/codegen/completion_queue_tag.h>
#include <grpcpp/impl/codegen/core_codegen_interface.h>
#include <grpcpp/impl/codegen/rpc_service_method.h>
+#include <grpcpp/impl/codegen/server_context.h>
namespace grpc {
@@ -148,44 +150,67 @@ class ServerInterface : public internal::CallHook {
public:
BaseAsyncRequest(ServerInterface* server, ServerContext* context,
internal::ServerAsyncStreamingInterface* stream,
- CompletionQueue* call_cq, void* tag,
+ CompletionQueue* call_cq,
+ ServerCompletionQueue* notification_cq, void* tag,
bool delete_on_finalize);
virtual ~BaseAsyncRequest();
bool FinalizeResult(void** tag, bool* status) override;
+ private:
+ void ContinueFinalizeResultAfterInterception();
+
protected:
ServerInterface* const server_;
ServerContext* const context_;
internal::ServerAsyncStreamingInterface* const stream_;
CompletionQueue* const call_cq_;
+ ServerCompletionQueue* const notification_cq_;
void* const tag_;
const bool delete_on_finalize_;
grpc_call* call_;
+ internal::Call call_wrapper_;
+ internal::InterceptorBatchMethodsImpl interceptor_methods_;
+ bool done_intercepting_;
};
class RegisteredAsyncRequest : public BaseAsyncRequest {
public:
RegisteredAsyncRequest(ServerInterface* server, ServerContext* context,
internal::ServerAsyncStreamingInterface* stream,
- CompletionQueue* call_cq, void* tag);
-
- // uses BaseAsyncRequest::FinalizeResult
+ CompletionQueue* call_cq,
+ ServerCompletionQueue* notification_cq, void* tag,
+ const char* name);
+
+ virtual bool FinalizeResult(void** tag, bool* status) override {
+ /* If we are done intercepting, then there is nothing more for us to do */
+ if (done_intercepting_) {
+ return BaseAsyncRequest::FinalizeResult(tag, status);
+ }
+ call_wrapper_ = internal::Call(
+ call_, server_, call_cq_, server_->max_receive_message_size(),
+ context_->set_server_rpc_info(name_,
+ *server_->interceptor_creators()));
+ return BaseAsyncRequest::FinalizeResult(tag, status);
+ }
protected:
void IssueRequest(void* registered_method, grpc_byte_buffer** payload,
ServerCompletionQueue* notification_cq);
+ const char* name_;
};
class NoPayloadAsyncRequest final : public RegisteredAsyncRequest {
public:
- NoPayloadAsyncRequest(void* registered_method, ServerInterface* server,
- ServerContext* context,
+ NoPayloadAsyncRequest(internal::RpcServiceMethod* registered_method,
+ ServerInterface* server, ServerContext* context,
internal::ServerAsyncStreamingInterface* stream,
CompletionQueue* call_cq,
ServerCompletionQueue* notification_cq, void* tag)
- : RegisteredAsyncRequest(server, context, stream, call_cq, tag) {
- IssueRequest(registered_method, nullptr, notification_cq);
+ : RegisteredAsyncRequest(server, context, stream, call_cq,
+ notification_cq, tag,
+ registered_method->name()) {
+ IssueRequest(registered_method->server_tag(), nullptr, notification_cq);
}
// uses RegisteredAsyncRequest::FinalizeResult
@@ -194,13 +219,15 @@ class ServerInterface : public internal::CallHook {
template <class Message>
class PayloadAsyncRequest final : public RegisteredAsyncRequest {
public:
- PayloadAsyncRequest(void* registered_method, ServerInterface* server,
- ServerContext* context,
+ PayloadAsyncRequest(internal::RpcServiceMethod* registered_method,
+ ServerInterface* server, ServerContext* context,
internal::ServerAsyncStreamingInterface* stream,
CompletionQueue* call_cq,
ServerCompletionQueue* notification_cq, void* tag,
Message* request)
- : RegisteredAsyncRequest(server, context, stream, call_cq, tag),
+ : RegisteredAsyncRequest(server, context, stream, call_cq,
+ notification_cq, tag,
+ registered_method->name()),
registered_method_(registered_method),
server_(server),
context_(context),
@@ -209,7 +236,8 @@ class ServerInterface : public internal::CallHook {
notification_cq_(notification_cq),
tag_(tag),
request_(request) {
- IssueRequest(registered_method, payload_.bbuf_ptr(), notification_cq);
+ IssueRequest(registered_method->server_tag(), payload_.bbuf_ptr(),
+ notification_cq);
}
~PayloadAsyncRequest() {
@@ -217,6 +245,10 @@ class ServerInterface : public internal::CallHook {
}
bool FinalizeResult(void** tag, bool* status) override {
+ /* If we are done intercepting, then there is nothing more for us to do */
+ if (done_intercepting_) {
+ return RegisteredAsyncRequest::FinalizeResult(tag, status);
+ }
if (*status) {
if (!payload_.Valid() || !SerializationTraits<Message>::Deserialize(
payload_.bbuf_ptr(), request_)
@@ -235,15 +267,20 @@ class ServerInterface : public internal::CallHook {
return false;
}
}
+ /* Set interception point for recv message */
+ interceptor_methods_.AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+ interceptor_methods_.SetRecvMessage(request_);
return RegisteredAsyncRequest::FinalizeResult(tag, status);
}
private:
- void* const registered_method_;
+ internal::RpcServiceMethod* const registered_method_;
ServerInterface* const server_;
ServerContext* const context_;
internal::ServerAsyncStreamingInterface* const stream_;
CompletionQueue* const call_cq_;
+
ServerCompletionQueue* const notification_cq_;
void* const tag_;
Message* const request_;
@@ -272,9 +309,8 @@ class ServerInterface : public internal::CallHook {
ServerCompletionQueue* notification_cq, void* tag,
Message* message) {
GPR_CODEGEN_ASSERT(method);
- new PayloadAsyncRequest<Message>(method->server_tag(), this, context,
- stream, call_cq, notification_cq, tag,
- message);
+ new PayloadAsyncRequest<Message>(method, this, context, stream, call_cq,
+ notification_cq, tag, message);
}
void RequestAsyncCall(internal::RpcServiceMethod* method,
@@ -283,8 +319,8 @@ class ServerInterface : public internal::CallHook {
CompletionQueue* call_cq,
ServerCompletionQueue* notification_cq, void* tag) {
GPR_CODEGEN_ASSERT(method);
- new NoPayloadAsyncRequest(method->server_tag(), this, context, stream,
- call_cq, notification_cq, tag);
+ new NoPayloadAsyncRequest(method, this, context, stream, call_cq,
+ notification_cq, tag);
}
void RequestAsyncGenericCall(GenericServerContext* context,
@@ -295,6 +331,28 @@ class ServerInterface : public internal::CallHook {
new GenericAsyncRequest(this, context, stream, call_cq, notification_cq,
tag, true);
}
+
+ private:
+ // EXPERIMENTAL
+ // Getter method for the vector of interceptor factory objects.
+ // Returns a nullptr (rather than being pure) since this is a new method and
+ // adding a new pure method to an interface would be a breaking change (even
+ // though this is private and non-API)
+ virtual std::vector<
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>*
+ interceptor_creators() {
+ return nullptr;
+ }
+
+ // EXPERIMENTAL
+ // A method to get the callbackable completion queue associated with this
+ // server. If the return value is nullptr, this server doesn't support
+ // callback operations.
+ // TODO(vjpai): Consider a better default like using a global CQ
+ // Returns nullptr (rather than being pure) since this is a post-1.0 method
+ // and adding a new pure method to an interface would be a breaking change
+ // (even though this is private and non-API)
+ virtual CompletionQueue* CallbackCQ() { return nullptr; }
};
} // namespace grpc
diff --git a/include/grpcpp/impl/codegen/service_type.h b/include/grpcpp/impl/codegen/service_type.h
index 9f1a052168..332a04c294 100644
--- a/include/grpcpp/impl/codegen/service_type.h
+++ b/include/grpcpp/impl/codegen/service_type.h
@@ -71,7 +71,20 @@ class Service {
bool has_synchronous_methods() const {
for (auto it = methods_.begin(); it != methods_.end(); ++it) {
- if (*it && (*it)->handler() != nullptr) {
+ if (*it &&
+ (*it)->api_type() == internal::RpcServiceMethod::ApiType::SYNC) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool has_callback_methods() const {
+ for (auto it = methods_.begin(); it != methods_.end(); ++it) {
+ if (*it && ((*it)->api_type() ==
+ internal::RpcServiceMethod::ApiType::CALL_BACK ||
+ (*it)->api_type() ==
+ internal::RpcServiceMethod::ApiType::RAW_CALL_BACK)) {
return true;
}
}
@@ -88,6 +101,43 @@ class Service {
}
protected:
+ // TODO(vjpai): Promote experimental contents once callback API is accepted
+ class experimental_type {
+ public:
+ explicit experimental_type(Service* service) : service_(service) {}
+
+ void MarkMethodCallback(int index, internal::MethodHandler* handler) {
+ // 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);
+ GPR_CODEGEN_ASSERT(
+ service_->methods_[idx].get() != nullptr &&
+ "Cannot mark the method as 'callback' because it has already been "
+ "marked as 'generic'.");
+ service_->methods_[idx]->SetHandler(handler);
+ service_->methods_[idx]->SetServerApiType(
+ internal::RpcServiceMethod::ApiType::CALL_BACK);
+ }
+
+ 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);
+ GPR_CODEGEN_ASSERT(
+ service_->methods_[idx].get() != nullptr &&
+ "Cannot mark the method as 'raw callback' because it has already "
+ "been marked as 'generic'.");
+ service_->methods_[idx]->SetHandler(handler);
+ service_->methods_[idx]->SetServerApiType(
+ internal::RpcServiceMethod::ApiType::RAW_CALL_BACK);
+ }
+
+ private:
+ Service* service_;
+ };
+
+ experimental_type experimental() { return experimental_type(this); }
+
template <class Message>
void RequestAsyncUnary(int index, ServerContext* context, Message* request,
internal::ServerAsyncStreamingInterface* stream,
@@ -138,8 +188,7 @@ class Service {
methods_[idx].get() != nullptr &&
"Cannot mark the method as 'async' because it has already been "
"marked as 'generic'.");
- methods_[idx]->SetServerAsyncType(
- internal::RpcServiceMethod::AsyncType::ASYNC);
+ methods_[idx]->SetServerApiType(internal::RpcServiceMethod::ApiType::ASYNC);
}
void MarkMethodRaw(int index) {
@@ -149,8 +198,7 @@ class Service {
GPR_CODEGEN_ASSERT(methods_[idx].get() != nullptr &&
"Cannot mark the method as 'raw' because it has already "
"been marked as 'generic'.");
- methods_[idx]->SetServerAsyncType(
- internal::RpcServiceMethod::AsyncType::RAW);
+ methods_[idx]->SetServerApiType(internal::RpcServiceMethod::ApiType::RAW);
}
void MarkMethodGeneric(int index) {
diff --git a/include/grpcpp/impl/codegen/sync_stream.h b/include/grpcpp/impl/codegen/sync_stream.h
index cbfcf25d0a..6981076f04 100644
--- a/include/grpcpp/impl/codegen/sync_stream.h
+++ b/include/grpcpp/impl/codegen/sync_stream.h
@@ -250,7 +250,7 @@ class ClientReader final : public ClientReaderInterface<R> {
::grpc::internal::CallOpSendMessage,
::grpc::internal::CallOpClientSendClose>
ops;
- ops.SendInitialMetadata(context->send_initial_metadata_,
+ ops.SendInitialMetadata(&context->send_initial_metadata_,
context->initial_metadata_flags());
// TODO(ctiller): don't assert
GPR_CODEGEN_ASSERT(ops.SendMessage(request).ok());
@@ -327,7 +327,7 @@ class ClientWriter : public ClientWriterInterface<W> {
ops.ClientSendClose();
}
if (context_->initial_metadata_corked_) {
- ops.SendInitialMetadata(context_->send_initial_metadata_,
+ ops.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
context_->set_initial_metadata_corked(false);
}
@@ -386,7 +386,7 @@ class ClientWriter : public ClientWriterInterface<W> {
if (!context_->initial_metadata_corked_) {
::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
ops;
- ops.SendInitialMetadata(context->send_initial_metadata_,
+ ops.SendInitialMetadata(&context->send_initial_metadata_,
context->initial_metadata_flags());
call_.PerformOps(&ops);
cq_.Pluck(&ops);
@@ -498,7 +498,7 @@ class ClientReaderWriter final : public ClientReaderWriterInterface<W, R> {
ops.ClientSendClose();
}
if (context_->initial_metadata_corked_) {
- ops.SendInitialMetadata(context_->send_initial_metadata_,
+ ops.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
context_->set_initial_metadata_corked(false);
}
@@ -557,7 +557,7 @@ class ClientReaderWriter final : public ClientReaderWriterInterface<W, R> {
if (!context_->initial_metadata_corked_) {
::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
ops;
- ops.SendInitialMetadata(context->send_initial_metadata_,
+ ops.SendInitialMetadata(&context->send_initial_metadata_,
context->initial_metadata_flags());
call_.PerformOps(&ops);
cq_.Pluck(&ops);
@@ -583,7 +583,7 @@ class ServerReader final : public ServerReaderInterface<R> {
GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
internal::CallOpSet<internal::CallOpSendInitialMetadata> ops;
- ops.SendInitialMetadata(ctx_->initial_metadata_,
+ ops.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ops.set_compression_level(ctx_->compression_level());
@@ -635,7 +635,7 @@ class ServerWriter final : public ServerWriterInterface<W> {
GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
internal::CallOpSet<internal::CallOpSendInitialMetadata> ops;
- ops.SendInitialMetadata(ctx_->initial_metadata_,
+ ops.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ops.set_compression_level(ctx_->compression_level());
@@ -660,7 +660,7 @@ class ServerWriter final : public ServerWriterInterface<W> {
return false;
}
if (!ctx_->sent_initial_metadata_) {
- ctx_->pending_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+ ctx_->pending_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ctx_->pending_ops_.set_compression_level(ctx_->compression_level());
@@ -708,7 +708,7 @@ class ServerReaderWriterBody final {
GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
CallOpSet<CallOpSendInitialMetadata> ops;
- ops.SendInitialMetadata(ctx_->initial_metadata_,
+ ops.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ops.set_compression_level(ctx_->compression_level());
@@ -738,7 +738,7 @@ class ServerReaderWriterBody final {
return false;
}
if (!ctx_->sent_initial_metadata_) {
- ctx_->pending_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+ ctx_->pending_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ctx_->pending_ops_.set_compression_level(ctx_->compression_level());
diff --git a/include/grpcpp/server.h b/include/grpcpp/server.h
index 8d3e856502..a14a4da578 100644
--- a/include/grpcpp/server.h
+++ b/include/grpcpp/server.h
@@ -174,7 +174,11 @@ class Server : public ServerInterface, private GrpcLibraryCodegen {
std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
sync_server_cqs,
int min_pollers, int max_pollers, int sync_cq_timeout_msec,
- grpc_resource_quota* server_rq = nullptr);
+ grpc_resource_quota* server_rq = nullptr,
+ std::vector<
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ interceptor_creators = std::vector<std::unique_ptr<
+ experimental::ServerInterceptorFactoryInterface>>());
/// Start the server.
///
@@ -187,11 +191,17 @@ class Server : public ServerInterface, private GrpcLibraryCodegen {
grpc_server* server() override { return server_; };
private:
+ std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>*
+ interceptor_creators() override {
+ return &interceptor_creators_;
+ }
+
friend class AsyncGenericService;
friend class ServerBuilder;
friend class ServerInitializer;
class SyncRequest;
+ class CallbackRequest;
class UnimplementedAsyncRequest;
class UnimplementedAsyncResponse;
@@ -214,8 +224,18 @@ class Server : public ServerInterface, private GrpcLibraryCodegen {
return max_receive_message_size_;
};
+ CompletionQueue* CallbackCQ() override;
+
ServerInitializer* initializer();
+ // A vector of interceptor factory objects.
+ // This should be destroyed after health_check_service_ and this requirement
+ // is satisfied by declaring interceptor_creators_ before
+ // health_check_service_. (C++ mandates that member objects be destroyed in
+ // the reverse order of initialization.)
+ std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ interceptor_creators_;
+
const int max_receive_message_size_;
/// The following completion queues are ONLY used in case of Sync API
@@ -228,6 +248,9 @@ class Server : public ServerInterface, private GrpcLibraryCodegen {
/// the \a sync_server_cqs)
std::vector<std::unique_ptr<SyncRequestThreadManager>> sync_req_mgrs_;
+ /// Outstanding callback requests
+ std::vector<std::unique_ptr<CallbackRequest>> callback_reqs_;
+
// Server status
std::mutex mu_;
bool started_;
@@ -251,6 +274,13 @@ class Server : public ServerInterface, private GrpcLibraryCodegen {
// A special handler for resource exhausted in sync case
std::unique_ptr<internal::MethodHandler> resource_exhausted_handler_;
+
+ // callback_cq_ references the callbackable completion queue associated
+ // with this server (if any). It is set on the first call to CallbackCQ().
+ // It is _not owned_ by the server; ownership belongs with its internal
+ // shutdown callback tag (invoked when the CQ is fully shutdown).
+ // It is protected by mu_
+ CompletionQueue* callback_cq_ = nullptr;
};
} // namespace grpc
diff --git a/include/grpcpp/server_builder.h b/include/grpcpp/server_builder.h
index a58a59c2d8..028b8cffaa 100644
--- a/include/grpcpp/server_builder.h
+++ b/include/grpcpp/server_builder.h
@@ -28,6 +28,7 @@
#include <grpc/support/cpu.h>
#include <grpc/support/workaround_list.h>
#include <grpcpp/impl/channel_argument_option.h>
+#include <grpcpp/impl/codegen/server_interceptor.h>
#include <grpcpp/impl/server_builder_option.h>
#include <grpcpp/impl/server_builder_plugin.h>
#include <grpcpp/support/config.h>
@@ -212,6 +213,29 @@ class ServerBuilder {
/// doc/workarounds.md.
ServerBuilder& EnableWorkaround(grpc_workaround_list id);
+ /// NOTE: class experimental_type is not part of the public API of this class.
+ /// TODO(yashykt): Integrate into public API when this is no longer
+ /// experimental.
+ class experimental_type {
+ public:
+ explicit experimental_type(ServerBuilder* builder) : builder_(builder) {}
+
+ void SetInterceptorCreators(
+ std::vector<
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ interceptor_creators) {
+ builder_->interceptor_creators_ = std::move(interceptor_creators);
+ }
+
+ private:
+ ServerBuilder* builder_;
+ };
+
+ /// 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); }
+
protected:
/// Experimental, to be deprecated
struct Port {
@@ -297,6 +321,8 @@ class ServerBuilder {
grpc_compression_algorithm algorithm;
} maybe_default_compression_algorithm_;
uint32_t enabled_compression_algorithms_bitset_;
+ std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ interceptor_creators_;
};
} // namespace grpc
diff --git a/include/grpcpp/support/server_callback.h b/include/grpcpp/support/server_callback.h
new file mode 100644
index 0000000000..b0aeeb53c5
--- /dev/null
+++ b/include/grpcpp/support/server_callback.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_SERVER_CALLBACK_H
+#define GRPCPP_SUPPORT_SERVER_CALLBACK_H
+
+#include <grpcpp/impl/codegen/server_callback.h>
+
+#endif // GRPCPP_SUPPORT_SERVER_CALLBACK_H
diff --git a/setup.py b/setup.py
index 388e629ec2..8845bd46d2 100644
--- a/setup.py
+++ b/setup.py
@@ -87,19 +87,19 @@ BUILD_WITH_CYTHON = os.environ.get('GRPC_PYTHON_BUILD_WITH_CYTHON', False)
# Export this variable to use the system installation of openssl. You need to
# have the header files installed (in /usr/include/openssl) and during
-# runtime, the shared libary must be installed
+# runtime, the shared library must be installed
BUILD_WITH_SYSTEM_OPENSSL = os.environ.get('GRPC_PYTHON_BUILD_SYSTEM_OPENSSL',
False)
# Export this variable to use the system installation of zlib. You need to
# have the header files installed (in /usr/include/) and during
-# runtime, the shared libary must be installed
+# runtime, the shared library must be installed
BUILD_WITH_SYSTEM_ZLIB = os.environ.get('GRPC_PYTHON_BUILD_SYSTEM_ZLIB',
False)
# Export this variable to use the system installation of cares. You need to
# have the header files installed (in /usr/include/) and during
-# runtime, the shared libary must be installed
+# runtime, the shared library must be installed
BUILD_WITH_SYSTEM_CARES = os.environ.get('GRPC_PYTHON_BUILD_SYSTEM_CARES',
False)
@@ -202,7 +202,7 @@ DEFINE_MACROS = (
('OPENSSL_NO_ASM', 1), ('_WIN32_WINNT', 0x600),
('GPR_BACKWARDS_COMPATIBILITY_MODE', 1))
if "win32" in sys.platform:
- # TODO(zyc): Re-enble c-ares on x64 and x86 windows after fixing the
+ # TODO(zyc): Re-enable c-ares on x64 and x86 windows after fixing the
# ares_library_init compilation issue
DEFINE_MACROS += (('WIN32_LEAN_AND_MEAN', 1), ('CARES_STATICLIB', 1),
('GRPC_ARES', 0), ('NTDDI_VERSION', 0x06000000),
diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc
index 56716493dc..7986aca696 100644
--- a/src/compiler/cpp_generator.cc
+++ b/src/compiler/cpp_generator.cc
@@ -135,6 +135,7 @@ grpc::string GetHeaderIncludes(grpc_generator::File* file,
"grpcpp/impl/codegen/method_handler_impl.h",
"grpcpp/impl/codegen/proto_utils.h",
"grpcpp/impl/codegen/rpc_method.h",
+ "grpcpp/impl/codegen/server_callback.h",
"grpcpp/impl/codegen/service_type.h",
"grpcpp/impl/codegen/status.h",
"grpcpp/impl/codegen/stub_options.h",
@@ -700,9 +701,9 @@ void PrintHeaderServerMethodSync(grpc_generator::Printer* printer,
printer->Print(method->GetTrailingComments("//").c_str());
}
-// Helper generator. Disabled the sync API for Request and Response, then adds
+// Helper generator. Disables the sync API for Request and Response, then adds
// in an async API for RealRequest and RealResponse types. This is to be used
-// to generate async and raw APIs.
+// to generate async and raw async APIs.
void PrintHeaderServerAsyncMethodsHelper(
grpc_generator::Printer* printer, const grpc_generator::Method* method,
std::map<grpc::string, grpc::string>* vars) {
@@ -829,6 +830,170 @@ void PrintHeaderServerMethodAsync(grpc_generator::Printer* printer,
printer->Print(*vars, "};\n");
}
+// Helper generator. Disables the sync API for Request and Response, then adds
+// in a callback API for RealRequest and RealResponse types. This is to be used
+// to generate callback and raw callback APIs.
+void PrintHeaderServerCallbackMethodsHelper(
+ grpc_generator::Printer* printer, const grpc_generator::Method* method,
+ std::map<grpc::string, grpc::string>* vars) {
+ if (method->NoStreaming()) {
+ printer->Print(
+ *vars,
+ "// disable synchronous version of this method\n"
+ "::grpc::Status $Method$("
+ "::grpc::ServerContext* context, const $Request$* request, "
+ "$Response$* response) override {\n"
+ " abort();\n"
+ " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+ "}\n");
+ printer->Print(
+ *vars,
+ "virtual void $Method$("
+ "::grpc::ServerContext* context, const $RealRequest$* request, "
+ "$RealResponse$* response, "
+ "::grpc::experimental::ServerCallbackRpcController* "
+ "controller) { controller->Finish(::grpc::Status("
+ "::grpc::StatusCode::UNIMPLEMENTED, \"\")); }\n");
+ } else if (ClientOnlyStreaming(method)) {
+ printer->Print(
+ *vars,
+ "// disable synchronous version of this method\n"
+ "::grpc::Status $Method$("
+ "::grpc::ServerContext* context, "
+ "::grpc::ServerReader< $Request$>* reader, "
+ "$Response$* response) override {\n"
+ " abort();\n"
+ " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+ "}\n");
+ } else if (ServerOnlyStreaming(method)) {
+ printer->Print(
+ *vars,
+ "// disable synchronous version of this method\n"
+ "::grpc::Status $Method$("
+ "::grpc::ServerContext* context, const $Request$* request, "
+ "::grpc::ServerWriter< $Response$>* writer) override "
+ "{\n"
+ " abort();\n"
+ " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+ "}\n");
+ } else if (method->BidiStreaming()) {
+ printer->Print(
+ *vars,
+ "// disable synchronous version of this method\n"
+ "::grpc::Status $Method$("
+ "::grpc::ServerContext* context, "
+ "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) "
+ " override {\n"
+ " abort();\n"
+ " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+ "}\n");
+ }
+}
+
+void PrintHeaderServerMethodCallback(
+ grpc_generator::Printer* printer, const grpc_generator::Method* method,
+ std::map<grpc::string, grpc::string>* vars) {
+ (*vars)["Method"] = method->name();
+ // These will be disabled
+ (*vars)["Request"] = method->input_type_name();
+ (*vars)["Response"] = method->output_type_name();
+ // These will be used for the callback API
+ (*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(
+ " private:\n"
+ " void BaseClassMustBeDerivedFromService(const Service *service) {}\n");
+ printer->Print(" public:\n");
+ printer->Indent();
+ printer->Print(*vars, "ExperimentalWithCallbackMethod_$Method$() {\n");
+ if (method->NoStreaming()) {
+ printer->Print(
+ *vars,
+ " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
+ " new ::grpc::internal::CallbackUnaryHandler< "
+ "ExperimentalWithCallbackMethod_$Method$<BaseClass>, $RealRequest$, "
+ "$RealResponse$>(\n"
+ " [this](::grpc::ServerContext* context,\n"
+ " const $RealRequest$* request,\n"
+ " $RealResponse$* response,\n"
+ " ::grpc::experimental::ServerCallbackRpcController* "
+ "controller) {\n"
+ " this->$"
+ "Method$(context, request, response, controller);\n"
+ " }, this));\n");
+ } else if (ClientOnlyStreaming(method)) {
+ // TODO(vjpai): Add in code generation for all streaming methods
+ } else if (ServerOnlyStreaming(method)) {
+ // TODO(vjpai): Add in code generation for all streaming methods
+ } else if (method->BidiStreaming()) {
+ // TODO(vjpai): Add in code generation for all streaming methods
+ }
+ printer->Print(*vars, "}\n");
+ printer->Print(*vars,
+ "~ExperimentalWithCallbackMethod_$Method$() override {\n"
+ " BaseClassMustBeDerivedFromService(this);\n"
+ "}\n");
+ PrintHeaderServerCallbackMethodsHelper(printer, method, vars);
+ printer->Outdent();
+ printer->Print(*vars, "};\n");
+}
+
+void PrintHeaderServerMethodRawCallback(
+ grpc_generator::Printer* printer, const grpc_generator::Method* method,
+ std::map<grpc::string, grpc::string>* vars) {
+ (*vars)["Method"] = method->name();
+ // These will be disabled
+ (*vars)["Request"] = method->input_type_name();
+ (*vars)["Response"] = method->output_type_name();
+ // These will be used for raw API
+ (*vars)["RealRequest"] = "::grpc::ByteBuffer";
+ (*vars)["RealResponse"] = "::grpc::ByteBuffer";
+ printer->Print(*vars, "template <class BaseClass>\n");
+ printer->Print(*vars,
+ "class ExperimentalWithRawCallbackMethod_$Method$ : public "
+ "BaseClass {\n");
+ printer->Print(
+ " private:\n"
+ " void BaseClassMustBeDerivedFromService(const Service *service) {}\n");
+ printer->Print(" public:\n");
+ printer->Indent();
+ printer->Print(*vars, "ExperimentalWithRawCallbackMethod_$Method$() {\n");
+ if (method->NoStreaming()) {
+ printer->Print(
+ *vars,
+ " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
+ " new ::grpc::internal::CallbackUnaryHandler< "
+ "ExperimentalWithRawCallbackMethod_$Method$<BaseClass>, $RealRequest$, "
+ "$RealResponse$>(\n"
+ " [this](::grpc::ServerContext* context,\n"
+ " const $RealRequest$* request,\n"
+ " $RealResponse$* response,\n"
+ " ::grpc::experimental::ServerCallbackRpcController* "
+ "controller) {\n"
+ " this->$"
+ "Method$(context, request, response, controller);\n"
+ " }, this));\n");
+ } else if (ClientOnlyStreaming(method)) {
+ // TODO(vjpai): Add in code generation for all streaming methods
+ } else if (ServerOnlyStreaming(method)) {
+ // TODO(vjpai): Add in code generation for all streaming methods
+ } else if (method->BidiStreaming()) {
+ // TODO(vjpai): Add in code generation for all streaming methods
+ }
+ printer->Print(*vars, "}\n");
+ printer->Print(*vars,
+ "~ExperimentalWithRawCallbackMethod_$Method$() override {\n"
+ " BaseClassMustBeDerivedFromService(this);\n"
+ "}\n");
+ PrintHeaderServerCallbackMethodsHelper(printer, method, vars);
+ printer->Outdent();
+ printer->Print(*vars, "};\n");
+}
+
void PrintHeaderServerMethodStreamedUnary(
grpc_generator::Printer* printer, const grpc_generator::Method* method,
std::map<grpc::string, grpc::string>* vars) {
@@ -1137,7 +1302,7 @@ void PrintHeaderService(grpc_generator::Printer* printer,
printer->Print("typedef ");
for (int i = 0; i < service->method_count(); ++i) {
- (*vars)["method_name"] = service->method(i).get()->name();
+ (*vars)["method_name"] = service->method(i)->name();
printer->Print(*vars, "WithAsyncMethod_$method_name$<");
}
printer->Print("Service");
@@ -1146,6 +1311,24 @@ void PrintHeaderService(grpc_generator::Printer* printer,
}
printer->Print(" AsyncService;\n");
+ // Server side - Callback
+ for (int i = 0; i < service->method_count(); ++i) {
+ (*vars)["Idx"] = as_string(i);
+ PrintHeaderServerMethodCallback(printer, service->method(i).get(), vars);
+ }
+
+ 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");
+
// Server side - Generic
for (int i = 0; i < service->method_count(); ++i) {
(*vars)["Idx"] = as_string(i);
@@ -1158,6 +1341,12 @@ void PrintHeaderService(grpc_generator::Printer* printer,
PrintHeaderServerMethodRaw(printer, service->method(i).get(), vars);
}
+ // Server side - Raw Callback
+ for (int i = 0; i < service->method_count(); ++i) {
+ (*vars)["Idx"] = as_string(i);
+ PrintHeaderServerMethodRawCallback(printer, service->method(i).get(), vars);
+ }
+
// Server side - Streamed Unary
for (int i = 0; i < service->method_count(); ++i) {
(*vars)["Idx"] = as_string(i);
@@ -1167,7 +1356,7 @@ void PrintHeaderService(grpc_generator::Printer* printer,
printer->Print("typedef ");
for (int i = 0; i < service->method_count(); ++i) {
- (*vars)["method_name"] = service->method(i).get()->name();
+ (*vars)["method_name"] = service->method(i)->name();
if (service->method(i)->NoStreaming()) {
printer->Print(*vars, "WithStreamedUnaryMethod_$method_name$<");
}
@@ -1189,7 +1378,7 @@ void PrintHeaderService(grpc_generator::Printer* printer,
printer->Print("typedef ");
for (int i = 0; i < service->method_count(); ++i) {
- (*vars)["method_name"] = service->method(i).get()->name();
+ (*vars)["method_name"] = service->method(i)->name();
auto method = service->method(i);
if (ServerOnlyStreaming(method.get())) {
printer->Print(*vars, "WithSplitStreamingMethod_$method_name$<");
@@ -1207,7 +1396,7 @@ void PrintHeaderService(grpc_generator::Printer* printer,
// Server side - typedef for controlled both unary and server-side streaming
printer->Print("typedef ");
for (int i = 0; i < service->method_count(); ++i) {
- (*vars)["method_name"] = service->method(i).get()->name();
+ (*vars)["method_name"] = service->method(i)->name();
auto method = service->method(i);
if (ServerOnlyStreaming(method.get())) {
printer->Print(*vars, "WithSplitStreamingMethod_$method_name$<");
@@ -1333,6 +1522,7 @@ grpc::string GetSourceIncludes(grpc_generator::File* file,
"grpcpp/impl/codegen/client_callback.h",
"grpcpp/impl/codegen/method_handler_impl.h",
"grpcpp/impl/codegen/rpc_service_method.h",
+ "grpcpp/impl/codegen/server_callback.h",
"grpcpp/impl/codegen/service_type.h",
"grpcpp/impl/codegen/sync_stream.h"};
std::vector<grpc::string> headers(headers_strs, array_end(headers_strs));
@@ -1577,7 +1767,7 @@ void PrintSourceService(grpc_generator::Printer* printer,
printer->Print(*vars,
"static const char* $prefix$$Service$_method_names[] = {\n");
for (int i = 0; i < service->method_count(); ++i) {
- (*vars)["Method"] = service->method(i).get()->name();
+ (*vars)["Method"] = service->method(i)->name();
printer->Print(*vars, " \"/$Package$$Service$/$Method$\",\n");
}
printer->Print(*vars, "};\n\n");
diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc
index daf1b89b09..91894689c3 100644
--- a/src/core/ext/filters/client_channel/client_channel.cc
+++ b/src/core/ext/filters/client_channel/client_channel.cc
@@ -2951,6 +2951,27 @@ static void apply_service_config_to_call_locked(grpc_call_element* elem) {
}
}
+// If the channel is in TRANSIENT_FAILURE and the call is not
+// wait_for_ready=true, fails the call and returns true.
+static bool fail_call_if_in_transient_failure(grpc_call_element* elem) {
+ channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+ call_data* calld = static_cast<call_data*>(elem->call_data);
+ grpc_transport_stream_op_batch* batch = calld->pending_batches[0].batch;
+ if (grpc_connectivity_state_check(&chand->state_tracker) ==
+ GRPC_CHANNEL_TRANSIENT_FAILURE &&
+ (batch->payload->send_initial_metadata.send_initial_metadata_flags &
+ GRPC_INITIAL_METADATA_WAIT_FOR_READY) == 0) {
+ pending_batches_fail(
+ elem,
+ grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+ "channel is in state TRANSIENT_FAILURE"),
+ GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE),
+ true /* yield_call_combiner */);
+ return true;
+ }
+ return false;
+}
+
// Invoked once resolver results are available.
static void process_service_config_and_start_lb_pick_locked(
grpc_call_element* elem) {
@@ -2958,6 +2979,9 @@ static void process_service_config_and_start_lb_pick_locked(
// Only get service config data on the first attempt.
if (GPR_LIKELY(calld->num_attempts_completed == 0)) {
apply_service_config_to_call_locked(elem);
+ // Check this after applying service config, since it may have
+ // affected the call's wait_for_ready value.
+ if (fail_call_if_in_transient_failure(elem)) return;
}
// Start LB pick.
grpc_core::LbPicker::StartLocked(elem);
@@ -3127,6 +3151,16 @@ static void start_pick_locked(void* arg, grpc_error* ignored) {
// We do not yet have an LB policy, so wait for a resolver result.
if (GPR_UNLIKELY(!chand->started_resolving)) {
start_resolving_locked(chand);
+ } else {
+ // Normally, we want to do this check in
+ // process_service_config_and_start_lb_pick_locked(), so that we
+ // can honor the wait_for_ready setting in the service config.
+ // However, if the channel is in TRANSIENT_FAILURE at this point, that
+ // means that the resolver has returned a failure, so we're not going
+ // to get a service config right away. In that case, we fail the
+ // call now based on the wait_for_ready value passed in from the
+ // application.
+ if (fail_call_if_in_transient_failure(elem)) return;
}
// Create a new waiter, which will delete itself when done.
grpc_core::New<grpc_core::ResolverResultWaiter>(elem);
diff --git a/src/core/ext/filters/client_channel/health/health_check_client.cc b/src/core/ext/filters/client_channel/health/health_check_client.cc
index 400d99b07c..591637aa86 100644
--- a/src/core/ext/filters/client_channel/health/health_check_client.cc
+++ b/src/core/ext/filters/client_channel/health/health_check_client.cc
@@ -19,6 +19,7 @@
#include <grpc/support/port_platform.h>
#include <stdint.h>
+#include <stdio.h>
#include "src/core/ext/filters/client_channel/health/health_check_client.h"
@@ -298,6 +299,11 @@ HealthCheckClient::CallState::~CallState() {
health_check_client_.get(), this);
}
if (call_ != nullptr) GRPC_SUBCHANNEL_CALL_UNREF(call_, "call_ended");
+ for (size_t i = 0; i < GRPC_CONTEXT_COUNT; i++) {
+ if (context_[i].destroy != nullptr) {
+ context_[i].destroy(context_[i].value);
+ }
+ }
// Unset the call combiner cancellation closure. This has the
// effect of scheduling the previously set cancellation closure, if
// any, so that it can release any internal references it may be
@@ -345,6 +351,7 @@ void HealthCheckClient::CallState::StartCall() {
}
// Initialize payload and batch.
memset(&batch_, 0, sizeof(batch_));
+ payload_.context = context_;
batch_.payload = &payload_;
// on_complete callback takes ref, handled manually.
Ref(DEBUG_LOCATION, "on_complete").release();
diff --git a/src/core/ext/transport/chttp2/client/chttp2_connector.cc b/src/core/ext/transport/chttp2/client/chttp2_connector.cc
index 1e9c5ee4d7..60a32022f5 100644
--- a/src/core/ext/transport/chttp2/client/chttp2_connector.cc
+++ b/src/core/ext/transport/chttp2/client/chttp2_connector.cc
@@ -212,9 +212,17 @@ static void chttp2_connector_connect(grpc_connector* con,
GRPC_CLOSURE_INIT(&c->connected, connected, c, grpc_schedule_on_exec_ctx);
GPR_ASSERT(!c->connecting);
c->connecting = true;
- grpc_tcp_client_connect(&c->connected, &c->endpoint, args->interested_parties,
- args->channel_args, &addr, args->deadline);
+ grpc_closure* closure = &c->connected;
+ grpc_endpoint** ep = &c->endpoint;
gpr_mu_unlock(&c->mu);
+ // In some implementations, the closure can be flushed before
+ // grpc_tcp_client_connect and since the closure requires access to c->mu,
+ // this can result in a deadlock. Refer
+ // https://github.com/grpc/grpc/issues/16427
+ // grpc_tcp_client_connect would fill c->endpoint with proper contents and we
+ // make sure that we would still exist at that point by taking a ref.
+ grpc_tcp_client_connect(closure, ep, args->interested_parties,
+ args->channel_args, &addr, args->deadline);
}
static const grpc_connector_vtable chttp2_connector_vtable = {
diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.cc b/src/core/ext/transport/chttp2/server/chttp2_server.cc
index 07b304b320..287bc0454e 100644
--- a/src/core/ext/transport/chttp2/server/chttp2_server.cc
+++ b/src/core/ext/transport/chttp2/server/chttp2_server.cc
@@ -37,8 +37,10 @@
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/handshaker.h"
#include "src/core/lib/channel/handshaker_registry.h"
+#include "src/core/lib/gpr/host_port.h"
#include "src/core/lib/iomgr/endpoint.h"
#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/resource_quota.h"
#include "src/core/lib/iomgr/tcp_server.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/surface/api_trace.h"
@@ -114,9 +116,16 @@ static void on_handshake_done(void* arg, grpc_error* error) {
server_connection_state* connection_state =
static_cast<server_connection_state*>(args->user_data);
gpr_mu_lock(&connection_state->svr_state->mu);
+ grpc_resource_user* resource_user = grpc_server_get_default_resource_user(
+ connection_state->svr_state->server);
if (error != GRPC_ERROR_NONE || connection_state->svr_state->shutdown) {
const char* error_str = grpc_error_string(error);
gpr_log(GPR_DEBUG, "Handshaking failed: %s", error_str);
+ grpc_resource_user* resource_user = grpc_server_get_default_resource_user(
+ connection_state->svr_state->server);
+ if (resource_user != nullptr) {
+ grpc_resource_user_free(resource_user, GRPC_RESOURCE_QUOTA_CHANNEL_SIZE);
+ }
if (error == GRPC_ERROR_NONE && args->endpoint != nullptr) {
// We were shut down after handshaking completed successfully, so
// destroy the endpoint here.
@@ -135,12 +144,12 @@ static void on_handshake_done(void* arg, grpc_error* error) {
// handshaker may have handed off the connection to some external
// code, so we can just clean up here without creating a transport.
if (args->endpoint != nullptr) {
- grpc_transport* transport =
- grpc_create_chttp2_transport(args->args, args->endpoint, false);
+ grpc_transport* transport = grpc_create_chttp2_transport(
+ args->args, args->endpoint, false, resource_user);
grpc_server_setup_transport(
connection_state->svr_state->server, transport,
connection_state->accepting_pollset, args->args,
- grpc_chttp2_transport_get_socket_uuid(transport));
+ grpc_chttp2_transport_get_socket_uuid(transport), resource_user);
// Use notify_on_receive_settings callback to enforce the
// handshake deadline.
connection_state->transport =
@@ -159,6 +168,11 @@ static void on_handshake_done(void* arg, grpc_error* error) {
connection_state, grpc_schedule_on_exec_ctx);
grpc_timer_init(&connection_state->timer, connection_state->deadline,
&connection_state->on_timeout);
+ } else {
+ if (resource_user != nullptr) {
+ grpc_resource_user_free(resource_user,
+ GRPC_RESOURCE_QUOTA_CHANNEL_SIZE);
+ }
}
}
grpc_handshake_manager_pending_list_remove(
@@ -183,6 +197,20 @@ static void on_accept(void* arg, grpc_endpoint* tcp,
gpr_free(acceptor);
return;
}
+ grpc_resource_user* resource_user =
+ grpc_server_get_default_resource_user(state->server);
+ if (resource_user != nullptr &&
+ !grpc_resource_user_safe_alloc(resource_user,
+ GRPC_RESOURCE_QUOTA_CHANNEL_SIZE)) {
+ gpr_log(
+ GPR_ERROR,
+ "Memory quota exhausted, rejecting the connection, no handshaking.");
+ gpr_mu_unlock(&state->mu);
+ grpc_endpoint_shutdown(tcp, GRPC_ERROR_NONE);
+ grpc_endpoint_destroy(tcp);
+ gpr_free(acceptor);
+ return;
+ }
grpc_handshake_manager* handshake_mgr = grpc_handshake_manager_create();
grpc_handshake_manager_pending_list_add(&state->pending_handshake_mgrs,
handshake_mgr);
@@ -339,8 +367,14 @@ grpc_error* grpc_chttp2_server_add_port(grpc_server* server, const char* addr,
arg = grpc_channel_args_find(args, GRPC_ARG_ENABLE_CHANNELZ);
if (grpc_channel_arg_get_bool(arg, false)) {
+ char* host;
+ char* port;
+ gpr_split_host_port(addr, &host, &port);
+ // allocated host's ownership is passed to ListenSocketNode.
state->channelz_listen_socket =
- grpc_core::MakeRefCounted<grpc_core::channelz::ListenSocketNode>();
+ grpc_core::MakeRefCounted<grpc_core::channelz::ListenSocketNode>(
+ grpc_core::UniquePtr<char>(host), *port_num);
+ gpr_free(port);
socket_uuid = state->channelz_listen_socket->uuid();
}
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
index 8a481bb7d5..04874a9494 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
@@ -478,7 +478,8 @@ static void init_keepalive_pings_if_enabled(grpc_chttp2_transport* t) {
static void init_transport(grpc_chttp2_transport* t,
const grpc_channel_args* channel_args,
- grpc_endpoint* ep, bool is_client) {
+ grpc_endpoint* ep, bool is_client,
+ grpc_resource_user* resource_user) {
GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) ==
GRPC_CHTTP2_CLIENT_CONNECT_STRLEN);
@@ -491,6 +492,7 @@ static void init_transport(grpc_chttp2_transport* t,
t->endpoint_reading = 1;
t->next_stream_id = is_client ? 1 : 2;
t->is_client = is_client;
+ t->resource_user = resource_user;
t->deframe_state = is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0;
t->is_first_frame = true;
grpc_connectivity_state_init(
@@ -778,6 +780,10 @@ static void destroy_stream_locked(void* sp, grpc_error* error) {
s->flow_control.Destroy();
+ if (t->resource_user != nullptr) {
+ grpc_resource_user_free(t->resource_user, GRPC_RESOURCE_QUOTA_CALL_SIZE);
+ }
+
GRPC_CHTTP2_UNREF_TRANSPORT(t, "stream");
GRPC_CLOSURE_SCHED(s->destroy_stream_arg, GRPC_ERROR_NONE);
@@ -816,7 +822,21 @@ grpc_chttp2_stream* grpc_chttp2_parsing_accept_stream(grpc_chttp2_transport* t,
if (t->channel_callback.accept_stream == nullptr) {
return nullptr;
}
- grpc_chttp2_stream* accepting;
+ // Don't accept the stream if memory quota doesn't allow. Note that we should
+ // simply refuse the stream here instead of canceling the stream after it's
+ // accepted since the latter will create the call which costs much memory.
+ if (t->resource_user != nullptr &&
+ !grpc_resource_user_safe_alloc(t->resource_user,
+ GRPC_RESOURCE_QUOTA_CALL_SIZE)) {
+ gpr_log(GPR_ERROR, "Memory exhausted, rejecting the stream.");
+ grpc_slice_buffer_add(
+ &t->qbuf,
+ grpc_chttp2_rst_stream_create(
+ id, static_cast<uint32_t>(GRPC_HTTP2_REFUSED_STREAM), nullptr));
+ grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM);
+ return nullptr;
+ }
+ grpc_chttp2_stream* accepting = nullptr;
GPR_ASSERT(t->accepting_stream == nullptr);
t->accepting_stream = &accepting;
t->channel_callback.accept_stream(t->channel_callback.accept_stream_user_data,
@@ -3185,10 +3205,11 @@ intptr_t grpc_chttp2_transport_get_socket_uuid(grpc_transport* transport) {
}
grpc_transport* grpc_create_chttp2_transport(
- const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client) {
+ const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client,
+ grpc_resource_user* resource_user) {
grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(
gpr_zalloc(sizeof(grpc_chttp2_transport)));
- init_transport(t, channel_args, ep, is_client);
+ init_transport(t, channel_args, ep, is_client, resource_user);
return &t->base;
}
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.h b/src/core/ext/transport/chttp2/transport/chttp2_transport.h
index e5872fee43..b3fe1c082e 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.h
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.h
@@ -32,7 +32,8 @@ extern grpc_core::DebugOnlyTraceFlag grpc_trace_chttp2_refcount;
extern bool g_flow_control_enabled;
grpc_transport* grpc_create_chttp2_transport(
- const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client);
+ const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client,
+ grpc_resource_user* resource_user = nullptr);
intptr_t grpc_chttp2_transport_get_socket_uuid(grpc_transport* transport);
diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc b/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
index 4bdd4309a4..a0a7534594 100644
--- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
+++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
@@ -32,7 +32,7 @@ grpc_slice grpc_chttp2_rst_stream_create(uint32_t id, uint32_t code,
grpc_transport_one_way_stats* stats) {
static const size_t frame_size = 13;
grpc_slice slice = GRPC_SLICE_MALLOC(frame_size);
- stats->framing_bytes += frame_size;
+ if (stats != nullptr) stats->framing_bytes += frame_size;
uint8_t* p = GRPC_SLICE_START_PTR(slice);
// Frame size.
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index ff26dd9255..202017641b 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -285,6 +285,8 @@ struct grpc_chttp2_transport {
grpc_endpoint* ep;
char* peer_string;
+ grpc_resource_user* resource_user;
+
grpc_combiner* combiner;
grpc_closure* notify_on_receive_settings;
diff --git a/src/core/lib/channel/channel_stack_builder.cc b/src/core/lib/channel/channel_stack_builder.cc
index df5a783631..8b3008f221 100644
--- a/src/core/lib/channel/channel_stack_builder.cc
+++ b/src/core/lib/channel/channel_stack_builder.cc
@@ -40,6 +40,7 @@ struct grpc_channel_stack_builder {
// various set/get-able parameters
grpc_channel_args* args;
grpc_transport* transport;
+ grpc_resource_user* resource_user;
char* target;
const char* name;
};
@@ -157,6 +158,11 @@ void grpc_channel_stack_builder_set_channel_arguments(
builder->args = grpc_channel_args_copy(args);
}
+const grpc_channel_args* grpc_channel_stack_builder_get_channel_arguments(
+ grpc_channel_stack_builder* builder) {
+ return builder->args;
+}
+
void grpc_channel_stack_builder_set_transport(
grpc_channel_stack_builder* builder, grpc_transport* transport) {
GPR_ASSERT(builder->transport == nullptr);
@@ -168,9 +174,15 @@ grpc_transport* grpc_channel_stack_builder_get_transport(
return builder->transport;
}
-const grpc_channel_args* grpc_channel_stack_builder_get_channel_arguments(
+void grpc_channel_stack_builder_set_resource_user(
+ grpc_channel_stack_builder* builder, grpc_resource_user* resource_user) {
+ GPR_ASSERT(builder->resource_user == nullptr);
+ builder->resource_user = resource_user;
+}
+
+grpc_resource_user* grpc_channel_stack_builder_get_resource_user(
grpc_channel_stack_builder* builder) {
- return builder->args;
+ return builder->resource_user;
}
bool grpc_channel_stack_builder_append_filter(
diff --git a/src/core/lib/channel/channel_stack_builder.h b/src/core/lib/channel/channel_stack_builder.h
index 9196de9378..89c30e0c5e 100644
--- a/src/core/lib/channel/channel_stack_builder.h
+++ b/src/core/lib/channel/channel_stack_builder.h
@@ -54,6 +54,14 @@ void grpc_channel_stack_builder_set_transport(
grpc_transport* grpc_channel_stack_builder_get_transport(
grpc_channel_stack_builder* builder);
+/// Attach \a resource_user to the builder (does not take ownership)
+void grpc_channel_stack_builder_set_resource_user(
+ grpc_channel_stack_builder* builder, grpc_resource_user* resource_user);
+
+/// Fetch attached resource user
+grpc_resource_user* grpc_channel_stack_builder_get_resource_user(
+ grpc_channel_stack_builder* builder);
+
/// Set channel arguments: copies args
void grpc_channel_stack_builder_set_channel_arguments(
grpc_channel_stack_builder* builder, const grpc_channel_args* args);
diff --git a/src/core/lib/channel/channelz.cc b/src/core/lib/channel/channelz.cc
index 33577d890a..032654b861 100644
--- a/src/core/lib/channel/channelz.cc
+++ b/src/core/lib/channel/channelz.cc
@@ -374,7 +374,8 @@ grpc_json* SocketNode::RenderJson() {
return top_level_json;
}
-ListenSocketNode::ListenSocketNode() : BaseNode(EntityType::kSocket) {}
+ListenSocketNode::ListenSocketNode(UniquePtr<char> host, int port)
+ : BaseNode(EntityType::kSocket), host_(std::move(host)), port_(port) {}
grpc_json* ListenSocketNode::RenderJson() {
// We need to track these three json objects to build our object
@@ -388,6 +389,21 @@ grpc_json* ListenSocketNode::RenderJson() {
json_iterator = nullptr;
json_iterator = grpc_json_add_number_string_child(json, json_iterator,
"socketId", uuid());
+ json = top_level_json;
+ json_iterator = nullptr;
+ json_iterator = grpc_json_create_child(json_iterator, json, "local", nullptr,
+ GRPC_JSON_OBJECT, false);
+ json = json_iterator;
+ json_iterator = nullptr;
+ json_iterator = grpc_json_create_child(json_iterator, json, "tcpip_address",
+ nullptr, GRPC_JSON_OBJECT, false);
+ json = json_iterator;
+ json_iterator = nullptr;
+ json_iterator =
+ grpc_json_add_number_string_child(json, json_iterator, "port", port_);
+ json_iterator = grpc_json_create_child(json_iterator, json, "ip_address",
+ host_.get(), GRPC_JSON_STRING, false);
+
return top_level_json;
}
diff --git a/src/core/lib/channel/channelz.h b/src/core/lib/channel/channelz.h
index 88551befc8..8e66623142 100644
--- a/src/core/lib/channel/channelz.h
+++ b/src/core/lib/channel/channelz.h
@@ -268,10 +268,15 @@ class SocketNode : public BaseNode {
// Handles channelz bookkeeping for listen sockets
class ListenSocketNode : public BaseNode {
public:
- ListenSocketNode();
+ // ListenSocketNode takes ownership of host.
+ ListenSocketNode(UniquePtr<char> host, int port);
~ListenSocketNode() override {}
grpc_json* RenderJson() override;
+
+ private:
+ UniquePtr<char> host_;
+ int port_;
};
// Creation functions
diff --git a/src/core/lib/channel/channelz_registry.cc b/src/core/lib/channel/channelz_registry.cc
index 1fe2fad3e1..8fc1b58adc 100644
--- a/src/core/lib/channel/channelz_registry.cc
+++ b/src/core/lib/channel/channelz_registry.cc
@@ -222,6 +222,24 @@ char* grpc_channelz_get_servers(intptr_t start_server_id) {
return grpc_core::channelz::ChannelzRegistry::GetServers(start_server_id);
}
+char* grpc_channelz_get_server(intptr_t server_id) {
+ grpc_core::channelz::BaseNode* server_node =
+ grpc_core::channelz::ChannelzRegistry::Get(server_id);
+ if (server_node == nullptr ||
+ server_node->type() !=
+ grpc_core::channelz::BaseNode::EntityType::kServer) {
+ return nullptr;
+ }
+ grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+ grpc_json* json = top_level_json;
+ grpc_json* channel_json = server_node->RenderJson();
+ channel_json->key = "server";
+ grpc_json_link_child(json, channel_json, nullptr);
+ char* json_str = grpc_json_dump_to_string(top_level_json, 0);
+ grpc_json_destroy(top_level_json);
+ return json_str;
+}
+
char* grpc_channelz_get_server_sockets(intptr_t server_id,
intptr_t start_socket_id) {
grpc_core::channelz::BaseNode* base_node =
diff --git a/src/core/lib/iomgr/resource_quota.cc b/src/core/lib/iomgr/resource_quota.cc
index b6fc7579f7..7e4b3c9b2f 100644
--- a/src/core/lib/iomgr/resource_quota.cc
+++ b/src/core/lib/iomgr/resource_quota.cc
@@ -90,7 +90,8 @@ struct grpc_resource_user {
grpc_closure_list on_allocated;
/* True if we are currently trying to allocate from the quota, false if not */
bool allocating;
- /* How many bytes of allocations are outstanding */
+ /* The amount of memory (in bytes) that has been requested from this user
+ * asynchronously but hasn't been granted yet. */
int64_t outstanding_allocations;
/* True if we are currently trying to add ourselves to the non-free quota
list, false otherwise */
@@ -135,6 +136,9 @@ struct grpc_resource_quota {
int64_t size;
/* Amount of free memory in the resource quota */
int64_t free_pool;
+ /* Used size of memory in the resource quota. Updated as soon as the resource
+ * users start to allocate or free the memory. */
+ gpr_atm used;
gpr_atm last_size;
@@ -371,6 +375,7 @@ static bool rq_reclaim_from_per_user_free_pool(
while ((resource_user = rulist_pop_head(resource_quota,
GRPC_RULIST_NON_EMPTY_FREE_POOL))) {
gpr_mu_lock(&resource_user->mu);
+ resource_user->added_to_free_pool = false;
if (resource_user->free_pool > 0) {
int64_t amt = resource_user->free_pool;
resource_user->free_pool = 0;
@@ -386,6 +391,13 @@ static bool rq_reclaim_from_per_user_free_pool(
gpr_mu_unlock(&resource_user->mu);
return true;
} else {
+ if (grpc_resource_quota_trace.enabled()) {
+ gpr_log(GPR_INFO,
+ "RQ %s %s: failed to reclaim_from_per_user_free_pool; "
+ "free_pool = %" PRId64 "; rq_free_pool = %" PRId64,
+ resource_quota->name, resource_user->name,
+ resource_user->free_pool, resource_quota->free_pool);
+ }
gpr_mu_unlock(&resource_user->mu);
}
}
@@ -622,6 +634,7 @@ grpc_resource_quota* grpc_resource_quota_create(const char* name) {
resource_quota->combiner = grpc_combiner_create();
resource_quota->free_pool = INT64_MAX;
resource_quota->size = INT64_MAX;
+ resource_quota->used = 0;
gpr_atm_no_barrier_store(&resource_quota->last_size, GPR_ATM_MAX);
gpr_mu_init(&resource_quota->thread_count_mu);
resource_quota->max_threads = INT_MAX;
@@ -712,7 +725,7 @@ size_t grpc_resource_quota_peek_size(grpc_resource_quota* resource_quota) {
*/
grpc_resource_quota* grpc_resource_quota_from_channel_args(
- const grpc_channel_args* channel_args) {
+ const grpc_channel_args* channel_args, bool create) {
for (size_t i = 0; i < channel_args->num_args; i++) {
if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
if (channel_args->args[i].type == GRPC_ARG_POINTER) {
@@ -724,7 +737,7 @@ grpc_resource_quota* grpc_resource_quota_from_channel_args(
}
}
}
- return grpc_resource_quota_create(nullptr);
+ return create ? grpc_resource_quota_create(nullptr) : nullptr;
}
static void* rq_copy(void* rq) {
@@ -863,33 +876,68 @@ void grpc_resource_user_free_threads(grpc_resource_user* resource_user,
gpr_mu_unlock(&resource_user->resource_quota->thread_count_mu);
}
-void grpc_resource_user_alloc(grpc_resource_user* resource_user, size_t size,
- grpc_closure* optional_on_done) {
- gpr_mu_lock(&resource_user->mu);
+static void resource_user_alloc_locked(grpc_resource_user* resource_user,
+ size_t size,
+ grpc_closure* optional_on_done) {
ru_ref_by(resource_user, static_cast<gpr_atm>(size));
resource_user->free_pool -= static_cast<int64_t>(size);
- resource_user->outstanding_allocations += static_cast<int64_t>(size);
if (grpc_resource_quota_trace.enabled()) {
gpr_log(GPR_INFO, "RQ %s %s: alloc %" PRIdPTR "; free_pool -> %" PRId64,
resource_user->resource_quota->name, resource_user->name, size,
resource_user->free_pool);
}
if (resource_user->free_pool < 0) {
- grpc_closure_list_append(&resource_user->on_allocated, optional_on_done,
- GRPC_ERROR_NONE);
+ if (optional_on_done != nullptr) {
+ resource_user->outstanding_allocations += static_cast<int64_t>(size);
+ grpc_closure_list_append(&resource_user->on_allocated, optional_on_done,
+ GRPC_ERROR_NONE);
+ }
if (!resource_user->allocating) {
resource_user->allocating = true;
GRPC_CLOSURE_SCHED(&resource_user->allocate_closure, GRPC_ERROR_NONE);
}
} else {
- resource_user->outstanding_allocations -= static_cast<int64_t>(size);
GRPC_CLOSURE_SCHED(optional_on_done, GRPC_ERROR_NONE);
}
+}
+
+bool grpc_resource_user_safe_alloc(grpc_resource_user* resource_user,
+ size_t size) {
+ if (gpr_atm_no_barrier_load(&resource_user->shutdown)) return false;
+ gpr_mu_lock(&resource_user->mu);
+ grpc_resource_quota* resource_quota = resource_user->resource_quota;
+ bool cas_success;
+ do {
+ gpr_atm used = gpr_atm_no_barrier_load(&resource_quota->used);
+ gpr_atm new_used = used + size;
+ if (static_cast<size_t>(new_used) >
+ grpc_resource_quota_peek_size(resource_quota)) {
+ gpr_mu_unlock(&resource_user->mu);
+ return false;
+ }
+ cas_success = gpr_atm_full_cas(&resource_quota->used, used, new_used);
+ } while (!cas_success);
+ resource_user_alloc_locked(resource_user, size, nullptr);
+ gpr_mu_unlock(&resource_user->mu);
+ return true;
+}
+
+void grpc_resource_user_alloc(grpc_resource_user* resource_user, size_t size,
+ grpc_closure* optional_on_done) {
+ // TODO(juanlishen): Maybe return immediately if shutting down. Deferring this
+ // because some tests become flaky after the change.
+ gpr_mu_lock(&resource_user->mu);
+ grpc_resource_quota* resource_quota = resource_user->resource_quota;
+ gpr_atm_no_barrier_fetch_add(&resource_quota->used, size);
+ resource_user_alloc_locked(resource_user, size, optional_on_done);
gpr_mu_unlock(&resource_user->mu);
}
void grpc_resource_user_free(grpc_resource_user* resource_user, size_t size) {
gpr_mu_lock(&resource_user->mu);
+ grpc_resource_quota* resource_quota = resource_user->resource_quota;
+ gpr_atm prior = gpr_atm_no_barrier_fetch_add(&resource_quota->used, -size);
+ GPR_ASSERT(prior >= static_cast<long>(size));
bool was_zero_or_negative = resource_user->free_pool <= 0;
resource_user->free_pool += static_cast<int64_t>(size);
if (grpc_resource_quota_trace.enabled()) {
@@ -940,6 +988,12 @@ void grpc_resource_user_slice_allocator_init(
void grpc_resource_user_alloc_slices(
grpc_resource_user_slice_allocator* slice_allocator, size_t length,
size_t count, grpc_slice_buffer* dest) {
+ if (gpr_atm_no_barrier_load(&slice_allocator->resource_user->shutdown)) {
+ GRPC_CLOSURE_SCHED(
+ &slice_allocator->on_allocated,
+ GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource user shutdown"));
+ return;
+ }
slice_allocator->length = length;
slice_allocator->count = count;
slice_allocator->dest = dest;
diff --git a/src/core/lib/iomgr/resource_quota.h b/src/core/lib/iomgr/resource_quota.h
index 7b0ed7417a..1c79b52e3f 100644
--- a/src/core/lib/iomgr/resource_quota.h
+++ b/src/core/lib/iomgr/resource_quota.h
@@ -65,11 +65,16 @@
extern grpc_core::TraceFlag grpc_resource_quota_trace;
+// TODO(juanlishen): This is a hack. We need to do real accounting instead of
+// hard coding.
+constexpr size_t GRPC_RESOURCE_QUOTA_CALL_SIZE = 15 * 1024;
+constexpr size_t GRPC_RESOURCE_QUOTA_CHANNEL_SIZE = 50 * 1024;
+
grpc_resource_quota* grpc_resource_quota_ref_internal(
grpc_resource_quota* resource_quota);
void grpc_resource_quota_unref_internal(grpc_resource_quota* resource_quota);
grpc_resource_quota* grpc_resource_quota_from_channel_args(
- const grpc_channel_args* channel_args);
+ const grpc_channel_args* channel_args, bool create = true);
/* Return a number indicating current memory pressure:
0.0 ==> no memory usage
@@ -109,11 +114,21 @@ bool grpc_resource_user_allocate_threads(grpc_resource_user* resource_user,
void grpc_resource_user_free_threads(grpc_resource_user* resource_user,
int thread_count);
-/* Allocate from the resource user (and its quota).
- If optional_on_done is NULL, then allocate immediately. This may push the
- quota over-limit, at which point reclamation will kick in.
- If optional_on_done is non-NULL, it will be scheduled when the allocation has
- been granted by the quota. */
+/* Allocates from the resource user 'size' worth of memory if this won't exceed
+ * the resource quota's total size. Returns whether the allocation is done
+ * successfully. If allocated successfully, the memory should be freed by the
+ * caller eventually. */
+bool grpc_resource_user_safe_alloc(grpc_resource_user* resource_user,
+ size_t size);
+/* Allocates from the resource user 'size' worth of memory.
+ * If optional_on_done is NULL, then allocate immediately. This may push the
+ * quota over-limit, at which point reclamation will kick in. The caller is
+ * always responsible to free the memory eventually.
+ * If optional_on_done is non-NULL, it will be scheduled without error when the
+ * allocation has been granted by the quota, and the caller is responsible to
+ * free the memory eventually. Or it may be scheduled with an error, in which
+ * case the caller fails to allocate the memory and shouldn't free the memory.
+ */
void grpc_resource_user_alloc(grpc_resource_user* resource_user, size_t size,
grpc_closure* optional_on_done);
/* Release memory back to the quota */
diff --git a/src/core/lib/iomgr/tcp_client_custom.cc b/src/core/lib/iomgr/tcp_client_custom.cc
index 9389861d07..73344b18d8 100644
--- a/src/core/lib/iomgr/tcp_client_custom.cc
+++ b/src/core/lib/iomgr/tcp_client_custom.cc
@@ -81,9 +81,8 @@ static void on_alarm(void* acp, grpc_error* error) {
}
}
-static void custom_connect_callback(grpc_custom_socket* socket,
- grpc_error* error) {
- grpc_core::ExecCtx exec_ctx;
+static void custom_connect_callback_internal(grpc_custom_socket* socket,
+ grpc_error* error) {
grpc_custom_tcp_connect* connect = socket->connector;
int done;
grpc_closure* closure = connect->closure;
@@ -100,6 +99,18 @@ static void custom_connect_callback(grpc_custom_socket* socket,
GRPC_CLOSURE_SCHED(closure, error);
}
+static void custom_connect_callback(grpc_custom_socket* socket,
+ grpc_error* error) {
+ if (grpc_core::ExecCtx::Get() == nullptr) {
+ /* If we are being run on a thread which does not have an exec_ctx created
+ * yet, we should create one. */
+ grpc_core::ExecCtx exec_ctx;
+ custom_connect_callback_internal(socket, error);
+ } else {
+ custom_connect_callback_internal(socket, error);
+ }
+}
+
static void tcp_connect(grpc_closure* closure, grpc_endpoint** ep,
grpc_pollset_set* interested_parties,
const grpc_channel_args* channel_args,
diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.cc b/src/core/lib/security/credentials/google_default/google_default_credentials.cc
index c456ffaf5d..fcab252959 100644
--- a/src/core/lib/security/credentials/google_default/google_default_credentials.cc
+++ b/src/core/lib/security/credentials/google_default/google_default_credentials.cc
@@ -49,8 +49,8 @@
/* -- Default credentials. -- */
-static grpc_channel_credentials* g_default_credentials = nullptr;
static int g_compute_engine_detection_done = 0;
+static int g_need_compute_engine_creds = 0;
static gpr_mu g_state_mu;
static gpr_once g_once = GPR_ONCE_INIT;
static grpc_core::internal::grpc_gce_tenancy_checker g_gce_tenancy_checker =
@@ -182,19 +182,13 @@ grpc_channel_credentials* grpc_google_default_credentials_create(void) {
grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Failed to create Google credentials");
grpc_error* err;
+ int need_compute_engine_creds = 0;
grpc_core::ExecCtx exec_ctx;
GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ());
gpr_once_init(&g_once, init_default_credentials);
- gpr_mu_lock(&g_state_mu);
-
- if (g_default_credentials != nullptr) {
- result = grpc_channel_credentials_ref(g_default_credentials);
- goto end;
- }
-
/* First, try the environment variable. */
err = create_default_creds_from_path(
gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR), &call_creds);
@@ -207,55 +201,50 @@ grpc_channel_credentials* grpc_google_default_credentials_create(void) {
if (err == GRPC_ERROR_NONE) goto end;
error = grpc_error_add_child(error, err);
+ gpr_mu_lock(&g_state_mu);
/* At last try to see if we're on compute engine (do the detection only once
since it requires a network test). */
if (!g_compute_engine_detection_done) {
- int need_compute_engine_creds = g_gce_tenancy_checker();
+ g_need_compute_engine_creds = g_gce_tenancy_checker();
g_compute_engine_detection_done = 1;
- if (need_compute_engine_creds) {
- call_creds = grpc_google_compute_engine_credentials_create(nullptr);
- if (call_creds == nullptr) {
- error = grpc_error_add_child(
- error, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
- "Failed to get credentials from network"));
- }
- }
}
+ need_compute_engine_creds = g_need_compute_engine_creds;
+ gpr_mu_unlock(&g_state_mu);
-end:
- if (result == nullptr) {
- if (call_creds != nullptr) {
- /* Create google default credentials. */
- auto creds = static_cast<grpc_google_default_channel_credentials*>(
- gpr_zalloc(sizeof(grpc_google_default_channel_credentials)));
- creds->base.vtable = &google_default_credentials_vtable;
- creds->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_GOOGLE_DEFAULT;
- gpr_ref_init(&creds->base.refcount, 1);
- creds->ssl_creds =
- grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr);
- GPR_ASSERT(creds->ssl_creds != nullptr);
- grpc_alts_credentials_options* options =
- grpc_alts_credentials_client_options_create();
- creds->alts_creds = grpc_alts_credentials_create(options);
- grpc_alts_credentials_options_destroy(options);
- /* Add a global reference so that it can be cached and re-served. */
- g_default_credentials = grpc_composite_channel_credentials_create(
- &creds->base, call_creds, nullptr);
- GPR_ASSERT(g_default_credentials != nullptr);
- grpc_channel_credentials_unref(&creds->base);
- grpc_call_credentials_unref(call_creds);
- result = grpc_channel_credentials_ref(g_default_credentials);
- } else {
- gpr_log(GPR_ERROR, "Could not create google default credentials.");
+ if (need_compute_engine_creds) {
+ call_creds = grpc_google_compute_engine_credentials_create(nullptr);
+ if (call_creds == nullptr) {
+ error = grpc_error_add_child(
+ error, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+ "Failed to get credentials from network"));
}
}
- gpr_mu_unlock(&g_state_mu);
- if (result == nullptr) {
- GRPC_LOG_IF_ERROR("grpc_google_default_credentials_create", error);
+
+end:
+ if (call_creds != nullptr) {
+ /* Create google default credentials. */
+ auto creds = static_cast<grpc_google_default_channel_credentials*>(
+ gpr_zalloc(sizeof(grpc_google_default_channel_credentials)));
+ creds->base.vtable = &google_default_credentials_vtable;
+ creds->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_GOOGLE_DEFAULT;
+ gpr_ref_init(&creds->base.refcount, 1);
+ creds->ssl_creds =
+ grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr);
+ GPR_ASSERT(creds->ssl_creds != nullptr);
+ grpc_alts_credentials_options* options =
+ grpc_alts_credentials_client_options_create();
+ creds->alts_creds = grpc_alts_credentials_create(options);
+ grpc_alts_credentials_options_destroy(options);
+ result = grpc_composite_channel_credentials_create(&creds->base, call_creds,
+ nullptr);
+ GPR_ASSERT(result != nullptr);
+ grpc_channel_credentials_unref(&creds->base);
+ grpc_call_credentials_unref(call_creds);
} else {
- GRPC_ERROR_UNREF(error);
+ gpr_log(GPR_ERROR, "Could not create google default credentials: %s",
+ grpc_error_string(error));
}
-
+ GRPC_ERROR_UNREF(error);
return result;
}
@@ -266,21 +255,17 @@ void set_gce_tenancy_checker_for_testing(grpc_gce_tenancy_checker checker) {
g_gce_tenancy_checker = checker;
}
-} // namespace internal
-} // namespace grpc_core
-
void grpc_flush_cached_google_default_credentials(void) {
grpc_core::ExecCtx exec_ctx;
gpr_once_init(&g_once, init_default_credentials);
gpr_mu_lock(&g_state_mu);
- if (g_default_credentials != nullptr) {
- grpc_channel_credentials_unref(g_default_credentials);
- g_default_credentials = nullptr;
- }
g_compute_engine_detection_done = 0;
gpr_mu_unlock(&g_state_mu);
}
+} // namespace internal
+} // namespace grpc_core
+
/* -- Well known credentials path. -- */
static grpc_well_known_credentials_path_getter creds_path_getter = nullptr;
diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.h b/src/core/lib/security/credentials/google_default/google_default_credentials.h
index a7dd0ea8ae..b9e2efb04f 100644
--- a/src/core/lib/security/credentials/google_default/google_default_credentials.h
+++ b/src/core/lib/security/credentials/google_default/google_default_credentials.h
@@ -45,8 +45,6 @@ typedef struct {
grpc_channel_credentials* ssl_creds;
} grpc_google_default_channel_credentials;
-void grpc_flush_cached_google_default_credentials(void);
-
namespace grpc_core {
namespace internal {
@@ -54,6 +52,9 @@ typedef bool (*grpc_gce_tenancy_checker)(void);
void set_gce_tenancy_checker_for_testing(grpc_gce_tenancy_checker checker);
+// TEST-ONLY. Reset the internal global state.
+void grpc_flush_cached_google_default_credentials(void);
+
} // namespace internal
} // namespace grpc_core
diff --git a/src/core/lib/surface/channel.cc b/src/core/lib/surface/channel.cc
index d7095c24d4..230b89b3fc 100644
--- a/src/core/lib/surface/channel.cc
+++ b/src/core/lib/surface/channel.cc
@@ -39,6 +39,7 @@
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/iomgr/resource_quota.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/call.h"
@@ -63,6 +64,7 @@ struct grpc_channel {
grpc_compression_options compression_options;
gpr_atm call_size_estimate;
+ grpc_resource_user* resource_user;
gpr_mu registered_call_mu;
registered_call* registered_calls;
@@ -82,6 +84,8 @@ grpc_channel* grpc_channel_create_with_builder(
char* target = gpr_strdup(grpc_channel_stack_builder_get_target(builder));
grpc_channel_args* args = grpc_channel_args_copy(
grpc_channel_stack_builder_get_channel_arguments(builder));
+ grpc_resource_user* resource_user =
+ grpc_channel_stack_builder_get_resource_user(builder);
grpc_channel* channel;
if (channel_stack_type == GRPC_SERVER_CHANNEL) {
GRPC_STATS_INC_SERVER_CHANNELS_CREATED();
@@ -101,9 +105,11 @@ grpc_channel* grpc_channel_create_with_builder(
}
channel->target = target;
+ channel->resource_user = resource_user;
channel->is_client = grpc_channel_stack_type_is_client(channel_stack_type);
bool channelz_enabled = GRPC_ENABLE_CHANNELZ_DEFAULT;
- size_t channel_tracer_max_memory = 0; // default to off
+ size_t channel_tracer_max_memory =
+ GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT;
bool internal_channel = false;
// this creates the default ChannelNode. Different types of channels may
// override this to ensure a correct ChannelNode is created.
@@ -142,7 +148,6 @@ grpc_channel* grpc_channel_create_with_builder(
0x1; /* always support no compression */
} else if (0 == strcmp(args->args[i].key,
GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE)) {
- GPR_ASSERT(channel_tracer_max_memory == 0);
const grpc_integer_options options = {
GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT, 0, INT_MAX};
channel_tracer_max_memory =
@@ -217,7 +222,8 @@ grpc_core::channelz::ChannelNode* grpc_channel_get_channelz_node(
grpc_channel* grpc_channel_create(const char* target,
const grpc_channel_args* input_args,
grpc_channel_stack_type channel_stack_type,
- grpc_transport* optional_transport) {
+ grpc_transport* optional_transport,
+ grpc_resource_user* resource_user) {
grpc_channel_stack_builder* builder = grpc_channel_stack_builder_create();
const grpc_core::UniquePtr<char> default_authority =
get_default_authority(input_args);
@@ -227,11 +233,17 @@ grpc_channel* grpc_channel_create(const char* target,
grpc_channel_args_destroy(args);
grpc_channel_stack_builder_set_target(builder, target);
grpc_channel_stack_builder_set_transport(builder, optional_transport);
+ grpc_channel_stack_builder_set_resource_user(builder, resource_user);
if (!grpc_channel_init_create_stack(builder, channel_stack_type)) {
grpc_channel_stack_builder_destroy(builder);
+ if (resource_user != nullptr) {
+ grpc_resource_user_free(resource_user, GRPC_RESOURCE_QUOTA_CHANNEL_SIZE);
+ }
return nullptr;
}
- return grpc_channel_create_with_builder(builder, channel_stack_type);
+ grpc_channel* channel =
+ grpc_channel_create_with_builder(builder, channel_stack_type);
+ return channel;
}
size_t grpc_channel_get_call_size_estimate(grpc_channel* channel) {
@@ -441,6 +453,10 @@ static void destroy_channel(void* arg, grpc_error* error) {
GRPC_MDELEM_UNREF(rc->authority);
gpr_free(rc);
}
+ if (channel->resource_user != nullptr) {
+ grpc_resource_user_free(channel->resource_user,
+ GRPC_RESOURCE_QUOTA_CHANNEL_SIZE);
+ }
gpr_mu_destroy(&channel->registered_call_mu);
gpr_free(channel->target);
gpr_free(channel);
diff --git a/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h
index 4ac76b8a29..ab00b8e94f 100644
--- a/src/core/lib/surface/channel.h
+++ b/src/core/lib/surface/channel.h
@@ -29,7 +29,8 @@
grpc_channel* grpc_channel_create(const char* target,
const grpc_channel_args* args,
grpc_channel_stack_type channel_stack_type,
- grpc_transport* optional_transport);
+ grpc_transport* optional_transport,
+ grpc_resource_user* resource_user = nullptr);
grpc_channel* grpc_channel_create_with_builder(
grpc_channel_stack_builder* builder,
diff --git a/src/core/lib/surface/server.cc b/src/core/lib/surface/server.cc
index 35ab2c3bce..72391ca697 100644
--- a/src/core/lib/surface/server.cc
+++ b/src/core/lib/surface/server.cc
@@ -189,6 +189,8 @@ typedef struct {
struct grpc_server {
grpc_channel_args* channel_args;
+ grpc_resource_user* default_resource_user;
+
grpc_completion_queue** cqs;
grpc_pollset** pollsets;
size_t cq_count;
@@ -1024,6 +1026,15 @@ grpc_server* grpc_server_create(const grpc_channel_args* args, void* reserved) {
grpc_slice_from_static_string("Server created"));
}
+ if (args != nullptr) {
+ grpc_resource_quota* resource_quota =
+ grpc_resource_quota_from_channel_args(args, false /* create */);
+ if (resource_quota != nullptr) {
+ server->default_resource_user =
+ grpc_resource_user_create(resource_quota, "default");
+ }
+ }
+
return server;
}
@@ -1122,7 +1133,8 @@ void grpc_server_get_pollsets(grpc_server* server, grpc_pollset*** pollsets,
void grpc_server_setup_transport(grpc_server* s, grpc_transport* transport,
grpc_pollset* accepting_pollset,
const grpc_channel_args* args,
- intptr_t socket_uuid) {
+ intptr_t socket_uuid,
+ grpc_resource_user* resource_user) {
size_t num_registered_methods;
size_t alloc;
registered_method* rm;
@@ -1135,7 +1147,8 @@ void grpc_server_setup_transport(grpc_server* s, grpc_transport* transport,
uint32_t max_probes = 0;
grpc_transport_op* op = nullptr;
- channel = grpc_channel_create(nullptr, args, GRPC_SERVER_CHANNEL, transport);
+ channel = grpc_channel_create(nullptr, args, GRPC_SERVER_CHANNEL, transport,
+ resource_user);
chand = static_cast<channel_data*>(
grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0)
->channel_data);
@@ -1330,6 +1343,13 @@ void grpc_server_shutdown_and_notify(grpc_server* server,
channel_broadcaster_shutdown(&broadcaster, true /* send_goaway */,
GRPC_ERROR_NONE);
+
+ if (server->default_resource_user != nullptr) {
+ grpc_resource_quota_unref(
+ grpc_resource_user_quota(server->default_resource_user));
+ grpc_resource_user_shutdown(server->default_resource_user);
+ grpc_resource_user_unref(server->default_resource_user);
+ }
}
void grpc_server_cancel_all_calls(grpc_server* server) {
@@ -1546,6 +1566,10 @@ const grpc_channel_args* grpc_server_get_channel_args(grpc_server* server) {
return server->channel_args;
}
+grpc_resource_user* grpc_server_get_default_resource_user(grpc_server* server) {
+ return server->default_resource_user;
+}
+
int grpc_server_has_open_connections(grpc_server* server) {
int r;
gpr_mu_lock(&server->mu_global);
diff --git a/src/core/lib/surface/server.h b/src/core/lib/surface/server.h
index 33c205417e..27038fdb7a 100644
--- a/src/core/lib/surface/server.h
+++ b/src/core/lib/surface/server.h
@@ -47,7 +47,8 @@ void grpc_server_add_listener(grpc_server* server, void* listener,
void grpc_server_setup_transport(grpc_server* server, grpc_transport* transport,
grpc_pollset* accepting_pollset,
const grpc_channel_args* args,
- intptr_t socket_uuid);
+ intptr_t socket_uuid,
+ grpc_resource_user* resource_user = nullptr);
/* fills in the uuids of all sockets used for connections on this server */
void grpc_server_populate_server_sockets(
@@ -63,6 +64,8 @@ grpc_core::channelz::ServerNode* grpc_server_get_channelz_node(
const grpc_channel_args* grpc_server_get_channel_args(grpc_server* server);
+grpc_resource_user* grpc_server_get_default_resource_user(grpc_server* server);
+
int grpc_server_has_open_connections(grpc_server* server);
/* Do not call this before grpc_server_start. Returns the pollsets and the
diff --git a/src/cpp/client/channel_cc.cc b/src/cpp/client/channel_cc.cc
index 2cab41b3f5..f5c1b4e2c5 100644
--- a/src/cpp/client/channel_cc.cc
+++ b/src/cpp/client/channel_cc.cc
@@ -33,6 +33,7 @@
#include <grpcpp/client_context.h>
#include <grpcpp/completion_queue.h>
#include <grpcpp/impl/call.h>
+#include <grpcpp/impl/codegen/call_op_set.h>
#include <grpcpp/impl/codegen/completion_queue_tag.h>
#include <grpcpp/impl/grpc_library.h>
#include <grpcpp/impl/rpc_method.h>
@@ -57,9 +58,8 @@ Channel::Channel(
std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>
interceptor_creators)
: host_(host), c_channel_(channel) {
- auto* vector = interceptor_creators.release();
- if (vector != nullptr) {
- interceptor_creators_ = std::move(*vector);
+ if (interceptor_creators != nullptr) {
+ interceptor_creators_ = std::move(*interceptor_creators);
}
g_gli_initializer.summon();
}
@@ -112,9 +112,10 @@ void ChannelResetConnectionBackoff(Channel* channel) {
} // namespace experimental
-internal::Call Channel::CreateCall(const internal::RpcMethod& method,
- ClientContext* context,
- CompletionQueue* cq) {
+internal::Call Channel::CreateCallInternal(const internal::RpcMethod& method,
+ ClientContext* context,
+ CompletionQueue* cq,
+ int interceptor_pos) {
const bool kRegistered = method.channel_tag() && context->authority().empty();
grpc_call* c_call = nullptr;
if (kRegistered) {
@@ -147,17 +148,22 @@ internal::Call Channel::CreateCall(const internal::RpcMethod& method,
}
grpc_census_call_set_context(c_call, context->census_context());
context->set_call(c_call, shared_from_this());
- return internal::Call(c_call, this, cq);
+
+ auto* info = context->set_client_rpc_info(
+ method.name(), this, interceptor_creators_, interceptor_pos);
+ return internal::Call(c_call, this, cq, info);
+}
+
+internal::Call Channel::CreateCall(const internal::RpcMethod& method,
+ ClientContext* context,
+ CompletionQueue* cq) {
+ return CreateCallInternal(method, context, cq, 0);
}
void Channel::PerformOpsOnCall(internal::CallOpSetInterface* ops,
internal::Call* call) {
- static const size_t MAX_OPS = 8;
- size_t nops = 0;
- grpc_op cops[MAX_OPS];
- ops->FillOps(call->call(), cops, &nops);
- GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call->call(), cops, nops,
- ops->cq_tag(), nullptr));
+ ops->FillOps(
+ call); // Make a copy of call. It's fine since Call just has pointers
}
void* Channel::RegisterMethod(const char* method) {
@@ -219,7 +225,7 @@ class ShutdownCallback : public grpc_experimental_completion_queue_functor {
static void Run(grpc_experimental_completion_queue_functor* cb, int) {
auto* callback = static_cast<ShutdownCallback*>(cb);
delete callback->cq_;
- grpc_core::Delete(callback);
+ delete callback;
}
private:
@@ -232,7 +238,7 @@ CompletionQueue* Channel::CallbackCQ() {
// if there is no explicit per-channel CQ registered
std::lock_guard<std::mutex> l(mu_);
if (callback_cq_ == nullptr) {
- auto* shutdown_callback = grpc_core::New<ShutdownCallback>();
+ auto* shutdown_callback = new ShutdownCallback;
callback_cq_ = new CompletionQueue(grpc_completion_queue_attributes{
GRPC_CQ_CURRENT_VERSION, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING,
shutdown_callback});
diff --git a/src/cpp/common/completion_queue_cc.cc b/src/cpp/common/completion_queue_cc.cc
index 6893201e2e..d93a54aed7 100644
--- a/src/cpp/common/completion_queue_cc.cc
+++ b/src/cpp/common/completion_queue_cc.cc
@@ -60,10 +60,10 @@ CompletionQueue::NextStatus CompletionQueue::AsyncNextInternal(
case GRPC_QUEUE_SHUTDOWN:
return SHUTDOWN;
case GRPC_OP_COMPLETE:
- auto cq_tag = static_cast<internal::CompletionQueueTag*>(ev.tag);
+ auto core_cq_tag = static_cast<internal::CompletionQueueTag*>(ev.tag);
*ok = ev.success != 0;
- *tag = cq_tag;
- if (cq_tag->FinalizeResult(tag, ok)) {
+ *tag = core_cq_tag;
+ if (core_cq_tag->FinalizeResult(tag, ok)) {
return GOT_EVENT;
}
break;
@@ -87,9 +87,9 @@ bool CompletionQueue::CompletionQueueTLSCache::Flush(void** tag, bool* ok) {
flushed_ = true;
if (grpc_completion_queue_thread_local_cache_flush(cq_->cq_, &res_tag,
&res)) {
- auto cq_tag = static_cast<internal::CompletionQueueTag*>(res_tag);
+ auto core_cq_tag = static_cast<internal::CompletionQueueTag*>(res_tag);
*ok = res == 1;
- if (cq_tag->FinalizeResult(tag, ok)) {
+ if (core_cq_tag->FinalizeResult(tag, ok)) {
return true;
}
}
diff --git a/src/cpp/common/core_codegen.cc b/src/cpp/common/core_codegen.cc
index 619aacadaa..cfaa2e7b19 100644
--- a/src/cpp/common/core_codegen.cc
+++ b/src/cpp/common/core_codegen.cc
@@ -102,6 +102,13 @@ size_t CoreCodegen::grpc_byte_buffer_length(grpc_byte_buffer* bb) {
return ::grpc_byte_buffer_length(bb);
}
+grpc_call_error CoreCodegen::grpc_call_start_batch(grpc_call* call,
+ const grpc_op* ops,
+ size_t nops, void* tag,
+ void* reserved) {
+ return ::grpc_call_start_batch(call, ops, nops, tag, reserved);
+}
+
grpc_call_error CoreCodegen::grpc_call_cancel_with_status(
grpc_call* call, grpc_status_code status, const char* description,
void* reserved) {
diff --git a/src/cpp/server/channelz/channelz_service.cc b/src/cpp/server/channelz/channelz_service.cc
index 79ed9102e5..9ecb9de7e4 100644
--- a/src/cpp/server/channelz/channelz_service.cc
+++ b/src/cpp/server/channelz/channelz_service.cc
@@ -20,9 +20,6 @@
#include "src/cpp/server/channelz/channelz_service.h"
-#include <google/protobuf/text_format.h>
-#include <google/protobuf/util/json_util.h>
-
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
@@ -33,13 +30,14 @@ Status ChannelzService::GetTopChannels(
channelz::v1::GetTopChannelsResponse* response) {
char* json_str = grpc_channelz_get_top_channels(request->start_channel_id());
if (json_str == nullptr) {
- return Status(INTERNAL, "grpc_channelz_get_top_channels returned null");
+ return Status(StatusCode::INTERNAL,
+ "grpc_channelz_get_top_channels returned null");
}
- google::protobuf::util::Status s =
- google::protobuf::util::JsonStringToMessage(json_str, response);
+ grpc::protobuf::util::Status s =
+ grpc::protobuf::json::JsonStringToMessage(json_str, response);
gpr_free(json_str);
- if (s != google::protobuf::util::Status::OK) {
- return Status(INTERNAL, s.ToString());
+ if (!s.ok()) {
+ return Status(StatusCode::INTERNAL, s.ToString());
}
return Status::OK;
}
@@ -49,13 +47,31 @@ Status ChannelzService::GetServers(
channelz::v1::GetServersResponse* response) {
char* json_str = grpc_channelz_get_servers(request->start_server_id());
if (json_str == nullptr) {
- return Status(INTERNAL, "grpc_channelz_get_servers returned null");
+ return Status(StatusCode::INTERNAL,
+ "grpc_channelz_get_servers returned null");
+ }
+ grpc::protobuf::util::Status s =
+ grpc::protobuf::json::JsonStringToMessage(json_str, response);
+ gpr_free(json_str);
+ if (!s.ok()) {
+ return Status(StatusCode::INTERNAL, s.ToString());
+ }
+ return Status::OK;
+}
+
+Status ChannelzService::GetServer(ServerContext* unused,
+ const channelz::v1::GetServerRequest* request,
+ channelz::v1::GetServerResponse* response) {
+ char* json_str = grpc_channelz_get_server(request->server_id());
+ if (json_str == nullptr) {
+ return Status(StatusCode::INTERNAL,
+ "grpc_channelz_get_server returned null");
}
- google::protobuf::util::Status s =
- google::protobuf::util::JsonStringToMessage(json_str, response);
+ grpc::protobuf::util::Status s =
+ grpc::protobuf::json::JsonStringToMessage(json_str, response);
gpr_free(json_str);
- if (s != google::protobuf::util::Status::OK) {
- return Status(INTERNAL, s.ToString());
+ if (!s.ok()) {
+ return Status(StatusCode::INTERNAL, s.ToString());
}
return Status::OK;
}
@@ -66,13 +82,14 @@ Status ChannelzService::GetServerSockets(
char* json_str = grpc_channelz_get_server_sockets(request->server_id(),
request->start_socket_id());
if (json_str == nullptr) {
- return Status(INTERNAL, "grpc_channelz_get_server_sockets returned null");
+ return Status(StatusCode::INTERNAL,
+ "grpc_channelz_get_server_sockets returned null");
}
- google::protobuf::util::Status s =
- google::protobuf::util::JsonStringToMessage(json_str, response);
+ grpc::protobuf::util::Status s =
+ grpc::protobuf::json::JsonStringToMessage(json_str, response);
gpr_free(json_str);
- if (s != google::protobuf::util::Status::OK) {
- return Status(INTERNAL, s.ToString());
+ if (!s.ok()) {
+ return Status(StatusCode::INTERNAL, s.ToString());
}
return Status::OK;
}
@@ -82,13 +99,13 @@ Status ChannelzService::GetChannel(
channelz::v1::GetChannelResponse* response) {
char* json_str = grpc_channelz_get_channel(request->channel_id());
if (json_str == nullptr) {
- return Status(NOT_FOUND, "No object found for that ChannelId");
+ return Status(StatusCode::NOT_FOUND, "No object found for that ChannelId");
}
- google::protobuf::util::Status s =
- google::protobuf::util::JsonStringToMessage(json_str, response);
+ grpc::protobuf::util::Status s =
+ grpc::protobuf::json::JsonStringToMessage(json_str, response);
gpr_free(json_str);
- if (s != google::protobuf::util::Status::OK) {
- return Status(INTERNAL, s.ToString());
+ if (!s.ok()) {
+ return Status(StatusCode::INTERNAL, s.ToString());
}
return Status::OK;
}
@@ -98,13 +115,14 @@ Status ChannelzService::GetSubchannel(
channelz::v1::GetSubchannelResponse* response) {
char* json_str = grpc_channelz_get_subchannel(request->subchannel_id());
if (json_str == nullptr) {
- return Status(NOT_FOUND, "No object found for that SubchannelId");
+ return Status(StatusCode::NOT_FOUND,
+ "No object found for that SubchannelId");
}
- google::protobuf::util::Status s =
- google::protobuf::util::JsonStringToMessage(json_str, response);
+ grpc::protobuf::util::Status s =
+ grpc::protobuf::json::JsonStringToMessage(json_str, response);
gpr_free(json_str);
- if (s != google::protobuf::util::Status::OK) {
- return Status(INTERNAL, s.ToString());
+ if (!s.ok()) {
+ return Status(StatusCode::INTERNAL, s.ToString());
}
return Status::OK;
}
@@ -114,13 +132,13 @@ Status ChannelzService::GetSocket(ServerContext* unused,
channelz::v1::GetSocketResponse* response) {
char* json_str = grpc_channelz_get_socket(request->socket_id());
if (json_str == nullptr) {
- return Status(NOT_FOUND, "No object found for that SocketId");
+ return Status(StatusCode::NOT_FOUND, "No object found for that SocketId");
}
- google::protobuf::util::Status s =
- google::protobuf::util::JsonStringToMessage(json_str, response);
+ grpc::protobuf::util::Status s =
+ grpc::protobuf::json::JsonStringToMessage(json_str, response);
gpr_free(json_str);
- if (s != google::protobuf::util::Status::OK) {
- return Status(INTERNAL, s.ToString());
+ if (!s.ok()) {
+ return Status(StatusCode::INTERNAL, s.ToString());
}
return Status::OK;
}
diff --git a/src/cpp/server/channelz/channelz_service.h b/src/cpp/server/channelz/channelz_service.h
index 590b5d492e..b4a66ba1c6 100644
--- a/src/cpp/server/channelz/channelz_service.h
+++ b/src/cpp/server/channelz/channelz_service.h
@@ -36,6 +36,10 @@ class ChannelzService final : public channelz::v1::Channelz::Service {
Status GetServers(ServerContext* unused,
const channelz::v1::GetServersRequest* request,
channelz::v1::GetServersResponse* response) override;
+ // implementation of GetServer rpc
+ Status GetServer(ServerContext* unused,
+ const channelz::v1::GetServerRequest* request,
+ channelz::v1::GetServerResponse* response) override;
// implementation of GetServerSockets rpc
Status GetServerSockets(
ServerContext* unused,
diff --git a/src/cpp/server/server_builder.cc b/src/cpp/server/server_builder.cc
index 8417c45e64..0dc03b6876 100644
--- a/src/cpp/server/server_builder.cc
+++ b/src/cpp/server/server_builder.cc
@@ -71,7 +71,9 @@ ServerBuilder::~ServerBuilder() {
std::unique_ptr<ServerCompletionQueue> ServerBuilder::AddCompletionQueue(
bool is_frequently_polled) {
ServerCompletionQueue* cq = new ServerCompletionQueue(
- is_frequently_polled ? GRPC_CQ_DEFAULT_POLLING : GRPC_CQ_NON_LISTENING);
+ GRPC_CQ_NEXT,
+ is_frequently_polled ? GRPC_CQ_DEFAULT_POLLING : GRPC_CQ_NON_LISTENING,
+ nullptr);
cqs_.push_back(cq);
return std::unique_ptr<ServerCompletionQueue>(cq);
}
@@ -256,14 +258,22 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
// Create completion queues to listen to incoming rpc requests
for (int i = 0; i < sync_server_settings_.num_cqs; i++) {
- sync_server_cqs->emplace_back(new ServerCompletionQueue(polling_type));
+ sync_server_cqs->emplace_back(
+ new ServerCompletionQueue(GRPC_CQ_NEXT, polling_type, nullptr));
}
}
- std::unique_ptr<Server> server(new Server(
- max_receive_message_size_, &args, sync_server_cqs,
- sync_server_settings_.min_pollers, sync_server_settings_.max_pollers,
- sync_server_settings_.cq_timeout_msec, resource_quota_));
+ // == Determine if the server has any callback methods ==
+ bool has_callback_methods = false;
+ for (auto it = services_.begin(); it != services_.end(); ++it) {
+ if ((*it)->service->has_callback_methods()) {
+ has_callback_methods = true;
+ break;
+ }
+ }
+
+ // TODO(vjpai): Add a section here for plugins once they can support callback
+ // methods
if (has_sync_methods) {
// This is a Sync server
@@ -275,6 +285,16 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
sync_server_settings_.cq_timeout_msec);
}
+ if (has_callback_methods) {
+ gpr_log(GPR_INFO, "Callback server.");
+ }
+
+ std::unique_ptr<Server> server(new Server(
+ max_receive_message_size_, &args, sync_server_cqs,
+ sync_server_settings_.min_pollers, sync_server_settings_.max_pollers,
+ sync_server_settings_.cq_timeout_msec, resource_quota_,
+ std::move(interceptor_creators_)));
+
ServerInitializer* initializer = server->initializer();
// Register all the completion queues with the server. i.e
@@ -288,6 +308,12 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
num_frequently_polled_cqs++;
}
+ if (has_callback_methods) {
+ auto* cq = server->CallbackCQ();
+ grpc_server_register_completion_queue(server->server_, cq->cq(), nullptr);
+ num_frequently_polled_cqs++;
+ }
+
// cqs_ contains the completion queue added by calling the ServerBuilder's
// AddCompletionQueue() API. Some of them may not be frequently polled (i.e by
// calling Next() or AsyncNext()) and hence are not safe to be used for
diff --git a/src/cpp/server/server_cc.cc b/src/cpp/server/server_cc.cc
index 7aeddff643..870ee84e3e 100644
--- a/src/cpp/server/server_cc.cc
+++ b/src/cpp/server/server_cc.cc
@@ -27,7 +27,9 @@
#include <grpcpp/completion_queue.h>
#include <grpcpp/generic/async_generic_service.h>
#include <grpcpp/impl/codegen/async_unary_call.h>
+#include <grpcpp/impl/codegen/call.h>
#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/server_interceptor.h>
#include <grpcpp/impl/grpc_library.h>
#include <grpcpp/impl/method_handler_impl.h>
#include <grpcpp/impl/rpc_service_method.h>
@@ -38,8 +40,10 @@
#include <grpcpp/support/time.h>
#include "src/core/ext/transport/inproc/inproc_transport.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/profiling/timers.h"
#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/completion_queue.h"
#include "src/cpp/client/create_channel_internal.h"
#include "src/cpp/server/health/default_health_check_service.h"
#include "src/cpp/thread_manager/thread_manager.h"
@@ -127,10 +131,13 @@ class Server::UnimplementedAsyncResponse final
~UnimplementedAsyncResponse() { delete request_; }
bool FinalizeResult(void** tag, bool* status) override {
- internal::CallOpSet<
- internal::CallOpSendInitialMetadata,
- internal::CallOpServerSendStatus>::FinalizeResult(tag, status);
- delete this;
+ if (internal::CallOpSet<
+ internal::CallOpSendInitialMetadata,
+ internal::CallOpServerSendStatus>::FinalizeResult(tag, status)) {
+ delete this;
+ } else {
+ // The tag was swallowed due to interception. We will see it again.
+ }
return false;
}
@@ -140,9 +147,9 @@ class Server::UnimplementedAsyncResponse final
class Server::SyncRequest final : public internal::CompletionQueueTag {
public:
- SyncRequest(internal::RpcServiceMethod* method, void* tag)
+ SyncRequest(internal::RpcServiceMethod* method, void* method_tag)
: method_(method),
- tag_(tag),
+ method_tag_(method_tag),
in_flight_(false),
has_request_payload_(
method->method_type() == internal::RpcMethod::NORMAL_RPC ||
@@ -169,10 +176,10 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
void Request(grpc_server* server, grpc_completion_queue* notify_cq) {
GPR_ASSERT(cq_ && !in_flight_);
in_flight_ = true;
- if (tag_) {
+ if (method_tag_) {
if (GRPC_CALL_OK !=
grpc_server_request_registered_call(
- server, tag_, &call_, &deadline_, &request_metadata_,
+ server, method_tag_, &call_, &deadline_, &request_metadata_,
has_request_payload_ ? &request_payload_ : nullptr, cq_,
notify_cq, this)) {
TeardownRequest();
@@ -204,17 +211,25 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
return true;
}
+ // The CallData class represents a call that is "active" as opposed
+ // to just being requested. It wraps and takes ownership of the cq from
+ // the call request
class CallData final {
public:
explicit CallData(Server* server, SyncRequest* mrd)
: cq_(mrd->cq_),
- call_(mrd->call_, server, &cq_, server->max_receive_message_size()),
ctx_(mrd->deadline_, &mrd->request_metadata_),
has_request_payload_(mrd->has_request_payload_),
request_payload_(has_request_payload_ ? mrd->request_payload_
: nullptr),
+ request_(nullptr),
method_(mrd->method_),
- server_(server) {
+ call_(mrd->call_, server, &cq_, server->max_receive_message_size(),
+ ctx_.set_server_rpc_info(method_->name(),
+ server->interceptor_creators_)),
+ server_(server),
+ global_callbacks_(nullptr),
+ resources_(false) {
ctx_.set_call(mrd->call_);
ctx_.cq_ = &cq_;
GPR_ASSERT(mrd->in_flight_);
@@ -230,38 +245,79 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
void Run(const std::shared_ptr<GlobalCallbacks>& global_callbacks,
bool resources) {
- ctx_.BeginCompletionOp(&call_);
- global_callbacks->PreSynchronousRequest(&ctx_);
- auto* handler = resources ? method_->handler()
- : server_->resource_exhausted_handler_.get();
- handler->RunHandler(internal::MethodHandler::HandlerParameter(
- &call_, &ctx_, request_payload_));
- global_callbacks->PostSynchronousRequest(&ctx_);
- request_payload_ = nullptr;
-
- cq_.Shutdown();
+ global_callbacks_ = global_callbacks;
+ resources_ = resources;
+
+ interceptor_methods_.SetCall(&call_);
+ interceptor_methods_.SetReverse();
+ // Set interception point for RECV INITIAL METADATA
+ interceptor_methods_.AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
+ interceptor_methods_.SetRecvInitialMetadata(&ctx_.client_metadata_);
+
+ if (has_request_payload_) {
+ // Set interception point for RECV MESSAGE
+ auto* handler = resources_ ? method_->handler()
+ : server_->resource_exhausted_handler_.get();
+ request_ = handler->Deserialize(call_.call(), request_payload_,
+ &request_status_);
+
+ request_payload_ = nullptr;
+ interceptor_methods_.AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+ interceptor_methods_.SetRecvMessage(request_);
+ }
- internal::CompletionQueueTag* op_tag = ctx_.GetCompletionOpTag();
- cq_.TryPluck(op_tag, gpr_inf_future(GPR_CLOCK_REALTIME));
+ auto f = std::bind(&CallData::ContinueRunAfterInterception, this);
+ if (interceptor_methods_.RunInterceptors(f)) {
+ ContinueRunAfterInterception();
+ } else {
+ // There were interceptors to be run, so ContinueRunAfterInterception
+ // will be run when interceptors are done.
+ }
+ }
- /* Ensure the cq_ is shutdown */
- DummyTag ignored_tag;
- GPR_ASSERT(cq_.Pluck(&ignored_tag) == false);
+ void ContinueRunAfterInterception() {
+ {
+ ctx_.BeginCompletionOp(&call_, false);
+ global_callbacks_->PreSynchronousRequest(&ctx_);
+ auto* handler = resources_ ? method_->handler()
+ : server_->resource_exhausted_handler_.get();
+ handler->RunHandler(internal::MethodHandler::HandlerParameter(
+ &call_, &ctx_, request_, request_status_, nullptr));
+ request_ = nullptr;
+ global_callbacks_->PostSynchronousRequest(&ctx_);
+
+ cq_.Shutdown();
+
+ internal::CompletionQueueTag* op_tag = ctx_.GetCompletionOpTag();
+ cq_.TryPluck(op_tag, gpr_inf_future(GPR_CLOCK_REALTIME));
+
+ /* Ensure the cq_ is shutdown */
+ DummyTag ignored_tag;
+ GPR_ASSERT(cq_.Pluck(&ignored_tag) == false);
+ }
+ delete this;
}
private:
CompletionQueue cq_;
- internal::Call call_;
ServerContext ctx_;
const bool has_request_payload_;
grpc_byte_buffer* request_payload_;
+ void* request_;
+ Status request_status_;
internal::RpcServiceMethod* const method_;
+ internal::Call call_;
Server* server_;
+ std::shared_ptr<GlobalCallbacks> global_callbacks_;
+ bool resources_;
+ internal::InterceptorBatchMethodsImpl interceptor_methods_;
};
private:
internal::RpcServiceMethod* const method_;
- void* const tag_;
+ void* const method_tag_;
bool in_flight_;
const bool has_request_payload_;
grpc_call* call_;
@@ -272,6 +328,176 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
grpc_completion_queue* cq_;
};
+class Server::CallbackRequest final : public internal::CompletionQueueTag {
+ public:
+ CallbackRequest(Server* server, internal::RpcServiceMethod* method,
+ void* method_tag)
+ : server_(server),
+ method_(method),
+ method_tag_(method_tag),
+ has_request_payload_(
+ method->method_type() == internal::RpcMethod::NORMAL_RPC ||
+ method->method_type() == internal::RpcMethod::SERVER_STREAMING),
+ cq_(server->CallbackCQ()),
+ tag_(this) {
+ Setup();
+ }
+
+ ~CallbackRequest() { Clear(); }
+
+ void Request() {
+ if (method_tag_) {
+ if (GRPC_CALL_OK !=
+ grpc_server_request_registered_call(
+ server_->c_server(), method_tag_, &call_, &deadline_,
+ &request_metadata_,
+ has_request_payload_ ? &request_payload_ : nullptr, cq_->cq(),
+ cq_->cq(), static_cast<void*>(&tag_))) {
+ return;
+ }
+ } else {
+ if (!call_details_) {
+ call_details_ = new grpc_call_details;
+ grpc_call_details_init(call_details_);
+ }
+ if (grpc_server_request_call(server_->c_server(), &call_, call_details_,
+ &request_metadata_, cq_->cq(), cq_->cq(),
+ static_cast<void*>(&tag_)) != GRPC_CALL_OK) {
+ return;
+ }
+ }
+ }
+
+ bool FinalizeResult(void** tag, bool* status) override { return false; }
+
+ private:
+ class CallbackCallTag : public grpc_experimental_completion_queue_functor {
+ public:
+ CallbackCallTag(Server::CallbackRequest* req) : req_(req) {
+ functor_run = &CallbackCallTag::StaticRun;
+ }
+
+ // force_run can not be performed on a tag if operations using this tag
+ // have been sent to PerformOpsOnCall. It is intended for error conditions
+ // that are detected before the operations are internally processed.
+ void force_run(bool ok) { Run(ok); }
+
+ private:
+ Server::CallbackRequest* req_;
+ internal::Call* call_;
+
+ static void StaticRun(grpc_experimental_completion_queue_functor* cb,
+ int ok) {
+ static_cast<CallbackCallTag*>(cb)->Run(static_cast<bool>(ok));
+ }
+ void Run(bool ok) {
+ void* ignored = req_;
+ bool new_ok = ok;
+ GPR_ASSERT(!req_->FinalizeResult(&ignored, &new_ok));
+ GPR_ASSERT(ignored == req_);
+
+ if (!ok) {
+ // The call has been shutdown
+ req_->Clear();
+ return;
+ }
+
+ // Bind the call, deadline, and metadata from what we got
+ req_->ctx_.set_call(req_->call_);
+ req_->ctx_.cq_ = req_->cq_;
+ req_->ctx_.BindDeadlineAndMetadata(req_->deadline_,
+ &req_->request_metadata_);
+ req_->request_metadata_.count = 0;
+
+ // Create a C++ Call to control the underlying core call
+ call_ = new (grpc_call_arena_alloc(req_->call_, sizeof(internal::Call)))
+ internal::Call(
+ req_->call_, req_->server_, req_->cq_,
+ req_->server_->max_receive_message_size(),
+ req_->ctx_.set_server_rpc_info(
+ req_->method_->name(), req_->server_->interceptor_creators_));
+
+ req_->interceptor_methods_.SetCall(call_);
+ req_->interceptor_methods_.SetReverse();
+ // Set interception point for RECV INITIAL METADATA
+ req_->interceptor_methods_.AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
+ req_->interceptor_methods_.SetRecvInitialMetadata(
+ &req_->ctx_.client_metadata_);
+
+ if (req_->has_request_payload_) {
+ // Set interception point for RECV MESSAGE
+ req_->request_ = req_->method_->handler()->Deserialize(
+ req_->call_, req_->request_payload_, &req_->request_status_);
+ req_->request_payload_ = nullptr;
+ req_->interceptor_methods_.AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+ req_->interceptor_methods_.SetRecvMessage(req_->request_);
+ }
+
+ if (req_->interceptor_methods_.RunInterceptors(
+ [this] { ContinueRunAfterInterception(); })) {
+ ContinueRunAfterInterception();
+ } else {
+ // There were interceptors to be run, so ContinueRunAfterInterception
+ // will be run when interceptors are done.
+ }
+ }
+ void ContinueRunAfterInterception() {
+ req_->ctx_.BeginCompletionOp(call_, true);
+ req_->method_->handler()->RunHandler(
+ internal::MethodHandler::HandlerParameter(
+ call_, &req_->ctx_, req_->request_, req_->request_status_,
+ [this] {
+ req_->Reset();
+ req_->Request();
+ }));
+ }
+ };
+
+ void Reset() {
+ Clear();
+ Setup();
+ }
+
+ void Clear() {
+ if (call_details_) {
+ delete call_details_;
+ call_details_ = nullptr;
+ }
+ grpc_metadata_array_destroy(&request_metadata_);
+ if (has_request_payload_ && request_payload_) {
+ grpc_byte_buffer_destroy(request_payload_);
+ }
+ ctx_.Clear();
+ interceptor_methods_.ClearState();
+ }
+
+ void Setup() {
+ grpc_metadata_array_init(&request_metadata_);
+ ctx_.Setup(gpr_inf_future(GPR_CLOCK_REALTIME));
+ request_payload_ = nullptr;
+ request_ = nullptr;
+ request_status_ = Status();
+ }
+
+ Server* const server_;
+ internal::RpcServiceMethod* const method_;
+ void* const method_tag_;
+ const bool has_request_payload_;
+ grpc_byte_buffer* request_payload_;
+ void* request_;
+ Status request_status_;
+ grpc_call_details* call_details_ = nullptr;
+ grpc_call* call_;
+ gpr_timespec deadline_;
+ grpc_metadata_array request_metadata_;
+ CompletionQueue* cq_;
+ CallbackCallTag tag_;
+ ServerContext ctx_;
+ internal::InterceptorBatchMethodsImpl interceptor_methods_;
+};
+
// Implementation of ThreadManager. Each instance of SyncRequestThreadManager
// manages a pool of threads that poll for incoming Sync RPCs and call the
// appropriate RPC handlers
@@ -318,8 +544,9 @@ class Server::SyncRequestThreadManager : public ThreadManager {
}
if (ok) {
- // Calldata takes ownership of the completion queue inside sync_req
- SyncRequest::CallData cd(server_, sync_req);
+ // Calldata takes ownership of the completion queue and interceptors
+ // inside sync_req
+ auto* cd = new SyncRequest::CallData(server_, sync_req);
// Prepare for the next request
if (!IsShutdown()) {
sync_req->SetupRequest(); // Create new completion queue for sync_req
@@ -327,7 +554,7 @@ class Server::SyncRequestThreadManager : public ThreadManager {
}
GPR_TIMER_SCOPE("cd.Run()", 0);
- cd.Run(global_callbacks_, resources);
+ cd->Run(global_callbacks_, resources);
}
// TODO (sreek) If ok is false here (which it isn't in case of
// grpc_request_registered_call), we should still re-queue the request
@@ -389,8 +616,12 @@ Server::Server(
std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
sync_server_cqs,
int min_pollers, int max_pollers, int sync_cq_timeout_msec,
- grpc_resource_quota* server_rq)
- : max_receive_message_size_(max_receive_message_size),
+ grpc_resource_quota* server_rq,
+ std::vector<
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ interceptor_creators)
+ : interceptor_creators_(std::move(interceptor_creators)),
+ max_receive_message_size_(max_receive_message_size),
sync_server_cqs_(std::move(sync_server_cqs)),
started_(false),
shutdown_(false),
@@ -446,6 +677,9 @@ Server::Server(
Server::~Server() {
{
std::unique_lock<std::mutex> lock(mu_);
+ if (callback_cq_ != nullptr) {
+ callback_cq_->Shutdown();
+ }
if (started_ && !shutdown_) {
lock.unlock();
Shutdown();
@@ -518,21 +752,28 @@ bool Server::RegisterService(const grpc::string* host, Service* service) {
}
internal::RpcServiceMethod* method = it->get();
- void* tag = grpc_server_register_method(
+ void* method_registration_tag = grpc_server_register_method(
server_, method->name(), host ? host->c_str() : nullptr,
PayloadHandlingForMethod(method), 0);
- if (tag == nullptr) {
+ if (method_registration_tag == nullptr) {
gpr_log(GPR_DEBUG, "Attempt to register %s multiple times",
method->name());
return false;
}
- if (method->handler() == nullptr) { // Async method
- method->set_server_tag(tag);
- } else {
+ if (method->handler() == nullptr) { // Async method without handler
+ method->set_server_tag(method_registration_tag);
+ } else if (method->api_type() ==
+ internal::RpcServiceMethod::ApiType::SYNC) {
for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
- (*it)->AddSyncMethod(method, tag);
+ (*it)->AddSyncMethod(method, method_registration_tag);
}
+ } else {
+ // a callback method
+ auto* req = new CallbackRequest(this, method, method_registration_tag);
+ callback_reqs_.emplace_back(req);
+ // Enqueue it so that it will be Request'ed later once
+ // all request matchers are created at core server startup
}
method_name = method->name();
@@ -583,7 +824,8 @@ void Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) {
// performance. This ensures that we don't introduce thread hops
// for application requests that wind up on this CQ, which is polled
// in its own thread.
- health_check_cq = new ServerCompletionQueue(GRPC_CQ_NON_POLLING);
+ health_check_cq =
+ new ServerCompletionQueue(GRPC_CQ_NEXT, GRPC_CQ_NON_POLLING, nullptr);
grpc_server_register_completion_queue(server_, health_check_cq->cq(),
nullptr);
default_health_check_service_impl =
@@ -620,6 +862,10 @@ void Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) {
(*it)->Start();
}
+ for (auto& cbreq : callback_reqs_) {
+ cbreq->Request();
+ }
+
if (default_health_check_service_impl != nullptr) {
default_health_check_service_impl->StartServingThread();
}
@@ -681,31 +927,27 @@ void Server::Wait() {
void Server::PerformOpsOnCall(internal::CallOpSetInterface* ops,
internal::Call* call) {
- static const size_t MAX_OPS = 8;
- size_t nops = 0;
- grpc_op cops[MAX_OPS];
- ops->FillOps(call->call(), cops, &nops);
- auto result =
- grpc_call_start_batch(call->call(), cops, nops, ops->cq_tag(), nullptr);
- if (result != GRPC_CALL_OK) {
- gpr_log(GPR_ERROR, "Fatal: grpc_call_start_batch returned %d", result);
- grpc_call_log_batch(__FILE__, __LINE__, GPR_LOG_SEVERITY_ERROR,
- call->call(), cops, nops, ops);
- abort();
- }
+ ops->FillOps(call);
}
ServerInterface::BaseAsyncRequest::BaseAsyncRequest(
ServerInterface* server, ServerContext* context,
internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
- void* tag, bool delete_on_finalize)
+ ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize)
: server_(server),
context_(context),
stream_(stream),
call_cq_(call_cq),
+ notification_cq_(notification_cq),
tag_(tag),
delete_on_finalize_(delete_on_finalize),
- call_(nullptr) {
+ call_(nullptr),
+ done_intercepting_(false) {
+ /* Set up interception state partially for the receive ops. call_wrapper_ is
+ * not filled at this point, but it will be filled before the interceptors are
+ * run. */
+ interceptor_methods_.SetCall(&call_wrapper_);
+ interceptor_methods_.SetReverse();
call_cq_->RegisterAvalanching(); // This op will trigger more ops
}
@@ -715,15 +957,45 @@ ServerInterface::BaseAsyncRequest::~BaseAsyncRequest() {
bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
bool* status) {
+ if (done_intercepting_) {
+ *tag = tag_;
+ if (delete_on_finalize_) {
+ delete this;
+ }
+ return true;
+ }
context_->set_call(call_);
context_->cq_ = call_cq_;
- internal::Call call(call_, server_, call_cq_,
- server_->max_receive_message_size());
- if (*status && call_) {
- context_->BeginCompletionOp(&call);
+ if (call_wrapper_.call() == nullptr) {
+ // Fill it since it is empty.
+ call_wrapper_ = internal::Call(
+ call_, server_, call_cq_, server_->max_receive_message_size(), nullptr);
}
+
// just the pointers inside call are copied here
- stream_->BindCall(&call);
+ stream_->BindCall(&call_wrapper_);
+
+ if (*status && call_ && call_wrapper_.server_rpc_info()) {
+ done_intercepting_ = true;
+ // Set interception point for RECV INITIAL METADATA
+ interceptor_methods_.AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
+ interceptor_methods_.SetRecvInitialMetadata(&context_->client_metadata_);
+ auto f = std::bind(&ServerInterface::BaseAsyncRequest::
+ ContinueFinalizeResultAfterInterception,
+ this);
+ if (interceptor_methods_.RunInterceptors(f)) {
+ // There are no interceptors to run. Continue
+ } else {
+ // There were interceptors to be run, so
+ // ContinueFinalizeResultAfterInterception will be run when interceptors
+ // are done.
+ return false;
+ }
+ }
+ if (*status && call_) {
+ context_->BeginCompletionOp(&call_wrapper_, false);
+ }
*tag = tag_;
if (delete_on_finalize_) {
delete this;
@@ -731,11 +1003,25 @@ bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
return true;
}
+void ServerInterface::BaseAsyncRequest::
+ ContinueFinalizeResultAfterInterception() {
+ context_->BeginCompletionOp(&call_wrapper_, false);
+ // Queue a tag which will be returned immediately
+ grpc_core::ExecCtx exec_ctx;
+ grpc_cq_begin_op(notification_cq_->cq(), this);
+ grpc_cq_end_op(
+ notification_cq_->cq(), this, GRPC_ERROR_NONE,
+ [](void* arg, grpc_cq_completion* completion) { delete completion; },
+ nullptr, new grpc_cq_completion());
+}
+
ServerInterface::RegisteredAsyncRequest::RegisteredAsyncRequest(
ServerInterface* server, ServerContext* context,
internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
- void* tag)
- : BaseAsyncRequest(server, context, stream, call_cq, tag, true) {}
+ ServerCompletionQueue* notification_cq, void* tag, const char* name)
+ : BaseAsyncRequest(server, context, stream, call_cq, notification_cq, tag,
+ true),
+ name_(name) {}
void ServerInterface::RegisteredAsyncRequest::IssueRequest(
void* registered_method, grpc_byte_buffer** payload,
@@ -751,7 +1037,7 @@ ServerInterface::GenericAsyncRequest::GenericAsyncRequest(
ServerInterface* server, GenericServerContext* context,
internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize)
- : BaseAsyncRequest(server, context, stream, call_cq, tag,
+ : BaseAsyncRequest(server, context, stream, call_cq, notification_cq, tag,
delete_on_finalize) {
grpc_call_details_init(&call_details_);
GPR_ASSERT(notification_cq);
@@ -764,6 +1050,10 @@ ServerInterface::GenericAsyncRequest::GenericAsyncRequest(
bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag,
bool* status) {
+ // If we are done intercepting, there is nothing more for us to do
+ if (done_intercepting_) {
+ return BaseAsyncRequest::FinalizeResult(tag, status);
+ }
// TODO(yangg) remove the copy here.
if (*status) {
static_cast<GenericServerContext*>(context_)->method_ =
@@ -774,16 +1064,26 @@ bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag,
}
grpc_slice_unref(call_details_.method);
grpc_slice_unref(call_details_.host);
+ call_wrapper_ = internal::Call(
+ call_, server_, call_cq_, server_->max_receive_message_size(),
+ context_->set_server_rpc_info(
+ static_cast<GenericServerContext*>(context_)->method_.c_str(),
+ *server_->interceptor_creators()));
return BaseAsyncRequest::FinalizeResult(tag, status);
}
bool Server::UnimplementedAsyncRequest::FinalizeResult(void** tag,
bool* status) {
- if (GenericAsyncRequest::FinalizeResult(tag, status) && *status) {
- new UnimplementedAsyncRequest(server_, cq_);
- new UnimplementedAsyncResponse(this);
+ if (GenericAsyncRequest::FinalizeResult(tag, status)) {
+ // We either had no interceptors run or we are done intercepting
+ if (*status) {
+ new UnimplementedAsyncRequest(server_, cq_);
+ new UnimplementedAsyncResponse(this);
+ } else {
+ delete this;
+ }
} else {
- delete this;
+ // The tag was swallowed due to interception. We will see it again.
}
return false;
}
@@ -798,4 +1098,41 @@ Server::UnimplementedAsyncResponse::UnimplementedAsyncResponse(
ServerInitializer* Server::initializer() { return server_initializer_.get(); }
+namespace {
+class ShutdownCallback : public grpc_experimental_completion_queue_functor {
+ public:
+ ShutdownCallback() { functor_run = &ShutdownCallback::Run; }
+ // TakeCQ takes ownership of the cq into the shutdown callback
+ // so that the shutdown callback will be responsible for destroying it
+ void TakeCQ(CompletionQueue* cq) { cq_ = cq; }
+
+ // 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) {
+ auto* callback = static_cast<ShutdownCallback*>(cb);
+ delete callback->cq_;
+ delete callback;
+ }
+
+ private:
+ CompletionQueue* cq_ = nullptr;
+};
+} // namespace
+
+CompletionQueue* Server::CallbackCQ() {
+ // TODO(vjpai): Consider using a single global CQ for the default CQ
+ // if there is no explicit per-server CQ registered
+ std::lock_guard<std::mutex> l(mu_);
+ if (callback_cq_ == nullptr) {
+ auto* shutdown_callback = new ShutdownCallback;
+ callback_cq_ = new CompletionQueue(grpc_completion_queue_attributes{
+ GRPC_CQ_CURRENT_VERSION, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING,
+ shutdown_callback});
+
+ // Transfer ownership of the new cq to its own shutdown callback
+ shutdown_callback->TakeCQ(callback_cq_);
+ }
+ return callback_cq_;
+};
+
} // namespace grpc
diff --git a/src/cpp/server/server_context.cc b/src/cpp/server/server_context.cc
index bd532a968d..355debb3fb 100644
--- a/src/cpp/server/server_context.cc
+++ b/src/cpp/server/server_context.cc
@@ -41,13 +41,29 @@ class ServerContext::CompletionOp final : public internal::CallOpSetInterface {
public:
// initial refs: one in the server context, one in the cq
// must ref the call before calling constructor and after deleting this
- CompletionOp(grpc_call* call)
- : call_(call),
+ CompletionOp(internal::Call* call)
+ : call_(*call),
has_tag_(false),
tag_(nullptr),
+ core_cq_tag_(this),
refs_(2),
finalized_(false),
- cancelled_(0) {}
+ cancelled_(0),
+ done_intercepting_(false) {}
+
+ // CompletionOp isn't copyable or movable
+ CompletionOp(const CompletionOp&) = delete;
+ CompletionOp& operator=(const CompletionOp&) = delete;
+ CompletionOp(CompletionOp&&) = delete;
+ CompletionOp& operator=(CompletionOp&&) = delete;
+
+ ~CompletionOp() {
+ if (call_.server_rpc_info()) {
+ call_.server_rpc_info()->Unref();
+ }
+ }
+
+ void FillOps(internal::Call* call) override;
// This should always be arena allocated in the call, so override delete.
// But this class is not trivially destructible, so must actually call delete
@@ -63,7 +79,6 @@ class ServerContext::CompletionOp final : public internal::CallOpSetInterface {
// there are no tests catching the compiler warning.
static void operator delete(void*, void*) { assert(0); }
- void FillOps(grpc_call* call, grpc_op* ops, size_t* nops) override;
bool FinalizeResult(void** tag, bool* status) override;
bool CheckCancelled(CompletionQueue* cq) {
@@ -77,107 +92,188 @@ class ServerContext::CompletionOp final : public internal::CallOpSetInterface {
tag_ = tag;
}
- /// TODO(vjpai): Allow override of cq_tag if appropriate for callback API
- void* cq_tag() override { return this; }
+ void set_core_cq_tag(void* core_cq_tag) { core_cq_tag_ = core_cq_tag; }
+
+ void* core_cq_tag() override { return core_cq_tag_; }
void Unref();
+ // This will be called while interceptors are run if the RPC is a hijacked
+ // RPC. This should set hijacking state for each of the ops.
+ void SetHijackingState() override {
+ /* Servers don't allow hijacking */
+ GPR_CODEGEN_ASSERT(false);
+ }
+
+ /* Should be called after interceptors are done running */
+ void ContinueFillOpsAfterInterception() override {}
+
+ /* Should be called after interceptors are done running on the finalize result
+ * path */
+ void ContinueFinalizeResultAfterInterception() override {
+ done_intercepting_ = true;
+ if (!has_tag_) {
+ /* We don't have a tag to return. */
+ std::unique_lock<std::mutex> lock(mu_);
+ if (--refs_ == 0) {
+ lock.unlock();
+ grpc_call* call = call_.call();
+ delete this;
+ grpc_call_unref(call);
+ }
+ return;
+ }
+ /* Start a dummy op so that we can return the tag */
+ GPR_CODEGEN_ASSERT(GRPC_CALL_OK ==
+ g_core_codegen_interface->grpc_call_start_batch(
+ call_.call(), nullptr, 0, this, nullptr));
+ }
+
private:
bool CheckCancelledNoPluck() {
std::lock_guard<std::mutex> g(mu_);
return finalized_ ? (cancelled_ != 0) : false;
}
- grpc_call* call_;
+ internal::Call call_;
bool has_tag_;
void* tag_;
+ void* core_cq_tag_;
std::mutex mu_;
int refs_;
bool finalized_;
int cancelled_;
+ bool done_intercepting_;
+ internal::InterceptorBatchMethodsImpl interceptor_methods_;
};
void ServerContext::CompletionOp::Unref() {
std::unique_lock<std::mutex> lock(mu_);
if (--refs_ == 0) {
lock.unlock();
- // Save aside the call pointer before deleting for later unref
- grpc_call* call = call_;
+ grpc_call* call = call_.call();
delete this;
grpc_call_unref(call);
}
}
-void ServerContext::CompletionOp::FillOps(grpc_call* call, grpc_op* ops,
- size_t* nops) {
- ops->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
- ops->data.recv_close_on_server.cancelled = &cancelled_;
- ops->flags = 0;
- ops->reserved = nullptr;
- *nops = 1;
+void ServerContext::CompletionOp::FillOps(internal::Call* call) {
+ grpc_op ops;
+ ops.op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+ ops.data.recv_close_on_server.cancelled = &cancelled_;
+ ops.flags = 0;
+ ops.reserved = nullptr;
+ interceptor_methods_.SetCall(&call_);
+ interceptor_methods_.SetReverse();
+ interceptor_methods_.SetCallOpSetInterface(this);
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call->call(), &ops, 1,
+ core_cq_tag_, nullptr));
+ /* No interceptors to run here */
}
bool ServerContext::CompletionOp::FinalizeResult(void** tag, bool* status) {
- std::unique_lock<std::mutex> lock(mu_);
- finalized_ = true;
bool ret = false;
- if (has_tag_) {
- *tag = tag_;
- ret = true;
+ std::unique_lock<std::mutex> lock(mu_);
+ if (done_intercepting_) {
+ /* We are done intercepting. */
+ if (has_tag_) {
+ *tag = tag_;
+ ret = true;
+ }
+ if (--refs_ == 0) {
+ lock.unlock();
+ grpc_call* call = call_.call();
+ delete this;
+ grpc_call_unref(call);
+ }
+ return ret;
}
+ finalized_ = true;
+
if (!*status) cancelled_ = 1;
- if (--refs_ == 0) {
- lock.unlock();
- // Save aside the call pointer before deleting for later unref
- grpc_call* call = call_;
- delete this;
- grpc_call_unref(call);
+ /* Release the lock since we are going to be running through interceptors now
+ */
+ lock.unlock();
+ /* Add interception point and run through interceptors */
+ interceptor_methods_.AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_CLOSE);
+ if (interceptor_methods_.RunInterceptors()) {
+ /* No interceptors were run */
+ if (has_tag_) {
+ *tag = tag_;
+ ret = true;
+ }
+ lock.lock();
+ if (--refs_ == 0) {
+ lock.unlock();
+ grpc_call* call = call_.call();
+ delete this;
+ grpc_call_unref(call);
+ }
+ return ret;
}
- return ret;
+ /* There are interceptors to be run. Return false for now */
+ return false;
}
// ServerContext body
-ServerContext::ServerContext()
- : completion_op_(nullptr),
- has_notify_when_done_tag_(false),
- async_notify_when_done_tag_(nullptr),
- deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)),
- call_(nullptr),
- cq_(nullptr),
- sent_initial_metadata_(false),
- compression_level_set_(false),
- has_pending_ops_(false) {}
-
-ServerContext::ServerContext(gpr_timespec deadline, grpc_metadata_array* arr)
- : completion_op_(nullptr),
- has_notify_when_done_tag_(false),
- async_notify_when_done_tag_(nullptr),
- deadline_(deadline),
- call_(nullptr),
- cq_(nullptr),
- sent_initial_metadata_(false),
- compression_level_set_(false),
- has_pending_ops_(false) {
+ServerContext::ServerContext() { Setup(gpr_inf_future(GPR_CLOCK_REALTIME)); }
+
+ServerContext::ServerContext(gpr_timespec deadline, grpc_metadata_array* arr) {
+ Setup(deadline);
+ std::swap(*client_metadata_.arr(), *arr);
+}
+
+void ServerContext::Setup(gpr_timespec deadline) {
+ completion_op_ = nullptr;
+ has_notify_when_done_tag_ = false;
+ async_notify_when_done_tag_ = nullptr;
+ deadline_ = deadline;
+ call_ = nullptr;
+ cq_ = nullptr;
+ sent_initial_metadata_ = false;
+ compression_level_set_ = false;
+ has_pending_ops_ = false;
+ rpc_info_ = nullptr;
+}
+
+void ServerContext::BindDeadlineAndMetadata(gpr_timespec deadline,
+ grpc_metadata_array* arr) {
+ deadline_ = deadline;
std::swap(*client_metadata_.arr(), *arr);
}
-ServerContext::~ServerContext() {
+ServerContext::~ServerContext() { Clear(); }
+
+void ServerContext::Clear() {
if (call_) {
grpc_call_unref(call_);
}
if (completion_op_) {
completion_op_->Unref();
}
+ if (rpc_info_) {
+ rpc_info_->Unref();
+ }
+ // Don't need to clear out call_, completion_op_, or rpc_info_ because this is
+ // either called from destructor or just before Setup
}
-void ServerContext::BeginCompletionOp(internal::Call* call) {
+void ServerContext::BeginCompletionOp(internal::Call* call, bool callback) {
GPR_ASSERT(!completion_op_);
+ if (rpc_info_) {
+ rpc_info_->Ref();
+ }
grpc_call_ref(call->call());
completion_op_ =
new (grpc_call_arena_alloc(call->call(), sizeof(CompletionOp)))
- CompletionOp(call->call());
- if (has_notify_when_done_tag_) {
+ CompletionOp(call);
+ if (callback) {
+ completion_tag_ =
+ internal::CallbackWithSuccessTag(call->call(), nullptr, completion_op_);
+ completion_op_->set_core_cq_tag(&completion_tag_);
+ } else if (has_notify_when_done_tag_) {
completion_op_->set_tag(async_notify_when_done_tag_);
}
call->PerformOps(completion_op_);
@@ -206,12 +302,15 @@ void ServerContext::TryCancel() const {
}
bool ServerContext::IsCancelled() const {
- if (has_notify_when_done_tag_) {
- // when using async API, but the result is only valid
+ if (completion_tag_) {
+ // When using callback API, this result is always valid.
+ return 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
return completion_op_ && completion_op_->CheckCancelledAsync();
} else {
- // when using sync API
+ // when using sync API, the result is always valid
return completion_op_ && completion_op_->CheckCancelled(cq_);
}
}
diff --git a/src/proto/grpc/channelz/channelz.proto b/src/proto/grpc/channelz/channelz.proto
index d930dfcfb4..6202e5e817 100644
--- a/src/proto/grpc/channelz/channelz.proto
+++ b/src/proto/grpc/channelz/channelz.proto
@@ -424,6 +424,8 @@ service Channelz {
rpc GetTopChannels(GetTopChannelsRequest) returns (GetTopChannelsResponse);
// Gets all servers that exist in the process.
rpc GetServers(GetServersRequest) returns (GetServersResponse);
+ // Returns a single Server, or else a NOT_FOUND code.
+ rpc GetServer(GetServerRequest) returns (GetServerResponse);
// Gets all server sockets that exist in the process.
rpc GetServerSockets(GetServerSocketsRequest) returns (GetServerSocketsResponse);
// Returns a single Channel, or else a NOT_FOUND code.
@@ -466,6 +468,17 @@ message GetServersResponse {
bool end = 2;
}
+message GetServerRequest {
+ // server_id is the identifier of the specific server to get.
+ int64 server_id = 1;
+}
+
+message GetServerResponse {
+ // The Server that corresponds to the requested server_id. This field
+ // should be set.
+ Server server = 1;
+}
+
message GetServerSocketsRequest {
int64 server_id = 1;
// start_socket_id indicates that only sockets at or above this id should be
diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py
index 0a3097111f..3cb0eb179e 100644
--- a/src/python/grpcio/commands.py
+++ b/src/python/grpcio/commands.py
@@ -274,8 +274,14 @@ class BuildExt(build_ext.build_ext):
extra_defines = [
'EXTRA_DEFINES="GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1"'
]
+ # Ensure the BoringSSL are built instead of using system provided
+ # libraries. It prevents dependency issues while distributing to
+ # Mac users who use MacPorts to manage their libraries. #17002
+ mod_env = dict(os.environ)
+ mod_env['REQUIRE_CUSTOM_LIBRARIES_opt'] = '1'
make_process = subprocess.Popen(
['make'] + extra_defines + targets,
+ env=mod_env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
make_out, make_err = make_process.communicate()
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index 0e192b6201..18245e9107 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -98,6 +98,7 @@ grpc_resource_quota_set_max_threads_type grpc_resource_quota_set_max_threads_imp
grpc_resource_quota_arg_vtable_type grpc_resource_quota_arg_vtable_import;
grpc_channelz_get_top_channels_type grpc_channelz_get_top_channels_import;
grpc_channelz_get_servers_type grpc_channelz_get_servers_import;
+grpc_channelz_get_server_type grpc_channelz_get_server_import;
grpc_channelz_get_server_sockets_type grpc_channelz_get_server_sockets_import;
grpc_channelz_get_channel_type grpc_channelz_get_channel_import;
grpc_channelz_get_subchannel_type grpc_channelz_get_subchannel_import;
@@ -355,6 +356,7 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_resource_quota_arg_vtable_import = (grpc_resource_quota_arg_vtable_type) GetProcAddress(library, "grpc_resource_quota_arg_vtable");
grpc_channelz_get_top_channels_import = (grpc_channelz_get_top_channels_type) GetProcAddress(library, "grpc_channelz_get_top_channels");
grpc_channelz_get_servers_import = (grpc_channelz_get_servers_type) GetProcAddress(library, "grpc_channelz_get_servers");
+ grpc_channelz_get_server_import = (grpc_channelz_get_server_type) GetProcAddress(library, "grpc_channelz_get_server");
grpc_channelz_get_server_sockets_import = (grpc_channelz_get_server_sockets_type) GetProcAddress(library, "grpc_channelz_get_server_sockets");
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");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index e075db89e8..56d222c7ec 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -269,6 +269,9 @@ extern grpc_channelz_get_top_channels_type grpc_channelz_get_top_channels_import
typedef char*(*grpc_channelz_get_servers_type)(intptr_t start_server_id);
extern grpc_channelz_get_servers_type grpc_channelz_get_servers_import;
#define grpc_channelz_get_servers grpc_channelz_get_servers_import
+typedef char*(*grpc_channelz_get_server_type)(intptr_t server_id);
+extern grpc_channelz_get_server_type grpc_channelz_get_server_import;
+#define grpc_channelz_get_server grpc_channelz_get_server_import
typedef char*(*grpc_channelz_get_server_sockets_type)(intptr_t server_id, intptr_t start_socket_id);
extern grpc_channelz_get_server_sockets_type grpc_channelz_get_server_sockets_import;
#define grpc_channelz_get_server_sockets grpc_channelz_get_server_sockets_import
diff --git a/templates/gRPC-C++.podspec.template b/templates/gRPC-C++.podspec.template
index cff252b3a9..ed69a1ed4c 100644
--- a/templates/gRPC-C++.podspec.template
+++ b/templates/gRPC-C++.podspec.template
@@ -177,6 +177,9 @@
s.default_subspecs = 'Interface', 'Implementation'
+ # Certificates, to be able to establish TLS connections:
+ s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] }
+
s.header_mappings_dir = 'include/grpcpp'
s.subspec 'Interface' do |ss|
diff --git a/templates/tools/dockerfile/bazel.include b/templates/tools/dockerfile/bazel.include
index ee4f9556af..c7714f5663 100644
--- a/templates/tools/dockerfile/bazel.include
+++ b/templates/tools/dockerfile/bazel.include
@@ -1,5 +1,6 @@
#========================
# Bazel installation
-RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" > /etc/apt/sources.list.d/bazel.list
-RUN curl https://bazel.build/bazel-release.pub.gpg | apt-key add -
-RUN apt-get -y update && apt-get -y install bazel=0.15.0 && apt-get clean
+
+RUN apt-get update && apt-get install -y wget && apt-get clean
+RUN wget -q https://github.com/bazelbuild/bazel/releases/download/0.17.1/bazel-0.17.1-linux-x86_64 -O /usr/local/bin/bazel
+RUN chmod 755 /usr/local/bin/bazel
diff --git a/test/core/channel/channelz_test.cc b/test/core/channel/channelz_test.cc
index 5f27320512..4d4b077002 100644
--- a/test/core/channel/channelz_test.cc
+++ b/test/core/channel/channelz_test.cc
@@ -224,6 +224,11 @@ void ValidateServer(ServerNode* server, validate_channel_data_args args) {
grpc::testing::ValidateServerProtoJsonTranslation(json_str);
ValidateCounters(json_str, args);
gpr_free(json_str);
+ // also check that the core API formats this the correct way
+ char* core_api_json_str = grpc_channelz_get_server(server->uuid());
+ grpc::testing::ValidateGetServerResponseProtoJsonTranslation(
+ core_api_json_str);
+ gpr_free(core_api_json_str);
}
grpc_millis GetLastCallStartedMillis(CallCountingHelper* channel) {
diff --git a/test/core/iomgr/tcp_client_uv_test.cc b/test/core/iomgr/tcp_client_uv_test.cc
index ffcc7937c7..27f894e3f3 100644
--- a/test/core/iomgr/tcp_client_uv_test.cc
+++ b/test/core/iomgr/tcp_client_uv_test.cc
@@ -129,6 +129,7 @@ void test_succeeds(void) {
uv_close((uv_handle_t*)svr_handle, close_cb);
gpr_mu_unlock(g_mu);
+ grpc_core::ExecCtx::Get()->Flush();
}
void test_fails(void) {
@@ -178,6 +179,7 @@ void test_fails(void) {
}
gpr_mu_unlock(g_mu);
+ grpc_core::ExecCtx::Get()->Flush();
}
static void destroy_pollset(void* p, grpc_error* error) {
@@ -186,21 +188,22 @@ static void destroy_pollset(void* p, grpc_error* error) {
int main(int argc, char** argv) {
grpc_closure destroyed;
- grpc_core::ExecCtx exec_ctx;
grpc_test_init(argc, argv);
grpc_init();
- g_pollset = static_cast<grpc_pollset*>(gpr_malloc(grpc_pollset_size()));
- grpc_pollset_init(g_pollset, &g_mu);
-
- test_succeeds();
- gpr_log(GPR_ERROR, "End of first test");
- test_fails();
- GRPC_CLOSURE_INIT(&destroyed, destroy_pollset, g_pollset,
- grpc_schedule_on_exec_ctx);
- grpc_pollset_shutdown(g_pollset, &destroyed);
-
+ {
+ grpc_core::ExecCtx exec_ctx;
+ g_pollset = static_cast<grpc_pollset*>(gpr_malloc(grpc_pollset_size()));
+ grpc_pollset_init(g_pollset, &g_mu);
+
+ test_succeeds();
+ gpr_log(GPR_ERROR, "End of first test");
+ test_fails();
+ GRPC_CLOSURE_INIT(&destroyed, destroy_pollset, g_pollset,
+ grpc_schedule_on_exec_ctx);
+ grpc_pollset_shutdown(g_pollset, &destroyed);
+ gpr_free(g_pollset);
+ }
grpc_shutdown();
- gpr_free(g_pollset);
return 0;
}
diff --git a/test/core/iomgr/tcp_server_uv_test.cc b/test/core/iomgr/tcp_server_uv_test.cc
index 35d62b51b7..e99fa79bfd 100644
--- a/test/core/iomgr/tcp_server_uv_test.cc
+++ b/test/core/iomgr/tcp_server_uv_test.cc
@@ -119,6 +119,7 @@ static void test_no_op(void) {
grpc_tcp_server* s;
GPR_ASSERT(GRPC_ERROR_NONE == grpc_tcp_server_create(NULL, NULL, &s));
grpc_tcp_server_unref(s);
+ grpc_core::ExecCtx::Get()->Flush();
}
static void test_no_op_with_start(void) {
@@ -128,6 +129,7 @@ static void test_no_op_with_start(void) {
LOG_TEST("test_no_op_with_start");
grpc_tcp_server_start(s, NULL, 0, on_connect, NULL);
grpc_tcp_server_unref(s);
+ grpc_core::ExecCtx::Get()->Flush();
}
static void test_no_op_with_port(void) {
@@ -147,6 +149,7 @@ static void test_no_op_with_port(void) {
port > 0);
grpc_tcp_server_unref(s);
+ grpc_core::ExecCtx::Get()->Flush();
}
static void test_no_op_with_port_and_start(void) {
@@ -168,6 +171,7 @@ static void test_no_op_with_port_and_start(void) {
grpc_tcp_server_start(s, NULL, 0, on_connect, NULL);
grpc_tcp_server_unref(s);
+ grpc_core::ExecCtx::Get()->Flush();
}
static void connect_cb(uv_connect_t* req, int status) {
@@ -273,7 +277,7 @@ static void test_connect(unsigned n) {
GPR_ASSERT(weak_ref.server != NULL);
grpc_tcp_server_unref(s);
-
+ grpc_core::ExecCtx::Get()->Flush();
/* Weak ref lost. */
GPR_ASSERT(weak_ref.server == NULL);
}
@@ -284,25 +288,27 @@ static void destroy_pollset(void* p, grpc_error* error) {
int main(int argc, char** argv) {
grpc_closure destroyed;
- grpc_core::ExecCtx exec_ctx;
grpc_test_init(argc, argv);
grpc_init();
- g_pollset = static_cast<grpc_pollset*>(gpr_malloc(grpc_pollset_size()));
- grpc_pollset_init(g_pollset, &g_mu);
-
- test_no_op();
- test_no_op_with_start();
- test_no_op_with_port();
- test_no_op_with_port_and_start();
- test_connect(1);
- test_connect(10);
-
- GRPC_CLOSURE_INIT(&destroyed, destroy_pollset, g_pollset,
- grpc_schedule_on_exec_ctx);
- grpc_pollset_shutdown(g_pollset, &destroyed);
-
+ {
+ grpc_core::ExecCtx exec_ctx;
+ g_pollset = static_cast<grpc_pollset*>(gpr_malloc(grpc_pollset_size()));
+ grpc_pollset_init(g_pollset, &g_mu);
+
+ test_no_op();
+ test_no_op_with_start();
+ test_no_op_with_port();
+ test_no_op_with_port_and_start();
+ test_connect(1);
+ test_connect(10);
+
+ GRPC_CLOSURE_INIT(&destroyed, destroy_pollset, g_pollset,
+ grpc_schedule_on_exec_ctx);
+ grpc_pollset_shutdown(g_pollset, &destroyed);
+
+ gpr_free(g_pollset);
+ }
grpc_shutdown();
- gpr_free(g_pollset);
return 0;
}
diff --git a/test/core/memory_usage/BUILD b/test/core/memory_usage/BUILD
new file mode 100644
index 0000000000..f39c309e36
--- /dev/null
+++ b/test/core/memory_usage/BUILD
@@ -0,0 +1,60 @@
+# 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_library", "grpc_cc_test", "grpc_package")
+
+grpc_package(name = "test/core/memory_usage")
+
+licenses(["notice"]) # Apache v2
+
+grpc_cc_library(
+ name = "client",
+ testonly = 1,
+ srcs = ["client.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ ],
+)
+
+grpc_cc_library(
+ name = "server",
+ testonly = 1,
+ srcs = ["server.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/core/end2end:ssl_test_data"
+ ],
+)
+
+grpc_cc_test(
+ name = "memory_usage_test",
+ srcs = ["memory_usage_test.cc"],
+ language = "C++",
+ data = [
+ ":client",
+ ":server",
+ ],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ ],
+)
diff --git a/test/core/memory_usage/server.cc b/test/core/memory_usage/server.cc
index 3e7bb7e11f..6f8a9bc0d4 100644
--- a/test/core/memory_usage/server.cc
+++ b/test/core/memory_usage/server.cc
@@ -292,9 +292,9 @@ int main(int argc, char** argv) {
send_status(&calls[k]);
}
}
+ /* fallthrough */
// no break here since we want to continue to case
// FLING_SERVER_SEND_STATUS_SNAPSHOT to destroy the snapshot call
- /* fallthrough */
case FLING_SERVER_SEND_STATUS_SNAPSHOT:
grpc_byte_buffer_destroy(payload_buffer);
grpc_byte_buffer_destroy(terminal_buffer);
diff --git a/test/core/security/credentials_test.cc b/test/core/security/credentials_test.cc
index 97156761bd..56c4c9c0ae 100644
--- a/test/core/security/credentials_test.cc
+++ b/test/core/security/credentials_test.cc
@@ -43,6 +43,7 @@
#include "src/core/lib/security/transport/auth_filters.h"
#include "test/core/util/test_config.h"
+using grpc_core::internal::grpc_flush_cached_google_default_credentials;
using grpc_core::internal::set_gce_tenancy_checker_for_testing;
/* -- Mock channel credentials. -- */
@@ -954,17 +955,9 @@ static void test_google_default_creds_gce(void) {
run_request_metadata_test(creds->call_creds, auth_md_ctx, state);
grpc_core::ExecCtx::Get()->Flush();
- /* Check that we get a cached creds if we call
- grpc_google_default_credentials_create again.
- GCE detection should not occur anymore either. */
- g_test_gce_tenancy_checker_called = false;
- grpc_channel_credentials* cached_creds =
- grpc_google_default_credentials_create();
- GPR_ASSERT(cached_creds == &creds->base);
- GPR_ASSERT(g_test_gce_tenancy_checker_called == false);
+ GPR_ASSERT(g_test_gce_tenancy_checker_called == true);
/* Cleanup. */
- grpc_channel_credentials_unref(cached_creds);
grpc_channel_credentials_unref(&creds->base);
grpc_httpcli_set_override(nullptr, nullptr);
grpc_override_well_known_credentials_path_getter(nullptr);
@@ -983,7 +976,7 @@ static void test_no_google_default_creds(void) {
/* Simulate a successful detection of GCE. */
GPR_ASSERT(grpc_google_default_credentials_create() == nullptr);
- /* Try a cached one. GCE detection should not occur anymore. */
+ /* Try a second one. GCE detection should not occur anymore. */
g_test_gce_tenancy_checker_called = false;
GPR_ASSERT(grpc_google_default_credentials_create() == nullptr);
GPR_ASSERT(g_test_gce_tenancy_checker_called == false);
diff --git a/test/core/surface/public_headers_must_be_c89.c b/test/core/surface/public_headers_must_be_c89.c
index 4640b8e4ca..426ef1e8b1 100644
--- a/test/core/surface/public_headers_must_be_c89.c
+++ b/test/core/surface/public_headers_must_be_c89.c
@@ -137,6 +137,7 @@ int main(int argc, char **argv) {
printf("%lx", (unsigned long) grpc_resource_quota_arg_vtable);
printf("%lx", (unsigned long) grpc_channelz_get_top_channels);
printf("%lx", (unsigned long) grpc_channelz_get_servers);
+ printf("%lx", (unsigned long) grpc_channelz_get_server);
printf("%lx", (unsigned long) grpc_channelz_get_server_sockets);
printf("%lx", (unsigned long) grpc_channelz_get_channel);
printf("%lx", (unsigned long) grpc_channelz_get_subchannel);
diff --git a/test/core/util/ubsan_suppressions.txt b/test/core/util/ubsan_suppressions.txt
index 2268adc169..63898ea3b1 100644
--- a/test/core/util/ubsan_suppressions.txt
+++ b/test/core/util/ubsan_suppressions.txt
@@ -15,3 +15,17 @@ enum:message_compress_test
enum:transport_security_test
enum:algorithm_test
alignment:transport_security_test
+# TODO(jtattermusch): address issues and remove the supressions
+nonnull-attribute:gsec_aes_gcm_aead_crypter_decrypt_iovec
+nonnull-attribute:gsec_test_random_encrypt_decrypt
+nonnull-attribute:gsec_test_multiple_random_encrypt_decrypt
+nonnull-attribute:gsec_test_copy
+nonnull-attribute:gsec_test_encrypt_decrypt_test_vector
+alignment:absl::little_endian::Store64
+alignment:absl::little_endian::Load64
+float-divide-by-zero:grpc::testing::postprocess_scenario_result
+enum:grpc_op_string
+nonnull-attribute:grpc_lb_addresses_copy
+signed-integer-overflow:chrono
+enum:grpc_http2_error_to_grpc_status
+enum:grpc_chttp2_cancel_stream \ No newline at end of file
diff --git a/test/cpp/codegen/compiler_test_golden b/test/cpp/codegen/compiler_test_golden
index 93e1e68654..fdc67969d9 100644
--- a/test/cpp/codegen/compiler_test_golden
+++ b/test/cpp/codegen/compiler_test_golden
@@ -33,6 +33,7 @@
#include <grpcpp/impl/codegen/method_handler_impl.h>
#include <grpcpp/impl/codegen/proto_utils.h>
#include <grpcpp/impl/codegen/rpc_method.h>
+#include <grpcpp/impl/codegen/server_callback.h>
#include <grpcpp/impl/codegen/service_type.h>
#include <grpcpp/impl/codegen/status.h>
#include <grpcpp/impl/codegen/stub_options.h>
@@ -308,6 +309,80 @@ class ServiceA final {
};
typedef WithAsyncMethod_MethodA1<WithAsyncMethod_MethodA2<WithAsyncMethod_MethodA3<WithAsyncMethod_MethodA4<Service > > > > AsyncService;
template <class BaseClass>
+ class ExperimentalWithCallbackMethod_MethodA1 : public BaseClass {
+ private:
+ void BaseClassMustBeDerivedFromService(const Service *service) {}
+ public:
+ ExperimentalWithCallbackMethod_MethodA1() {
+ ::grpc::Service::experimental().MarkMethodCallback(0,
+ new ::grpc::internal::CallbackUnaryHandler< ExperimentalWithCallbackMethod_MethodA1<BaseClass>, ::grpc::testing::Request, ::grpc::testing::Response>(
+ [this](::grpc::ServerContext* context,
+ const ::grpc::testing::Request* request,
+ ::grpc::testing::Response* response,
+ ::grpc::experimental::ServerCallbackRpcController* controller) {
+ this->MethodA1(context, request, response, controller);
+ }, this));
+ }
+ ~ExperimentalWithCallbackMethod_MethodA1() override {
+ BaseClassMustBeDerivedFromService(this);
+ }
+ // disable synchronous version of this method
+ ::grpc::Status MethodA1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) override {
+ abort();
+ return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+ }
+ virtual void MethodA1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); }
+ };
+ template <class BaseClass>
+ class ExperimentalWithCallbackMethod_MethodA2 : public BaseClass {
+ private:
+ void BaseClassMustBeDerivedFromService(const Service *service) {}
+ public:
+ ExperimentalWithCallbackMethod_MethodA2() {
+ }
+ ~ExperimentalWithCallbackMethod_MethodA2() override {
+ BaseClassMustBeDerivedFromService(this);
+ }
+ // disable synchronous version of this method
+ ::grpc::Status MethodA2(::grpc::ServerContext* context, ::grpc::ServerReader< ::grpc::testing::Request>* reader, ::grpc::testing::Response* response) override {
+ abort();
+ return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+ }
+ };
+ template <class BaseClass>
+ class ExperimentalWithCallbackMethod_MethodA3 : public BaseClass {
+ private:
+ void BaseClassMustBeDerivedFromService(const Service *service) {}
+ public:
+ ExperimentalWithCallbackMethod_MethodA3() {
+ }
+ ~ExperimentalWithCallbackMethod_MethodA3() override {
+ BaseClassMustBeDerivedFromService(this);
+ }
+ // disable synchronous version of this method
+ ::grpc::Status MethodA3(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::ServerWriter< ::grpc::testing::Response>* writer) override {
+ abort();
+ return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+ }
+ };
+ template <class BaseClass>
+ class ExperimentalWithCallbackMethod_MethodA4 : public BaseClass {
+ private:
+ void BaseClassMustBeDerivedFromService(const Service *service) {}
+ public:
+ ExperimentalWithCallbackMethod_MethodA4() {
+ }
+ ~ExperimentalWithCallbackMethod_MethodA4() override {
+ BaseClassMustBeDerivedFromService(this);
+ }
+ // disable synchronous version of this method
+ ::grpc::Status MethodA4(::grpc::ServerContext* context, ::grpc::ServerReaderWriter< ::grpc::testing::Response, ::grpc::testing::Request>* stream) override {
+ abort();
+ return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+ }
+ };
+ typedef ExperimentalWithCallbackMethod_MethodA1<ExperimentalWithCallbackMethod_MethodA2<ExperimentalWithCallbackMethod_MethodA3<ExperimentalWithCallbackMethod_MethodA4<Service > > > > ExperimentalCallbackService;
+ template <class BaseClass>
class WithGenericMethod_MethodA1 : public BaseClass {
private:
void BaseClassMustBeDerivedFromService(const Service *service) {}
@@ -456,6 +531,79 @@ class ServiceA final {
}
};
template <class BaseClass>
+ class ExperimentalWithRawCallbackMethod_MethodA1 : public BaseClass {
+ private:
+ void BaseClassMustBeDerivedFromService(const Service *service) {}
+ public:
+ ExperimentalWithRawCallbackMethod_MethodA1() {
+ ::grpc::Service::experimental().MarkMethodRawCallback(0,
+ new ::grpc::internal::CallbackUnaryHandler< ExperimentalWithRawCallbackMethod_MethodA1<BaseClass>, ::grpc::ByteBuffer, ::grpc::ByteBuffer>(
+ [this](::grpc::ServerContext* context,
+ const ::grpc::ByteBuffer* request,
+ ::grpc::ByteBuffer* response,
+ ::grpc::experimental::ServerCallbackRpcController* controller) {
+ this->MethodA1(context, request, response, controller);
+ }, this));
+ }
+ ~ExperimentalWithRawCallbackMethod_MethodA1() override {
+ BaseClassMustBeDerivedFromService(this);
+ }
+ // disable synchronous version of this method
+ ::grpc::Status MethodA1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) override {
+ abort();
+ return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+ }
+ virtual void MethodA1(::grpc::ServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); }
+ };
+ template <class BaseClass>
+ class ExperimentalWithRawCallbackMethod_MethodA2 : public BaseClass {
+ private:
+ void BaseClassMustBeDerivedFromService(const Service *service) {}
+ public:
+ ExperimentalWithRawCallbackMethod_MethodA2() {
+ }
+ ~ExperimentalWithRawCallbackMethod_MethodA2() override {
+ BaseClassMustBeDerivedFromService(this);
+ }
+ // disable synchronous version of this method
+ ::grpc::Status MethodA2(::grpc::ServerContext* context, ::grpc::ServerReader< ::grpc::testing::Request>* reader, ::grpc::testing::Response* response) override {
+ abort();
+ return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+ }
+ };
+ template <class BaseClass>
+ class ExperimentalWithRawCallbackMethod_MethodA3 : public BaseClass {
+ private:
+ void BaseClassMustBeDerivedFromService(const Service *service) {}
+ public:
+ ExperimentalWithRawCallbackMethod_MethodA3() {
+ }
+ ~ExperimentalWithRawCallbackMethod_MethodA3() override {
+ BaseClassMustBeDerivedFromService(this);
+ }
+ // disable synchronous version of this method
+ ::grpc::Status MethodA3(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::ServerWriter< ::grpc::testing::Response>* writer) override {
+ abort();
+ return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+ }
+ };
+ template <class BaseClass>
+ class ExperimentalWithRawCallbackMethod_MethodA4 : public BaseClass {
+ private:
+ void BaseClassMustBeDerivedFromService(const Service *service) {}
+ public:
+ ExperimentalWithRawCallbackMethod_MethodA4() {
+ }
+ ~ExperimentalWithRawCallbackMethod_MethodA4() override {
+ BaseClassMustBeDerivedFromService(this);
+ }
+ // disable synchronous version of this method
+ ::grpc::Status MethodA4(::grpc::ServerContext* context, ::grpc::ServerReaderWriter< ::grpc::testing::Response, ::grpc::testing::Request>* stream) override {
+ abort();
+ return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+ }
+ };
+ template <class BaseClass>
class WithStreamedUnaryMethod_MethodA1 : public BaseClass {
private:
void BaseClassMustBeDerivedFromService(const Service *service) {}
@@ -591,6 +739,32 @@ class ServiceB final {
};
typedef WithAsyncMethod_MethodB1<Service > AsyncService;
template <class BaseClass>
+ class ExperimentalWithCallbackMethod_MethodB1 : public BaseClass {
+ private:
+ void BaseClassMustBeDerivedFromService(const Service *service) {}
+ public:
+ ExperimentalWithCallbackMethod_MethodB1() {
+ ::grpc::Service::experimental().MarkMethodCallback(0,
+ new ::grpc::internal::CallbackUnaryHandler< ExperimentalWithCallbackMethod_MethodB1<BaseClass>, ::grpc::testing::Request, ::grpc::testing::Response>(
+ [this](::grpc::ServerContext* context,
+ const ::grpc::testing::Request* request,
+ ::grpc::testing::Response* response,
+ ::grpc::experimental::ServerCallbackRpcController* controller) {
+ this->MethodB1(context, request, response, controller);
+ }, this));
+ }
+ ~ExperimentalWithCallbackMethod_MethodB1() override {
+ BaseClassMustBeDerivedFromService(this);
+ }
+ // disable synchronous version of this method
+ ::grpc::Status MethodB1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) override {
+ abort();
+ return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+ }
+ virtual void MethodB1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); }
+ };
+ typedef ExperimentalWithCallbackMethod_MethodB1<Service > ExperimentalCallbackService;
+ template <class BaseClass>
class WithGenericMethod_MethodB1 : public BaseClass {
private:
void BaseClassMustBeDerivedFromService(const Service *service) {}
@@ -628,6 +802,31 @@ class ServiceB final {
}
};
template <class BaseClass>
+ class ExperimentalWithRawCallbackMethod_MethodB1 : public BaseClass {
+ private:
+ void BaseClassMustBeDerivedFromService(const Service *service) {}
+ public:
+ ExperimentalWithRawCallbackMethod_MethodB1() {
+ ::grpc::Service::experimental().MarkMethodRawCallback(0,
+ new ::grpc::internal::CallbackUnaryHandler< ExperimentalWithRawCallbackMethod_MethodB1<BaseClass>, ::grpc::ByteBuffer, ::grpc::ByteBuffer>(
+ [this](::grpc::ServerContext* context,
+ const ::grpc::ByteBuffer* request,
+ ::grpc::ByteBuffer* response,
+ ::grpc::experimental::ServerCallbackRpcController* controller) {
+ this->MethodB1(context, request, response, controller);
+ }, this));
+ }
+ ~ExperimentalWithRawCallbackMethod_MethodB1() override {
+ BaseClassMustBeDerivedFromService(this);
+ }
+ // disable synchronous version of this method
+ ::grpc::Status MethodB1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) override {
+ abort();
+ return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+ }
+ virtual void MethodB1(::grpc::ServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); }
+ };
+ template <class BaseClass>
class WithStreamedUnaryMethod_MethodB1 : public BaseClass {
private:
void BaseClassMustBeDerivedFromService(const Service *service) {}
diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD
index 0415efc1ef..235249e8bf 100644
--- a/test/cpp/end2end/BUILD
+++ b/test/cpp/end2end/BUILD
@@ -35,6 +35,19 @@ grpc_cc_library(
],
)
+grpc_cc_library(
+ name = "interceptors_util",
+ testonly = True,
+ hdrs = ["interceptors_util.h"],
+ external_deps = [
+ "gtest",
+ ],
+ deps = [
+ "//src/proto/grpc/testing:echo_proto",
+ "//test/cpp/util:test_util",
+ ],
+)
+
grpc_cc_test(
name = "async_end2end_test",
srcs = ["async_end2end_test.cc"],
@@ -117,6 +130,26 @@ grpc_cc_test(
],
)
+grpc_cc_test(
+ name = "client_interceptors_end2end_test",
+ srcs = ["client_interceptors_end2end_test.cc"],
+ external_deps = [
+ "gtest",
+ ],
+ deps = [
+ ":interceptors_util",
+ ":test_service_impl",
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
grpc_cc_library(
name = "end2end_test_lib",
testonly = True,
@@ -470,6 +503,26 @@ grpc_cc_binary(
)
grpc_cc_test(
+ name = "server_interceptors_end2end_test",
+ srcs = ["server_interceptors_end2end_test.cc"],
+ external_deps = [
+ "gtest",
+ ],
+ deps = [
+ ":interceptors_util",
+ ":test_service_impl",
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+grpc_cc_test(
name = "server_load_reporting_end2end_test",
srcs = ["server_load_reporting_end2end_test.cc"],
external_deps = [
diff --git a/test/cpp/end2end/channelz_service_test.cc b/test/cpp/end2end/channelz_service_test.cc
index 7a7a716e79..5f25278534 100644
--- a/test/cpp/end2end/channelz_service_test.cc
+++ b/test/cpp/end2end/channelz_service_test.cc
@@ -41,6 +41,8 @@
using grpc::channelz::v1::GetChannelRequest;
using grpc::channelz::v1::GetChannelResponse;
+using grpc::channelz::v1::GetServerRequest;
+using grpc::channelz::v1::GetServerResponse;
using grpc::channelz::v1::GetServerSocketsRequest;
using grpc::channelz::v1::GetServerSocketsResponse;
using grpc::channelz::v1::GetServersRequest;
@@ -460,6 +462,29 @@ TEST_F(ChannelzServerTest, BasicServerTest) {
EXPECT_EQ(response.server_size(), 1);
}
+TEST_F(ChannelzServerTest, BasicGetServerTest) {
+ ResetStubs();
+ ConfigureProxy(1);
+ GetServersRequest get_servers_request;
+ GetServersResponse get_servers_response;
+ get_servers_request.set_start_server_id(0);
+ ClientContext get_servers_context;
+ Status s = channelz_stub_->GetServers(
+ &get_servers_context, get_servers_request, &get_servers_response);
+ EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message();
+ EXPECT_EQ(get_servers_response.server_size(), 1);
+ GetServerRequest get_server_request;
+ GetServerResponse get_server_response;
+ get_server_request.set_server_id(
+ get_servers_response.server(0).ref().server_id());
+ ClientContext get_server_context;
+ s = channelz_stub_->GetServer(&get_server_context, get_server_request,
+ &get_server_response);
+ EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message();
+ EXPECT_EQ(get_servers_response.server(0).ref().server_id(),
+ get_server_response.server().ref().server_id());
+}
+
TEST_F(ChannelzServerTest, ServerCallTest) {
ResetStubs();
ConfigureProxy(1);
diff --git a/test/cpp/end2end/client_callback_end2end_test.cc b/test/cpp/end2end/client_callback_end2end_test.cc
index 62a85641c7..7ffc610ce2 100644
--- a/test/cpp/end2end/client_callback_end2end_test.cc
+++ b/test/cpp/end2end/client_callback_end2end_test.cc
@@ -41,13 +41,38 @@ namespace grpc {
namespace testing {
namespace {
-class ClientCallbackEnd2endTest : public ::testing::Test {
+class TestScenario {
+ public:
+ TestScenario(bool serve_callback) : callback_server(serve_callback) {}
+ void Log() const;
+ bool callback_server;
+};
+
+static std::ostream& operator<<(std::ostream& out,
+ const TestScenario& scenario) {
+ return out << "TestScenario{callback_server="
+ << (scenario.callback_server ? "true" : "false") << "}";
+}
+
+void TestScenario::Log() const {
+ std::ostringstream out;
+ out << *this;
+ gpr_log(GPR_DEBUG, "%s", out.str().c_str());
+}
+
+class ClientCallbackEnd2endTest
+ : public ::testing::TestWithParam<TestScenario> {
protected:
- ClientCallbackEnd2endTest() {}
+ ClientCallbackEnd2endTest() { GetParam().Log(); }
void SetUp() override {
ServerBuilder builder;
- builder.RegisterService(&service_);
+
+ if (!GetParam().callback_server) {
+ builder.RegisterService(&service_);
+ } else {
+ builder.RegisterService(&callback_service_);
+ }
server_ = builder.BuildAndStart();
is_server_started_ = true;
@@ -151,37 +176,38 @@ class ClientCallbackEnd2endTest : public ::testing::Test {
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
std::unique_ptr<grpc::GenericStub> generic_stub_;
TestServiceImpl service_;
+ CallbackTestServiceImpl callback_service_;
std::unique_ptr<Server> server_;
};
-TEST_F(ClientCallbackEnd2endTest, SimpleRpc) {
+TEST_P(ClientCallbackEnd2endTest, SimpleRpc) {
ResetStub();
SendRpcs(1, false);
}
-TEST_F(ClientCallbackEnd2endTest, SequentialRpcs) {
+TEST_P(ClientCallbackEnd2endTest, SequentialRpcs) {
ResetStub();
SendRpcs(10, false);
}
-TEST_F(ClientCallbackEnd2endTest, SequentialRpcsWithVariedBinaryMetadataValue) {
+TEST_P(ClientCallbackEnd2endTest, SequentialRpcsWithVariedBinaryMetadataValue) {
ResetStub();
SendRpcs(10, true);
}
-TEST_F(ClientCallbackEnd2endTest, SequentialGenericRpcs) {
+TEST_P(ClientCallbackEnd2endTest, SequentialGenericRpcs) {
ResetStub();
SendRpcsGeneric(10, false);
}
#if GRPC_ALLOW_EXCEPTIONS
-TEST_F(ClientCallbackEnd2endTest, ExceptingRpc) {
+TEST_P(ClientCallbackEnd2endTest, ExceptingRpc) {
ResetStub();
SendRpcsGeneric(10, true);
}
#endif
-TEST_F(ClientCallbackEnd2endTest, MultipleRpcsWithVariedBinaryMetadataValue) {
+TEST_P(ClientCallbackEnd2endTest, MultipleRpcsWithVariedBinaryMetadataValue) {
ResetStub();
std::vector<std::thread> threads;
threads.reserve(10);
@@ -193,7 +219,7 @@ TEST_F(ClientCallbackEnd2endTest, MultipleRpcsWithVariedBinaryMetadataValue) {
}
}
-TEST_F(ClientCallbackEnd2endTest, MultipleRpcs) {
+TEST_P(ClientCallbackEnd2endTest, MultipleRpcs) {
ResetStub();
std::vector<std::thread> threads;
threads.reserve(10);
@@ -205,7 +231,7 @@ TEST_F(ClientCallbackEnd2endTest, MultipleRpcs) {
}
}
-TEST_F(ClientCallbackEnd2endTest, CancelRpcBeforeStart) {
+TEST_P(ClientCallbackEnd2endTest, CancelRpcBeforeStart) {
ResetStub();
EchoRequest request;
EchoResponse response;
@@ -230,6 +256,11 @@ TEST_F(ClientCallbackEnd2endTest, CancelRpcBeforeStart) {
}
}
+TestScenario scenarios[] = {TestScenario{false}, TestScenario{true}};
+
+INSTANTIATE_TEST_CASE_P(ClientCallbackEnd2endTest, ClientCallbackEnd2endTest,
+ ::testing::ValuesIn(scenarios));
+
} // namespace
} // namespace testing
} // namespace grpc
diff --git a/test/cpp/end2end/client_interceptors_end2end_test.cc b/test/cpp/end2end/client_interceptors_end2end_test.cc
new file mode 100644
index 0000000000..e8ffd46344
--- /dev/null
+++ b/test/cpp/end2end/client_interceptors_end2end_test.cc
@@ -0,0 +1,612 @@
+/*
+ *
+ * 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 <memory>
+#include <vector>
+
+#include <grpcpp/channel.h>
+#include <grpcpp/client_context.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/generic/generic_stub.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/impl/codegen/proto_utils.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_builder.h>
+#include <grpcpp/server_context.h>
+
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+#include "test/cpp/end2end/interceptors_util.h"
+#include "test/cpp/end2end/test_service_impl.h"
+#include "test/cpp/util/byte_buffer_proto_helper.h"
+#include "test/cpp/util/string_ref_helper.h"
+
+#include <gtest/gtest.h>
+
+namespace grpc {
+namespace testing {
+namespace {
+
+class ClientInterceptorsStreamingEnd2endTest : public ::testing::Test {
+ protected:
+ ClientInterceptorsStreamingEnd2endTest() {
+ int port = grpc_pick_unused_port_or_die();
+
+ ServerBuilder builder;
+ server_address_ = "localhost:" + std::to_string(port);
+ builder.AddListeningPort(server_address_, InsecureServerCredentials());
+ builder.RegisterService(&service_);
+ server_ = builder.BuildAndStart();
+ }
+
+ ~ClientInterceptorsStreamingEnd2endTest() { server_->Shutdown(); }
+
+ std::string server_address_;
+ EchoTestServiceStreamingImpl service_;
+ std::unique_ptr<Server> server_;
+};
+
+class ClientInterceptorsEnd2endTest : public ::testing::Test {
+ protected:
+ ClientInterceptorsEnd2endTest() {
+ int port = grpc_pick_unused_port_or_die();
+
+ ServerBuilder builder;
+ server_address_ = "localhost:" + std::to_string(port);
+ builder.AddListeningPort(server_address_, InsecureServerCredentials());
+ builder.RegisterService(&service_);
+ server_ = builder.BuildAndStart();
+ }
+
+ ~ClientInterceptorsEnd2endTest() { server_->Shutdown(); }
+
+ std::string server_address_;
+ TestServiceImpl service_;
+ std::unique_ptr<Server> server_;
+};
+
+/* This interceptor does nothing. Just keeps a global count on the number of
+ * times it was invoked. */
+class DummyInterceptor : public experimental::Interceptor {
+ public:
+ DummyInterceptor(experimental::ClientRpcInfo* info) {}
+
+ virtual void Intercept(experimental::InterceptorBatchMethods* methods) {
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) {
+ num_times_run_++;
+ } else if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::
+ POST_RECV_INITIAL_METADATA)) {
+ num_times_run_reverse_++;
+ }
+ methods->Proceed();
+ }
+
+ static void Reset() {
+ num_times_run_.store(0);
+ num_times_run_reverse_.store(0);
+ }
+
+ static int GetNumTimesRun() {
+ EXPECT_EQ(num_times_run_.load(), num_times_run_reverse_.load());
+ return num_times_run_.load();
+ }
+
+ private:
+ static std::atomic<int> num_times_run_;
+ static std::atomic<int> num_times_run_reverse_;
+};
+
+std::atomic<int> DummyInterceptor::num_times_run_;
+std::atomic<int> DummyInterceptor::num_times_run_reverse_;
+
+class DummyInterceptorFactory
+ : public experimental::ClientInterceptorFactoryInterface {
+ public:
+ virtual experimental::Interceptor* CreateClientInterceptor(
+ experimental::ClientRpcInfo* info) override {
+ return new DummyInterceptor(info);
+ }
+};
+
+/* Hijacks Echo RPC and fills in the expected values */
+class HijackingInterceptor : public experimental::Interceptor {
+ public:
+ HijackingInterceptor(experimental::ClientRpcInfo* info) {
+ info_ = info;
+ // Make sure it is the right method
+ EXPECT_EQ(strcmp("/grpc.testing.EchoTestService/Echo", info->method()), 0);
+ }
+
+ virtual void Intercept(experimental::InterceptorBatchMethods* methods) {
+ bool hijack = false;
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) {
+ auto* map = methods->GetSendInitialMetadata();
+ // Check that we can see the test metadata
+ ASSERT_EQ(map->size(), static_cast<unsigned>(1));
+ auto iterator = map->begin();
+ EXPECT_EQ("testkey", iterator->first);
+ EXPECT_EQ("testvalue", iterator->second);
+ hijack = true;
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_MESSAGE)) {
+ EchoRequest req;
+ auto* buffer = methods->GetSendMessage();
+ auto copied_buffer = *buffer;
+ EXPECT_TRUE(
+ SerializationTraits<EchoRequest>::Deserialize(&copied_buffer, &req)
+ .ok());
+ EXPECT_EQ(req.message(), "Hello");
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_CLOSE)) {
+ // Got nothing to do here for now
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA)) {
+ auto* map = methods->GetRecvInitialMetadata();
+ // Got nothing better to do here for now
+ EXPECT_EQ(map->size(), static_cast<unsigned>(0));
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_MESSAGE)) {
+ EchoResponse* resp =
+ static_cast<EchoResponse*>(methods->GetRecvMessage());
+ // Check that we got the hijacked message, and re-insert the expected
+ // message
+ EXPECT_EQ(resp->message(), "Hello1");
+ resp->set_message("Hello");
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_STATUS)) {
+ auto* map = methods->GetRecvTrailingMetadata();
+ bool found = false;
+ // Check that we received the metadata as an echo
+ for (const auto& pair : *map) {
+ found = pair.first.starts_with("testkey") &&
+ pair.second.starts_with("testvalue");
+ if (found) break;
+ }
+ EXPECT_EQ(found, true);
+ auto* status = methods->GetRecvStatus();
+ EXPECT_EQ(status->ok(), true);
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_RECV_INITIAL_METADATA)) {
+ auto* map = methods->GetRecvInitialMetadata();
+ // Got nothing better to do here at the moment
+ EXPECT_EQ(map->size(), static_cast<unsigned>(0));
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_RECV_MESSAGE)) {
+ // Insert a different message than expected
+ EchoResponse* resp =
+ static_cast<EchoResponse*>(methods->GetRecvMessage());
+ resp->set_message("Hello1");
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_RECV_STATUS)) {
+ auto* map = methods->GetRecvTrailingMetadata();
+ // insert the metadata that we want
+ EXPECT_EQ(map->size(), static_cast<unsigned>(0));
+ map->insert(std::make_pair("testkey", "testvalue"));
+ auto* status = methods->GetRecvStatus();
+ *status = Status(StatusCode::OK, "");
+ }
+ if (hijack) {
+ methods->Hijack();
+ } else {
+ methods->Proceed();
+ }
+ }
+
+ private:
+ experimental::ClientRpcInfo* info_;
+};
+
+class HijackingInterceptorFactory
+ : public experimental::ClientInterceptorFactoryInterface {
+ public:
+ virtual experimental::Interceptor* CreateClientInterceptor(
+ experimental::ClientRpcInfo* info) override {
+ return new HijackingInterceptor(info);
+ }
+};
+
+class HijackingInterceptorMakesAnotherCall : public experimental::Interceptor {
+ public:
+ HijackingInterceptorMakesAnotherCall(experimental::ClientRpcInfo* info) {
+ info_ = info;
+ // Make sure it is the right method
+ EXPECT_EQ(strcmp("/grpc.testing.EchoTestService/Echo", info->method()), 0);
+ }
+
+ virtual void Intercept(experimental::InterceptorBatchMethods* methods) {
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) {
+ auto* map = methods->GetSendInitialMetadata();
+ // Check that we can see the test metadata
+ ASSERT_EQ(map->size(), static_cast<unsigned>(1));
+ auto iterator = map->begin();
+ EXPECT_EQ("testkey", iterator->first);
+ EXPECT_EQ("testvalue", iterator->second);
+ // Make a copy of the map
+ metadata_map_ = *map;
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_MESSAGE)) {
+ EchoRequest req;
+ auto* buffer = methods->GetSendMessage();
+ auto copied_buffer = *buffer;
+ EXPECT_TRUE(
+ SerializationTraits<EchoRequest>::Deserialize(&copied_buffer, &req)
+ .ok());
+ EXPECT_EQ(req.message(), "Hello");
+ req_ = req;
+ stub_ = grpc::testing::EchoTestService::NewStub(
+ 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();
+ });
+ // There isn't going to be any other interesting operation in this batch,
+ // so it is fine to return
+ return;
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_CLOSE)) {
+ // Got nothing to do here for now
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA)) {
+ auto* map = methods->GetRecvInitialMetadata();
+ // Got nothing better to do here for now
+ EXPECT_EQ(map->size(), static_cast<unsigned>(0));
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_MESSAGE)) {
+ EchoResponse* resp =
+ static_cast<EchoResponse*>(methods->GetRecvMessage());
+ // Check that we got the hijacked message, and re-insert the expected
+ // message
+ EXPECT_EQ(resp->message(), "Hello");
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_STATUS)) {
+ auto* map = methods->GetRecvTrailingMetadata();
+ bool found = false;
+ // Check that we received the metadata as an echo
+ for (const auto& pair : *map) {
+ found = pair.first.starts_with("testkey") &&
+ pair.second.starts_with("testvalue");
+ if (found) break;
+ }
+ EXPECT_EQ(found, true);
+ auto* status = methods->GetRecvStatus();
+ EXPECT_EQ(status->ok(), true);
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_RECV_INITIAL_METADATA)) {
+ auto* map = methods->GetRecvInitialMetadata();
+ // Got nothing better to do here at the moment
+ EXPECT_EQ(map->size(), static_cast<unsigned>(0));
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_RECV_MESSAGE)) {
+ // Insert a different message than expected
+ EchoResponse* resp =
+ static_cast<EchoResponse*>(methods->GetRecvMessage());
+ resp->set_message(resp_.message());
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_RECV_STATUS)) {
+ auto* map = methods->GetRecvTrailingMetadata();
+ // insert the metadata that we want
+ EXPECT_EQ(map->size(), static_cast<unsigned>(0));
+ map->insert(std::make_pair("testkey", "testvalue"));
+ auto* status = methods->GetRecvStatus();
+ *status = Status(StatusCode::OK, "");
+ }
+
+ methods->Proceed();
+ }
+
+ private:
+ experimental::ClientRpcInfo* info_;
+ std::multimap<grpc::string, grpc::string> metadata_map_;
+ ClientContext ctx_;
+ EchoRequest req_;
+ EchoResponse resp_;
+ std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
+};
+
+class HijackingInterceptorMakesAnotherCallFactory
+ : public experimental::ClientInterceptorFactoryInterface {
+ public:
+ virtual experimental::Interceptor* CreateClientInterceptor(
+ experimental::ClientRpcInfo* info) override {
+ return new HijackingInterceptorMakesAnotherCall(info);
+ }
+};
+
+class LoggingInterceptor : public experimental::Interceptor {
+ public:
+ LoggingInterceptor(experimental::ClientRpcInfo* info) { info_ = info; }
+
+ virtual void Intercept(experimental::InterceptorBatchMethods* methods) {
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) {
+ auto* map = methods->GetSendInitialMetadata();
+ // Check that we can see the test metadata
+ ASSERT_EQ(map->size(), static_cast<unsigned>(1));
+ auto iterator = map->begin();
+ EXPECT_EQ("testkey", iterator->first);
+ EXPECT_EQ("testvalue", iterator->second);
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_MESSAGE)) {
+ EchoRequest req;
+ auto* buffer = methods->GetSendMessage();
+ auto copied_buffer = *buffer;
+ EXPECT_TRUE(
+ SerializationTraits<EchoRequest>::Deserialize(&copied_buffer, &req)
+ .ok());
+ EXPECT_TRUE(req.message().find("Hello") == 0);
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_CLOSE)) {
+ // Got nothing to do here for now
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA)) {
+ auto* map = methods->GetRecvInitialMetadata();
+ // Got nothing better to do here for now
+ EXPECT_EQ(map->size(), static_cast<unsigned>(0));
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_MESSAGE)) {
+ EchoResponse* resp =
+ static_cast<EchoResponse*>(methods->GetRecvMessage());
+ EXPECT_TRUE(resp->message().find("Hello") == 0);
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_STATUS)) {
+ auto* map = methods->GetRecvTrailingMetadata();
+ bool found = false;
+ // Check that we received the metadata as an echo
+ for (const auto& pair : *map) {
+ found = pair.first.starts_with("testkey") &&
+ pair.second.starts_with("testvalue");
+ if (found) break;
+ }
+ EXPECT_EQ(found, true);
+ auto* status = methods->GetRecvStatus();
+ EXPECT_EQ(status->ok(), true);
+ }
+ methods->Proceed();
+ }
+
+ private:
+ experimental::ClientRpcInfo* info_;
+};
+
+class LoggingInterceptorFactory
+ : public experimental::ClientInterceptorFactoryInterface {
+ public:
+ virtual experimental::Interceptor* CreateClientInterceptor(
+ experimental::ClientRpcInfo* info) override {
+ return new LoggingInterceptor(info);
+ }
+};
+
+TEST_F(ClientInterceptorsEnd2endTest, ClientInterceptorLoggingTest) {
+ ChannelArguments args;
+ DummyInterceptor::Reset();
+ auto creators = std::unique_ptr<std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>(
+ new std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+ creators->push_back(std::unique_ptr<LoggingInterceptorFactory>(
+ new LoggingInterceptorFactory()));
+ // Add 20 dummy interceptors
+ for (auto i = 0; i < 20; i++) {
+ creators->push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ auto channel = experimental::CreateCustomChannelWithInterceptors(
+ server_address_, InsecureChannelCredentials(), args, std::move(creators));
+ MakeCall(channel);
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+}
+
+TEST_F(ClientInterceptorsEnd2endTest, ClientInterceptorHijackingTest) {
+ ChannelArguments args;
+ DummyInterceptor::Reset();
+ auto creators = std::unique_ptr<std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>(
+ new std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+ // Add 20 dummy interceptors before hijacking interceptor
+ for (auto i = 0; i < 20; i++) {
+ creators->push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ creators->push_back(std::unique_ptr<HijackingInterceptorFactory>(
+ new HijackingInterceptorFactory()));
+ // Add 20 dummy interceptors after hijacking interceptor
+ for (auto i = 0; i < 20; i++) {
+ creators->push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ auto channel = experimental::CreateCustomChannelWithInterceptors(
+ server_address_, InsecureChannelCredentials(), args, std::move(creators));
+
+ MakeCall(channel);
+ // Make sure only 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+}
+
+TEST_F(ClientInterceptorsEnd2endTest, ClientInterceptorLogThenHijackTest) {
+ ChannelArguments args;
+ auto creators = std::unique_ptr<std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>(
+ new std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+ creators->push_back(std::unique_ptr<LoggingInterceptorFactory>(
+ new LoggingInterceptorFactory()));
+ creators->push_back(std::unique_ptr<HijackingInterceptorFactory>(
+ new HijackingInterceptorFactory()));
+ auto channel = experimental::CreateCustomChannelWithInterceptors(
+ server_address_, InsecureChannelCredentials(), args, std::move(creators));
+
+ MakeCall(channel);
+}
+
+TEST_F(ClientInterceptorsEnd2endTest,
+ ClientInterceptorHijackingMakesAnotherCallTest) {
+ ChannelArguments args;
+ DummyInterceptor::Reset();
+ auto creators = std::unique_ptr<std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>(
+ new std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+ // Add 5 dummy interceptors before hijacking interceptor
+ for (auto i = 0; i < 5; i++) {
+ creators->push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ creators->push_back(
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>(
+ new HijackingInterceptorMakesAnotherCallFactory()));
+ // Add 7 dummy interceptors after hijacking interceptor
+ for (auto i = 0; i < 7; i++) {
+ creators->push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ auto channel = server_->experimental().InProcessChannelWithInterceptors(
+ args, std::move(creators));
+
+ MakeCall(channel);
+ // Make sure all interceptors were run once, since the hijacking interceptor
+ // makes an RPC on the intercepted channel
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 12);
+}
+
+TEST_F(ClientInterceptorsEnd2endTest,
+ ClientInterceptorLoggingTestWithCallback) {
+ ChannelArguments args;
+ DummyInterceptor::Reset();
+ auto creators = std::unique_ptr<std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>(
+ new std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+ creators->push_back(std::unique_ptr<LoggingInterceptorFactory>(
+ new LoggingInterceptorFactory()));
+ // Add 20 dummy interceptors
+ for (auto i = 0; i < 20; i++) {
+ creators->push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ auto channel = server_->experimental().InProcessChannelWithInterceptors(
+ args, std::move(creators));
+ MakeCallbackCall(channel);
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+}
+
+TEST_F(ClientInterceptorsStreamingEnd2endTest, ClientStreamingTest) {
+ ChannelArguments args;
+ DummyInterceptor::Reset();
+ auto creators = std::unique_ptr<std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>(
+ new std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+ creators->push_back(std::unique_ptr<LoggingInterceptorFactory>(
+ new LoggingInterceptorFactory()));
+ // Add 20 dummy interceptors
+ for (auto i = 0; i < 20; i++) {
+ creators->push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ auto channel = experimental::CreateCustomChannelWithInterceptors(
+ server_address_, InsecureChannelCredentials(), args, std::move(creators));
+ MakeClientStreamingCall(channel);
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+}
+
+TEST_F(ClientInterceptorsStreamingEnd2endTest, ServerStreamingTest) {
+ ChannelArguments args;
+ DummyInterceptor::Reset();
+ auto creators = std::unique_ptr<std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>(
+ new std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+ creators->push_back(std::unique_ptr<LoggingInterceptorFactory>(
+ new LoggingInterceptorFactory()));
+ // Add 20 dummy interceptors
+ for (auto i = 0; i < 20; i++) {
+ creators->push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ auto channel = experimental::CreateCustomChannelWithInterceptors(
+ server_address_, InsecureChannelCredentials(), args, std::move(creators));
+ MakeServerStreamingCall(channel);
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+}
+
+TEST_F(ClientInterceptorsStreamingEnd2endTest, BidiStreamingTest) {
+ ChannelArguments args;
+ DummyInterceptor::Reset();
+ auto creators = std::unique_ptr<std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>(
+ new std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+ creators->push_back(std::unique_ptr<LoggingInterceptorFactory>(
+ new LoggingInterceptorFactory()));
+ // Add 20 dummy interceptors
+ for (auto i = 0; i < 20; i++) {
+ creators->push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ auto channel = experimental::CreateCustomChannelWithInterceptors(
+ server_address_, InsecureChannelCredentials(), args, std::move(creators));
+ MakeBidiStreamingCall(channel);
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+}
+
+} // namespace
+} // namespace testing
+} // namespace grpc
+
+int main(int argc, char** argv) {
+ grpc_test_init(argc, argv);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/cpp/end2end/client_lb_end2end_test.cc b/test/cpp/end2end/client_lb_end2end_test.cc
index 77c25c1416..9218c85717 100644
--- a/test/cpp/end2end/client_lb_end2end_test.cc
+++ b/test/cpp/end2end/client_lb_end2end_test.cc
@@ -42,6 +42,9 @@
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/security/credentials/fake/fake_credentials.h"
+#include "src/cpp/client/secure_credentials.h"
+#include "src/cpp/server/secure_server_credentials.h"
#include "src/proto/grpc/testing/echo.grpc.pb.h"
#include "test/core/util/port.h"
@@ -207,19 +210,22 @@ class ClientLbEnd2endTest : public ::testing::Test {
} // else, default to pick first
args.SetPointer(GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
response_generator_.get());
- return CreateCustomChannel("fake:///", InsecureChannelCredentials(), args);
+ std::shared_ptr<ChannelCredentials> creds(new SecureChannelCredentials(
+ grpc_fake_transport_security_credentials_create()));
+ return CreateCustomChannel("fake:///", std::move(creds), args);
}
bool SendRpc(
const std::unique_ptr<grpc::testing::EchoTestService::Stub>& stub,
EchoResponse* response = nullptr, int timeout_ms = 1000,
- Status* result = nullptr) {
+ Status* result = nullptr, bool wait_for_ready = false) {
const bool local_response = (response == nullptr);
if (local_response) response = new EchoResponse;
EchoRequest request;
request.set_message(kRequestMessage_);
ClientContext context;
context.set_deadline(grpc_timeout_milliseconds_to_deadline(timeout_ms));
+ if (wait_for_ready) context.set_wait_for_ready(true);
Status status = stub->Echo(&context, request, response);
if (result != nullptr) *result = status;
if (local_response) delete response;
@@ -228,10 +234,11 @@ class ClientLbEnd2endTest : public ::testing::Test {
void CheckRpcSendOk(
const std::unique_ptr<grpc::testing::EchoTestService::Stub>& stub,
- const grpc_core::DebugLocation& location) {
+ const grpc_core::DebugLocation& location, bool wait_for_ready = false) {
EchoResponse response;
Status status;
- const bool success = SendRpc(stub, &response, 2000, &status);
+ const bool success =
+ SendRpc(stub, &response, 2000, &status, wait_for_ready);
ASSERT_TRUE(success) << "From " << location.file() << ":" << location.line()
<< "\n"
<< "Error: " << status.error_message() << " "
@@ -275,8 +282,9 @@ class ClientLbEnd2endTest : public ::testing::Test {
std::ostringstream server_address;
server_address << server_host << ":" << port_;
ServerBuilder builder;
- builder.AddListeningPort(server_address.str(),
- InsecureServerCredentials());
+ std::shared_ptr<ServerCredentials> creds(new SecureServerCredentials(
+ grpc_fake_transport_security_server_credentials_create()));
+ builder.AddListeningPort(server_address.str(), std::move(creds));
builder.RegisterService(&service_);
server_ = builder.BuildAndStart();
std::lock_guard<std::mutex> lock(*mu);
@@ -306,7 +314,7 @@ class ClientLbEnd2endTest : public ::testing::Test {
if (ignore_failure) {
SendRpc(stub);
} else {
- CheckRpcSendOk(stub, location);
+ CheckRpcSendOk(stub, location, true);
}
} while (servers_[server_idx]->service_.request_count() == 0);
ResetCounters();
@@ -518,7 +526,7 @@ TEST_F(ClientLbEnd2endTest, PickFirstUpdates) {
do {
channel_state = channel->GetState(true /* try to connect */);
} while (channel_state == GRPC_CHANNEL_READY);
- GPR_ASSERT(channel_state != GRPC_CHANNEL_READY);
+ ASSERT_NE(channel_state, GRPC_CHANNEL_READY);
servers_[0]->service_.ResetCounters();
// Next update introduces servers_[1], making the channel recover.
@@ -835,7 +843,7 @@ TEST_F(ClientLbEnd2endTest, RoundRobinUpdates) {
do {
channel_state = channel->GetState(true /* try to connect */);
} while (channel_state == GRPC_CHANNEL_READY);
- GPR_ASSERT(channel_state != GRPC_CHANNEL_READY);
+ ASSERT_NE(channel_state, GRPC_CHANNEL_READY);
servers_[0]->service_.ResetCounters();
// Next update introduces servers_[1], making the channel recover.
@@ -844,7 +852,7 @@ TEST_F(ClientLbEnd2endTest, RoundRobinUpdates) {
SetNextResolution(ports);
WaitForServer(stub, 1, DEBUG_LOCATION);
channel_state = channel->GetState(false /* try to connect */);
- GPR_ASSERT(channel_state == GRPC_CHANNEL_READY);
+ ASSERT_EQ(channel_state, GRPC_CHANNEL_READY);
// Check LB policy name for the channel.
EXPECT_EQ("round_robin", channel->GetLoadBalancingPolicyName());
@@ -954,7 +962,7 @@ TEST_F(ClientLbEnd2endTest, RoundRobinReresolve) {
if (SendRpc(stub)) break;
now = gpr_now(GPR_CLOCK_MONOTONIC);
}
- GPR_ASSERT(gpr_time_cmp(deadline, now) > 0);
+ ASSERT_GT(gpr_time_cmp(deadline, now), 0);
}
TEST_F(ClientLbEnd2endTest, RoundRobinSingleReconnect) {
diff --git a/test/cpp/end2end/grpclb_end2end_test.cc b/test/cpp/end2end/grpclb_end2end_test.cc
index b69b861fcf..6ce0696114 100644
--- a/test/cpp/end2end/grpclb_end2end_test.cc
+++ b/test/cpp/end2end/grpclb_end2end_test.cc
@@ -539,13 +539,15 @@ class GrpclbEnd2endTest : public ::testing::Test {
balancers_.at(i)->add_response(response, delay_ms);
}
- Status SendRpc(EchoResponse* response = nullptr, int timeout_ms = 1000) {
+ Status SendRpc(EchoResponse* response = nullptr, int timeout_ms = 1000,
+ bool wait_for_ready = false) {
const bool local_response = (response == nullptr);
if (local_response) response = new EchoResponse;
EchoRequest request;
request.set_message(kRequestMessage_);
ClientContext context;
context.set_deadline(grpc_timeout_milliseconds_to_deadline(timeout_ms));
+ if (wait_for_ready) context.set_wait_for_ready(true);
Status status = stub_->Echo(&context, request, response);
if (local_response) delete response;
return status;
@@ -1366,7 +1368,7 @@ TEST_F(SingleBalancerTest, DropAllFirst) {
{}, {{"rate_limiting", num_of_drop_by_rate_limiting_addresses},
{"load_balancing", num_of_drop_by_load_balancing_addresses}}),
0);
- const Status status = SendRpc();
+ const Status status = SendRpc(nullptr, 1000, true);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.error_message(), "Call dropped by load balancing policy");
}
@@ -1391,7 +1393,7 @@ TEST_F(SingleBalancerTest, DropAll) {
// fail.
Status status;
do {
- status = SendRpc();
+ status = SendRpc(nullptr, 1000, true);
} while (status.ok());
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.error_message(), "Call dropped by load balancing policy");
diff --git a/test/cpp/end2end/interceptors_util.h b/test/cpp/end2end/interceptors_util.h
new file mode 100644
index 0000000000..5f0aa37dc0
--- /dev/null
+++ b/test/cpp/end2end/interceptors_util.h
@@ -0,0 +1,308 @@
+/*
+ *
+ * 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 "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/cpp/util/string_ref_helper.h"
+
+#include <gtest/gtest.h>
+
+namespace grpc {
+namespace testing {
+class EchoTestServiceStreamingImpl : public EchoTestService::Service {
+ public:
+ ~EchoTestServiceStreamingImpl() override {}
+
+ Status BidiStream(
+ ServerContext* context,
+ grpc::ServerReaderWriter<EchoResponse, EchoRequest>* stream) override {
+ EchoRequest req;
+ EchoResponse resp;
+ auto client_metadata = context->client_metadata();
+ for (const auto& pair : client_metadata) {
+ context->AddTrailingMetadata(ToString(pair.first), ToString(pair.second));
+ }
+
+ while (stream->Read(&req)) {
+ resp.set_message(req.message());
+ EXPECT_TRUE(stream->Write(resp, grpc::WriteOptions()));
+ }
+ return Status::OK;
+ }
+
+ Status RequestStream(ServerContext* context,
+ ServerReader<EchoRequest>* reader,
+ EchoResponse* resp) override {
+ auto client_metadata = context->client_metadata();
+ for (const auto& pair : client_metadata) {
+ context->AddTrailingMetadata(ToString(pair.first), ToString(pair.second));
+ }
+
+ EchoRequest req;
+ string response_str = "";
+ while (reader->Read(&req)) {
+ response_str += req.message();
+ }
+ resp->set_message(response_str);
+ return Status::OK;
+ }
+
+ Status ResponseStream(ServerContext* context, const EchoRequest* req,
+ ServerWriter<EchoResponse>* writer) override {
+ auto client_metadata = context->client_metadata();
+ for (const auto& pair : client_metadata) {
+ context->AddTrailingMetadata(ToString(pair.first), ToString(pair.second));
+ }
+
+ EchoResponse resp;
+ resp.set_message(req->message());
+ for (int i = 0; i < 10; i++) {
+ EXPECT_TRUE(writer->Write(resp));
+ }
+ return Status::OK;
+ }
+};
+
+void MakeCall(const std::shared_ptr<Channel>& channel) {
+ auto stub = grpc::testing::EchoTestService::NewStub(channel);
+ ClientContext ctx;
+ EchoRequest req;
+ req.mutable_param()->set_echo_metadata(true);
+ ctx.AddMetadata("testkey", "testvalue");
+ req.set_message("Hello");
+ EchoResponse resp;
+ Status s = stub->Echo(&ctx, req, &resp);
+ EXPECT_EQ(s.ok(), true);
+ EXPECT_EQ(resp.message(), "Hello");
+}
+
+void MakeClientStreamingCall(const std::shared_ptr<Channel>& channel) {
+ auto stub = grpc::testing::EchoTestService::NewStub(channel);
+ ClientContext ctx;
+ EchoRequest req;
+ req.mutable_param()->set_echo_metadata(true);
+ ctx.AddMetadata("testkey", "testvalue");
+ req.set_message("Hello");
+ EchoResponse resp;
+ string expected_resp = "";
+ auto writer = stub->RequestStream(&ctx, &resp);
+ for (int i = 0; i < 10; i++) {
+ writer->Write(req);
+ expected_resp += "Hello";
+ }
+ writer->WritesDone();
+ Status s = writer->Finish();
+ EXPECT_EQ(s.ok(), true);
+ EXPECT_EQ(resp.message(), expected_resp);
+}
+
+void MakeServerStreamingCall(const std::shared_ptr<Channel>& channel) {
+ auto stub = grpc::testing::EchoTestService::NewStub(channel);
+ ClientContext ctx;
+ EchoRequest req;
+ req.mutable_param()->set_echo_metadata(true);
+ ctx.AddMetadata("testkey", "testvalue");
+ req.set_message("Hello");
+ EchoResponse resp;
+ string expected_resp = "";
+ auto reader = stub->ResponseStream(&ctx, req);
+ int count = 0;
+ while (reader->Read(&resp)) {
+ EXPECT_EQ(resp.message(), "Hello");
+ count++;
+ }
+ ASSERT_EQ(count, 10);
+ Status s = reader->Finish();
+ EXPECT_EQ(s.ok(), true);
+}
+
+void MakeBidiStreamingCall(const std::shared_ptr<Channel>& channel) {
+ auto stub = grpc::testing::EchoTestService::NewStub(channel);
+ ClientContext ctx;
+ EchoRequest req;
+ EchoResponse resp;
+ ctx.AddMetadata("testkey", "testvalue");
+ auto stream = stub->BidiStream(&ctx);
+ for (auto i = 0; i < 10; i++) {
+ req.set_message("Hello" + std::to_string(i));
+ stream->Write(req);
+ stream->Read(&resp);
+ EXPECT_EQ(req.message(), resp.message());
+ }
+ ASSERT_TRUE(stream->WritesDone());
+ Status s = stream->Finish();
+ EXPECT_EQ(s.ok(), true);
+}
+
+void MakeCallbackCall(const std::shared_ptr<Channel>& channel) {
+ auto stub = grpc::testing::EchoTestService::NewStub(channel);
+ ClientContext ctx;
+ EchoRequest req;
+ std::mutex mu;
+ std::condition_variable cv;
+ bool done = false;
+ req.mutable_param()->set_echo_metadata(true);
+ ctx.AddMetadata("testkey", "testvalue");
+ req.set_message("Hello");
+ EchoResponse resp;
+ stub->experimental_async()->Echo(&ctx, &req, &resp,
+ [&resp, &mu, &done, &cv](Status s) {
+ // gpr_log(GPR_ERROR, "got the callback");
+ 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);
+ }
+}
+
+bool CheckMetadata(const std::multimap<grpc::string_ref, grpc::string_ref>& map,
+ const string& key, const string& value) {
+ for (const auto& pair : map) {
+ if (pair.first.starts_with(key) && pair.second.starts_with(value)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void* tag(int i) { return (void*)static_cast<intptr_t>(i); }
+int detag(void* p) { return static_cast<int>(reinterpret_cast<intptr_t>(p)); }
+
+class Verifier {
+ public:
+ Verifier() : lambda_run_(false) {}
+ // Expect sets the expected ok value for a specific tag
+ Verifier& Expect(int i, bool expect_ok) {
+ return ExpectUnless(i, expect_ok, false);
+ }
+ // ExpectUnless sets the expected ok value for a specific tag
+ // unless the tag was already marked seen (as a result of ExpectMaybe)
+ Verifier& ExpectUnless(int i, bool expect_ok, bool seen) {
+ if (!seen) {
+ expectations_[tag(i)] = expect_ok;
+ }
+ return *this;
+ }
+ // ExpectMaybe sets the expected ok value for a specific tag, but does not
+ // require it to appear
+ // If it does, sets *seen to true
+ Verifier& ExpectMaybe(int i, bool expect_ok, bool* seen) {
+ if (!*seen) {
+ maybe_expectations_[tag(i)] = MaybeExpect{expect_ok, seen};
+ }
+ return *this;
+ }
+
+ // Next waits for 1 async tag to complete, checks its
+ // expectations, and returns the tag
+ int Next(CompletionQueue* cq, bool ignore_ok) {
+ bool ok;
+ void* got_tag;
+ EXPECT_TRUE(cq->Next(&got_tag, &ok));
+ GotTag(got_tag, ok, ignore_ok);
+ return detag(got_tag);
+ }
+
+ template <typename T>
+ CompletionQueue::NextStatus DoOnceThenAsyncNext(
+ CompletionQueue* cq, void** got_tag, bool* ok, T deadline,
+ std::function<void(void)> lambda) {
+ if (lambda_run_) {
+ return cq->AsyncNext(got_tag, ok, deadline);
+ } else {
+ lambda_run_ = true;
+ return cq->DoThenAsyncNext(lambda, got_tag, ok, deadline);
+ }
+ }
+
+ // Verify keeps calling Next until all currently set
+ // expected tags are complete
+ void Verify(CompletionQueue* cq) { Verify(cq, false); }
+
+ // This version of Verify allows optionally ignoring the
+ // outcome of the expectation
+ void Verify(CompletionQueue* cq, bool ignore_ok) {
+ GPR_ASSERT(!expectations_.empty() || !maybe_expectations_.empty());
+ while (!expectations_.empty()) {
+ Next(cq, ignore_ok);
+ }
+ }
+
+ // This version of Verify stops after a certain deadline, and uses the
+ // DoThenAsyncNext API
+ // to call the lambda
+ void Verify(CompletionQueue* cq,
+ std::chrono::system_clock::time_point deadline,
+ const std::function<void(void)>& lambda) {
+ if (expectations_.empty()) {
+ bool ok;
+ void* got_tag;
+ EXPECT_EQ(DoOnceThenAsyncNext(cq, &got_tag, &ok, deadline, lambda),
+ CompletionQueue::TIMEOUT);
+ } else {
+ while (!expectations_.empty()) {
+ bool ok;
+ void* got_tag;
+ EXPECT_EQ(DoOnceThenAsyncNext(cq, &got_tag, &ok, deadline, lambda),
+ CompletionQueue::GOT_EVENT);
+ GotTag(got_tag, ok, false);
+ }
+ }
+ }
+
+ private:
+ void GotTag(void* got_tag, bool ok, bool ignore_ok) {
+ auto it = expectations_.find(got_tag);
+ if (it != expectations_.end()) {
+ if (!ignore_ok) {
+ EXPECT_EQ(it->second, ok);
+ }
+ expectations_.erase(it);
+ } else {
+ auto it2 = maybe_expectations_.find(got_tag);
+ if (it2 != maybe_expectations_.end()) {
+ if (it2->second.seen != nullptr) {
+ EXPECT_FALSE(*it2->second.seen);
+ *it2->second.seen = true;
+ }
+ if (!ignore_ok) {
+ EXPECT_EQ(it2->second.ok, ok);
+ }
+ } else {
+ gpr_log(GPR_ERROR, "Unexpected tag: %p", got_tag);
+ abort();
+ }
+ }
+ }
+
+ struct MaybeExpect {
+ bool ok;
+ bool* seen;
+ };
+
+ std::map<void*, bool> expectations_;
+ std::map<void*, MaybeExpect> maybe_expectations_;
+ bool lambda_run_;
+};
+
+} // namespace testing
+} // namespace grpc
diff --git a/test/cpp/end2end/server_interceptors_end2end_test.cc b/test/cpp/end2end/server_interceptors_end2end_test.cc
new file mode 100644
index 0000000000..e08a4493d3
--- /dev/null
+++ b/test/cpp/end2end/server_interceptors_end2end_test.cc
@@ -0,0 +1,625 @@
+/*
+ *
+ * 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 <memory>
+#include <vector>
+
+#include <grpcpp/channel.h>
+#include <grpcpp/client_context.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/generic/generic_stub.h>
+#include <grpcpp/impl/codegen/proto_utils.h>
+#include <grpcpp/impl/codegen/server_interceptor.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_builder.h>
+#include <grpcpp/server_context.h>
+
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+#include "test/cpp/end2end/interceptors_util.h"
+#include "test/cpp/end2end/test_service_impl.h"
+#include "test/cpp/util/byte_buffer_proto_helper.h"
+
+#include <gtest/gtest.h>
+
+namespace grpc {
+namespace testing {
+namespace {
+
+/* This interceptor does nothing. Just keeps a global count on the number of
+ * times it was invoked. */
+class DummyInterceptor : public experimental::Interceptor {
+ public:
+ DummyInterceptor(experimental::ServerRpcInfo* info) {}
+
+ virtual void Intercept(experimental::InterceptorBatchMethods* methods) {
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) {
+ num_times_run_++;
+ } else if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::
+ POST_RECV_INITIAL_METADATA)) {
+ num_times_run_reverse_++;
+ }
+ methods->Proceed();
+ }
+
+ static void Reset() {
+ num_times_run_.store(0);
+ num_times_run_reverse_.store(0);
+ }
+
+ static int GetNumTimesRun() {
+ EXPECT_EQ(num_times_run_.load(), num_times_run_reverse_.load());
+ return num_times_run_.load();
+ }
+
+ private:
+ static std::atomic<int> num_times_run_;
+ static std::atomic<int> num_times_run_reverse_;
+};
+
+std::atomic<int> DummyInterceptor::num_times_run_;
+std::atomic<int> DummyInterceptor::num_times_run_reverse_;
+
+class DummyInterceptorFactory
+ : public experimental::ServerInterceptorFactoryInterface {
+ public:
+ virtual experimental::Interceptor* CreateServerInterceptor(
+ experimental::ServerRpcInfo* info) override {
+ return new DummyInterceptor(info);
+ }
+};
+
+class LoggingInterceptor : public experimental::Interceptor {
+ public:
+ LoggingInterceptor(experimental::ServerRpcInfo* info) { info_ = info; }
+
+ virtual void Intercept(experimental::InterceptorBatchMethods* methods) {
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) {
+ auto* map = methods->GetSendInitialMetadata();
+ // Got nothing better to do here for now
+ EXPECT_EQ(map->size(), static_cast<unsigned>(0));
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_MESSAGE)) {
+ EchoRequest req;
+ auto* buffer = methods->GetSendMessage();
+ auto copied_buffer = *buffer;
+ EXPECT_TRUE(
+ SerializationTraits<EchoRequest>::Deserialize(&copied_buffer, &req)
+ .ok());
+ EXPECT_TRUE(req.message().find("Hello") == 0);
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_STATUS)) {
+ auto* map = methods->GetSendTrailingMetadata();
+ bool found = false;
+ // Check that we received the metadata as an echo
+ for (const auto& pair : *map) {
+ found = pair.first.find("testkey") == 0 &&
+ pair.second.find("testvalue") == 0;
+ if (found) break;
+ }
+ EXPECT_EQ(found, true);
+ auto status = methods->GetSendStatus();
+ EXPECT_EQ(status.ok(), true);
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA)) {
+ auto* map = methods->GetRecvInitialMetadata();
+ bool found = false;
+ // Check that we received the metadata as an echo
+ for (const auto& pair : *map) {
+ found = pair.first.find("testkey") == 0 &&
+ pair.second.find("testvalue") == 0;
+ if (found) break;
+ }
+ EXPECT_EQ(found, true);
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_MESSAGE)) {
+ EchoResponse* resp =
+ static_cast<EchoResponse*>(methods->GetRecvMessage());
+ EXPECT_TRUE(resp->message().find("Hello") == 0);
+ }
+ if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_CLOSE)) {
+ // Got nothing interesting to do here
+ }
+ methods->Proceed();
+ }
+
+ private:
+ experimental::ServerRpcInfo* info_;
+};
+
+class LoggingInterceptorFactory
+ : public experimental::ServerInterceptorFactoryInterface {
+ public:
+ virtual experimental::Interceptor* CreateServerInterceptor(
+ experimental::ServerRpcInfo* info) override {
+ return new LoggingInterceptor(info);
+ }
+};
+
+void MakeBidiStreamingCall(const std::shared_ptr<Channel>& channel) {
+ auto stub = grpc::testing::EchoTestService::NewStub(channel);
+ ClientContext ctx;
+ EchoRequest req;
+ EchoResponse resp;
+ ctx.AddMetadata("testkey", "testvalue");
+ auto stream = stub->BidiStream(&ctx);
+ for (auto i = 0; i < 10; i++) {
+ req.set_message("Hello" + std::to_string(i));
+ stream->Write(req);
+ stream->Read(&resp);
+ EXPECT_EQ(req.message(), resp.message());
+ }
+ ASSERT_TRUE(stream->WritesDone());
+ Status s = stream->Finish();
+ EXPECT_EQ(s.ok(), true);
+}
+
+class ServerInterceptorsEnd2endSyncUnaryTest : public ::testing::Test {
+ protected:
+ ServerInterceptorsEnd2endSyncUnaryTest() {
+ int port = grpc_pick_unused_port_or_die();
+
+ ServerBuilder builder;
+ server_address_ = "localhost:" + std::to_string(port);
+ builder.AddListeningPort(server_address_, InsecureServerCredentials());
+ builder.RegisterService(&service_);
+
+ std::vector<
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ creators;
+ creators.push_back(
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>(
+ new LoggingInterceptorFactory()));
+ for (auto i = 0; i < 20; i++) {
+ creators.push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ builder.experimental().SetInterceptorCreators(std::move(creators));
+ server_ = builder.BuildAndStart();
+ }
+ std::string server_address_;
+ TestServiceImpl service_;
+ std::unique_ptr<Server> server_;
+};
+
+TEST_F(ServerInterceptorsEnd2endSyncUnaryTest, UnaryTest) {
+ ChannelArguments args;
+ DummyInterceptor::Reset();
+ auto channel = CreateChannel(server_address_, InsecureChannelCredentials());
+ MakeCall(channel);
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+}
+
+class ServerInterceptorsEnd2endSyncStreamingTest : public ::testing::Test {
+ protected:
+ ServerInterceptorsEnd2endSyncStreamingTest() {
+ int port = grpc_pick_unused_port_or_die();
+
+ ServerBuilder builder;
+ server_address_ = "localhost:" + std::to_string(port);
+ builder.AddListeningPort(server_address_, InsecureServerCredentials());
+ builder.RegisterService(&service_);
+
+ std::vector<
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ creators;
+ creators.push_back(
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>(
+ new LoggingInterceptorFactory()));
+ for (auto i = 0; i < 20; i++) {
+ creators.push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ builder.experimental().SetInterceptorCreators(std::move(creators));
+ server_ = builder.BuildAndStart();
+ }
+ std::string server_address_;
+ EchoTestServiceStreamingImpl service_;
+ std::unique_ptr<Server> server_;
+};
+
+TEST_F(ServerInterceptorsEnd2endSyncStreamingTest, ClientStreamingTest) {
+ ChannelArguments args;
+ DummyInterceptor::Reset();
+ auto channel = CreateChannel(server_address_, InsecureChannelCredentials());
+ MakeClientStreamingCall(channel);
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+}
+
+TEST_F(ServerInterceptorsEnd2endSyncStreamingTest, ServerStreamingTest) {
+ ChannelArguments args;
+ DummyInterceptor::Reset();
+ auto channel = CreateChannel(server_address_, InsecureChannelCredentials());
+ MakeServerStreamingCall(channel);
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+}
+
+TEST_F(ServerInterceptorsEnd2endSyncStreamingTest, BidiStreamingTest) {
+ ChannelArguments args;
+ DummyInterceptor::Reset();
+ auto channel = CreateChannel(server_address_, InsecureChannelCredentials());
+ MakeBidiStreamingCall(channel);
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+}
+
+class ServerInterceptorsAsyncEnd2endTest : public ::testing::Test {};
+
+TEST_F(ServerInterceptorsAsyncEnd2endTest, UnaryTest) {
+ DummyInterceptor::Reset();
+ int port = grpc_pick_unused_port_or_die();
+ string server_address = "localhost:" + std::to_string(port);
+ ServerBuilder builder;
+ EchoTestService::AsyncService service;
+ builder.AddListeningPort(server_address, InsecureServerCredentials());
+ builder.RegisterService(&service);
+ std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ creators;
+ creators.push_back(
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>(
+ new LoggingInterceptorFactory()));
+ for (auto i = 0; i < 20; i++) {
+ creators.push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ builder.experimental().SetInterceptorCreators(std::move(creators));
+ auto cq = builder.AddCompletionQueue();
+ auto server = builder.BuildAndStart();
+
+ ChannelArguments args;
+ auto channel = CreateChannel(server_address, InsecureChannelCredentials());
+ auto stub = grpc::testing::EchoTestService::NewStub(channel);
+
+ EchoRequest send_request;
+ EchoRequest recv_request;
+ EchoResponse send_response;
+ EchoResponse recv_response;
+ Status recv_status;
+
+ ClientContext cli_ctx;
+ ServerContext srv_ctx;
+ grpc::ServerAsyncResponseWriter<EchoResponse> response_writer(&srv_ctx);
+
+ send_request.set_message("Hello");
+ cli_ctx.AddMetadata("testkey", "testvalue");
+ std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> response_reader(
+ stub->AsyncEcho(&cli_ctx, send_request, cq.get()));
+
+ service.RequestEcho(&srv_ctx, &recv_request, &response_writer, cq.get(),
+ cq.get(), tag(2));
+
+ response_reader->Finish(&recv_response, &recv_status, tag(4));
+
+ Verifier().Expect(2, true).Verify(cq.get());
+ EXPECT_EQ(send_request.message(), recv_request.message());
+
+ EXPECT_TRUE(CheckMetadata(srv_ctx.client_metadata(), "testkey", "testvalue"));
+ srv_ctx.AddTrailingMetadata("testkey", "testvalue");
+
+ send_response.set_message(recv_request.message());
+ response_writer.Finish(send_response, Status::OK, tag(3));
+ Verifier().Expect(3, true).Expect(4, true).Verify(cq.get());
+
+ EXPECT_EQ(send_response.message(), recv_response.message());
+ EXPECT_TRUE(recv_status.ok());
+ EXPECT_TRUE(CheckMetadata(cli_ctx.GetServerTrailingMetadata(), "testkey",
+ "testvalue"));
+
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+
+ server->Shutdown();
+ cq->Shutdown();
+ void* ignored_tag;
+ bool ignored_ok;
+ while (cq->Next(&ignored_tag, &ignored_ok))
+ ;
+ grpc_recycle_unused_port(port);
+}
+
+TEST_F(ServerInterceptorsAsyncEnd2endTest, BidiStreamingTest) {
+ DummyInterceptor::Reset();
+ int port = grpc_pick_unused_port_or_die();
+ string server_address = "localhost:" + std::to_string(port);
+ ServerBuilder builder;
+ EchoTestService::AsyncService service;
+ builder.AddListeningPort(server_address, InsecureServerCredentials());
+ builder.RegisterService(&service);
+ std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ creators;
+ creators.push_back(
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>(
+ new LoggingInterceptorFactory()));
+ for (auto i = 0; i < 20; i++) {
+ creators.push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ builder.experimental().SetInterceptorCreators(std::move(creators));
+ auto cq = builder.AddCompletionQueue();
+ auto server = builder.BuildAndStart();
+
+ ChannelArguments args;
+ auto channel = CreateChannel(server_address, InsecureChannelCredentials());
+ auto stub = grpc::testing::EchoTestService::NewStub(channel);
+
+ EchoRequest send_request;
+ EchoRequest recv_request;
+ EchoResponse send_response;
+ EchoResponse recv_response;
+ Status recv_status;
+
+ ClientContext cli_ctx;
+ ServerContext srv_ctx;
+ grpc::ServerAsyncReaderWriter<EchoResponse, EchoRequest> srv_stream(&srv_ctx);
+
+ send_request.set_message("Hello");
+ cli_ctx.AddMetadata("testkey", "testvalue");
+ std::unique_ptr<ClientAsyncReaderWriter<EchoRequest, EchoResponse>>
+ cli_stream(stub->AsyncBidiStream(&cli_ctx, cq.get(), tag(1)));
+
+ service.RequestBidiStream(&srv_ctx, &srv_stream, cq.get(), cq.get(), tag(2));
+
+ Verifier().Expect(1, true).Expect(2, true).Verify(cq.get());
+
+ EXPECT_TRUE(CheckMetadata(srv_ctx.client_metadata(), "testkey", "testvalue"));
+ srv_ctx.AddTrailingMetadata("testkey", "testvalue");
+
+ cli_stream->Write(send_request, tag(3));
+ srv_stream.Read(&recv_request, tag(4));
+ Verifier().Expect(3, true).Expect(4, true).Verify(cq.get());
+ EXPECT_EQ(send_request.message(), recv_request.message());
+
+ send_response.set_message(recv_request.message());
+ srv_stream.Write(send_response, tag(5));
+ cli_stream->Read(&recv_response, tag(6));
+ Verifier().Expect(5, true).Expect(6, true).Verify(cq.get());
+ EXPECT_EQ(send_response.message(), recv_response.message());
+
+ cli_stream->WritesDone(tag(7));
+ srv_stream.Read(&recv_request, tag(8));
+ Verifier().Expect(7, true).Expect(8, false).Verify(cq.get());
+
+ srv_stream.Finish(Status::OK, tag(9));
+ cli_stream->Finish(&recv_status, tag(10));
+ Verifier().Expect(9, true).Expect(10, true).Verify(cq.get());
+
+ EXPECT_TRUE(recv_status.ok());
+ EXPECT_TRUE(CheckMetadata(cli_ctx.GetServerTrailingMetadata(), "testkey",
+ "testvalue"));
+
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+
+ server->Shutdown();
+ cq->Shutdown();
+ void* ignored_tag;
+ bool ignored_ok;
+ while (cq->Next(&ignored_tag, &ignored_ok))
+ ;
+ grpc_recycle_unused_port(port);
+}
+
+TEST_F(ServerInterceptorsAsyncEnd2endTest, GenericRPCTest) {
+ DummyInterceptor::Reset();
+ int port = grpc_pick_unused_port_or_die();
+ string server_address = "localhost:" + std::to_string(port);
+ ServerBuilder builder;
+ AsyncGenericService service;
+ builder.AddListeningPort(server_address, InsecureServerCredentials());
+ builder.RegisterAsyncGenericService(&service);
+ std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ creators;
+ for (auto i = 0; i < 20; i++) {
+ creators.push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ builder.experimental().SetInterceptorCreators(std::move(creators));
+ auto cq = builder.AddCompletionQueue();
+ auto server = builder.BuildAndStart();
+
+ ChannelArguments args;
+ auto channel = CreateChannel(server_address, InsecureChannelCredentials());
+ GenericStub generic_stub(channel);
+
+ const grpc::string kMethodName("/grpc.cpp.test.util.EchoTestService/Echo");
+ EchoRequest send_request;
+ EchoRequest recv_request;
+ EchoResponse send_response;
+ EchoResponse recv_response;
+ Status recv_status;
+
+ ClientContext cli_ctx;
+ GenericServerContext srv_ctx;
+ GenericServerAsyncReaderWriter stream(&srv_ctx);
+
+ // The string needs to be long enough to test heap-based slice.
+ send_request.set_message("Hello");
+ cli_ctx.AddMetadata("testkey", "testvalue");
+
+ std::unique_ptr<GenericClientAsyncReaderWriter> call =
+ generic_stub.PrepareCall(&cli_ctx, kMethodName, cq.get());
+ call->StartCall(tag(1));
+ Verifier().Expect(1, true).Verify(cq.get());
+ std::unique_ptr<ByteBuffer> send_buffer =
+ SerializeToByteBuffer(&send_request);
+ call->Write(*send_buffer, tag(2));
+ // Send ByteBuffer can be destroyed after calling Write.
+ send_buffer.reset();
+ Verifier().Expect(2, true).Verify(cq.get());
+ call->WritesDone(tag(3));
+ Verifier().Expect(3, true).Verify(cq.get());
+
+ service.RequestCall(&srv_ctx, &stream, cq.get(), cq.get(), tag(4));
+
+ Verifier().Expect(4, true).Verify(cq.get());
+ EXPECT_EQ(kMethodName, srv_ctx.method());
+ EXPECT_TRUE(CheckMetadata(srv_ctx.client_metadata(), "testkey", "testvalue"));
+ srv_ctx.AddTrailingMetadata("testkey", "testvalue");
+
+ ByteBuffer recv_buffer;
+ stream.Read(&recv_buffer, tag(5));
+ Verifier().Expect(5, true).Verify(cq.get());
+ EXPECT_TRUE(ParseFromByteBuffer(&recv_buffer, &recv_request));
+ EXPECT_EQ(send_request.message(), recv_request.message());
+
+ send_response.set_message(recv_request.message());
+ send_buffer = SerializeToByteBuffer(&send_response);
+ stream.Write(*send_buffer, tag(6));
+ send_buffer.reset();
+ Verifier().Expect(6, true).Verify(cq.get());
+
+ stream.Finish(Status::OK, tag(7));
+ Verifier().Expect(7, true).Verify(cq.get());
+
+ recv_buffer.Clear();
+ call->Read(&recv_buffer, tag(8));
+ Verifier().Expect(8, true).Verify(cq.get());
+ EXPECT_TRUE(ParseFromByteBuffer(&recv_buffer, &recv_response));
+
+ call->Finish(&recv_status, tag(9));
+ Verifier().Expect(9, true).Verify(cq.get());
+
+ EXPECT_EQ(send_response.message(), recv_response.message());
+ EXPECT_TRUE(recv_status.ok());
+ EXPECT_TRUE(CheckMetadata(cli_ctx.GetServerTrailingMetadata(), "testkey",
+ "testvalue"));
+
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+
+ server->Shutdown();
+ cq->Shutdown();
+ void* ignored_tag;
+ bool ignored_ok;
+ while (cq->Next(&ignored_tag, &ignored_ok))
+ ;
+ grpc_recycle_unused_port(port);
+}
+
+TEST_F(ServerInterceptorsAsyncEnd2endTest, UnimplementedRpcTest) {
+ DummyInterceptor::Reset();
+ int port = grpc_pick_unused_port_or_die();
+ string server_address = "localhost:" + std::to_string(port);
+ ServerBuilder builder;
+ builder.AddListeningPort(server_address, InsecureServerCredentials());
+ std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ creators;
+ for (auto i = 0; i < 20; i++) {
+ creators.push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ builder.experimental().SetInterceptorCreators(std::move(creators));
+ auto cq = builder.AddCompletionQueue();
+ auto server = builder.BuildAndStart();
+
+ ChannelArguments args;
+ std::shared_ptr<Channel> channel =
+ CreateChannel(server_address, InsecureChannelCredentials());
+ std::unique_ptr<grpc::testing::UnimplementedEchoService::Stub> stub;
+ stub = grpc::testing::UnimplementedEchoService::NewStub(channel);
+ EchoRequest send_request;
+ EchoResponse recv_response;
+ Status recv_status;
+
+ ClientContext cli_ctx;
+ send_request.set_message("Hello");
+ std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> response_reader(
+ stub->AsyncUnimplemented(&cli_ctx, send_request, cq.get()));
+
+ response_reader->Finish(&recv_response, &recv_status, tag(4));
+ Verifier().Expect(4, true).Verify(cq.get());
+
+ EXPECT_EQ(StatusCode::UNIMPLEMENTED, recv_status.error_code());
+ EXPECT_EQ("", recv_status.error_message());
+
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+
+ server->Shutdown();
+ cq->Shutdown();
+ void* ignored_tag;
+ bool ignored_ok;
+ while (cq->Next(&ignored_tag, &ignored_ok))
+ ;
+ grpc_recycle_unused_port(port);
+}
+
+class ServerInterceptorsSyncUnimplementedEnd2endTest : public ::testing::Test {
+};
+
+TEST_F(ServerInterceptorsSyncUnimplementedEnd2endTest, UnimplementedRpcTest) {
+ DummyInterceptor::Reset();
+ int port = grpc_pick_unused_port_or_die();
+ string server_address = "localhost:" + std::to_string(port);
+ ServerBuilder builder;
+ TestServiceImpl service;
+ builder.RegisterService(&service);
+ builder.AddListeningPort(server_address, InsecureServerCredentials());
+ std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ creators;
+ for (auto i = 0; i < 20; i++) {
+ creators.push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ builder.experimental().SetInterceptorCreators(std::move(creators));
+ auto server = builder.BuildAndStart();
+
+ ChannelArguments args;
+ std::shared_ptr<Channel> channel =
+ CreateChannel(server_address, InsecureChannelCredentials());
+ std::unique_ptr<grpc::testing::UnimplementedEchoService::Stub> stub;
+ stub = grpc::testing::UnimplementedEchoService::NewStub(channel);
+ EchoRequest send_request;
+ EchoResponse recv_response;
+
+ ClientContext cli_ctx;
+ send_request.set_message("Hello");
+ Status recv_status =
+ stub->Unimplemented(&cli_ctx, send_request, &recv_response);
+
+ EXPECT_EQ(StatusCode::UNIMPLEMENTED, recv_status.error_code());
+ EXPECT_EQ("", recv_status.error_message());
+
+ // Make sure all 20 dummy interceptors were run
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 20);
+
+ server->Shutdown();
+ grpc_recycle_unused_port(port);
+}
+
+} // namespace
+} // namespace testing
+} // namespace grpc
+
+int main(int argc, char** argv) {
+ grpc_test_init(argc, argv);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/cpp/end2end/test_service_impl.cc b/test/cpp/end2end/test_service_impl.cc
index 3c3a5d9cd4..605356724f 100644
--- a/test/cpp/end2end/test_service_impl.cc
+++ b/test/cpp/end2end/test_service_impl.cc
@@ -165,6 +165,138 @@ Status TestServiceImpl::Echo(ServerContext* context, const EchoRequest* request,
return Status::OK;
}
+void CallbackTestServiceImpl::Echo(
+ ServerContext* context, const EchoRequest* request, EchoResponse* response,
+ experimental::ServerCallbackRpcController* controller) {
+ // A bit of sleep to make sure that short deadline tests fail
+ if (request->has_param() && request->param().server_sleep_us() > 0) {
+ // Set an alarm for that much time
+ alarm_.experimental().Set(
+ gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_time_from_micros(request->param().server_sleep_us(),
+ GPR_TIMESPAN)),
+ [this, context, request, response, controller](bool) {
+ EchoNonDelayed(context, request, response, controller);
+ });
+ } else {
+ EchoNonDelayed(context, request, response, controller);
+ }
+}
+
+void CallbackTestServiceImpl::EchoNonDelayed(
+ ServerContext* context, const EchoRequest* request, EchoResponse* response,
+ experimental::ServerCallbackRpcController* controller) {
+ if (request->has_param() && request->param().server_die()) {
+ gpr_log(GPR_ERROR, "The request should not reach application handler.");
+ GPR_ASSERT(0);
+ }
+ if (request->has_param() && request->param().has_expected_error()) {
+ const auto& error = request->param().expected_error();
+ controller->Finish(Status(static_cast<StatusCode>(error.code()),
+ error.error_message(),
+ error.binary_error_details()));
+ }
+ int server_try_cancel = GetIntValueFromMetadata(
+ kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
+ if (server_try_cancel > DO_NOT_CANCEL) {
+ // Since this is a unary RPC, by the time this server handler is called,
+ // the 'request' message is already read from the client. So the scenarios
+ // in server_try_cancel don't make much sense. Just cancel the RPC as long
+ // as server_try_cancel is not DO_NOT_CANCEL
+ EXPECT_FALSE(context->IsCancelled());
+ context->TryCancel();
+ gpr_log(GPR_INFO, "Server called TryCancel() to cancel the request");
+ // Now wait until it's really canceled
+
+ std::function<void(bool)> recurrence = [this, context, controller,
+ &recurrence](bool) {
+ if (!context->IsCancelled()) {
+ alarm_.experimental().Set(
+ gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+ gpr_time_from_micros(1000, GPR_TIMESPAN)),
+ recurrence);
+ } else {
+ controller->Finish(Status::CANCELLED);
+ }
+ };
+ recurrence(true);
+ return;
+ }
+
+ response->set_message(request->message());
+ MaybeEchoDeadline(context, request, response);
+ if (host_) {
+ response->mutable_param()->set_host(*host_);
+ }
+ if (request->has_param() && request->param().client_cancel_after_us()) {
+ {
+ std::unique_lock<std::mutex> lock(mu_);
+ signal_client_ = true;
+ }
+ std::function<void(bool)> recurrence = [this, context, request, controller,
+ &recurrence](bool) {
+ if (!context->IsCancelled()) {
+ alarm_.experimental().Set(
+ gpr_time_add(
+ gpr_now(GPR_CLOCK_REALTIME),
+ gpr_time_from_micros(request->param().client_cancel_after_us(),
+ GPR_TIMESPAN)),
+ recurrence);
+ } else {
+ controller->Finish(Status::CANCELLED);
+ }
+ };
+ recurrence(true);
+ return;
+ } else if (request->has_param() &&
+ request->param().server_cancel_after_us()) {
+ alarm_.experimental().Set(
+ gpr_time_add(
+ gpr_now(GPR_CLOCK_REALTIME),
+ gpr_time_from_micros(request->param().client_cancel_after_us(),
+ GPR_TIMESPAN)),
+ [controller](bool) { controller->Finish(Status::CANCELLED); });
+ return;
+ } else if (!request->has_param() ||
+ !request->param().skip_cancelled_check()) {
+ EXPECT_FALSE(context->IsCancelled());
+ }
+
+ if (request->has_param() && request->param().echo_metadata()) {
+ const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
+ context->client_metadata();
+ for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator
+ iter = client_metadata.begin();
+ iter != client_metadata.end(); ++iter) {
+ context->AddTrailingMetadata(ToString(iter->first),
+ ToString(iter->second));
+ }
+ // Terminate rpc with error and debug info in trailer.
+ if (request->param().debug_info().stack_entries_size() ||
+ !request->param().debug_info().detail().empty()) {
+ grpc::string serialized_debug_info =
+ request->param().debug_info().SerializeAsString();
+ context->AddTrailingMetadata(kDebugInfoTrailerKey, serialized_debug_info);
+ controller->Finish(Status::CANCELLED);
+ }
+ }
+ if (request->has_param() &&
+ (request->param().expected_client_identity().length() > 0 ||
+ request->param().check_auth_context())) {
+ CheckServerAuthContext(context,
+ request->param().expected_transport_security_type(),
+ request->param().expected_client_identity());
+ }
+ if (request->has_param() && request->param().response_message_length() > 0) {
+ response->set_message(
+ grpc::string(request->param().response_message_length(), '\0'));
+ }
+ if (request->has_param() && request->param().echo_peer()) {
+ response->mutable_param()->set_peer(context->peer());
+ }
+ controller->Finish(Status::OK);
+}
+
// Unimplemented is left unimplemented to test the returned error.
Status TestServiceImpl::RequestStream(ServerContext* context,
@@ -332,7 +464,8 @@ Status TestServiceImpl::BidiStream(
return Status::OK;
}
-int TestServiceImpl::GetIntValueFromMetadata(
+namespace {
+int GetIntValueFromMetadataHelper(
const char* key,
const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
int default_value) {
@@ -344,6 +477,21 @@ int TestServiceImpl::GetIntValueFromMetadata(
return default_value;
}
+}; // namespace
+
+int TestServiceImpl::GetIntValueFromMetadata(
+ const char* key,
+ const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
+ int default_value) {
+ return GetIntValueFromMetadataHelper(key, metadata, default_value);
+}
+
+int CallbackTestServiceImpl::GetIntValueFromMetadata(
+ const char* key,
+ const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
+ int default_value) {
+ return GetIntValueFromMetadataHelper(key, metadata, default_value);
+}
void TestServiceImpl::ServerTryCancel(ServerContext* context) {
EXPECT_FALSE(context->IsCancelled());
diff --git a/test/cpp/end2end/test_service_impl.h b/test/cpp/end2end/test_service_impl.h
index 052543a03e..ddfe94487e 100644
--- a/test/cpp/end2end/test_service_impl.h
+++ b/test/cpp/end2end/test_service_impl.h
@@ -22,6 +22,7 @@
#include <mutex>
#include <grpc/grpc.h>
+#include <grpcpp/alarm.h>
#include <grpcpp/server_context.h>
#include "src/proto/grpc/testing/echo.grpc.pb.h"
@@ -78,7 +79,39 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
void ServerTryCancel(ServerContext* context);
+ bool signal_client_;
+ std::mutex mu_;
+ std::unique_ptr<grpc::string> host_;
+};
+
+class CallbackTestServiceImpl
+ : public ::grpc::testing::EchoTestService::ExperimentalCallbackService {
+ public:
+ CallbackTestServiceImpl() : signal_client_(false), host_() {}
+ explicit CallbackTestServiceImpl(const grpc::string& host)
+ : signal_client_(false), host_(new grpc::string(host)) {}
+
+ void Echo(ServerContext* context, const EchoRequest* request,
+ EchoResponse* response,
+ experimental::ServerCallbackRpcController* controller) override;
+
+ // Unimplemented is left unimplemented to test the returned error.
+ bool signal_client() {
+ std::unique_lock<std::mutex> lock(mu_);
+ return signal_client_;
+ }
+
private:
+ void EchoNonDelayed(ServerContext* context, const EchoRequest* request,
+ EchoResponse* response,
+ experimental::ServerCallbackRpcController* controller);
+
+ int GetIntValueFromMetadata(
+ const char* key,
+ const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
+ int default_value);
+
+ Alarm alarm_;
bool signal_client_;
std::mutex mu_;
std::unique_ptr<grpc::string> host_;
diff --git a/test/cpp/interop/client_helper.h b/test/cpp/interop/client_helper.h
index eada2f671f..7dee85cc98 100644
--- a/test/cpp/interop/client_helper.h
+++ b/test/cpp/interop/client_helper.h
@@ -19,10 +19,12 @@
#ifndef GRPC_TEST_CPP_INTEROP_CLIENT_HELPER_H
#define GRPC_TEST_CPP_INTEROP_CLIENT_HELPER_H
+#include <functional>
#include <memory>
#include <unordered_map>
#include <grpcpp/channel.h>
+#include <grpcpp/client_context.h>
#include "src/core/lib/surface/call_test_only.h"
diff --git a/test/cpp/util/channel_trace_proto_helper.cc b/test/cpp/util/channel_trace_proto_helper.cc
index 42a436d49b..ff9d887385 100644
--- a/test/cpp/util/channel_trace_proto_helper.cc
+++ b/test/cpp/util/channel_trace_proto_helper.cc
@@ -82,6 +82,11 @@ void ValidateGetChannelResponseProtoJsonTranslation(char* json_c_str) {
json_c_str);
}
+void ValidateGetServerResponseProtoJsonTranslation(char* json_c_str) {
+ VaidateProtoJsonTranslation<grpc::channelz::v1::GetServerResponse>(
+ json_c_str);
+}
+
void ValidateSubchannelProtoJsonTranslation(char* json_c_str) {
VaidateProtoJsonTranslation<grpc::channelz::v1::Subchannel>(json_c_str);
}
diff --git a/test/cpp/util/channel_trace_proto_helper.h b/test/cpp/util/channel_trace_proto_helper.h
index 67c363e89b..4f74e02f10 100644
--- a/test/cpp/util/channel_trace_proto_helper.h
+++ b/test/cpp/util/channel_trace_proto_helper.h
@@ -26,6 +26,7 @@ void ValidateChannelTraceProtoJsonTranslation(char* json_c_str);
void ValidateChannelProtoJsonTranslation(char* json_c_str);
void ValidateGetTopChannelsResponseProtoJsonTranslation(char* json_c_str);
void ValidateGetChannelResponseProtoJsonTranslation(char* json_c_str);
+void ValidateGetServerResponseProtoJsonTranslation(char* json_c_str);
void ValidateSubchannelProtoJsonTranslation(char* json_c_str);
void ValidateServerProtoJsonTranslation(char* json_c_str);
void ValidateGetServersResponseProtoJsonTranslation(char* json_c_str);
diff --git a/test/cpp/util/config_grpc_cli.h b/test/cpp/util/config_grpc_cli.h
index 1df7b36e2e..358884196d 100644
--- a/test/cpp/util/config_grpc_cli.h
+++ b/test/cpp/util/config_grpc_cli.h
@@ -40,11 +40,6 @@
#define GRPC_CUSTOM_TEXTFORMAT ::google::protobuf::TextFormat
#endif
-#ifndef GRPC_CUSTOM_JSONUTIL
-#include <google/protobuf/util/json_util.h>
-#define GRPC_CUSTOM_JSONUTIL ::google::protobuf::util
-#endif
-
#ifndef GRPC_CUSTOM_DISKSOURCETREE
#include <google/protobuf/compiler/importer.h>
#define GRPC_CUSTOM_DISKSOURCETREE ::google::protobuf::compiler::DiskSourceTree
@@ -63,8 +58,6 @@ typedef GRPC_CUSTOM_MERGEDDESCRIPTORDATABASE MergedDescriptorDatabase;
typedef GRPC_CUSTOM_TEXTFORMAT TextFormat;
-namespace json = GRPC_CUSTOM_JSONUTIL;
-
namespace compiler {
typedef GRPC_CUSTOM_DISKSOURCETREE DiskSourceTree;
typedef GRPC_CUSTOM_IMPORTER Importer;
diff --git a/third_party/boringssl-with-bazel b/third_party/boringssl-with-bazel
-Subproject 8149b351bf797bd80e063787886b7618f508e45
+Subproject afc30d43eef92979b05776ec0963c9cede5fb80
diff --git a/tools/bazel.rc b/tools/bazel.rc
index 088c9da63c..d33e6e086b 100644
--- a/tools/bazel.rc
+++ b/tools/bazel.rc
@@ -18,16 +18,13 @@ build:asan --action_env=ASAN_OPTIONS=detect_leaks=1:color=always
build:asan --action_env=LSAN_OPTIONS=suppressions=test/core/util/lsan_suppressions.txt:report_objects=1
build:msan --strip=never
-build:msan --copt=-fsanitize-coverage=edge
build:msan --copt=-fsanitize=memory
build:msan --copt=-O0
build:msan --copt=-fsanitize-memory-track-origins
build:msan --copt=-fsanitize-memory-use-after-dtor
build:msan --copt=-fno-omit-frame-pointer
-build:msan --copt=-fPIE
build:msan --copt=-DGPR_NO_DIRECT_SYSCALLS
build:msan --linkopt=-fsanitize=memory
-build:msan --linkopt=-fPIE
build:msan --action_env=MSAN_OPTIONS=poison_in_dtor=1
build:tsan --strip=never
diff --git a/tools/dockerfile/test/bazel/Dockerfile b/tools/dockerfile/test/bazel/Dockerfile
index 4f913dc396..0aa6209f4f 100644
--- a/tools/dockerfile/test/bazel/Dockerfile
+++ b/tools/dockerfile/test/bazel/Dockerfile
@@ -44,9 +44,10 @@ RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 t
#========================
# Bazel installation
-RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" > /etc/apt/sources.list.d/bazel.list
-RUN curl https://bazel.build/bazel-release.pub.gpg | apt-key add -
-RUN apt-get -y update && apt-get -y install bazel=0.15.0 && apt-get clean
+
+RUN apt-get update && apt-get install -y wget && apt-get clean
+RUN wget -q https://github.com/bazelbuild/bazel/releases/download/0.17.1/bazel-0.17.1-linux-x86_64 -O /usr/local/bin/bazel
+RUN chmod 755 /usr/local/bin/bazel
RUN mkdir -p /var/local/jenkins
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index 1abf6bddce..392113c284 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -943,6 +943,8 @@ include/grpcpp/impl/codegen/async_unary_call.h \
include/grpcpp/impl/codegen/byte_buffer.h \
include/grpcpp/impl/codegen/call.h \
include/grpcpp/impl/codegen/call_hook.h \
+include/grpcpp/impl/codegen/call_op_set.h \
+include/grpcpp/impl/codegen/call_op_set_interface.h \
include/grpcpp/impl/codegen/callback_common.h \
include/grpcpp/impl/codegen/channel_interface.h \
include/grpcpp/impl/codegen/client_callback.h \
@@ -957,7 +959,9 @@ include/grpcpp/impl/codegen/core_codegen.h \
include/grpcpp/impl/codegen/core_codegen_interface.h \
include/grpcpp/impl/codegen/create_auth_context.h \
include/grpcpp/impl/codegen/grpc_library.h \
+include/grpcpp/impl/codegen/intercepted_channel.h \
include/grpcpp/impl/codegen/interceptor.h \
+include/grpcpp/impl/codegen/interceptor_common.h \
include/grpcpp/impl/codegen/metadata_map.h \
include/grpcpp/impl/codegen/method_handler_impl.h \
include/grpcpp/impl/codegen/proto_buffer_reader.h \
@@ -967,7 +971,9 @@ include/grpcpp/impl/codegen/rpc_method.h \
include/grpcpp/impl/codegen/rpc_service_method.h \
include/grpcpp/impl/codegen/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \
+include/grpcpp/impl/codegen/server_callback.h \
include/grpcpp/impl/codegen/server_context.h \
+include/grpcpp/impl/codegen/server_interceptor.h \
include/grpcpp/impl/codegen/server_interface.h \
include/grpcpp/impl/codegen/service_type.h \
include/grpcpp/impl/codegen/slice.h \
@@ -1003,6 +1009,7 @@ include/grpcpp/support/client_callback.h \
include/grpcpp/support/config.h \
include/grpcpp/support/proto_buffer_reader.h \
include/grpcpp/support/proto_buffer_writer.h \
+include/grpcpp/support/server_callback.h \
include/grpcpp/support/slice.h \
include/grpcpp/support/status.h \
include/grpcpp/support/status_code_enum.h \
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 26e671668e..33dd985914 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -944,6 +944,8 @@ include/grpcpp/impl/codegen/async_unary_call.h \
include/grpcpp/impl/codegen/byte_buffer.h \
include/grpcpp/impl/codegen/call.h \
include/grpcpp/impl/codegen/call_hook.h \
+include/grpcpp/impl/codegen/call_op_set.h \
+include/grpcpp/impl/codegen/call_op_set_interface.h \
include/grpcpp/impl/codegen/callback_common.h \
include/grpcpp/impl/codegen/channel_interface.h \
include/grpcpp/impl/codegen/client_callback.h \
@@ -959,7 +961,9 @@ include/grpcpp/impl/codegen/core_codegen.h \
include/grpcpp/impl/codegen/core_codegen_interface.h \
include/grpcpp/impl/codegen/create_auth_context.h \
include/grpcpp/impl/codegen/grpc_library.h \
+include/grpcpp/impl/codegen/intercepted_channel.h \
include/grpcpp/impl/codegen/interceptor.h \
+include/grpcpp/impl/codegen/interceptor_common.h \
include/grpcpp/impl/codegen/metadata_map.h \
include/grpcpp/impl/codegen/method_handler_impl.h \
include/grpcpp/impl/codegen/proto_buffer_reader.h \
@@ -969,7 +973,9 @@ include/grpcpp/impl/codegen/rpc_method.h \
include/grpcpp/impl/codegen/rpc_service_method.h \
include/grpcpp/impl/codegen/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \
+include/grpcpp/impl/codegen/server_callback.h \
include/grpcpp/impl/codegen/server_context.h \
+include/grpcpp/impl/codegen/server_interceptor.h \
include/grpcpp/impl/codegen/server_interface.h \
include/grpcpp/impl/codegen/service_type.h \
include/grpcpp/impl/codegen/slice.h \
@@ -1005,6 +1011,7 @@ include/grpcpp/support/client_callback.h \
include/grpcpp/support/config.h \
include/grpcpp/support/proto_buffer_reader.h \
include/grpcpp/support/proto_buffer_writer.h \
+include/grpcpp/support/server_callback.h \
include/grpcpp/support/slice.h \
include/grpcpp/support/status.h \
include/grpcpp/support/status_code_enum.h \
diff --git a/tools/internal_ci/helper_scripts/prepare_build_macos_rc b/tools/internal_ci/helper_scripts/prepare_build_macos_rc
index 3212e80854..4b7477db14 100644
--- a/tools/internal_ci/helper_scripts/prepare_build_macos_rc
+++ b/tools/internal_ci/helper_scripts/prepare_build_macos_rc
@@ -93,6 +93,7 @@ git submodule update --init
# Store intermediate build files of ObjC tests into /tmpfs
mkdir /tmpfs/Build-ios-binary-size
ln -s /tmpfs/Build-ios-binary-size src/objective-c/examples/Sample/Build
-mkdir /tmpfs/DerivedData
+mkdir -p /tmpfs/DerivedData
rm -rf ~/Library/Developer/Xcode/DerivedData
+mkdir -p ~/Library/Developer/Xcode
ln -s /tmpfs/DerivedData ~/Library/Developer/Xcode/DerivedData
diff --git a/tools/internal_ci/linux/grpc_asan_on_foundry.sh b/tools/internal_ci/linux/grpc_asan_on_foundry.sh
index 24fbdf74c2..87ec60c7c0 100755
--- a/tools/internal_ci/linux/grpc_asan_on_foundry.sh
+++ b/tools/internal_ci/linux/grpc_asan_on_foundry.sh
@@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+set -ex
+
export UPLOAD_TEST_RESULTS=true
github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh --config=asan --cache_test_results=no
diff --git a/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh b/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh
index 768e15b380..74778d9d29 100755
--- a/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh
+++ b/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh
@@ -21,23 +21,26 @@ set -ex
mkdir -p ${KOKORO_KEYSTORE_DIR}
cp ${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json ${KOKORO_KEYSTORE_DIR}/4321_grpc-testing-service
-temp_dir=$(mktemp -d)
-ln -f "${KOKORO_GFILE_DIR}/bazel-latest-release" ${temp_dir}/bazel
-chmod 755 "${KOKORO_GFILE_DIR}/bazel-latest-release"
+# Download bazel
+temp_dir="$(mktemp -d)"
+wget -q https://github.com/bazelbuild/bazel/releases/download/0.17.1/bazel-0.17.1-linux-x86_64 -O "${temp_dir}/bazel"
+chmod 755 "${temp_dir}/bazel"
export PATH="${temp_dir}:${PATH}"
# This should show ${temp_dir}/bazel
which bazel
-chmod +x "${KOKORO_GFILE_DIR}/bazel_wrapper.py"
# change to grpc repo root
cd $(dirname $0)/../../..
source tools/internal_ci/helper_scripts/prepare_build_linux_rc
-export KOKORO_FOUNDRY_PROJECT_ID="projects/grpc-testing/instances/default_instance"
+# to get "bazel" link for kokoro build, we need to generate
+# invocation UUID, set an env var for bazel to pick it up
+# and upload "bazel_invocation_ids" file as artifact.
+export BAZEL_INTERNAL_INVOCATION_ID="$(uuidgen)"
+echo "${BAZEL_INTERNAL_INVOCATION_ID}" >"${KOKORO_ARTIFACTS_DIR}/bazel_invocation_ids"
-# TODO(adelez): implement size for test targets and change test_timeout back
-"${KOKORO_GFILE_DIR}/bazel_wrapper.py" \
+bazel \
--bazelrc=tools/remote_build/kokoro.bazelrc \
test \
$@ \
diff --git a/tools/internal_ci/linux/grpc_bazel_on_foundry_dbg.sh b/tools/internal_ci/linux/grpc_bazel_on_foundry_dbg.sh
index 51cb66f5b8..d3cd9c20cc 100644
--- a/tools/internal_ci/linux/grpc_bazel_on_foundry_dbg.sh
+++ b/tools/internal_ci/linux/grpc_bazel_on_foundry_dbg.sh
@@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+set -ex
+
export UPLOAD_TEST_RESULTS=true
EXTRA_FLAGS="-c dbg --test_timeout=300,450,1200,3600 --cache_test_results=no"
github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}"
diff --git a/tools/internal_ci/linux/grpc_bazel_on_foundry_opt.sh b/tools/internal_ci/linux/grpc_bazel_on_foundry_opt.sh
index cbba9067ad..48b3f9e674 100644
--- a/tools/internal_ci/linux/grpc_bazel_on_foundry_opt.sh
+++ b/tools/internal_ci/linux/grpc_bazel_on_foundry_opt.sh
@@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+set -ex
+
export UPLOAD_TEST_RESULTS=true
EXTRA_FLAGS="-c opt --test_timeout=300,450,1200,3600 --cache_test_results=no"
github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}"
diff --git a/tools/internal_ci/linux/grpc_e2e_performance_singlevm.sh b/tools/internal_ci/linux/grpc_e2e_performance_singlevm.sh
index 3c9798d375..21f9d48ac4 100755
--- a/tools/internal_ci/linux/grpc_e2e_performance_singlevm.sh
+++ b/tools/internal_ci/linux/grpc_e2e_performance_singlevm.sh
@@ -21,7 +21,7 @@ source tools/internal_ci/helper_scripts/prepare_build_linux_perf_multilang_rc
# "smoketest" scenarios on a single VM (=no remote VM for running qps_workers)
tools/run_tests/run_performance_tests.py \
- -l c++ csharp ruby java python go php7 php7_protobuf_c node node_purejs \
+ -l c++ csharp ruby java python go php7 php7_protobuf_c node \
--netperf \
--category smoketest \
-u kbuilder \
diff --git a/tools/internal_ci/linux/grpc_full_performance_master.sh b/tools/internal_ci/linux/grpc_full_performance_master.sh
index 24ee71edd1..05a014b88a 100755
--- a/tools/internal_ci/linux/grpc_full_performance_master.sh
+++ b/tools/internal_ci/linux/grpc_full_performance_master.sh
@@ -21,7 +21,7 @@ source tools/internal_ci/helper_scripts/prepare_build_linux_perf_multilang_rc
# run 8core client vs 8core server
tools/run_tests/run_performance_tests.py \
- -l c++ csharp ruby java python go php7 php7_protobuf_c node node_purejs \
+ -l c++ csharp ruby java python go php7 php7_protobuf_c node \
--netperf \
--category scalable \
--remote_worker_host grpc-kokoro-performance-server-8core grpc-kokoro-performance-client-8core grpc-kokoro-performance-client2-8core \
diff --git a/tools/internal_ci/linux/grpc_interop_matrix.cfg b/tools/internal_ci/linux/grpc_interop_matrix.cfg
index 696a55c0df..e13c26e8ba 100644
--- a/tools/internal_ci/linux/grpc_interop_matrix.cfg
+++ b/tools/internal_ci/linux/grpc_interop_matrix.cfg
@@ -16,7 +16,6 @@
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/linux/grpc_interop_matrix.sh"
-# grpc_interop tests can take 1 hours to complete.
timeout_mins: 300
action {
define_artifacts {
@@ -24,3 +23,8 @@ action {
regex: "github/grpc/reports/**"
}
}
+
+env_vars {
+ key: "RUN_TESTS_FLAGS"
+ value: "--language=all --release=all --allow_flakes --report_file=sponge_log.xml --bq_result_table interop_results"
+}
diff --git a/tools/internal_ci/linux/grpc_interop_matrix.sh b/tools/internal_ci/linux/grpc_interop_matrix.sh
index 4c24c43488..a5220ea087 100755
--- a/tools/internal_ci/linux/grpc_interop_matrix.sh
+++ b/tools/internal_ci/linux/grpc_interop_matrix.sh
@@ -22,4 +22,4 @@ cd $(dirname $0)/../../..
source tools/internal_ci/helper_scripts/prepare_build_linux_rc
-tools/interop_matrix/run_interop_matrix_tests.py --language=all --release=all --allow_flakes --report_file=sponge_log.xml --bq_result_table interop_results $@
+tools/interop_matrix/run_interop_matrix_tests.py $RUN_TESTS_FLAGS
diff --git a/tools/internal_ci/linux/grpc_msan_on_foundry.sh b/tools/internal_ci/linux/grpc_msan_on_foundry.sh
index 1ef13ef0d4..c7e4b1fa1a 100644
--- a/tools/internal_ci/linux/grpc_msan_on_foundry.sh
+++ b/tools/internal_ci/linux/grpc_msan_on_foundry.sh
@@ -15,67 +15,5 @@
set -ex
-# A temporary solution to give Kokoro credentials.
-# The file name 4321_grpc-testing-service needs to match auth_credential in
-# the build config.
-# TODO: Use keystore.
-mkdir -p ${KOKORO_KEYSTORE_DIR}
-cp ${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json ${KOKORO_KEYSTORE_DIR}/4321_grpc-testing-service
-
-temp_dir=$(mktemp -d)
-ln -f "${KOKORO_GFILE_DIR}/bazel-latest-release" ${temp_dir}/bazel
-chmod 755 "${KOKORO_GFILE_DIR}/bazel-latest-release"
-export PATH="${temp_dir}:${PATH}"
-# This should show ${temp_dir}/bazel
-which bazel
-chmod +x "${KOKORO_GFILE_DIR}/bazel_wrapper.py"
-
-# change to grpc repo root
-cd $(dirname $0)/../../..
-
-source tools/internal_ci/helper_scripts/prepare_build_linux_rc
-
-export KOKORO_FOUNDRY_PROJECT_ID="projects/grpc-testing/instances/default_instance"
-
-"${KOKORO_GFILE_DIR}/bazel_wrapper.py" \
- --host_jvm_args=-Dbazel.DigestFunction=SHA256 \
- test --jobs="200" \
- --test_timeout="3600,3600,3600,3600" \
- --test_output=errors \
- --verbose_failures=true \
- --keep_going \
- --remote_accept_cached=true \
- --spawn_strategy=remote \
- --remote_local_fallback=false \
- --remote_timeout=3600 \
- --strategy=Javac=remote \
- --strategy=Closure=remote \
- --genrule_strategy=remote \
- --experimental_strict_action_env=true \
- --define GRPC_PORT_ISOLATED_RUNTIME=1 \
- --copt=-gmlt \
- --strip=never \
- --cxxopt=--stdlib=libc++ \
- --copt=-fsanitize=memory \
- --linkopt=-fsanitize=memory \
- --copt=-fsanitize-memory-track-origins \
- --action_env=LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH \
- --host_crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/ubuntu16_04_clang/1.0/bazel_0.16.1/default:toolchain \
- --crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/ubuntu16_04_clang/1.0/bazel_0.16.1/msan:toolchain \
- --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 \
- --extra_toolchains=@com_github_bazelbuild_bazeltoolchains//configs/ubuntu16_04_clang/1.0/bazel_0.16.1/cpp:cc-toolchain-clang-x86_64-default \
- --extra_execution_platforms=//third_party/toolchains:rbe_ubuntu1604 \
- --host_platform=//third_party/toolchains:rbe_ubuntu1604 \
- --platforms=//third_party/toolchains:rbe_ubuntu1604 \
- --test_env=GRPC_VERBOSITY=debug \
- --remote_instance_name=projects/grpc-testing/instances/default_instance \
- -- //test/... || FAILED="true"
-
-# Sleep to let ResultStore finish writing results before querying
-sleep 60
-python ./tools/run_tests/python_utils/upload_rbe_results.py
-
-if [ "$FAILED" != "" ]
-then
- exit 1
-fi
+export UPLOAD_TEST_RESULTS=true
+github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh --config=msan --cache_test_results=no
diff --git a/tools/internal_ci/linux/grpc_tsan_on_foundry.sh b/tools/internal_ci/linux/grpc_tsan_on_foundry.sh
index 7686794784..fcf3095d48 100644
--- a/tools/internal_ci/linux/grpc_tsan_on_foundry.sh
+++ b/tools/internal_ci/linux/grpc_tsan_on_foundry.sh
@@ -13,5 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+set -ex
+
export UPLOAD_TEST_RESULTS=true
github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh --config=tsan --cache_test_results=no
diff --git a/tools/internal_ci/linux/grpc_ubsan_on_foundry.sh b/tools/internal_ci/linux/grpc_ubsan_on_foundry.sh
index 4350cb1ba5..f45be4d1c9 100644
--- a/tools/internal_ci/linux/grpc_ubsan_on_foundry.sh
+++ b/tools/internal_ci/linux/grpc_ubsan_on_foundry.sh
@@ -13,5 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+set -ex
+
export UPLOAD_TEST_RESULTS=true
github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh --config=ubsan --cache_test_results=no
diff --git a/tools/internal_ci/linux/pull_request/grpc_asan_on_foundry.sh b/tools/internal_ci/linux/pull_request/grpc_asan_on_foundry.sh
index 84b65a3eb1..00f92921de 100644
--- a/tools/internal_ci/linux/pull_request/grpc_asan_on_foundry.sh
+++ b/tools/internal_ci/linux/pull_request/grpc_asan_on_foundry.sh
@@ -13,5 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+set -ex
+
github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh --config=asan
diff --git a/tools/internal_ci/linux/pull_request/grpc_bazel_on_foundry_dbg.sh b/tools/internal_ci/linux/pull_request/grpc_bazel_on_foundry_dbg.sh
index 8e2aaebaee..eb1c7320a7 100644
--- a/tools/internal_ci/linux/pull_request/grpc_bazel_on_foundry_dbg.sh
+++ b/tools/internal_ci/linux/pull_request/grpc_bazel_on_foundry_dbg.sh
@@ -13,5 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+set -ex
+
EXTRA_FLAGS="-c dbg --test_timeout=300,450,1200,3600"
github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}"
diff --git a/tools/internal_ci/linux/pull_request/grpc_bazel_on_foundry_opt.sh b/tools/internal_ci/linux/pull_request/grpc_bazel_on_foundry_opt.sh
index ded0d368a5..f179dc9483 100644
--- a/tools/internal_ci/linux/pull_request/grpc_bazel_on_foundry_opt.sh
+++ b/tools/internal_ci/linux/pull_request/grpc_bazel_on_foundry_opt.sh
@@ -13,5 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+set -ex
+
EXTRA_FLAGS="-c opt --test_timeout=300,450,1200,3600"
github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}"
diff --git a/tools/internal_ci/linux/pull_request/grpc_interop_matrix_adhoc.cfg b/tools/internal_ci/linux/pull_request/grpc_interop_matrix_adhoc.cfg
new file mode 100644
index 0000000000..6726384f18
--- /dev/null
+++ b/tools/internal_ci/linux/pull_request/grpc_interop_matrix_adhoc.cfg
@@ -0,0 +1,30 @@
+# Copyright 2017 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# 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_interop_matrix.sh"
+timeout_mins: 300
+action {
+ define_artifacts {
+ regex: "**/sponge_log.xml"
+ regex: "github/grpc/reports/**"
+ }
+}
+
+env_vars {
+ key: "RUN_TESTS_FLAGS"
+ value: "--language=all --release=all --allow_flakes --report_file=sponge_log.xml"
+}
diff --git a/tools/internal_ci/linux/pull_request/grpc_tsan_on_foundry.sh b/tools/internal_ci/linux/pull_request/grpc_tsan_on_foundry.sh
index 4a9687f7e2..f3e98e63da 100644
--- a/tools/internal_ci/linux/pull_request/grpc_tsan_on_foundry.sh
+++ b/tools/internal_ci/linux/pull_request/grpc_tsan_on_foundry.sh
@@ -13,4 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+set -ex
+
github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh --config=tsan
diff --git a/tools/internal_ci/linux/pull_request/grpc_ubsan_on_foundry.sh b/tools/internal_ci/linux/pull_request/grpc_ubsan_on_foundry.sh
index fb69d73635..b94935eab1 100644
--- a/tools/internal_ci/linux/pull_request/grpc_ubsan_on_foundry.sh
+++ b/tools/internal_ci/linux/pull_request/grpc_ubsan_on_foundry.sh
@@ -13,4 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+set -ex
+
github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh --config=ubsan
diff --git a/tools/internal_ci/macos/grpc_basictests_objc_dbg.cfg b/tools/internal_ci/macos/grpc_basictests_objc_dbg.cfg
new file mode 100644
index 0000000000..068961234b
--- /dev/null
+++ b/tools/internal_ci/macos/grpc_basictests_objc_dbg.cfg
@@ -0,0 +1,31 @@
+# Copyright 2018 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/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 120
+action {
+ define_artifacts {
+ regex: "**/*sponge_log.*"
+ regex: "github/grpc/reports/**"
+ }
+}
+
+env_vars {
+ key: "RUN_TESTS_FLAGS"
+ value: "-f basictests macos objc dbg --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/macos/grpc_basictests_objc_opt.cfg b/tools/internal_ci/macos/grpc_basictests_objc_opt.cfg
new file mode 100644
index 0000000000..927fa50deb
--- /dev/null
+++ b/tools/internal_ci/macos/grpc_basictests_objc_opt.cfg
@@ -0,0 +1,31 @@
+# Copyright 2018 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/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 120
+action {
+ define_artifacts {
+ regex: "**/*sponge_log.*"
+ regex: "github/grpc/reports/**"
+ }
+}
+
+env_vars {
+ key: "RUN_TESTS_FLAGS"
+ value: "-f basictests macos objc opt --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
+}
diff --git a/tools/internal_ci/macos/pull_request/grpc_basictests_objc_dbg.cfg b/tools/internal_ci/macos/pull_request/grpc_basictests_objc_dbg.cfg
new file mode 100644
index 0000000000..775fd355a5
--- /dev/null
+++ b/tools/internal_ci/macos/pull_request/grpc_basictests_objc_dbg.cfg
@@ -0,0 +1,31 @@
+# Copyright 2018 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/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 120
+action {
+ define_artifacts {
+ regex: "**/*sponge_log.*"
+ regex: "github/grpc/reports/**"
+ }
+}
+
+env_vars {
+ key: "RUN_TESTS_FLAGS"
+ value: "-f basictests macos objc dbg --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
+}
diff --git a/tools/internal_ci/macos/pull_request/grpc_basictests_objc_opt.cfg b/tools/internal_ci/macos/pull_request/grpc_basictests_objc_opt.cfg
new file mode 100644
index 0000000000..652ef1bb77
--- /dev/null
+++ b/tools/internal_ci/macos/pull_request/grpc_basictests_objc_opt.cfg
@@ -0,0 +1,31 @@
+# Copyright 2018 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/macos/grpc_run_tests_matrix.sh"
+gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
+timeout_mins: 120
+action {
+ define_artifacts {
+ regex: "**/*sponge_log.*"
+ regex: "github/grpc/reports/**"
+ }
+}
+
+env_vars {
+ key: "RUN_TESTS_FLAGS"
+ value: "-f basictests macos objc opt --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
+}
diff --git a/tools/interop_matrix/client_matrix.py b/tools/interop_matrix/client_matrix.py
index d18c81df86..ff3344cd95 100644
--- a/tools/interop_matrix/client_matrix.py
+++ b/tools/interop_matrix/client_matrix.py
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Dictionaries used for client matrix testing.
+# Defines languages, runtimes and releases for backward compatibility testing
def get_github_repo(lang):
@@ -53,8 +53,7 @@ LANG_RUNTIME_MATRIX = {
'csharp': ['csharp', 'csharpcoreclr'],
}
-# Dictionary of releases per language. For each language, we need to provide
-# a release tag pointing to the latest build of the branch.
+# Dictionary of known releases for given language.
LANG_RELEASE_MATRIX = {
'cxx': [
{
@@ -205,6 +204,9 @@ LANG_RELEASE_MATRIX = {
{
'v1.15.0': None
},
+ {
+ 'v1.16.1': None
+ },
],
'python': [
{
diff --git a/tools/interop_matrix/run_interop_matrix_tests.py b/tools/interop_matrix/run_interop_matrix_tests.py
index 6cd6f43167..6cf2a9b036 100755
--- a/tools/interop_matrix/run_interop_matrix_tests.py
+++ b/tools/interop_matrix/run_interop_matrix_tests.py
@@ -26,7 +26,7 @@ import subprocess
import sys
import uuid
-# Langauage Runtime Matrix
+# Language Runtime Matrix
import client_matrix
python_util_dir = os.path.abspath(
@@ -37,6 +37,9 @@ import jobset
import report_utils
import upload_test_results
+_TEST_TIMEOUT_SECONDS = 60
+_PULL_IMAGE_TIMEOUT_SECONDS = 15 * 60
+_MAX_PARALLEL_DOWNLOADS = 6
_LANGUAGES = client_matrix.LANG_RUNTIME_MATRIX.keys()
# All gRPC release tags, flattened, deduped and sorted.
_RELEASES = sorted(
@@ -45,7 +48,6 @@ _RELEASES = sorted(
client_matrix.get_release_tag_name(info)
for lang in client_matrix.LANG_RELEASE_MATRIX.values()
for info in lang)))
-_TEST_TIMEOUT = 60
argp = argparse.ArgumentParser(description='Run interop tests.')
argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int)
@@ -56,7 +58,7 @@ argp.add_argument(
argp.add_argument(
'--release',
default='all',
- choices=['all', 'master'] + _RELEASES,
+ choices=['all'] + _RELEASES,
help='Release tags to test. When testing all '
'releases defined in client_matrix.py, use "all".')
argp.add_argument(
@@ -92,136 +94,154 @@ argp.add_argument(
nargs='?',
help='The gateway to backend services.')
-args = argp.parse_args()
-
-print(str(args))
-
-def find_all_images_for_lang(lang):
+def _get_test_images_for_lang(lang, release_arg, image_path_prefix):
"""Find docker images for a language across releases and runtimes.
Returns dictionary of list of (<tag>, <image-full-path>) keyed by runtime.
"""
- # Find all defined releases.
- if args.release == 'all':
- releases = ['master'] + client_matrix.get_release_tags(lang)
+ if release_arg == 'all':
+ # Use all defined releases for given language
+ releases = client_matrix.get_release_tags(lang)
else:
# Look for a particular release.
- if args.release not in ['master'
- ] + client_matrix.get_release_tags(lang):
+ if release_arg not in client_matrix.get_release_tags(lang):
jobset.message(
'SKIPPED',
- '%s for %s is not defined' % (args.release, lang),
+ 'release %s for %s is not defined' % (release_arg, lang),
do_newline=True)
return {}
- releases = [args.release]
+ releases = [release_arg]
- # TODO(jtattermusch): why do we need to query the existing images/tags?
- # From LANG_RUNTIME_MATRIX and LANG_RELEASE_MATRIX it should be obvious
- # which tags we want to test - and it should be an error if they are
- # missing.
# Images tuples keyed by runtime.
images = {}
for runtime in client_matrix.LANG_RUNTIME_MATRIX[lang]:
- image_path = '%s/grpc_interop_%s' % (args.gcr_path, runtime)
- output = subprocess.check_output([
- 'gcloud', 'beta', 'container', 'images', 'list-tags',
- '--format=json', image_path
- ])
- docker_image_list = json.loads(output)
- # All images should have a single tag or no tag.
- # TODO(adelez): Remove tagless images.
- tags = [i['tags'][0] for i in docker_image_list if i['tags']]
- jobset.message(
- 'START',
- 'Found images for %s: %s' % (image_path, tags),
- do_newline=True)
- skipped = len(docker_image_list) - len(tags)
- jobset.message(
- 'SKIPPED',
- 'Skipped images (no-tag/unknown-tag): %d' % skipped,
- do_newline=True)
- # Filter tags based on the releases.
- images[runtime] = [(tag, '%s:%s' % (image_path, tag))
- for tag in tags
- if tag in releases]
+ image_path = '%s/grpc_interop_%s' % (image_path_prefix, runtime)
+ images[runtime] = [
+ (tag, '%s:%s' % (image_path, tag)) for tag in releases
+ ]
return images
-# caches test cases (list of JobSpec) loaded from file. Keyed by lang and runtime.
-def find_test_cases(lang, runtime, release, suite_name):
- """Returns the list of test cases from testcase files per lang/release."""
+def _read_test_cases_file(lang, runtime, release):
+ """Read test cases from a bash-like file and return a list of commands"""
testcase_dir = os.path.join(os.path.dirname(__file__), 'testcases')
filename_prefix = lang
if lang == 'csharp':
+ # TODO(jtattermusch): remove this odd specialcase
filename_prefix = runtime
# Check to see if we need to use a particular version of test cases.
lang_version = '%s_%s' % (filename_prefix, release)
if lang_version in client_matrix.TESTCASES_VERSION_MATRIX:
- testcases = os.path.join(
+ testcase_file = os.path.join(
testcase_dir, client_matrix.TESTCASES_VERSION_MATRIX[lang_version])
else:
- testcases = os.path.join(testcase_dir, '%s__master' % filename_prefix)
+ # TODO(jtattermusch): remove the double-underscore, it is pointless
+ testcase_file = os.path.join(testcase_dir,
+ '%s__master' % filename_prefix)
+
+ lines = []
+ with open(testcase_file) as f:
+ for line in f.readlines():
+ line = re.sub('\\#.*$', '', line) # remove hash comments
+ line = line.strip()
+ if line and not line.startswith('echo'):
+ # Each non-empty line is a treated as a test case command
+ lines.append(line)
+ return lines
+
+
+def _cleanup_docker_image(image):
+ jobset.message('START', 'Cleanup docker image %s' % image, do_newline=True)
+ dockerjob.remove_image(image, skip_nonexistent=True)
+
+
+args = argp.parse_args()
+
+
+# caches test cases (list of JobSpec) loaded from file. Keyed by lang and runtime.
+def _generate_test_case_jobspecs(lang, runtime, release, suite_name):
+ """Returns the list of test cases from testcase files per lang/release."""
+ testcase_lines = _read_test_cases_file(lang, runtime, release)
job_spec_list = []
- try:
- with open(testcases) as f:
- # Only line start with 'docker run' are test cases.
- for line in f.readlines():
- if line.startswith('docker run'):
- m = re.search('--test_case=(.*)"', line)
- shortname = m.group(1) if m else 'unknown_test'
- m = re.search(
- '--server_host_override=(.*).sandbox.googleapis.com',
- line)
- server = m.group(1) if m else 'unknown_server'
-
- # If server_host arg is not None, replace the original
- # server_host with the one provided or append to the end of
- # the command if server_host does not appear originally.
- if args.server_host:
- if line.find('--server_host=') > -1:
- line = re.sub('--server_host=[^ ]*',
- '--server_host=%s' % args.server_host,
- line)
- else:
- line = '%s --server_host=%s"' % (line[:-1],
- args.server_host)
- print(line)
-
- spec = jobset.JobSpec(
- cmdline=line,
- shortname='%s:%s:%s:%s' % (suite_name, lang, server,
- shortname),
- timeout_seconds=_TEST_TIMEOUT,
- shell=True,
- flake_retries=5 if args.allow_flakes else 0)
- job_spec_list.append(spec)
- jobset.message(
- 'START',
- 'Loaded %s tests from %s' % (len(job_spec_list), testcases),
- do_newline=True)
- except IOError as err:
- jobset.message('FAILED', err, do_newline=True)
+ for line in testcase_lines:
+ # TODO(jtattermusch): revisit the logic for updating test case commands
+ # what it currently being done seems fragile.
+ m = re.search('--test_case=(.*)"', line)
+ shortname = m.group(1) if m else 'unknown_test'
+ m = re.search('--server_host_override=(.*).sandbox.googleapis.com',
+ line)
+ server = m.group(1) if m else 'unknown_server'
+
+ # If server_host arg is not None, replace the original
+ # server_host with the one provided or append to the end of
+ # the command if server_host does not appear originally.
+ if args.server_host:
+ if line.find('--server_host=') > -1:
+ line = re.sub('--server_host=[^ ]*',
+ '--server_host=%s' % args.server_host, line)
+ else:
+ line = '%s --server_host=%s"' % (line[:-1], args.server_host)
+
+ spec = jobset.JobSpec(
+ cmdline=line,
+ shortname='%s:%s:%s:%s' % (suite_name, lang, server, shortname),
+ timeout_seconds=_TEST_TIMEOUT_SECONDS,
+ shell=True,
+ flake_retries=5 if args.allow_flakes else 0)
+ job_spec_list.append(spec)
return job_spec_list
-_xml_report_tree = report_utils.new_junit_xml_tree()
+def _pull_images_for_lang(lang, images):
+ """Pull all images for given lang from container registry."""
+ jobset.message(
+ 'START', 'Downloading images for language "%s"' % lang, do_newline=True)
+ download_specs = []
+ for release, image in images:
+ # Pull the image and warm it up.
+ # First time we use an image with "docker run", it takes time to unpack
+ # the image and later this delay would fail our test cases.
+ cmdline = [
+ 'time gcloud docker -- pull %s && time docker run --rm=true %s /bin/true'
+ % (image, image)
+ ]
+ spec = jobset.JobSpec(
+ cmdline=cmdline,
+ shortname='pull_image_%s' % (image),
+ timeout_seconds=_PULL_IMAGE_TIMEOUT_SECONDS,
+ shell=True)
+ download_specs.append(spec)
+ # too many image downloads at once tend to get stuck
+ max_pull_jobs = min(args.jobs, _MAX_PARALLEL_DOWNLOADS)
+ num_failures, resultset = jobset.run(
+ download_specs, newline_on_success=True, maxjobs=max_pull_jobs)
+ if num_failures:
+ jobset.message(
+ 'FAILED', 'Failed to download some images', do_newline=True)
+ return False
+ else:
+ jobset.message(
+ 'SUCCESS', 'All images downloaded successfully.', do_newline=True)
+ return True
-def run_tests_for_lang(lang, runtime, images):
+def _run_tests_for_lang(lang, runtime, images, xml_report_tree):
"""Find and run all test cases for a language.
images is a list of (<release-tag>, <image-full-path>) tuple.
"""
+ if not _pull_images_for_lang(lang, images):
+ jobset.message(
+ 'FAILED', 'Image download failed. Exiting.', do_newline=True)
+ return 1
+
total_num_failures = 0
- for image_tuple in images:
- release, image = image_tuple
- jobset.message('START', 'Testing %s' % image, do_newline=True)
- # Download the docker image before running each test case.
- subprocess.check_call(['gcloud', 'docker', '--', 'pull', image])
+ for release, image in images:
suite_name = '%s__%s_%s' % (lang, runtime, release)
- job_spec_list = find_test_cases(lang, runtime, release, suite_name)
+ job_spec_list = _generate_test_case_jobspecs(lang, runtime, release,
+ suite_name)
if not job_spec_list:
jobset.message(
@@ -242,28 +262,24 @@ def run_tests_for_lang(lang, runtime, images):
else:
jobset.message('SUCCESS', 'All tests passed', do_newline=True)
- report_utils.append_junit_xml_results(_xml_report_tree, resultset,
+ report_utils.append_junit_xml_results(xml_report_tree, resultset,
'grpc_interop_matrix', suite_name,
str(uuid.uuid4()))
if not args.keep:
- cleanup(image)
+ _cleanup_docker_image(image)
return total_num_failures
-def cleanup(image):
- jobset.message('START', 'Cleanup docker image %s' % image, do_newline=True)
- dockerjob.remove_image(image, skip_nonexistent=True)
-
-
languages = args.language if args.language != ['all'] else _LANGUAGES
total_num_failures = 0
+_xml_report_tree = report_utils.new_junit_xml_tree()
for lang in languages:
- docker_images = find_all_images_for_lang(lang)
+ docker_images = _get_test_images_for_lang(lang, args.release, args.gcr_path)
for runtime in sorted(docker_images.keys()):
- total_num_failures += run_tests_for_lang(lang, runtime,
- docker_images[runtime])
+ total_num_failures += _run_tests_for_lang(
+ lang, runtime, docker_images[runtime], _xml_report_tree)
report_utils.create_xml_report_file(_xml_report_tree, args.report_file)
diff --git a/tools/remote_build/README.md b/tools/remote_build/README.md
index 7492de75a5..c4d03547a2 100644
--- a/tools/remote_build/README.md
+++ b/tools/remote_build/README.md
@@ -28,3 +28,6 @@ Sanitizer runs (asan, msan, tsan, ubsan):
# manual run of bazel tests remotely on Foundry with given sanitizer
bazel --bazelrc=tools/remote_build/manual.bazelrc test --config=asan //test/...
```
+
+Available command line options can be found in
+[Bazel command line reference](https://docs.bazel.build/versions/master/command-line-reference.html)
diff --git a/tools/remote_build/kokoro.bazelrc b/tools/remote_build/kokoro.bazelrc
index 0aacddf663..11462bd301 100644
--- a/tools/remote_build/kokoro.bazelrc
+++ b/tools/remote_build/kokoro.bazelrc
@@ -16,8 +16,23 @@
import %workspace%/tools/remote_build/rbe_common.bazelrc
+build --remote_cache=remotebuildexecution.googleapis.com
+build --remote_executor=remotebuildexecution.googleapis.com
+build --tls_enabled=true
+
+build --auth_enabled=true
+# magic location where kokoro script puts the credentials
+build --auth_credentials=/tmpfs/src/keystore/4321_grpc-testing-service
+build --auth_scope=https://www.googleapis.com/auth/cloud-source-tools
+
+build --bes_backend=buildeventservice.googleapis.com
+build --bes_best_effort=false
+build --bes_timeout=600s
+build --project_id=grpc-testing
+
+# required by kokoro for some reason
+build --test_env=USER=anon
+
build --jobs=200
build --test_output=errors
-build --keep_going
-build --remote_accept_cached=true
-build --remote_local_fallback=true
+build --keep_going=true
diff --git a/tools/remote_build/manual.bazelrc b/tools/remote_build/manual.bazelrc
index d67bafedf3..b4fdc70637 100644
--- a/tools/remote_build/manual.bazelrc
+++ b/tools/remote_build/manual.bazelrc
@@ -40,3 +40,6 @@ build --jobs=100
# TODO(jtattermusch): this should be part of the common config
# but currently sanitizers use different test_timeout values
build --test_timeout=300,450,1200,3600
+
+# print output for tests that fail (default is "summary")
+build --test_output=errors
diff --git a/tools/remote_build/rbe_common.bazelrc b/tools/remote_build/rbe_common.bazelrc
index cdf492a2cc..e8c7f0b9cb 100644
--- a/tools/remote_build/rbe_common.bazelrc
+++ b/tools/remote_build/rbe_common.bazelrc
@@ -49,15 +49,19 @@ build:asan --copt=-gmlt
# TODO(jtattermusch): use more reasonable test timeout
build:asan --test_timeout=3600
build:asan --test_tag_filters=-qps_json_driver,-json_run_localhost
-# TODO: revisit these from bazel.rc:
-#build:asan --copt=-O0
-#build:asan --copt=-fno-omit-frame-pointer
-#build:asan --copt=-DGPR_NO_DIRECT_SYSCALLS
-#build:asan --action_env=ASAN_OPTIONS=detect_leaks=1:color=always
-#build:asan --action_env=LSAN_OPTIONS=suppressions=test/core/util/lsan_suppressions.txt:report_objects=1
-# TODO(jtattermusch): align msan settings from tools/bazel.rc
-# and grpc_msan_on_foundry.sh
+# memory sanitizer: most settings are already in %workspace%/.bazelrc
+# we only need a few additional ones that are Foundry specific
+build:msan --copt=-gmlt
+# TODO(jtattermusch): use more reasonable test timeout
+build:msan --test_timeout=3600
+build:msan --cxxopt=--stdlib=libc++
+# setting LD_LIBRARY_PATH is necessary
+# to avoid "libc++.so.1: cannot open shared object file"
+build:msan --action_env=LD_LIBRARY_PATH=/usr/local/lib
+build:msan --host_crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/ubuntu16_04_clang/1.0/bazel_0.16.1/default:toolchain
+# override the config-agnostic crosstool_top
+build:msan --crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/ubuntu16_04_clang/1.0/bazel_0.16.1/msan:toolchain
# thread sanitizer: most settings are already in %workspace%/.bazelrc
# we only need a few additional ones that are Foundry specific
@@ -65,23 +69,14 @@ build:tsan --copt=-gmlt
# TODO(jtattermusch): use more reasonable test timeout
build:tsan --test_timeout=3600
build:tsan --test_tag_filters=-qps_json_driver,-json_run_localhost
-# TODO: revisit these from bazel.rc:
-#build:tsan --copt=-fno-omit-frame-pointer
-#build:tsan --copt=-DGPR_NO_DIRECT_SYSCALLS
-#build:tsan --copt=-DGRPC_TSAN
# undefined behavior sanitizer: most settings are already in %workspace%/.bazelrc
# we only need a few additional ones that are Foundry specific
build:ubsan --copt=-gmlt
# TODO(jtattermusch): use more reasonable test timeout
build:ubsan --test_timeout=3600
-# TODO: revisit these from bazel.rc:
-#build:ubsan --copt=-fno-omit-frame-pointer
-#build:ubsan --copt=-DGRPC_UBSAN
-#build:ubsan --copt=-DNDEBUG
-#build:ubsan --copt=-fno-sanitize=function,vptr
-# TODO: revisit this from grpc_ubsan_on_foundry.sh:
-#--crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/experimental/ubuntu16_04_clang/1.0/bazel_0.15.0/ubsan:toolchain
+# override the config-agnostic crosstool_top
+--crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/experimental/ubuntu16_04_clang/1.0/bazel_0.16.1/ubsan:toolchain
# TODO(jtattermusch): remove this once Foundry adds the env to the docker image.
# ubsan needs symbolizer to work properly, otherwise the suppression file doesn't work
# and we get test failures.
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index 3a442864e8..33ac22e33c 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -3394,6 +3394,28 @@
"grpc++_test_util",
"grpc_test_util"
],
+ "headers": [
+ "test/cpp/end2end/interceptors_util.h"
+ ],
+ "is_filegroup": false,
+ "language": "c++",
+ "name": "client_interceptors_end2end_test",
+ "src": [
+ "test/cpp/end2end/client_interceptors_end2end_test.cc",
+ "test/cpp/end2end/interceptors_util.h"
+ ],
+ "third_party": false,
+ "type": "target"
+ },
+ {
+ "deps": [
+ "gpr",
+ "gpr_test_util",
+ "grpc",
+ "grpc++",
+ "grpc++_test_util",
+ "grpc_test_util"
+ ],
"headers": [],
"is_filegroup": false,
"language": "c++",
@@ -4702,6 +4724,28 @@
"deps": [
"gpr",
"gpr_test_util",
+ "grpc",
+ "grpc++",
+ "grpc++_test_util",
+ "grpc_test_util"
+ ],
+ "headers": [
+ "test/cpp/end2end/interceptors_util.h"
+ ],
+ "is_filegroup": false,
+ "language": "c++",
+ "name": "server_interceptors_end2end_test",
+ "src": [
+ "test/cpp/end2end/interceptors_util.h",
+ "test/cpp/end2end/server_interceptors_end2end_test.cc"
+ ],
+ "third_party": false,
+ "type": "target"
+ },
+ {
+ "deps": [
+ "gpr",
+ "gpr_test_util",
"grpc++_test_util_unsecure",
"grpc++_unsecure",
"grpc_test_util_unsecure",
@@ -11154,6 +11198,8 @@
"include/grpcpp/impl/codegen/byte_buffer.h",
"include/grpcpp/impl/codegen/call.h",
"include/grpcpp/impl/codegen/call_hook.h",
+ "include/grpcpp/impl/codegen/call_op_set.h",
+ "include/grpcpp/impl/codegen/call_op_set_interface.h",
"include/grpcpp/impl/codegen/callback_common.h",
"include/grpcpp/impl/codegen/channel_interface.h",
"include/grpcpp/impl/codegen/client_callback.h",
@@ -11166,14 +11212,18 @@
"include/grpcpp/impl/codegen/core_codegen_interface.h",
"include/grpcpp/impl/codegen/create_auth_context.h",
"include/grpcpp/impl/codegen/grpc_library.h",
+ "include/grpcpp/impl/codegen/intercepted_channel.h",
"include/grpcpp/impl/codegen/interceptor.h",
+ "include/grpcpp/impl/codegen/interceptor_common.h",
"include/grpcpp/impl/codegen/metadata_map.h",
"include/grpcpp/impl/codegen/method_handler_impl.h",
"include/grpcpp/impl/codegen/rpc_method.h",
"include/grpcpp/impl/codegen/rpc_service_method.h",
"include/grpcpp/impl/codegen/security/auth_context.h",
"include/grpcpp/impl/codegen/serialization_traits.h",
+ "include/grpcpp/impl/codegen/server_callback.h",
"include/grpcpp/impl/codegen/server_context.h",
+ "include/grpcpp/impl/codegen/server_interceptor.h",
"include/grpcpp/impl/codegen/server_interface.h",
"include/grpcpp/impl/codegen/service_type.h",
"include/grpcpp/impl/codegen/slice.h",
@@ -11224,6 +11274,8 @@
"include/grpcpp/impl/codegen/byte_buffer.h",
"include/grpcpp/impl/codegen/call.h",
"include/grpcpp/impl/codegen/call_hook.h",
+ "include/grpcpp/impl/codegen/call_op_set.h",
+ "include/grpcpp/impl/codegen/call_op_set_interface.h",
"include/grpcpp/impl/codegen/callback_common.h",
"include/grpcpp/impl/codegen/channel_interface.h",
"include/grpcpp/impl/codegen/client_callback.h",
@@ -11236,14 +11288,18 @@
"include/grpcpp/impl/codegen/core_codegen_interface.h",
"include/grpcpp/impl/codegen/create_auth_context.h",
"include/grpcpp/impl/codegen/grpc_library.h",
+ "include/grpcpp/impl/codegen/intercepted_channel.h",
"include/grpcpp/impl/codegen/interceptor.h",
+ "include/grpcpp/impl/codegen/interceptor_common.h",
"include/grpcpp/impl/codegen/metadata_map.h",
"include/grpcpp/impl/codegen/method_handler_impl.h",
"include/grpcpp/impl/codegen/rpc_method.h",
"include/grpcpp/impl/codegen/rpc_service_method.h",
"include/grpcpp/impl/codegen/security/auth_context.h",
"include/grpcpp/impl/codegen/serialization_traits.h",
+ "include/grpcpp/impl/codegen/server_callback.h",
"include/grpcpp/impl/codegen/server_context.h",
+ "include/grpcpp/impl/codegen/server_interceptor.h",
"include/grpcpp/impl/codegen/server_interface.h",
"include/grpcpp/impl/codegen/service_type.h",
"include/grpcpp/impl/codegen/slice.h",
@@ -11391,6 +11447,7 @@
"include/grpcpp/support/config.h",
"include/grpcpp/support/proto_buffer_reader.h",
"include/grpcpp/support/proto_buffer_writer.h",
+ "include/grpcpp/support/server_callback.h",
"include/grpcpp/support/slice.h",
"include/grpcpp/support/status.h",
"include/grpcpp/support/status_code_enum.h",
@@ -11495,6 +11552,7 @@
"include/grpcpp/support/config.h",
"include/grpcpp/support/proto_buffer_reader.h",
"include/grpcpp/support/proto_buffer_writer.h",
+ "include/grpcpp/support/server_callback.h",
"include/grpcpp/support/slice.h",
"include/grpcpp/support/status.h",
"include/grpcpp/support/status_code_enum.h",
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 145d2526e8..ef34cd6556 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -4036,6 +4036,30 @@
"posix",
"windows"
],
+ "cpu_cost": 0.5,
+ "exclude_configs": [],
+ "exclude_iomgrs": [],
+ "flaky": false,
+ "gtest": true,
+ "language": "c++",
+ "name": "client_interceptors_end2end_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": [],
@@ -5126,6 +5150,30 @@
"posix",
"windows"
],
+ "cpu_cost": 0.5,
+ "exclude_configs": [],
+ "exclude_iomgrs": [],
+ "flaky": false,
+ "gtest": true,
+ "language": "c++",
+ "name": "server_interceptors_end2end_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": [],
diff --git a/tools/run_tests/python_utils/upload_rbe_results.py b/tools/run_tests/python_utils/upload_rbe_results.py
index 74f329048f..3f3bd382bb 100644
--- a/tools/run_tests/python_utils/upload_rbe_results.py
+++ b/tools/run_tests/python_utils/upload_rbe_results.py
@@ -62,7 +62,7 @@ def _get_invocation_id():
bazel_id_directory = os.getenv('KOKORO_ARTIFACTS_DIR')
bazel_id_file = os.path.join(bazel_id_directory, 'bazel_invocation_ids')
assert os.path.isfile(bazel_id_file), 'bazel_invocation_ids file, written ' \
- 'by bazel_wrapper.py, expected but not found.'
+ 'by RBE initialization script, expected but not found.'
with open(bazel_id_file, 'r') as f:
return f.read().replace('\n', '')
diff --git a/tools/run_tests/sanity/check_submodules.sh b/tools/run_tests/sanity/check_submodules.sh
index 2c7c140716..8ea53dfec5 100755
--- a/tools/run_tests/sanity/check_submodules.sh
+++ b/tools/run_tests/sanity/check_submodules.sh
@@ -30,7 +30,7 @@ cat << EOF | awk '{ print $1 }' | sort > "$want_submodules"
5b7683f49e1e9223cf9927b24f6fd3d6bd82e3f8 third_party/benchmark (v1.2.0)
73594cde8c9a52a102c4341c244c833aa61b9c06 third_party/bloaty (remotes/origin/wide-14-g73594cd)
b29b21a81b32ec273f118f589f46d56ad3332420 third_party/boringssl (remotes/origin/chromium-stable)
- 8149b351bf797bd80e063787886b7618f508e451 third_party/boringssl-with-bazel (version_for_cocoapods_10.0-434-g8149b351)
+ afc30d43eef92979b05776ec0963c9cede5fb80f third_party/boringssl-with-bazel (fips-20180716-116-gafc30d43e)
3be1924221e1326df520f8498d704a5c4c8d0cce third_party/cares/cares (cares-1_13_0)
30dbc81fb5ffdc98ea9b14b1918bfe4e8779b26e third_party/gflags (v2.2.0-5-g30dbc81)
ec44c6c1675c25b9827aacd08c02433cccde7780 third_party/googletest (release-1.8.0)
diff --git a/tools/run_tests/sanity/core_banned_functions.py b/tools/run_tests/sanity/core_banned_functions.py
index 8afd826453..2a5dcda5be 100755
--- a/tools/run_tests/sanity/core_banned_functions.py
+++ b/tools/run_tests/sanity/core_banned_functions.py
@@ -24,7 +24,8 @@ os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '../../..'))
# map of banned function signature to whitelist
BANNED_EXCEPT = {
'grpc_resource_quota_ref(': ['src/core/lib/iomgr/resource_quota.cc'],
- 'grpc_resource_quota_unref(': ['src/core/lib/iomgr/resource_quota.cc'],
+ 'grpc_resource_quota_unref(':
+ ['src/core/lib/iomgr/resource_quota.cc', 'src/core/lib/surface/server.cc'],
'grpc_slice_buffer_destroy(': ['src/core/lib/slice/slice_buffer.cc'],
'grpc_slice_buffer_reset_and_unref(':
['src/core/lib/slice/slice_buffer.cc'],