aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.bazelrc3
-rw-r--r--.gitmodules9
-rw-r--r--BUILD22
-rw-r--r--CMakeLists.txt154
-rw-r--r--Makefile177
-rw-r--r--PYTHON-MANIFEST.in1
-rw-r--r--bazel/grpc_deps.bzl10
-rw-r--r--build.yaml62
-rw-r--r--config.m411
-rw-r--r--config.w328
-rw-r--r--doc/connectivity-semantics-and-api.md11
-rw-r--r--doc/environment_variables.md10
-rw-r--r--doc/python/sphinx/conf.py102
-rw-r--r--doc/python/sphinx/glossary.rst16
-rw-r--r--doc/python/sphinx/grpc.rst169
-rw-r--r--doc/python/sphinx/grpc_health_checking.rst7
-rw-r--r--doc/python/sphinx/grpc_reflection.rst19
-rw-r--r--doc/python/sphinx/grpc_testing.rst7
-rw-r--r--doc/python/sphinx/index.rst24
-rw-r--r--examples/objective-c/helloworld_macos/HelloWorld.podspec66
-rw-r--r--examples/objective-c/helloworld_macos/HelloWorld.xcodeproj/project.pbxproj399
-rw-r--r--examples/objective-c/helloworld_macos/HelloWorld/AppDelegate.h25
-rw-r--r--examples/objective-c/helloworld_macos/HelloWorld/AppDelegate.m37
-rw-r--r--examples/objective-c/helloworld_macos/HelloWorld/Assets.xcassets/AppIcon.appiconset/Contents.json58
-rw-r--r--examples/objective-c/helloworld_macos/HelloWorld/Assets.xcassets/Contents.json6
-rw-r--r--examples/objective-c/helloworld_macos/HelloWorld/Base.lproj/Main.storyboard717
-rw-r--r--examples/objective-c/helloworld_macos/HelloWorld/HelloWorld.entitlements5
-rw-r--r--examples/objective-c/helloworld_macos/HelloWorld/Info.plist32
-rw-r--r--examples/objective-c/helloworld_macos/HelloWorld/ViewController.h25
-rw-r--r--examples/objective-c/helloworld_macos/HelloWorld/ViewController.m37
-rw-r--r--examples/objective-c/helloworld_macos/Podfile9
-rw-r--r--examples/objective-c/helloworld_macos/README.md6
-rw-r--r--examples/objective-c/helloworld_macos/main.m43
-rw-r--r--gRPC-C++.podspec19
-rw-r--r--gRPC-Core.podspec21
-rw-r--r--grpc.def1
-rw-r--r--grpc.gemspec14
-rw-r--r--grpc.gyp22
-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.h919
-rw-r--r--include/grpcpp/impl/codegen/call_op_set_interface.h59
-rw-r--r--include/grpcpp/impl/codegen/callback_common.h58
-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.h16
-rw-r--r--include/grpcpp/impl/codegen/client_interceptor.h99
-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.h12
-rw-r--r--include/grpcpp/impl/codegen/intercepted_channel.h80
-rw-r--r--include/grpcpp/impl/codegen/interceptor.h70
-rw-r--r--include/grpcpp/impl/codegen/interceptor_common.h458
-rw-r--r--include/grpcpp/impl/codegen/metadata_map.h21
-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--package.xml14
-rw-r--r--requirements.bazel.txt5
-rw-r--r--setup.py23
-rw-r--r--src/compiler/cpp_generator.cc204
-rw-r--r--src/compiler/csharp_generator.cc37
-rw-r--r--src/core/ext/filters/client_channel/client_channel.cc478
-rw-r--r--src/core/ext/filters/client_channel/health/health_check_client.cc12
-rw-r--r--src/core/ext/filters/client_channel/health/health_check_client.h2
-rw-r--r--src/core/ext/filters/client_channel/http_connect_handshaker.cc2
-rw-r--r--src/core/ext/filters/client_channel/http_proxy.cc2
-rw-r--r--src/core/ext/filters/client_channel/lb_policy.h21
-rw-r--r--src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc26
-rw-r--r--src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc14
-rw-r--r--src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc8
-rw-r--r--src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc8
-rw-r--r--src/core/ext/filters/client_channel/lb_policy/xds/xds.cc417
-rw-r--r--src/core/ext/filters/client_channel/lb_policy_factory.h2
-rw-r--r--src/core/ext/filters/client_channel/lb_policy_registry.cc5
-rw-r--r--src/core/ext/filters/client_channel/lb_policy_registry.h4
-rw-r--r--src/core/ext/filters/client_channel/method_params.cc178
-rw-r--r--src/core/ext/filters/client_channel/method_params.h78
-rw-r--r--src/core/ext/filters/client_channel/parse_address.h2
-rw-r--r--src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc34
-rw-r--r--src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc17
-rw-r--r--src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h6
-rw-r--r--src/core/ext/filters/client_channel/resolver_factory.h2
-rw-r--r--src/core/ext/filters/client_channel/resolver_result_parsing.cc384
-rw-r--r--src/core/ext/filters/client_channel/resolver_result_parsing.h146
-rw-r--r--src/core/ext/filters/client_channel/subchannel.cc20
-rw-r--r--src/core/ext/filters/deadline/deadline_filter.cc42
-rw-r--r--src/core/ext/filters/deadline/deadline_filter.h22
-rw-r--r--src/core/ext/filters/http/client/http_client_filter.cc48
-rw-r--r--src/core/ext/filters/http/message_compress/message_compress_filter.cc44
-rw-r--r--src/core/ext/filters/http/server/http_server_filter.cc51
-rw-r--r--src/core/ext/filters/load_reporting/server_load_reporting_filter.cc2
-rw-r--r--src/core/ext/filters/message_size/message_size_filter.cc95
-rw-r--r--src/core/ext/transport/chttp2/client/chttp2_connector.cc12
-rw-r--r--src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc2
-rw-r--r--src/core/ext/transport/chttp2/server/chttp2_server.cc39
-rw-r--r--src/core/ext/transport/chttp2/transport/chttp2_transport.cc345
-rw-r--r--src/core/ext/transport/chttp2/transport/chttp2_transport.h3
-rw-r--r--src/core/ext/transport/chttp2/transport/frame_data.cc14
-rw-r--r--src/core/ext/transport/chttp2/transport/frame_data.h22
-rw-r--r--src/core/ext/transport/chttp2/transport/frame_rst_stream.cc2
-rw-r--r--src/core/ext/transport/chttp2/transport/hpack_encoder.cc53
-rw-r--r--src/core/ext/transport/chttp2/transport/incoming_metadata.cc12
-rw-r--r--src/core/ext/transport/chttp2/transport/incoming_metadata.h21
-rw-r--r--src/core/ext/transport/chttp2/transport/internal.h203
-rw-r--r--src/core/ext/transport/cronet/transport/cronet_transport.cc153
-rw-r--r--src/core/ext/transport/inproc/inproc_transport.cc576
-rw-r--r--src/core/lib/channel/channel_stack.cc9
-rw-r--r--src/core/lib/channel/channel_stack.h8
-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.cc68
-rw-r--r--src/core/lib/channel/channelz.h11
-rw-r--r--src/core/lib/channel/channelz_registry.cc29
-rw-r--r--src/core/lib/channel/channelz_registry.h6
-rw-r--r--src/core/lib/channel/context.h8
-rw-r--r--src/core/lib/gpr/arena.cc121
-rw-r--r--src/core/lib/gpr/arena.h2
-rw-r--r--src/core/lib/gprpp/ref_counted.h123
-rw-r--r--src/core/lib/gprpp/ref_counted_ptr.h11
-rw-r--r--src/core/lib/iomgr/call_combiner.cc2
-rw-r--r--src/core/lib/iomgr/call_combiner.h4
-rw-r--r--src/core/lib/iomgr/closure.h1
-rw-r--r--src/core/lib/iomgr/polling_entity.h8
-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/socket_utils_common_posix.cc11
-rw-r--r--src/core/lib/iomgr/tcp_client_custom.cc17
-rw-r--r--src/core/lib/iomgr/tcp_client_posix.cc2
-rw-r--r--src/core/lib/security/context/security_context.cc33
-rw-r--r--src/core/lib/security/context/security_context.h46
-rw-r--r--src/core/lib/security/credentials/credentials.h4
-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/security/transport/client_auth_filter.cc44
-rw-r--r--src/core/lib/security/transport/secure_endpoint.cc92
-rw-r--r--src/core/lib/security/transport/security_handshaker.cc3
-rw-r--r--src/core/lib/security/transport/server_auth_filter.cc70
-rw-r--r--src/core/lib/surface/call.cc180
-rw-r--r--src/core/lib/surface/call.h4
-rw-r--r--src/core/lib/surface/channel.cc26
-rw-r--r--src/core/lib/surface/channel.h3
-rw-r--r--src/core/lib/surface/completion_queue.cc2
-rw-r--r--src/core/lib/surface/init.cc1
-rw-r--r--src/core/lib/surface/server.cc124
-rw-r--r--src/core/lib/surface/server.h5
-rw-r--r--src/core/lib/transport/metadata_batch.h6
-rw-r--r--src/core/lib/transport/transport.h70
-rw-r--r--src/core/lib/uri/uri_parser.cc (renamed from src/core/ext/filters/client_channel/uri_parser.cc)2
-rw-r--r--src/core/lib/uri/uri_parser.h (renamed from src/core/ext/filters/client_channel/uri_parser.h)6
-rw-r--r--src/core/plugin_registry/grpc_cronet_plugin_registry.cc4
-rw-r--r--src/core/plugin_registry/grpc_plugin_registry.cc4
-rw-r--r--src/core/tsi/alts/handshaker/alts_handshaker_client.cc433
-rw-r--r--src/core/tsi/alts/handshaker/alts_handshaker_client.h82
-rw-r--r--src/core/tsi/alts/handshaker/alts_shared_resource.cc83
-rw-r--r--src/core/tsi/alts/handshaker/alts_shared_resource.h73
-rw-r--r--src/core/tsi/alts/handshaker/alts_tsi_event.cc75
-rw-r--r--src/core/tsi/alts/handshaker/alts_tsi_event.h93
-rw-r--r--src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc294
-rw-r--r--src/core/tsi/alts/handshaker/alts_tsi_handshaker.h50
-rw-r--r--src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h48
-rw-r--r--src/core/tsi/alts_transport_security.cc65
-rw-r--r--src/core/tsi/alts_transport_security.h47
-rw-r--r--src/core/tsi/transport_security.cc2
-rw-r--r--src/cpp/client/channel_cc.cc40
-rw-r--r--src/cpp/client/client_context.cc12
-rw-r--r--src/cpp/client/client_interceptor.cc34
-rw-r--r--src/cpp/client/secure_credentials.cc7
-rw-r--r--src/cpp/common/completion_queue_cc.cc10
-rw-r--r--src/cpp/common/core_codegen.cc7
-rw-r--r--src/cpp/ext/filters/census/context.cc12
-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/secure_server_credentials.cc7
-rw-r--r--src/cpp/server/server_builder.cc38
-rw-r--r--src/cpp/server/server_cc.cc495
-rw-r--r--src/cpp/server/server_context.cc221
-rw-r--r--src/csharp/BUILD-INTEGRATION.md357
-rw-r--r--src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs2
-rw-r--r--src/csharp/Grpc.Core.Tests/MarshallerTest.cs7
-rw-r--r--src/csharp/Grpc.Core/DeserializationContext.cs7
-rw-r--r--src/csharp/Grpc.Core/Internal/AsyncCall.cs2
-rw-r--r--src/csharp/Grpc.Core/Internal/AsyncCallBase.cs27
-rw-r--r--src/csharp/Grpc.Core/Internal/AsyncCallServer.cs2
-rw-r--r--src/csharp/Grpc.Core/Internal/DefaultDeserializationContext.cs66
-rw-r--r--src/csharp/Grpc.Core/Internal/DefaultSerializationContext.cs62
-rw-r--r--src/csharp/Grpc.Core/Internal/ServerCallHandler.cs16
-rw-r--r--src/csharp/Grpc.Core/Marshaller.cs67
-rw-r--r--src/csharp/Grpc.Core/SerializationContext.cs7
-rw-r--r--src/csharp/Grpc.Core/ServerServiceDefinition.cs8
-rw-r--r--src/csharp/Grpc.Core/ServiceBinderBase.cs101
-rw-r--r--src/csharp/Grpc.Examples/MathGrpc.cs12
-rw-r--r--src/csharp/Grpc.HealthCheck/Health.cs20
-rw-r--r--src/csharp/Grpc.HealthCheck/HealthGrpc.cs135
-rw-r--r--src/csharp/Grpc.IntegrationTesting/BenchmarkServiceGrpc.cs13
-rw-r--r--src/csharp/Grpc.IntegrationTesting/Control.cs144
-rw-r--r--src/csharp/Grpc.IntegrationTesting/EmptyServiceGrpc.cs8
-rw-r--r--src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs10
-rw-r--r--src/csharp/Grpc.IntegrationTesting/ReportQpsScenarioServiceGrpc.cs9
-rw-r--r--src/csharp/Grpc.IntegrationTesting/TestGrpc.cs35
-rw-r--r--src/csharp/Grpc.IntegrationTesting/WorkerServiceGrpc.cs12
-rw-r--r--src/csharp/Grpc.Reflection/ReflectionGrpc.cs9
-rw-r--r--src/csharp/README.md1
-rw-r--r--src/csharp/doc/integration.md-fig.1-classic.pngbin0 -> 15266 bytes
-rw-r--r--src/csharp/doc/integration.md-fig.2-sdk.pngbin0 -> 15863 bytes
-rw-r--r--src/php/ext/grpc/channel.c2
-rw-r--r--src/php/ext/grpc/channel.h3
-rw-r--r--src/php/ext/grpc/php_grpc.c118
-rw-r--r--src/proto/grpc/channelz/channelz.proto13
-rw-r--r--src/proto/grpc/reflection/v1alpha/BUILD8
-rw-r--r--src/proto/grpc/testing/BUILD34
-rw-r--r--src/proto/grpc/testing/control.proto1
-rw-r--r--src/proto/grpc/testing/proto2/BUILD.bazel30
-rw-r--r--src/python/.gitignore1
-rw-r--r--src/python/grpcio/_parallel_compile_patch.py63
-rw-r--r--src/python/grpcio/commands.py69
-rw-r--r--src/python/grpcio/grpc/__init__.py128
-rw-r--r--src/python/grpcio/grpc/_channel.py127
-rw-r--r--src/python/grpcio/grpc/_common.py14
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi4
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/grpc_string.pyx.pxi1
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/metadata.pyx.pxi6
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi8
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi1
-rw-r--r--src/python/grpcio/grpc/_cython/cygrpc.pyx1
-rw-r--r--src/python/grpcio/grpc/_interceptor.py126
-rw-r--r--src/python/grpcio/grpc/_plugin_wrapping.py1
-rw-r--r--src/python/grpcio/grpc/_server.py1
-rw-r--r--src/python/grpcio/grpc/framework/foundation/callable_util.py1
-rw-r--r--src/python/grpcio/grpc/framework/foundation/logging_pool.py1
-rw-r--r--src/python/grpcio/grpc/framework/foundation/stream_util.py1
-rw-r--r--src/python/grpcio/grpc_core_dependencies.py7
-rw-r--r--src/python/grpcio_reflection/grpc_reflection/v1alpha/BUILD.bazel34
-rw-r--r--src/python/grpcio_tests/commands.py4
-rw-r--r--src/python/grpcio_tests/tests/_sanity/_sanity_test.py4
-rw-r--r--src/python/grpcio_tests/tests/interop/BUILD.bazel101
-rw-r--r--src/python/grpcio_tests/tests/interop/credentials/BUILD.bazel9
-rw-r--r--src/python/grpcio_tests/tests/interop/methods.py19
-rw-r--r--src/python/grpcio_tests/tests/interop/resources.py11
-rw-r--r--src/python/grpcio_tests/tests/interop/server.py2
-rw-r--r--src/python/grpcio_tests/tests/reflection/BUILD.bazel21
-rw-r--r--src/python/grpcio_tests/tests/tests.json3
-rw-r--r--src/python/grpcio_tests/tests/unit/BUILD.bazel1
-rw-r--r--src/python/grpcio_tests/tests/unit/_api_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_auth_context_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_auth_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_channel_args_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_channel_close_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_compression_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_credentials_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_empty_message_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_error_message_encoding_test.py86
-rw-r--r--src/python/grpcio_tests/tests/unit/_exit_scenarios.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_exit_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_interceptor_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_invocation_defects_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_logging_test.py80
-rw-r--r--src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_metadata_flags_test.py251
-rw-r--r--src/python/grpcio_tests/tests/unit/_metadata_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_reconnect_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_resource_exhausted_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_rpc_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_server_ssl_cert_config_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_server_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/_session_cache_test.py2
-rw-r--r--src/python/grpcio_tests/tests/unit/resources.py35
-rw-r--r--src/python/grpcio_tests/tests/unit/test_common.py26
-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/config.m4.template3
-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/client_channel/uri_fuzzer_test.cc2
-rw-r--r--test/core/client_channel/uri_parser_test.cc2
-rw-r--r--test/core/debug/BUILD34
-rw-r--r--test/core/end2end/tests/channelz.cc5
-rw-r--r--test/core/end2end/tests/streaming_error_response.cc31
-rw-r--r--test/core/gprpp/ref_counted_test.cc47
-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/tsi/alts/handshaker/alts_handshaker_client_test.cc225
-rw-r--r--test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc305
-rw-r--r--test/core/util/ubsan_suppressions.txt14
-rw-r--r--test/cpp/codegen/compiler_test_golden199
-rw-r--r--test/cpp/end2end/BUILD55
-rw-r--r--test/cpp/end2end/channelz_service_test.cc47
-rw-r--r--test/cpp/end2end/client_callback_end2end_test.cc70
-rw-r--r--test/cpp/end2end/client_interceptors_end2end_test.cc667
-rw-r--r--test/cpp/end2end/client_lb_end2end_test.cc54
-rw-r--r--test/cpp/end2end/end2end_test.cc84
-rw-r--r--test/cpp/end2end/grpclb_end2end_test.cc8
-rw-r--r--test/cpp/end2end/interceptors_util.cc151
-rw-r--r--test/cpp/end2end/interceptors_util.h279
-rw-r--r--test/cpp/end2end/server_interceptors_end2end_test.cc583
-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/interop/interop_client.cc4
-rw-r--r--test/cpp/interop/stress_interop_client.cc2
-rw-r--r--test/cpp/microbenchmarks/bm_call_create.cc2
-rw-r--r--test/cpp/microbenchmarks/bm_chttp2_hpack.cc4
-rw-r--r--test/cpp/microbenchmarks/bm_chttp2_transport.cc14
-rw-r--r--test/cpp/qps/BUILD1
-rw-r--r--test/cpp/qps/client.h8
-rw-r--r--test/cpp/qps/qps_worker.cc2
-rw-r--r--test/cpp/qps/server.h1
-rw-r--r--test/cpp/qps/server_async.cc1
-rw-r--r--test/cpp/qps/server_callback.cc96
-rw-r--r--test/cpp/util/BUILD1
-rw-r--r--test/cpp/util/byte_buffer_proto_helper.h7
-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
m---------third_party/data-plane-api0
m---------third_party/googleapis0
m---------third_party/protoc-gen-validate0
-rw-r--r--tools/bazel.rc76
-rwxr-xr-xtools/distrib/python/docgen.py39
-rw-r--r--tools/distrib/python/grpcio_tools/MANIFEST.in1
-rw-r--r--tools/distrib/python/grpcio_tools/_parallel_compile_patch.py63
-rw-r--r--tools/distrib/python/grpcio_tools/setup.py3
-rwxr-xr-xtools/dockerfile/interoptest/grpc_interop_cxx/build_interop.sh6
-rwxr-xr-xtools/dockerfile/interoptest/grpc_interop_php/build_interop.sh9
-rwxr-xr-xtools/dockerfile/interoptest/grpc_interop_php7/build_interop.sh9
-rw-r--r--tools/dockerfile/test/bazel/Dockerfile7
-rw-r--r--tools/doxygen/Doxyfile.c++7
-rw-r--r--tools/doxygen/Doxyfile.c++.internal9
-rw-r--r--tools/doxygen/Doxyfile.core.internal14
-rwxr-xr-xtools/gcp/utils/big_query_utils.py28
-rw-r--r--tools/internal_ci/README.md11
-rw-r--r--tools/internal_ci/helper_scripts/prepare_build_macos_rc29
-rwxr-xr-xtools/internal_ci/linux/grpc_asan_on_foundry.sh6
-rwxr-xr-xtools/internal_ci/linux/grpc_bazel_on_foundry_base.sh17
-rw-r--r--tools/internal_ci/linux/grpc_bazel_on_foundry_dbg.sh4
-rw-r--r--tools/internal_ci/linux/grpc_bazel_on_foundry_opt.sh4
-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
-rwxr-xr-xtools/internal_ci/linux/grpc_python_bazel_test_in_docker.sh2
-rw-r--r--tools/internal_ci/linux/grpc_tsan_on_foundry.sh6
-rw-r--r--tools/internal_ci/linux/grpc_ubsan_on_foundry.sh63
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_asan_on_foundry.sh6
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_bazel_on_foundry_dbg.sh4
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_bazel_on_foundry_opt.sh4
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_interop_matrix_adhoc.cfg30
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_msan_on_foundry.sh18
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_tsan_on_foundry.sh6
-rw-r--r--tools/internal_ci/linux/pull_request/grpc_ubsan_on_foundry.sh57
-rw-r--r--tools/internal_ci/macos/grpc_basictests_dbg.cfg2
-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/grpc_basictests_opt.cfg2
-rw-r--r--tools/internal_ci/macos/pull_request/grpc_basictests_dbg.cfg2
-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/internal_ci/macos/pull_request/grpc_basictests_opt.cfg2
-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.md13
-rw-r--r--tools/remote_build/kokoro.bazelrc21
-rw-r--r--tools/remote_build/manual.bazelrc11
-rw-r--r--tools/remote_build/rbe_common.bazelrc41
-rw-r--r--tools/run_tests/artifacts/artifact_targets.py2
-rw-r--r--tools/run_tests/artifacts/build_artifact_python.bat4
-rwxr-xr-xtools/run_tests/artifacts/build_artifact_python.sh5
-rw-r--r--tools/run_tests/generated/sources_and_headers.json101
-rw-r--r--tools/run_tests/generated/tests.json50
-rwxr-xr-xtools/run_tests/helper_scripts/build_python.sh29
-rwxr-xr-xtools/run_tests/python_utils/jobset.py6
-rwxr-xr-xtools/run_tests/python_utils/port_server.py16
-rw-r--r--tools/run_tests/python_utils/report_utils.py2
-rw-r--r--tools/run_tests/python_utils/start_port_server.py19
-rw-r--r--tools/run_tests/python_utils/upload_rbe_results.py2
-rwxr-xr-xtools/run_tests/python_utils/watch_dirs.py3
-rwxr-xr-xtools/run_tests/run_interop_tests.py54
-rwxr-xr-xtools/run_tests/run_tests.py12
-rwxr-xr-xtools/run_tests/run_tests_matrix.py7
-rwxr-xr-xtools/run_tests/sanity/check_submodules.sh5
-rwxr-xr-xtools/run_tests/sanity/core_banned_functions.py4
405 files changed, 15639 insertions, 5147 deletions
diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 0000000000..bc31c54e94
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1,3 @@
+# load bazelrc from the legacy location
+# as recommended in https://github.com/bazelbuild/bazel/issues/6319
+import %workspace%/tools/bazel.rc
diff --git a/.gitmodules b/.gitmodules
index afde4d34f3..bb4b749bee 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -42,3 +42,12 @@
path = third_party/libcxx
url = https://github.com/llvm-mirror/libcxx.git
branch = release_60
+[submodule "third_party/data-plane-api"]
+ path = third_party/data-plane-api
+ url = https://github.com/envoyproxy/data-plane-api.git
+[submodule "third_party/googleapis"]
+ path = third_party/googleapis
+ url = https://github.com/googleapis/googleapis.git
+[submodule "third_party/protoc-gen-validate"]
+ path = third_party/protoc-gen-validate
+ url = https://github.com/lyft/protoc-gen-validate.git
diff --git a/BUILD b/BUILD
index 65cd4715f1..e4c40ad57b 100644
--- a/BUILD
+++ b/BUILD
@@ -113,6 +113,7 @@ GRPC_SECURE_PUBLIC_HDRS = [
GRPCXX_SRCS = [
"src/cpp/client/channel_cc.cc",
"src/cpp/client/client_context.cc",
+ "src/cpp/client/client_interceptor.cc",
"src/cpp/client/create_channel.cc",
"src/cpp/client/create_channel_internal.cc",
"src/cpp/client/create_channel_posix.cc",
@@ -245,6 +246,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",
@@ -820,6 +822,7 @@ grpc_cc_library(
"src/core/lib/transport/timeout_encoding.cc",
"src/core/lib/transport/transport.cc",
"src/core/lib/transport/transport_op_string.cc",
+ "src/core/lib/uri/uri_parser.cc",
],
hdrs = [
"src/core/lib/avl/avl.h",
@@ -954,6 +957,7 @@ grpc_cc_library(
"src/core/lib/transport/timeout_encoding.h",
"src/core/lib/transport/transport.h",
"src/core/lib/transport/transport_impl.h",
+ "src/core/lib/uri/uri_parser.h",
],
external_deps = [
"zlib",
@@ -1044,16 +1048,15 @@ grpc_cc_library(
"src/core/ext/filters/client_channel/lb_policy.cc",
"src/core/ext/filters/client_channel/lb_policy_factory.cc",
"src/core/ext/filters/client_channel/lb_policy_registry.cc",
- "src/core/ext/filters/client_channel/method_params.cc",
"src/core/ext/filters/client_channel/parse_address.cc",
"src/core/ext/filters/client_channel/proxy_mapper.cc",
"src/core/ext/filters/client_channel/proxy_mapper_registry.cc",
"src/core/ext/filters/client_channel/resolver.cc",
"src/core/ext/filters/client_channel/resolver_registry.cc",
+ "src/core/ext/filters/client_channel/resolver_result_parsing.cc",
"src/core/ext/filters/client_channel/retry_throttle.cc",
"src/core/ext/filters/client_channel/subchannel.cc",
"src/core/ext/filters/client_channel/subchannel_index.cc",
- "src/core/ext/filters/client_channel/uri_parser.cc",
],
hdrs = [
"src/core/ext/filters/client_channel/backup_poller.h",
@@ -1067,17 +1070,16 @@ grpc_cc_library(
"src/core/ext/filters/client_channel/lb_policy.h",
"src/core/ext/filters/client_channel/lb_policy_factory.h",
"src/core/ext/filters/client_channel/lb_policy_registry.h",
- "src/core/ext/filters/client_channel/method_params.h",
"src/core/ext/filters/client_channel/parse_address.h",
"src/core/ext/filters/client_channel/proxy_mapper.h",
"src/core/ext/filters/client_channel/proxy_mapper_registry.h",
"src/core/ext/filters/client_channel/resolver.h",
"src/core/ext/filters/client_channel/resolver_factory.h",
"src/core/ext/filters/client_channel/resolver_registry.h",
+ "src/core/ext/filters/client_channel/resolver_result_parsing.h",
"src/core/ext/filters/client_channel/retry_throttle.h",
"src/core/ext/filters/client_channel/subchannel.h",
"src/core/ext/filters/client_channel/subchannel_index.h",
- "src/core/ext/filters/client_channel/uri_parser.h",
],
language = "c++",
deps = [
@@ -1957,10 +1959,9 @@ grpc_cc_library(
name = "tsi",
srcs = [
"src/core/tsi/alts/handshaker/alts_handshaker_client.cc",
- "src/core/tsi/alts/handshaker/alts_tsi_event.cc",
+ "src/core/tsi/alts/handshaker/alts_shared_resource.cc",
"src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc",
"src/core/tsi/alts/handshaker/alts_tsi_utils.cc",
- "src/core/tsi/alts_transport_security.cc",
"src/core/tsi/fake_transport_security.cc",
"src/core/tsi/local_transport_security.cc",
"src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc",
@@ -1971,11 +1972,10 @@ grpc_cc_library(
],
hdrs = [
"src/core/tsi/alts/handshaker/alts_handshaker_client.h",
- "src/core/tsi/alts/handshaker/alts_tsi_event.h",
+ "src/core/tsi/alts/handshaker/alts_shared_resource.h",
"src/core/tsi/alts/handshaker/alts_tsi_handshaker.h",
"src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h",
"src/core/tsi/alts/handshaker/alts_tsi_utils.h",
- "src/core/tsi/alts_transport_security.h",
"src/core/tsi/fake_transport_security.h",
"src/core/tsi/local_transport_security.h",
"src/core/tsi/ssl/session_cache/ssl_session.h",
@@ -2065,6 +2065,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 +2079,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..5c2ba2048c 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)
@@ -1123,6 +1125,7 @@ add_library(grpc
src/core/lib/transport/timeout_encoding.cc
src/core/lib/transport/transport.cc
src/core/lib/transport/transport_op_string.cc
+ src/core/lib/uri/uri_parser.cc
src/core/lib/debug/trace.cc
src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc
src/core/ext/transport/chttp2/transport/bin_decoder.cc
@@ -1195,7 +1198,7 @@ add_library(grpc
src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc
src/core/tsi/alts/frame_protector/frame_handler.cc
src/core/tsi/alts/handshaker/alts_handshaker_client.cc
- src/core/tsi/alts/handshaker/alts_tsi_event.cc
+ src/core/tsi/alts/handshaker/alts_shared_resource.cc
src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc
src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc
@@ -1237,19 +1240,17 @@ add_library(grpc
src/core/ext/filters/client_channel/lb_policy.cc
src/core/ext/filters/client_channel/lb_policy_factory.cc
src/core/ext/filters/client_channel/lb_policy_registry.cc
- src/core/ext/filters/client_channel/method_params.cc
src/core/ext/filters/client_channel/parse_address.cc
src/core/ext/filters/client_channel/proxy_mapper.cc
src/core/ext/filters/client_channel/proxy_mapper_registry.cc
src/core/ext/filters/client_channel/resolver.cc
src/core/ext/filters/client_channel/resolver_registry.cc
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc
src/core/ext/filters/client_channel/retry_throttle.cc
src/core/ext/filters/client_channel/subchannel.cc
src/core/ext/filters/client_channel/subchannel_index.cc
- src/core/ext/filters/client_channel/uri_parser.cc
src/core/ext/filters/deadline/deadline_filter.cc
src/core/ext/filters/client_channel/health/health.pb.c
- src/core/tsi/alts_transport_security.cc
src/core/tsi/fake_transport_security.cc
src/core/tsi/local_transport_security.cc
src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc
@@ -1544,6 +1545,7 @@ add_library(grpc_cronet
src/core/lib/transport/timeout_encoding.cc
src/core/lib/transport/transport.cc
src/core/lib/transport/transport_op_string.cc
+ src/core/lib/uri/uri_parser.cc
src/core/lib/debug/trace.cc
src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc
src/core/ext/transport/cronet/transport/cronet_api_dummy.cc
@@ -1589,16 +1591,15 @@ add_library(grpc_cronet
src/core/ext/filters/client_channel/lb_policy.cc
src/core/ext/filters/client_channel/lb_policy_factory.cc
src/core/ext/filters/client_channel/lb_policy_registry.cc
- src/core/ext/filters/client_channel/method_params.cc
src/core/ext/filters/client_channel/parse_address.cc
src/core/ext/filters/client_channel/proxy_mapper.cc
src/core/ext/filters/client_channel/proxy_mapper_registry.cc
src/core/ext/filters/client_channel/resolver.cc
src/core/ext/filters/client_channel/resolver_registry.cc
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc
src/core/ext/filters/client_channel/retry_throttle.cc
src/core/ext/filters/client_channel/subchannel.cc
src/core/ext/filters/client_channel/subchannel_index.cc
- src/core/ext/filters/client_channel/uri_parser.cc
src/core/ext/filters/deadline/deadline_filter.cc
src/core/ext/filters/client_channel/health/health.pb.c
third_party/nanopb/pb_common.c
@@ -1647,7 +1648,7 @@ add_library(grpc_cronet
src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc
src/core/tsi/alts/frame_protector/frame_handler.cc
src/core/tsi/alts/handshaker/alts_handshaker_client.cc
- src/core/tsi/alts/handshaker/alts_tsi_event.cc
+ src/core/tsi/alts/handshaker/alts_shared_resource.cc
src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc
src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc
@@ -1673,7 +1674,6 @@ add_library(grpc_cronet
src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc
src/core/ext/transport/chttp2/client/authority.cc
src/core/ext/transport/chttp2/client/chttp2_connector.cc
- src/core/tsi/alts_transport_security.cc
src/core/tsi/fake_transport_security.cc
src/core/tsi/local_transport_security.cc
src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc
@@ -1947,6 +1947,7 @@ add_library(grpc_test_util
src/core/lib/transport/timeout_encoding.cc
src/core/lib/transport/transport.cc
src/core/lib/transport/transport_op_string.cc
+ src/core/lib/uri/uri_parser.cc
src/core/lib/debug/trace.cc
src/core/ext/filters/client_channel/backup_poller.cc
src/core/ext/filters/client_channel/channel_connectivity.cc
@@ -1961,16 +1962,15 @@ add_library(grpc_test_util
src/core/ext/filters/client_channel/lb_policy.cc
src/core/ext/filters/client_channel/lb_policy_factory.cc
src/core/ext/filters/client_channel/lb_policy_registry.cc
- src/core/ext/filters/client_channel/method_params.cc
src/core/ext/filters/client_channel/parse_address.cc
src/core/ext/filters/client_channel/proxy_mapper.cc
src/core/ext/filters/client_channel/proxy_mapper_registry.cc
src/core/ext/filters/client_channel/resolver.cc
src/core/ext/filters/client_channel/resolver_registry.cc
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc
src/core/ext/filters/client_channel/retry_throttle.cc
src/core/ext/filters/client_channel/subchannel.cc
src/core/ext/filters/client_channel/subchannel_index.cc
- src/core/ext/filters/client_channel/uri_parser.cc
src/core/ext/filters/deadline/deadline_filter.cc
src/core/ext/filters/client_channel/health/health.pb.c
third_party/nanopb/pb_common.c
@@ -2266,6 +2266,7 @@ add_library(grpc_test_util_unsecure
src/core/lib/transport/timeout_encoding.cc
src/core/lib/transport/transport.cc
src/core/lib/transport/transport_op_string.cc
+ src/core/lib/uri/uri_parser.cc
src/core/lib/debug/trace.cc
src/core/ext/filters/client_channel/backup_poller.cc
src/core/ext/filters/client_channel/channel_connectivity.cc
@@ -2280,16 +2281,15 @@ add_library(grpc_test_util_unsecure
src/core/ext/filters/client_channel/lb_policy.cc
src/core/ext/filters/client_channel/lb_policy_factory.cc
src/core/ext/filters/client_channel/lb_policy_registry.cc
- src/core/ext/filters/client_channel/method_params.cc
src/core/ext/filters/client_channel/parse_address.cc
src/core/ext/filters/client_channel/proxy_mapper.cc
src/core/ext/filters/client_channel/proxy_mapper_registry.cc
src/core/ext/filters/client_channel/resolver.cc
src/core/ext/filters/client_channel/resolver_registry.cc
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc
src/core/ext/filters/client_channel/retry_throttle.cc
src/core/ext/filters/client_channel/subchannel.cc
src/core/ext/filters/client_channel/subchannel_index.cc
- src/core/ext/filters/client_channel/uri_parser.cc
src/core/ext/filters/deadline/deadline_filter.cc
src/core/ext/filters/client_channel/health/health.pb.c
third_party/nanopb/pb_common.c
@@ -2564,6 +2564,7 @@ add_library(grpc_unsecure
src/core/lib/transport/timeout_encoding.cc
src/core/lib/transport/transport.cc
src/core/lib/transport/transport_op_string.cc
+ src/core/lib/uri/uri_parser.cc
src/core/lib/debug/trace.cc
src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc
src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc
@@ -2612,16 +2613,15 @@ add_library(grpc_unsecure
src/core/ext/filters/client_channel/lb_policy.cc
src/core/ext/filters/client_channel/lb_policy_factory.cc
src/core/ext/filters/client_channel/lb_policy_registry.cc
- src/core/ext/filters/client_channel/method_params.cc
src/core/ext/filters/client_channel/parse_address.cc
src/core/ext/filters/client_channel/proxy_mapper.cc
src/core/ext/filters/client_channel/proxy_mapper_registry.cc
src/core/ext/filters/client_channel/resolver.cc
src/core/ext/filters/client_channel/resolver_registry.cc
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc
src/core/ext/filters/client_channel/retry_throttle.cc
src/core/ext/filters/client_channel/subchannel.cc
src/core/ext/filters/client_channel/subchannel_index.cc
- src/core/ext/filters/client_channel/uri_parser.cc
src/core/ext/filters/deadline/deadline_filter.cc
src/core/ext/filters/client_channel/health/health.pb.c
third_party/nanopb/pb_common.c
@@ -2860,6 +2860,7 @@ add_library(grpc++
src/cpp/server/secure_server_credentials.cc
src/cpp/client/channel_cc.cc
src/cpp/client/client_context.cc
+ src/cpp/client/client_interceptor.cc
src/cpp/client/create_channel.cc
src/cpp/client/create_channel_internal.cc
src/cpp/client/create_channel_posix.cc
@@ -3018,6 +3019,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 +3114,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 +3128,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
@@ -3224,6 +3232,7 @@ add_library(grpc++_cronet
src/cpp/server/insecure_server_credentials.cc
src/cpp/client/channel_cc.cc
src/cpp/client/client_context.cc
+ src/cpp/client/client_interceptor.cc
src/cpp/client/create_channel.cc
src/cpp/client/create_channel_internal.cc
src/cpp/client/create_channel_posix.cc
@@ -3435,6 +3444,7 @@ add_library(grpc++_cronet
src/core/lib/transport/timeout_encoding.cc
src/core/lib/transport/transport.cc
src/core/lib/transport/transport_op_string.cc
+ src/core/lib/uri/uri_parser.cc
src/core/lib/debug/trace.cc
src/core/ext/transport/chttp2/alpn/alpn.cc
src/core/ext/filters/http/client/http_client_filter.cc
@@ -3454,16 +3464,15 @@ add_library(grpc++_cronet
src/core/ext/filters/client_channel/lb_policy.cc
src/core/ext/filters/client_channel/lb_policy_factory.cc
src/core/ext/filters/client_channel/lb_policy_registry.cc
- src/core/ext/filters/client_channel/method_params.cc
src/core/ext/filters/client_channel/parse_address.cc
src/core/ext/filters/client_channel/proxy_mapper.cc
src/core/ext/filters/client_channel/proxy_mapper_registry.cc
src/core/ext/filters/client_channel/resolver.cc
src/core/ext/filters/client_channel/resolver_registry.cc
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc
src/core/ext/filters/client_channel/retry_throttle.cc
src/core/ext/filters/client_channel/subchannel.cc
src/core/ext/filters/client_channel/subchannel_index.cc
- src/core/ext/filters/client_channel/uri_parser.cc
src/core/ext/filters/deadline/deadline_filter.cc
src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc
src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc
@@ -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
@@ -4351,6 +4379,7 @@ add_library(grpc++_unsecure
src/cpp/server/insecure_server_credentials.cc
src/cpp/client/channel_cc.cc
src/cpp/client/client_context.cc
+ src/cpp/client/client_interceptor.cc
src/cpp/client/create_channel.cc
src/cpp/client/create_channel_internal.cc
src/cpp/client/create_channel_posix.cc
@@ -4508,6 +4537,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 +4632,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 +4646,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
@@ -5296,6 +5332,7 @@ add_library(qps
test/cpp/qps/qps_worker.cc
test/cpp/qps/report.cc
test/cpp/qps/server_async.cc
+ test/cpp/qps/server_callback.cc
test/cpp/qps/server_sync.cc
test/cpp/qps/usage_timer.cc
)
@@ -12405,6 +12442,48 @@ 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
+ test/cpp/end2end/interceptors_util.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
@@ -12819,6 +12898,7 @@ if (gRPC_BUILD_TESTS)
add_executable(end2end_test
test/cpp/end2end/end2end_test.cc
+ test/cpp/end2end/interceptors_util.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
@@ -15275,6 +15355,48 @@ target_link_libraries(server_early_return_test
endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS)
+add_executable(server_interceptors_end2end_test
+ test/cpp/end2end/interceptors_util.cc
+ 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 4a66cf9dbe..12603f1fc8 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"
@@ -3590,6 +3600,7 @@ LIBGRPC_SRC = \
src/core/lib/transport/timeout_encoding.cc \
src/core/lib/transport/transport.cc \
src/core/lib/transport/transport_op_string.cc \
+ src/core/lib/uri/uri_parser.cc \
src/core/lib/debug/trace.cc \
src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc \
src/core/ext/transport/chttp2/transport/bin_decoder.cc \
@@ -3662,7 +3673,7 @@ LIBGRPC_SRC = \
src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc \
src/core/tsi/alts/frame_protector/frame_handler.cc \
src/core/tsi/alts/handshaker/alts_handshaker_client.cc \
- src/core/tsi/alts/handshaker/alts_tsi_event.cc \
+ src/core/tsi/alts/handshaker/alts_shared_resource.cc \
src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc \
src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc \
src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc \
@@ -3704,19 +3715,17 @@ LIBGRPC_SRC = \
src/core/ext/filters/client_channel/lb_policy.cc \
src/core/ext/filters/client_channel/lb_policy_factory.cc \
src/core/ext/filters/client_channel/lb_policy_registry.cc \
- src/core/ext/filters/client_channel/method_params.cc \
src/core/ext/filters/client_channel/parse_address.cc \
src/core/ext/filters/client_channel/proxy_mapper.cc \
src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
src/core/ext/filters/client_channel/resolver.cc \
src/core/ext/filters/client_channel/resolver_registry.cc \
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/subchannel.cc \
src/core/ext/filters/client_channel/subchannel_index.cc \
- src/core/ext/filters/client_channel/uri_parser.cc \
src/core/ext/filters/deadline/deadline_filter.cc \
src/core/ext/filters/client_channel/health/health.pb.c \
- src/core/tsi/alts_transport_security.cc \
src/core/tsi/fake_transport_security.cc \
src/core/tsi/local_transport_security.cc \
src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc \
@@ -4005,6 +4014,7 @@ LIBGRPC_CRONET_SRC = \
src/core/lib/transport/timeout_encoding.cc \
src/core/lib/transport/transport.cc \
src/core/lib/transport/transport_op_string.cc \
+ src/core/lib/uri/uri_parser.cc \
src/core/lib/debug/trace.cc \
src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc \
src/core/ext/transport/cronet/transport/cronet_api_dummy.cc \
@@ -4050,16 +4060,15 @@ LIBGRPC_CRONET_SRC = \
src/core/ext/filters/client_channel/lb_policy.cc \
src/core/ext/filters/client_channel/lb_policy_factory.cc \
src/core/ext/filters/client_channel/lb_policy_registry.cc \
- src/core/ext/filters/client_channel/method_params.cc \
src/core/ext/filters/client_channel/parse_address.cc \
src/core/ext/filters/client_channel/proxy_mapper.cc \
src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
src/core/ext/filters/client_channel/resolver.cc \
src/core/ext/filters/client_channel/resolver_registry.cc \
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/subchannel.cc \
src/core/ext/filters/client_channel/subchannel_index.cc \
- src/core/ext/filters/client_channel/uri_parser.cc \
src/core/ext/filters/deadline/deadline_filter.cc \
src/core/ext/filters/client_channel/health/health.pb.c \
third_party/nanopb/pb_common.c \
@@ -4108,7 +4117,7 @@ LIBGRPC_CRONET_SRC = \
src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc \
src/core/tsi/alts/frame_protector/frame_handler.cc \
src/core/tsi/alts/handshaker/alts_handshaker_client.cc \
- src/core/tsi/alts/handshaker/alts_tsi_event.cc \
+ src/core/tsi/alts/handshaker/alts_shared_resource.cc \
src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc \
src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc \
src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc \
@@ -4134,7 +4143,6 @@ LIBGRPC_CRONET_SRC = \
src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc \
src/core/ext/transport/chttp2/client/authority.cc \
src/core/ext/transport/chttp2/client/chttp2_connector.cc \
- src/core/tsi/alts_transport_security.cc \
src/core/tsi/fake_transport_security.cc \
src/core/tsi/local_transport_security.cc \
src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc \
@@ -4401,6 +4409,7 @@ LIBGRPC_TEST_UTIL_SRC = \
src/core/lib/transport/timeout_encoding.cc \
src/core/lib/transport/transport.cc \
src/core/lib/transport/transport_op_string.cc \
+ src/core/lib/uri/uri_parser.cc \
src/core/lib/debug/trace.cc \
src/core/ext/filters/client_channel/backup_poller.cc \
src/core/ext/filters/client_channel/channel_connectivity.cc \
@@ -4415,16 +4424,15 @@ LIBGRPC_TEST_UTIL_SRC = \
src/core/ext/filters/client_channel/lb_policy.cc \
src/core/ext/filters/client_channel/lb_policy_factory.cc \
src/core/ext/filters/client_channel/lb_policy_registry.cc \
- src/core/ext/filters/client_channel/method_params.cc \
src/core/ext/filters/client_channel/parse_address.cc \
src/core/ext/filters/client_channel/proxy_mapper.cc \
src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
src/core/ext/filters/client_channel/resolver.cc \
src/core/ext/filters/client_channel/resolver_registry.cc \
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/subchannel.cc \
src/core/ext/filters/client_channel/subchannel_index.cc \
- src/core/ext/filters/client_channel/uri_parser.cc \
src/core/ext/filters/deadline/deadline_filter.cc \
src/core/ext/filters/client_channel/health/health.pb.c \
third_party/nanopb/pb_common.c \
@@ -4706,6 +4714,7 @@ LIBGRPC_TEST_UTIL_UNSECURE_SRC = \
src/core/lib/transport/timeout_encoding.cc \
src/core/lib/transport/transport.cc \
src/core/lib/transport/transport_op_string.cc \
+ src/core/lib/uri/uri_parser.cc \
src/core/lib/debug/trace.cc \
src/core/ext/filters/client_channel/backup_poller.cc \
src/core/ext/filters/client_channel/channel_connectivity.cc \
@@ -4720,16 +4729,15 @@ LIBGRPC_TEST_UTIL_UNSECURE_SRC = \
src/core/ext/filters/client_channel/lb_policy.cc \
src/core/ext/filters/client_channel/lb_policy_factory.cc \
src/core/ext/filters/client_channel/lb_policy_registry.cc \
- src/core/ext/filters/client_channel/method_params.cc \
src/core/ext/filters/client_channel/parse_address.cc \
src/core/ext/filters/client_channel/proxy_mapper.cc \
src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
src/core/ext/filters/client_channel/resolver.cc \
src/core/ext/filters/client_channel/resolver_registry.cc \
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/subchannel.cc \
src/core/ext/filters/client_channel/subchannel_index.cc \
- src/core/ext/filters/client_channel/uri_parser.cc \
src/core/ext/filters/deadline/deadline_filter.cc \
src/core/ext/filters/client_channel/health/health.pb.c \
third_party/nanopb/pb_common.c \
@@ -4977,6 +4985,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/transport/timeout_encoding.cc \
src/core/lib/transport/transport.cc \
src/core/lib/transport/transport_op_string.cc \
+ src/core/lib/uri/uri_parser.cc \
src/core/lib/debug/trace.cc \
src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc \
src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc \
@@ -5025,16 +5034,15 @@ LIBGRPC_UNSECURE_SRC = \
src/core/ext/filters/client_channel/lb_policy.cc \
src/core/ext/filters/client_channel/lb_policy_factory.cc \
src/core/ext/filters/client_channel/lb_policy_registry.cc \
- src/core/ext/filters/client_channel/method_params.cc \
src/core/ext/filters/client_channel/parse_address.cc \
src/core/ext/filters/client_channel/proxy_mapper.cc \
src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
src/core/ext/filters/client_channel/resolver.cc \
src/core/ext/filters/client_channel/resolver_registry.cc \
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/subchannel.cc \
src/core/ext/filters/client_channel/subchannel_index.cc \
- src/core/ext/filters/client_channel/uri_parser.cc \
src/core/ext/filters/deadline/deadline_filter.cc \
src/core/ext/filters/client_channel/health/health.pb.c \
third_party/nanopb/pb_common.c \
@@ -5238,6 +5246,7 @@ LIBGRPC++_SRC = \
src/cpp/server/secure_server_credentials.cc \
src/cpp/client/channel_cc.cc \
src/cpp/client/client_context.cc \
+ src/cpp/client/client_interceptor.cc \
src/cpp/client/create_channel.cc \
src/cpp/client/create_channel_internal.cc \
src/cpp/client/create_channel_posix.cc \
@@ -5361,6 +5370,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 +5465,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 +5479,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 \
@@ -5612,6 +5628,7 @@ LIBGRPC++_CRONET_SRC = \
src/cpp/server/insecure_server_credentials.cc \
src/cpp/client/channel_cc.cc \
src/cpp/client/client_context.cc \
+ src/cpp/client/client_interceptor.cc \
src/cpp/client/create_channel.cc \
src/cpp/client/create_channel_internal.cc \
src/cpp/client/create_channel_posix.cc \
@@ -5823,6 +5840,7 @@ LIBGRPC++_CRONET_SRC = \
src/core/lib/transport/timeout_encoding.cc \
src/core/lib/transport/transport.cc \
src/core/lib/transport/transport_op_string.cc \
+ src/core/lib/uri/uri_parser.cc \
src/core/lib/debug/trace.cc \
src/core/ext/transport/chttp2/alpn/alpn.cc \
src/core/ext/filters/http/client/http_client_filter.cc \
@@ -5842,16 +5860,15 @@ LIBGRPC++_CRONET_SRC = \
src/core/ext/filters/client_channel/lb_policy.cc \
src/core/ext/filters/client_channel/lb_policy_factory.cc \
src/core/ext/filters/client_channel/lb_policy_registry.cc \
- src/core/ext/filters/client_channel/method_params.cc \
src/core/ext/filters/client_channel/parse_address.cc \
src/core/ext/filters/client_channel/proxy_mapper.cc \
src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
src/core/ext/filters/client_channel/resolver.cc \
src/core/ext/filters/client_channel/resolver_registry.cc \
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/subchannel.cc \
src/core/ext/filters/client_channel/subchannel_index.cc \
- src/core/ext/filters/client_channel/uri_parser.cc \
src/core/ext/filters/deadline/deadline_filter.cc \
src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc \
src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc \
@@ -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 \
@@ -6704,6 +6740,7 @@ LIBGRPC++_UNSECURE_SRC = \
src/cpp/server/insecure_server_credentials.cc \
src/cpp/client/channel_cc.cc \
src/cpp/client/client_context.cc \
+ src/cpp/client/client_interceptor.cc \
src/cpp/client/create_channel.cc \
src/cpp/client/create_channel_internal.cc \
src/cpp/client/create_channel_posix.cc \
@@ -6827,6 +6864,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 +6959,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 +6973,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 \
@@ -7556,6 +7600,7 @@ LIBQPS_SRC = \
test/cpp/qps/qps_worker.cc \
test/cpp/qps/report.cc \
test/cpp/qps/server_async.cc \
+ test/cpp/qps/server_callback.cc \
test/cpp/qps/server_sync.cc \
test/cpp/qps/usage_timer.cc \
@@ -7613,6 +7658,7 @@ $(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_server_builder.o: $(GENDIR)/src/proto/grpc/
$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_worker.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/benchmark_service.pb.cc $(GENDIR)/src/proto/grpc/testing/benchmark_service.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/report_qps_scenario_service.pb.cc $(GENDIR)/src/proto/grpc/testing/report_qps_scenario_service.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/worker_service.pb.cc $(GENDIR)/src/proto/grpc/testing/worker_service.grpc.pb.cc
$(OBJDIR)/$(CONFIG)/test/cpp/qps/report.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/benchmark_service.pb.cc $(GENDIR)/src/proto/grpc/testing/benchmark_service.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/report_qps_scenario_service.pb.cc $(GENDIR)/src/proto/grpc/testing/report_qps_scenario_service.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/worker_service.pb.cc $(GENDIR)/src/proto/grpc/testing/worker_service.grpc.pb.cc
$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_async.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/benchmark_service.pb.cc $(GENDIR)/src/proto/grpc/testing/benchmark_service.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/report_qps_scenario_service.pb.cc $(GENDIR)/src/proto/grpc/testing/report_qps_scenario_service.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/worker_service.pb.cc $(GENDIR)/src/proto/grpc/testing/worker_service.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_callback.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/benchmark_service.pb.cc $(GENDIR)/src/proto/grpc/testing/benchmark_service.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/report_qps_scenario_service.pb.cc $(GENDIR)/src/proto/grpc/testing/report_qps_scenario_service.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/worker_service.pb.cc $(GENDIR)/src/proto/grpc/testing/worker_service.grpc.pb.cc
$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_sync.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/benchmark_service.pb.cc $(GENDIR)/src/proto/grpc/testing/benchmark_service.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/report_qps_scenario_service.pb.cc $(GENDIR)/src/proto/grpc/testing/report_qps_scenario_service.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/worker_service.pb.cc $(GENDIR)/src/proto/grpc/testing/worker_service.grpc.pb.cc
$(OBJDIR)/$(CONFIG)/test/cpp/qps/usage_timer.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/benchmark_service.pb.cc $(GENDIR)/src/proto/grpc/testing/benchmark_service.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/report_qps_scenario_service.pb.cc $(GENDIR)/src/proto/grpc/testing/report_qps_scenario_service.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/worker_service.pb.cc $(GENDIR)/src/proto/grpc/testing/worker_service.grpc.pb.cc
@@ -17279,6 +17325,52 @@ endif
endif
+CLIENT_INTERCEPTORS_END2END_TEST_SRC = \
+ test/cpp/end2end/client_interceptors_end2end_test.cc \
+ test/cpp/end2end/interceptors_util.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
+
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/interceptors_util.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 \
@@ -17673,6 +17765,7 @@ endif
END2END_TEST_SRC = \
test/cpp/end2end/end2end_test.cc \
+ test/cpp/end2end/interceptors_util.cc \
END2END_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(END2END_TEST_SRC))))
ifeq ($(NO_SECURE),true)
@@ -17705,6 +17798,8 @@ endif
$(OBJDIR)/$(CONFIG)/test/cpp/end2end/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
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/interceptors_util.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_end2end_test: $(END2END_TEST_OBJS:.o=.dep)
ifneq ($(NO_SECURE),true)
@@ -20064,6 +20159,52 @@ endif
endif
+SERVER_INTERCEPTORS_END2END_TEST_SRC = \
+ test/cpp/end2end/interceptors_util.cc \
+ 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/interceptors_util.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
+
+$(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 \
@@ -24882,7 +25023,7 @@ src/core/tsi/alts/frame_protector/frame_handler.cc: $(OPENSSL_DEP)
src/core/tsi/alts/handshaker/alts_handshaker_client.cc: $(OPENSSL_DEP)
src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc: $(OPENSSL_DEP)
src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc: $(OPENSSL_DEP)
-src/core/tsi/alts/handshaker/alts_tsi_event.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/handshaker/alts_shared_resource.cc: $(OPENSSL_DEP)
src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc: $(OPENSSL_DEP)
src/core/tsi/alts/handshaker/alts_tsi_utils.cc: $(OPENSSL_DEP)
src/core/tsi/alts/handshaker/altscontext.pb.c: $(OPENSSL_DEP)
@@ -24894,7 +25035,6 @@ src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_p
src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc: $(OPENSSL_DEP)
src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc: $(OPENSSL_DEP)
src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc: $(OPENSSL_DEP)
-src/core/tsi/alts_transport_security.cc: $(OPENSSL_DEP)
src/core/tsi/fake_transport_security.cc: $(OPENSSL_DEP)
src/core/tsi/local_transport_security.cc: $(OPENSSL_DEP)
src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc: $(OPENSSL_DEP)
@@ -24949,6 +25089,7 @@ test/cpp/qps/qps_server_builder.cc: $(OPENSSL_DEP)
test/cpp/qps/qps_worker.cc: $(OPENSSL_DEP)
test/cpp/qps/report.cc: $(OPENSSL_DEP)
test/cpp/qps/server_async.cc: $(OPENSSL_DEP)
+test/cpp/qps/server_callback.cc: $(OPENSSL_DEP)
test/cpp/qps/server_sync.cc: $(OPENSSL_DEP)
test/cpp/qps/usage_timer.cc: $(OPENSSL_DEP)
test/cpp/util/byte_buffer_proto_helper.cc: $(OPENSSL_DEP)
diff --git a/PYTHON-MANIFEST.in b/PYTHON-MANIFEST.in
index b30a1fbf90..c0de5289ee 100644
--- a/PYTHON-MANIFEST.in
+++ b/PYTHON-MANIFEST.in
@@ -9,6 +9,7 @@ graft third_party/boringssl
graft third_party/cares
graft third_party/nanopb
graft third_party/zlib
+include src/python/grpcio/_parallel_compile_patch.py
include src/python/grpcio/_spawn_patch.py
include src/python/grpcio/commands.py
include src/python/grpcio/grpc_version.py
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..09acdbe6f1 100644
--- a/build.yaml
+++ b/build.yaml
@@ -36,7 +36,7 @@ filegroups:
- src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h
- src/core/tsi/alts/frame_protector/frame_handler.h
- src/core/tsi/alts/handshaker/alts_handshaker_client.h
- - src/core/tsi/alts/handshaker/alts_tsi_event.h
+ - src/core/tsi/alts/handshaker/alts_shared_resource.h
- src/core/tsi/alts/handshaker/alts_tsi_handshaker.h
- src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h
- src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h
@@ -56,7 +56,7 @@ filegroups:
- src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc
- src/core/tsi/alts/frame_protector/frame_handler.cc
- src/core/tsi/alts/handshaker/alts_handshaker_client.cc
- - src/core/tsi/alts/handshaker/alts_tsi_event.cc
+ - src/core/tsi/alts/handshaker/alts_shared_resource.cc
- src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
- src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc
- src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc
@@ -382,6 +382,7 @@ filegroups:
- src/core/lib/transport/timeout_encoding.cc
- src/core/lib/transport/transport.cc
- src/core/lib/transport/transport_op_string.cc
+ - src/core/lib/uri/uri_parser.cc
deps:
- gpr
filegroups:
@@ -538,6 +539,7 @@ filegroups:
- src/core/lib/transport/timeout_encoding.h
- src/core/lib/transport/transport.h
- src/core/lib/transport/transport_impl.h
+ - src/core/lib/uri/uri_parser.h
deps:
- gpr
uses:
@@ -578,17 +580,16 @@ filegroups:
- src/core/ext/filters/client_channel/lb_policy.h
- src/core/ext/filters/client_channel/lb_policy_factory.h
- src/core/ext/filters/client_channel/lb_policy_registry.h
- - src/core/ext/filters/client_channel/method_params.h
- src/core/ext/filters/client_channel/parse_address.h
- src/core/ext/filters/client_channel/proxy_mapper.h
- src/core/ext/filters/client_channel/proxy_mapper_registry.h
- src/core/ext/filters/client_channel/resolver.h
- src/core/ext/filters/client_channel/resolver_factory.h
- src/core/ext/filters/client_channel/resolver_registry.h
+ - src/core/ext/filters/client_channel/resolver_result_parsing.h
- src/core/ext/filters/client_channel/retry_throttle.h
- src/core/ext/filters/client_channel/subchannel.h
- src/core/ext/filters/client_channel/subchannel_index.h
- - src/core/ext/filters/client_channel/uri_parser.h
src:
- src/core/ext/filters/client_channel/backup_poller.cc
- src/core/ext/filters/client_channel/channel_connectivity.cc
@@ -603,16 +604,15 @@ filegroups:
- src/core/ext/filters/client_channel/lb_policy.cc
- src/core/ext/filters/client_channel/lb_policy_factory.cc
- src/core/ext/filters/client_channel/lb_policy_registry.cc
- - src/core/ext/filters/client_channel/method_params.cc
- src/core/ext/filters/client_channel/parse_address.cc
- src/core/ext/filters/client_channel/proxy_mapper.cc
- src/core/ext/filters/client_channel/proxy_mapper_registry.cc
- src/core/ext/filters/client_channel/resolver.cc
- src/core/ext/filters/client_channel/resolver_registry.cc
+ - src/core/ext/filters/client_channel/resolver_result_parsing.cc
- src/core/ext/filters/client_channel/retry_throttle.cc
- src/core/ext/filters/client_channel/subchannel.cc
- src/core/ext/filters/client_channel/subchannel_index.cc
- - src/core/ext/filters/client_channel/uri_parser.cc
plugin: grpc_client_channel
uses:
- grpc_base
@@ -1146,7 +1146,6 @@ filegroups:
- grpc
- name: tsi
headers:
- - src/core/tsi/alts_transport_security.h
- src/core/tsi/fake_transport_security.h
- src/core/tsi/local_transport_security.h
- src/core/tsi/ssl/session_cache/ssl_session.h
@@ -1155,7 +1154,6 @@ filegroups:
- src/core/tsi/ssl_types.h
- src/core/tsi/transport_security_grpc.h
src:
- - src/core/tsi/alts_transport_security.cc
- src/core/tsi/fake_transport_security.cc
- src/core/tsi/local_transport_security.cc
- src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc
@@ -1165,7 +1163,6 @@ filegroups:
- src/core/tsi/transport_security_grpc.cc
deps:
- gpr
- plugin: grpc_tsi_alts
secure: true
uses:
- tsi_interface
@@ -1222,6 +1219,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 +1233,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 +1361,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
@@ -1375,6 +1379,7 @@ filegroups:
src:
- src/cpp/client/channel_cc.cc
- src/cpp/client/client_context.cc
+ - src/cpp/client/client_interceptor.cc
- src/cpp/client/create_channel.cc
- src/cpp/client/create_channel_internal.cc
- src/cpp/client/create_channel_posix.cc
@@ -2029,6 +2034,7 @@ libs:
- test/cpp/qps/qps_worker.cc
- test/cpp/qps/report.cc
- test/cpp/qps/server_async.cc
+ - test/cpp/qps/server_callback.cc
- test/cpp/qps/server_sync.cc
- test/cpp/qps/usage_timer.cc
deps:
@@ -4480,6 +4486,7 @@ targets:
- gpr
- name: client_channel_stress_test
gtest: false
+ cpu_cost: 10.0
build: test
language: c++
src:
@@ -4523,6 +4530,23 @@ 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
+ - test/cpp/end2end/interceptors_util.cc
+ deps:
+ - grpc++_test_util
+ - grpc_test_util
+ - grpc++
+ - grpc
+ - gpr_test_util
+ - gpr
- name: client_lb_end2end_test
gtest: true
build: test
@@ -4642,8 +4666,11 @@ targets:
cpu_cost: 0.5
build: test
language: c++
+ headers:
+ - test/cpp/end2end/interceptors_util.h
src:
- test/cpp/end2end/end2end_test.cc
+ - test/cpp/end2end/interceptors_util.cc
deps:
- grpc++_test_util
- grpc_test_util
@@ -5435,6 +5462,23 @@ 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/interceptors_util.cc
+ - 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/config.m4 b/config.m4
index cf6769796b..a087a6b48b 100644
--- a/config.m4
+++ b/config.m4
@@ -234,6 +234,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/transport/timeout_encoding.cc \
src/core/lib/transport/transport.cc \
src/core/lib/transport/transport_op_string.cc \
+ src/core/lib/uri/uri_parser.cc \
src/core/lib/debug/trace.cc \
src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc \
src/core/ext/transport/chttp2/transport/bin_decoder.cc \
@@ -306,7 +307,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc \
src/core/tsi/alts/frame_protector/frame_handler.cc \
src/core/tsi/alts/handshaker/alts_handshaker_client.cc \
- src/core/tsi/alts/handshaker/alts_tsi_event.cc \
+ src/core/tsi/alts/handshaker/alts_shared_resource.cc \
src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc \
src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc \
src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc \
@@ -348,19 +349,17 @@ if test "$PHP_GRPC" != "no"; then
src/core/ext/filters/client_channel/lb_policy.cc \
src/core/ext/filters/client_channel/lb_policy_factory.cc \
src/core/ext/filters/client_channel/lb_policy_registry.cc \
- src/core/ext/filters/client_channel/method_params.cc \
src/core/ext/filters/client_channel/parse_address.cc \
src/core/ext/filters/client_channel/proxy_mapper.cc \
src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
src/core/ext/filters/client_channel/resolver.cc \
src/core/ext/filters/client_channel/resolver_registry.cc \
+ src/core/ext/filters/client_channel/resolver_result_parsing.cc \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/subchannel.cc \
src/core/ext/filters/client_channel/subchannel_index.cc \
- src/core/ext/filters/client_channel/uri_parser.cc \
src/core/ext/filters/deadline/deadline_filter.cc \
src/core/ext/filters/client_channel/health/health.pb.c \
- src/core/tsi/alts_transport_security.cc \
src/core/tsi/fake_transport_security.cc \
src/core/tsi/local_transport_security.cc \
src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc \
@@ -665,7 +664,8 @@ if test "$PHP_GRPC" != "no"; then
third_party/boringssl/third_party/fiat/curve25519.c \
, $ext_shared, , -fvisibility=hidden \
-DOPENSSL_NO_ASM -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN \
- -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0)
+ -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0 \
+ -DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1)
PHP_ADD_BUILD_DIR($ext_builddir/src/php/ext/grpc)
@@ -733,6 +733,7 @@ if test "$PHP_GRPC" != "no"; then
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/slice)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/surface)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/transport)
+ PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/uri)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/plugin_registry)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi/alts/crypt)
diff --git a/config.w32 b/config.w32
index da1279ccbc..12520fef3b 100644
--- a/config.w32
+++ b/config.w32
@@ -209,6 +209,7 @@ if (PHP_GRPC != "no") {
"src\\core\\lib\\transport\\timeout_encoding.cc " +
"src\\core\\lib\\transport\\transport.cc " +
"src\\core\\lib\\transport\\transport_op_string.cc " +
+ "src\\core\\lib\\uri\\uri_parser.cc " +
"src\\core\\lib\\debug\\trace.cc " +
"src\\core\\ext\\transport\\chttp2\\server\\secure\\server_secure_chttp2.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\bin_decoder.cc " +
@@ -281,7 +282,7 @@ if (PHP_GRPC != "no") {
"src\\core\\tsi\\alts\\frame_protector\\alts_unseal_privacy_integrity_crypter.cc " +
"src\\core\\tsi\\alts\\frame_protector\\frame_handler.cc " +
"src\\core\\tsi\\alts\\handshaker\\alts_handshaker_client.cc " +
- "src\\core\\tsi\\alts\\handshaker\\alts_tsi_event.cc " +
+ "src\\core\\tsi\\alts\\handshaker\\alts_shared_resource.cc " +
"src\\core\\tsi\\alts\\handshaker\\alts_tsi_handshaker.cc " +
"src\\core\\tsi\\alts\\zero_copy_frame_protector\\alts_grpc_integrity_only_record_protocol.cc " +
"src\\core\\tsi\\alts\\zero_copy_frame_protector\\alts_grpc_privacy_integrity_record_protocol.cc " +
@@ -323,19 +324,17 @@ if (PHP_GRPC != "no") {
"src\\core\\ext\\filters\\client_channel\\lb_policy.cc " +
"src\\core\\ext\\filters\\client_channel\\lb_policy_factory.cc " +
"src\\core\\ext\\filters\\client_channel\\lb_policy_registry.cc " +
- "src\\core\\ext\\filters\\client_channel\\method_params.cc " +
"src\\core\\ext\\filters\\client_channel\\parse_address.cc " +
"src\\core\\ext\\filters\\client_channel\\proxy_mapper.cc " +
"src\\core\\ext\\filters\\client_channel\\proxy_mapper_registry.cc " +
"src\\core\\ext\\filters\\client_channel\\resolver.cc " +
"src\\core\\ext\\filters\\client_channel\\resolver_registry.cc " +
+ "src\\core\\ext\\filters\\client_channel\\resolver_result_parsing.cc " +
"src\\core\\ext\\filters\\client_channel\\retry_throttle.cc " +
"src\\core\\ext\\filters\\client_channel\\subchannel.cc " +
"src\\core\\ext\\filters\\client_channel\\subchannel_index.cc " +
- "src\\core\\ext\\filters\\client_channel\\uri_parser.cc " +
"src\\core\\ext\\filters\\deadline\\deadline_filter.cc " +
"src\\core\\ext\\filters\\client_channel\\health\\health.pb.c " +
- "src\\core\\tsi\\alts_transport_security.cc " +
"src\\core\\tsi\\fake_transport_security.cc " +
"src\\core\\tsi\\local_transport_security.cc " +
"src\\core\\tsi\\ssl\\session_cache\\ssl_session_boringssl.cc " +
@@ -749,6 +748,7 @@ if (PHP_GRPC != "no") {
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\slice");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\surface");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\transport");
+ FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\uri");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\plugin_registry");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\alts");
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/doc/environment_variables.md b/doc/environment_variables.md
index 1eb850e976..d1172e62f4 100644
--- a/doc/environment_variables.md
+++ b/doc/environment_variables.md
@@ -140,3 +140,13 @@ some configuration as environment variables that can be set.
* grpc_cfstream
set to 1 to turn on CFStream experiment. With this experiment gRPC uses CFStream API to make TCP
connections. The option is only available on iOS platform and when macro GRPC_CFSTREAM is defined.
+
+* GRPC_ARENA_INIT_STRATEGY
+ Selects the initialization strategy for blocks allocated in the arena. Valid
+ values are:
+ - no_init (default): Do not inialize the arena block.
+ - zero_init: Initialize the arena blocks with 0.
+ - non_zero_init: Initialize the arena blocks with a non-zero value.
+
+ NOTE: This environment variable is experimental and will be removed. Thus, it
+ should not be relied upon.
diff --git a/doc/python/sphinx/conf.py b/doc/python/sphinx/conf.py
new file mode 100644
index 0000000000..1eb3a5de7f
--- /dev/null
+++ b/doc/python/sphinx/conf.py
@@ -0,0 +1,102 @@
+# 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.
+
+# -- Path setup --------------------------------------------------------------
+
+import os
+import sys
+PYTHON_FOLDER = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ '..', '..', '..', 'src', 'python')
+sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio'))
+sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_health_checking'))
+sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_reflection'))
+sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_testing'))
+
+# -- Project information -----------------------------------------------------
+
+project = 'gRPC Python'
+copyright = '2018, The gRPC Authors'
+author = 'The gRPC Authors'
+
+# Import generated grpc_version after the path been modified
+import grpc_version
+version = ".".join(grpc_version.VERSION.split(".")[:3])
+release = grpc_version.VERSION
+
+# -- General configuration ---------------------------------------------------
+
+templates_path = ['_templates']
+source_suffix = ['.rst', '.md']
+master_doc = 'index'
+language = 'en'
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+pygments_style = None
+
+# --- Extensions Configuration -----------------------------------------------
+
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.viewcode',
+ 'sphinx.ext.todo',
+ 'sphinx.ext.napoleon',
+ 'sphinx.ext.coverage',
+]
+
+napoleon_google_docstring = True
+napoleon_numpy_docstring = True
+napoleon_include_special_with_doc = True
+
+autodoc_default_options = {
+ 'members': None,
+}
+
+autodoc_mock_imports = [
+ 'grpc._cython',
+ 'grpc_health.v1.health_pb2',
+ 'grpc_health.v1.health_pb2_grpc',
+ 'grpc_reflection.v1alpha.reflection_pb2',
+ 'grpc_reflection.v1alpha.reflection_pb2_grpc',
+]
+
+# -- HTML Configuration -------------------------------------------------
+
+html_theme = 'alabaster'
+html_theme_options = {
+ 'fixed_sidebar': True,
+ 'page_width': '1140px',
+ 'show_related': True,
+ 'analytics_id': 'UA-60127042-1',
+ 'description': grpc_version.VERSION,
+ 'show_powered_by': False,
+}
+
+# -- Options for manual page output ------------------------------------------
+
+man_pages = [(master_doc, 'grpcio', 'grpcio Documentation', [author], 1)]
+
+# -- Options for Texinfo output ----------------------------------------------
+
+texinfo_documents = [
+ (master_doc, 'grpcio', 'grpcio Documentation', author, 'grpcio',
+ 'One line description of project.', 'Miscellaneous'),
+]
+
+# -- Options for Epub output -------------------------------------------------
+
+epub_title = project
+epub_exclude_files = ['search.html']
+
+# -- Options for todo extension ----------------------------------------------
+
+todo_include_todos = True
diff --git a/doc/python/sphinx/glossary.rst b/doc/python/sphinx/glossary.rst
new file mode 100644
index 0000000000..dee5d16143
--- /dev/null
+++ b/doc/python/sphinx/glossary.rst
@@ -0,0 +1,16 @@
+Glossary
+================
+
+.. glossary::
+
+ metadatum
+ A key-value pair included in the HTTP header. It is a
+ 2-tuple where the first entry is the key and the
+ second is the value, i.e. (key, value). The metadata key is an ASCII str,
+ and must be a valid HTTP header name. The metadata value can be
+ either a valid HTTP ASCII str, or bytes. If bytes are provided,
+ the key must end with '-bin', i.e.
+ ``('binary-metadata-bin', b'\\x00\\xFF')``
+
+ metadata
+ A sequence of metadatum.
diff --git a/doc/python/sphinx/grpc.rst b/doc/python/sphinx/grpc.rst
new file mode 100644
index 0000000000..bd2df9596b
--- /dev/null
+++ b/doc/python/sphinx/grpc.rst
@@ -0,0 +1,169 @@
+gRPC
+=============
+
+.. module:: grpc
+
+Tutorial
+--------
+
+If you want to see gRPC in action first, visit the `Python Quickstart <https://grpc.io/docs/quickstart/python.html>`_.
+Or, if you would like dive in with more extensive usage of gRPC Python, check `gRPC Basics - Python <https://grpc.io/docs/tutorials/basic/python.html>`_ out.
+
+
+Example
+-------
+
+Go to `gRPC Python Examples <https://github.com/grpc/grpc/tree/master/examples/python>`_
+
+
+Module Contents
+---------------
+
+Create Client
+^^^^^^^^^^^^^
+
+.. autofunction:: insecure_channel
+.. autofunction:: secure_channel
+.. autofunction:: intercept_channel
+
+
+Create Client Credentials
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autofunction:: ssl_channel_credentials
+.. autofunction:: metadata_call_credentials
+.. autofunction:: access_token_call_credentials
+.. autofunction:: composite_call_credentials
+.. autofunction:: composite_channel_credentials
+
+
+Create Server
+^^^^^^^^^^^^^
+
+.. autofunction:: server
+
+
+Create Server Credentials
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autofunction:: ssl_server_credentials
+.. autofunction:: ssl_server_certificate_configuration
+.. autofunction:: dynamic_ssl_server_credentials
+
+
+RPC Method Handlers
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autofunction:: unary_unary_rpc_method_handler
+.. autofunction:: unary_stream_rpc_method_handler
+.. autofunction:: stream_unary_rpc_method_handler
+.. autofunction:: stream_stream_rpc_method_handler
+.. autofunction:: method_handlers_generic_handler
+
+
+Channel Ready Future
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autofunction:: channel_ready_future
+
+
+Channel Connectivity
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autoclass:: ChannelConnectivity
+
+
+gRPC Status Code
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autoclass:: StatusCode
+
+
+Channel Object
+^^^^^^^^^^^^^^
+
+.. autoclass:: Channel
+
+
+Server Object
+^^^^^^^^^^^^^
+
+.. autoclass:: Server
+
+
+Authentication & Authorization Objects
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autoclass:: ChannelCredentials
+.. autoclass:: CallCredentials
+.. autoclass:: AuthMetadataContext
+.. autoclass:: AuthMetadataPluginCallback
+.. autoclass:: AuthMetadataPlugin
+.. autoclass:: ServerCredentials
+.. autoclass:: ServerCertificateConfiguration
+
+
+gRPC Exceptions
+^^^^^^^^^^^^^^^
+
+.. autoexception:: RpcError
+
+
+Shared Context
+^^^^^^^^^^^^^^
+
+.. autoclass:: RpcContext
+
+
+Client-Side Context
+^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autoclass:: Call
+
+
+Client-Side Interceptor
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autoclass:: ClientCallDetails
+.. autoclass:: UnaryUnaryClientInterceptor
+.. autoclass:: UnaryStreamClientInterceptor
+.. autoclass:: StreamUnaryClientInterceptor
+.. autoclass:: StreamStreamClientInterceptor
+
+
+Service-Side Context
+^^^^^^^^^^^^^^^^^^^^
+
+.. autoclass:: ServicerContext
+
+
+Service-Side Handler
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autoclass:: RpcMethodHandler
+.. autoclass:: HandlerCallDetails
+.. autoclass:: GenericRpcHandler
+.. autoclass:: ServiceRpcHandler
+
+
+Service-Side Interceptor
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autoclass:: ServerInterceptor
+
+
+Multi-Callable Interfaces
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autoclass:: UnaryUnaryMultiCallable
+.. autoclass:: UnaryStreamMultiCallable
+.. autoclass:: StreamUnaryMultiCallable
+.. autoclass:: StreamStreamMultiCallable
+
+
+Future Interfaces
+^^^^^^^^^^^^^^^^^
+
+.. autoexception:: FutureTimeoutError
+.. autoexception:: FutureCancelledError
+.. autoclass:: Future
diff --git a/doc/python/sphinx/grpc_health_checking.rst b/doc/python/sphinx/grpc_health_checking.rst
new file mode 100644
index 0000000000..b344e34ac9
--- /dev/null
+++ b/doc/python/sphinx/grpc_health_checking.rst
@@ -0,0 +1,7 @@
+gRPC Health Checking
+====================
+
+Module Contents
+---------------
+
+.. autoclass:: grpc_health.v1.health.HealthServicer
diff --git a/doc/python/sphinx/grpc_reflection.rst b/doc/python/sphinx/grpc_reflection.rst
new file mode 100644
index 0000000000..043f2edb96
--- /dev/null
+++ b/doc/python/sphinx/grpc_reflection.rst
@@ -0,0 +1,19 @@
+gRPC Reflection
+====================
+
+What is gRPC reflection?
+---------------------------------------------
+
+Check this out `gRPC Python Server Reflection <https://github.com/grpc/grpc/blob/master/doc/python/server_reflection.md>`_
+
+
+Example
+-------
+
+Refer to the GitHub `reflection example <https://github.com/grpc/grpc/blob/master/examples/python/helloworld/greeter_server_with_reflection.py>`_
+
+
+Module Contents
+---------------
+
+.. automodule:: grpc_reflection.v1alpha.reflection
diff --git a/doc/python/sphinx/grpc_testing.rst b/doc/python/sphinx/grpc_testing.rst
new file mode 100644
index 0000000000..adfeb8b384
--- /dev/null
+++ b/doc/python/sphinx/grpc_testing.rst
@@ -0,0 +1,7 @@
+gRPC Testing
+====================
+
+Module Contents
+---------------
+
+.. automodule:: grpc_testing
diff --git a/doc/python/sphinx/index.rst b/doc/python/sphinx/index.rst
new file mode 100644
index 0000000000..322ca33e15
--- /dev/null
+++ b/doc/python/sphinx/index.rst
@@ -0,0 +1,24 @@
+Welcome to gRPC Python's documentation!
+=======================================
+
+Version: |version| Release: |release|
+
+API Reference
+=============
+
+.. toctree::
+ :caption: Contents:
+
+ grpc
+ grpc_health_checking
+ grpc_reflection
+ grpc_testing
+ glossary
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/examples/objective-c/helloworld_macos/HelloWorld.podspec b/examples/objective-c/helloworld_macos/HelloWorld.podspec
new file mode 100644
index 0000000000..fc671ef4da
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/HelloWorld.podspec
@@ -0,0 +1,66 @@
+Pod::Spec.new do |s|
+ s.name = "HelloWorld"
+ s.version = "0.0.1"
+ s.license = "Apache License, Version 2.0"
+ s.authors = { 'gRPC contributors' => 'grpc-io@googlegroups.com' }
+ s.homepage = "https://grpc.io/"
+ s.summary = "HelloWorld example"
+ s.source = { :git => 'https://github.com/grpc/grpc.git' }
+
+ s.ios.deployment_target = "7.1"
+ s.osx.deployment_target = "10.9"
+
+ # Base directory where the .proto files are.
+ src = "../../protos"
+
+ # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
+ s.dependency "!ProtoCompiler-gRPCPlugin", "~> 1.0"
+
+ # Pods directory corresponding to this app's Podfile, relative to the location of this podspec.
+ pods_root = 'Pods'
+
+ # Path where Cocoapods downloads protoc and the gRPC plugin.
+ protoc_dir = "#{pods_root}/!ProtoCompiler"
+ protoc = "#{protoc_dir}/protoc"
+ plugin = "#{pods_root}/!ProtoCompiler-gRPCPlugin/grpc_objective_c_plugin"
+
+ # Directory where the generated files will be placed.
+ dir = "#{pods_root}/#{s.name}"
+
+ s.prepare_command = <<-CMD
+ mkdir -p #{dir}
+ #{protoc} \
+ --plugin=protoc-gen-grpc=#{plugin} \
+ --objc_out=#{dir} \
+ --grpc_out=#{dir} \
+ -I #{src} \
+ -I #{protoc_dir} \
+ #{src}/helloworld.proto
+ CMD
+
+ # Files generated by protoc
+ s.subspec "Messages" do |ms|
+ ms.source_files = "#{dir}/*.pbobjc.{h,m}", "#{dir}/**/*.pbobjc.{h,m}"
+ ms.header_mappings_dir = dir
+ ms.requires_arc = false
+ # The generated files depend on the protobuf runtime.
+ ms.dependency "Protobuf"
+ end
+
+ # Files generated by the gRPC plugin
+ s.subspec "Services" do |ss|
+ ss.source_files = "#{dir}/*.pbrpc.{h,m}", "#{dir}/**/*.pbrpc.{h,m}"
+ ss.header_mappings_dir = dir
+ ss.requires_arc = true
+ # The generated files depend on the gRPC runtime, and on the files generated by protoc.
+ ss.dependency "gRPC-ProtoRPC"
+ ss.dependency "#{s.name}/Messages"
+ end
+
+ s.pod_target_xcconfig = {
+ # This is needed by all pods that depend on Protobuf:
+ 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
+ # This is needed by all pods that depend on gRPC-RxLibrary:
+ 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
+ }
+end
diff --git a/examples/objective-c/helloworld_macos/HelloWorld.xcodeproj/project.pbxproj b/examples/objective-c/helloworld_macos/HelloWorld.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..06f06290d5
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/HelloWorld.xcodeproj/project.pbxproj
@@ -0,0 +1,399 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 5EF711A4215174880077496F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EF711A3215174880077496F /* AppDelegate.m */; };
+ 5EF711A7215174880077496F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EF711A6215174880077496F /* ViewController.m */; };
+ 5EF711A9215174890077496F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5EF711A8215174890077496F /* Assets.xcassets */; };
+ 5EF711AC215174890077496F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5EF711AA215174890077496F /* Main.storyboard */; };
+ 5EF711AF215174890077496F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EF711AE215174890077496F /* main.m */; };
+ 827B966E84F6A63FD0F3F6BC /* libPods-HelloWorld.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 369D887F6054EBA486218C69 /* libPods-HelloWorld.a */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 2AAF8E8BA7DBFD2D3886AA25 /* Pods-HelloWorld.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.release.xcconfig"; path = "Pods/Target Support Files/Pods-HelloWorld/Pods-HelloWorld.release.xcconfig"; sourceTree = "<group>"; };
+ 369D887F6054EBA486218C69 /* libPods-HelloWorld.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HelloWorld.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 5EF7119F215174870077496F /* HelloWorld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 5EF711A2215174880077496F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+ 5EF711A3215174880077496F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+ 5EF711A5215174880077496F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
+ 5EF711A6215174880077496F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
+ 5EF711A8215174890077496F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ 5EF711AB215174890077496F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 5EF711AD215174890077496F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 5EF711AE215174890077496F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 5EF711B0215174890077496F /* HelloWorld.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = HelloWorld.entitlements; sourceTree = "<group>"; };
+ CA4699782F6344C8E67C9FEE /* Pods-HelloWorld.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.debug.xcconfig"; path = "Pods/Target Support Files/Pods-HelloWorld/Pods-HelloWorld.debug.xcconfig"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 5EF7119C215174870077496F /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 827B966E84F6A63FD0F3F6BC /* libPods-HelloWorld.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 5EF71196215174870077496F = {
+ isa = PBXGroup;
+ children = (
+ 5EF711AE215174890077496F /* main.m */,
+ 5EF711A1215174870077496F /* HelloWorld */,
+ 5EF711A0215174870077496F /* Products */,
+ 8D3EFBB796129582177142C4 /* Pods */,
+ A986548CB5622AF6CC3ECCCE /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ 5EF711A0215174870077496F /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 5EF7119F215174870077496F /* HelloWorld.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 5EF711A1215174870077496F /* HelloWorld */ = {
+ isa = PBXGroup;
+ children = (
+ 5EF711A2215174880077496F /* AppDelegate.h */,
+ 5EF711A3215174880077496F /* AppDelegate.m */,
+ 5EF711A5215174880077496F /* ViewController.h */,
+ 5EF711A6215174880077496F /* ViewController.m */,
+ 5EF711A8215174890077496F /* Assets.xcassets */,
+ 5EF711AA215174890077496F /* Main.storyboard */,
+ 5EF711AD215174890077496F /* Info.plist */,
+ 5EF711B0215174890077496F /* HelloWorld.entitlements */,
+ );
+ path = HelloWorld;
+ sourceTree = "<group>";
+ };
+ 8D3EFBB796129582177142C4 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ CA4699782F6344C8E67C9FEE /* Pods-HelloWorld.debug.xcconfig */,
+ 2AAF8E8BA7DBFD2D3886AA25 /* Pods-HelloWorld.release.xcconfig */,
+ );
+ name = Pods;
+ sourceTree = "<group>";
+ };
+ A986548CB5622AF6CC3ECCCE /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 369D887F6054EBA486218C69 /* libPods-HelloWorld.a */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 5EF7119E215174870077496F /* HelloWorld */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 5EF711B3215174890077496F /* Build configuration list for PBXNativeTarget "HelloWorld" */;
+ buildPhases = (
+ 3694AEA482289A5BDD5EA5A4 /* [CP] Check Pods Manifest.lock */,
+ 5EF7119B215174870077496F /* Sources */,
+ 5EF7119C215174870077496F /* Frameworks */,
+ 5EF7119D215174870077496F /* Resources */,
+ DF5241368CCEAA9DC73E7EA8 /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = HelloWorld;
+ productName = HelloWorld;
+ productReference = 5EF7119F215174870077496F /* HelloWorld.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 5EF71197215174870077496F /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0930;
+ ORGANIZATIONNAME = gRPC;
+ TargetAttributes = {
+ 5EF7119E215174870077496F = {
+ CreatedOnToolsVersion = 9.3;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 0;
+ };
+ };
+ };
+ };
+ };
+ buildConfigurationList = 5EF7119A215174870077496F /* Build configuration list for PBXProject "HelloWorld" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 5EF71196215174870077496F;
+ productRefGroup = 5EF711A0215174870077496F /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 5EF7119E215174870077496F /* HelloWorld */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 5EF7119D215174870077496F /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5EF711A9215174890077496F /* Assets.xcassets in Resources */,
+ 5EF711AC215174890077496F /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3694AEA482289A5BDD5EA5A4 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-HelloWorld-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ DF5241368CCEAA9DC73E7EA8 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${SRCROOT}/Pods/Target Support Files/Pods-HelloWorld/Pods-HelloWorld-resources.sh",
+ "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputPaths = (
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-HelloWorld/Pods-HelloWorld-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 5EF7119B215174870077496F /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5EF711A7215174880077496F /* ViewController.m in Sources */,
+ 5EF711AF215174890077496F /* main.m in Sources */,
+ 5EF711A4215174880077496F /* AppDelegate.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 5EF711AA215174890077496F /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 5EF711AB215174890077496F /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 5EF711B1215174890077496F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ 5EF711B2215174890077496F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+ 5EF711B4215174890077496F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = CA4699782F6344C8E67C9FEE /* Pods-HelloWorld.debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = HelloWorld/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = io.grpc.HelloWorld;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 5EF711B5215174890077496F /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 2AAF8E8BA7DBFD2D3886AA25 /* Pods-HelloWorld.release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = HelloWorld/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = io.grpc.HelloWorld;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 5EF7119A215174870077496F /* Build configuration list for PBXProject "HelloWorld" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 5EF711B1215174890077496F /* Debug */,
+ 5EF711B2215174890077496F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 5EF711B3215174890077496F /* Build configuration list for PBXNativeTarget "HelloWorld" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 5EF711B4215174890077496F /* Debug */,
+ 5EF711B5215174890077496F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 5EF71197215174870077496F /* Project object */;
+}
diff --git a/examples/objective-c/helloworld_macos/HelloWorld/AppDelegate.h b/examples/objective-c/helloworld_macos/HelloWorld/AppDelegate.h
new file mode 100644
index 0000000000..1b88e35d22
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/HelloWorld/AppDelegate.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@interface AppDelegate : NSObject <NSApplicationDelegate>
+
+
+@end
+
diff --git a/examples/objective-c/helloworld_macos/HelloWorld/AppDelegate.m b/examples/objective-c/helloworld_macos/HelloWorld/AppDelegate.m
new file mode 100644
index 0000000000..7da5e1171b
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/HelloWorld/AppDelegate.m
@@ -0,0 +1,37 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+ // Insert code here to initialize your application
+}
+
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
+ // Insert code here to tear down your application
+}
+
+
+@end
diff --git a/examples/objective-c/helloworld_macos/HelloWorld/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/objective-c/helloworld_macos/HelloWorld/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..2db2b1c7c6
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/HelloWorld/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,58 @@
+{
+ "images" : [
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/examples/objective-c/helloworld_macos/HelloWorld/Assets.xcassets/Contents.json b/examples/objective-c/helloworld_macos/HelloWorld/Assets.xcassets/Contents.json
new file mode 100644
index 0000000000..da4a164c91
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/HelloWorld/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/examples/objective-c/helloworld_macos/HelloWorld/Base.lproj/Main.storyboard b/examples/objective-c/helloworld_macos/HelloWorld/Base.lproj/Main.storyboard
new file mode 100644
index 0000000000..3ed4144fba
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/HelloWorld/Base.lproj/Main.storyboard
@@ -0,0 +1,717 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11134"/>
+ </dependencies>
+ <scenes>
+ <!--Application-->
+ <scene sceneID="JPo-4y-FX3">
+ <objects>
+ <application id="hnw-xV-0zn" sceneMemberID="viewController">
+ <menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+ <items>
+ <menuItem title="HelloWorld" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="HelloWorld" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About HelloWorld" id="5kV-Vb-QxS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+ <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+ <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+ <menuItem title="Services" id="NMo-om-nkz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+ <menuItem title="Hide HelloWorld" keyEquivalent="h" id="Olw-nP-bQN">
+ <connections>
+ <action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All" id="Kd2-mp-pUS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+ <menuItem title="Quit HelloWorld" keyEquivalent="q" id="4sb-4s-VLi">
+ <connections>
+ <action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="File" id="dMs-cI-mzQ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="File" id="bib-Uj-vzu">
+ <items>
+ <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
+ <connections>
+ <action selector="newDocument:" target="Ady-hI-5gd" id="4Si-XN-c54"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
+ <connections>
+ <action selector="openDocument:" target="Ady-hI-5gd" id="bVn-NM-KNZ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Open Recent" id="tXI-mr-wws">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
+ <items>
+ <menuItem title="Clear Menu" id="vNY-rz-j42">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="Daa-9d-B3U"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
+ <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
+ <connections>
+ <action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
+ <connections>
+ <action selector="saveDocument:" target="Ady-hI-5gd" id="teZ-XB-qJY"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
+ <connections>
+ <action selector="saveDocumentAs:" target="Ady-hI-5gd" id="mDf-zr-I0C"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
+ <connections>
+ <action selector="revertDocumentToSaved:" target="Ady-hI-5gd" id="iJ3-Pv-kwq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
+ <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
+ <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+ <connections>
+ <action selector="runPageLayout:" target="Ady-hI-5gd" id="Din-rz-gC5"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
+ <connections>
+ <action selector="print:" target="Ady-hI-5gd" id="qaZ-4w-aoO"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Edit" id="5QF-Oa-p0T">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+ <items>
+ <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+ <connections>
+ <action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+ <connections>
+ <action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+ <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+ <connections>
+ <action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+ <connections>
+ <action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+ <connections>
+ <action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Delete" id="pa3-QI-u2k">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+ <connections>
+ <action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+ <menuItem title="Find" id="4EN-yA-p0u">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Find" id="1b7-l0-nxx">
+ <items>
+ <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+ <connections>
+ <action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+ <items>
+ <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+ <connections>
+ <action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+ <connections>
+ <action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+ <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Substitutions" id="9ic-FL-obx">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+ <items>
+ <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+ <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Links" id="cwL-P1-jid">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Data Detectors" id="tRr-pd-1PS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Transformations" id="2oI-Rn-ZJC">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+ <items>
+ <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Speech" id="xrE-MZ-jX0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+ <items>
+ <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Format" id="jxT-CU-nIS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Format" id="GEO-Iw-cKr">
+ <items>
+ <menuItem title="Font" id="Gi5-1S-RQB">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
+ <items>
+ <menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
+ <connections>
+ <action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
+ <connections>
+ <action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
+ <connections>
+ <action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
+ <connections>
+ <action selector="underline:" target="Ady-hI-5gd" id="FYS-2b-JAY"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
+ <menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
+ <connections>
+ <action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
+ <connections>
+ <action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
+ <menuItem title="Kern" id="jBQ-r6-VK2">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Kern" id="tlD-Oa-oAM">
+ <items>
+ <menuItem title="Use Default" id="GUa-eO-cwY">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useStandardKerning:" target="Ady-hI-5gd" id="6dk-9l-Ckg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use None" id="cDB-IK-hbR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="turnOffKerning:" target="Ady-hI-5gd" id="U8a-gz-Maa"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Tighten" id="46P-cB-AYj">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="tightenKerning:" target="Ady-hI-5gd" id="hr7-Nz-8ro"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Loosen" id="ogc-rX-tC1">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="loosenKerning:" target="Ady-hI-5gd" id="8i4-f9-FKE"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Ligatures" id="o6e-r0-MWq">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
+ <items>
+ <menuItem title="Use Default" id="agt-UL-0e3">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useStandardLigatures:" target="Ady-hI-5gd" id="7uR-wd-Dx6"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use None" id="J7y-lM-qPV">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="turnOffLigatures:" target="Ady-hI-5gd" id="iX2-gA-Ilz"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use All" id="xQD-1f-W4t">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useAllLigatures:" target="Ady-hI-5gd" id="KcB-kA-TuK"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Baseline" id="OaQ-X3-Vso">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Baseline" id="ijk-EB-dga">
+ <items>
+ <menuItem title="Use Default" id="3Om-Ey-2VK">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unscript:" target="Ady-hI-5gd" id="0vZ-95-Ywn"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Superscript" id="Rqc-34-cIF">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="superscript:" target="Ady-hI-5gd" id="3qV-fo-wpU"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Subscript" id="I0S-gh-46l">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="subscript:" target="Ady-hI-5gd" id="Q6W-4W-IGz"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Raise" id="2h7-ER-AoG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="raiseBaseline:" target="Ady-hI-5gd" id="4sk-31-7Q9"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Lower" id="1tx-W0-xDw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowerBaseline:" target="Ady-hI-5gd" id="OF1-bc-KW4"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
+ <menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
+ <connections>
+ <action selector="orderFrontColorPanel:" target="Ady-hI-5gd" id="mSX-Xz-DV3"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
+ <menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="copyFont:" target="Ady-hI-5gd" id="GJO-xA-L4q"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteFont:" target="Ady-hI-5gd" id="JfD-CL-leO"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Text" id="Fal-I4-PZk">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Text" id="d9c-me-L2H">
+ <items>
+ <menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
+ <connections>
+ <action selector="alignLeft:" target="Ady-hI-5gd" id="zUv-R1-uAa"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
+ <connections>
+ <action selector="alignCenter:" target="Ady-hI-5gd" id="spX-mk-kcS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Justify" id="J5U-5w-g23">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="alignJustified:" target="Ady-hI-5gd" id="ljL-7U-jND"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
+ <connections>
+ <action selector="alignRight:" target="Ady-hI-5gd" id="r48-bG-YeY"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
+ <menuItem title="Writing Direction" id="H1b-Si-o9J">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
+ <items>
+ <menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem id="YGs-j5-SAR">
+ <string key="title"> Default</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionNatural:" target="Ady-hI-5gd" id="qtV-5e-UBP"/>
+ </connections>
+ </menuItem>
+ <menuItem id="Lbh-J2-qVU">
+ <string key="title"> Left to Right</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="S0X-9S-QSf"/>
+ </connections>
+ </menuItem>
+ <menuItem id="jFq-tB-4Kx">
+ <string key="title"> Right to Left</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="5fk-qB-AqJ"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
+ <menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem id="Nop-cj-93Q">
+ <string key="title"> Default</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionNatural:" target="Ady-hI-5gd" id="lPI-Se-ZHp"/>
+ </connections>
+ </menuItem>
+ <menuItem id="BgM-ve-c93">
+ <string key="title"> Left to Right</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="caW-Bv-w94"/>
+ </connections>
+ </menuItem>
+ <menuItem id="RB4-Sm-HuC">
+ <string key="title"> Right to Left</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="EXD-6r-ZUu"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
+ <menuItem title="Show Ruler" id="vLm-3I-IUL">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleRuler:" target="Ady-hI-5gd" id="FOx-HJ-KwY"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="copyRuler:" target="Ady-hI-5gd" id="71i-fW-3W2"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="pasteRuler:" target="Ady-hI-5gd" id="cSh-wd-qM2"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="View" id="H8h-7b-M4v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="View" id="HyV-fh-RgO">
+ <items>
+ <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
+ <menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="toggleSourceList:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Window" id="aUF-d1-5bR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+ <items>
+ <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+ <connections>
+ <action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Zoom" id="R4o-n2-Eq4">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+ <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Help" id="wpr-3q-Mcd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
+ <items>
+ <menuItem title="HelloWorld Help" keyEquivalent="?" id="FKE-Sm-Kum">
+ <connections>
+ <action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ <connections>
+ <outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
+ </connections>
+ </application>
+ <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider=""/>
+ <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+ <customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="75" y="0.0"/>
+ </scene>
+ <!--Window Controller-->
+ <scene sceneID="R2V-B0-nI4">
+ <objects>
+ <windowController id="B8D-0N-5wS" sceneMemberID="viewController">
+ <window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="196" y="240" width="480" height="270"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
+ <connections>
+ <outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
+ </connections>
+ </window>
+ <connections>
+ <segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
+ </connections>
+ </windowController>
+ <customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="75" y="250"/>
+ </scene>
+ <!--View Controller-->
+ <scene sceneID="hIz-AP-VOD">
+ <objects>
+ <viewController id="XfG-lQ-9wD" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
+ <view key="view" wantsLayer="YES" id="m2S-Jp-Qdl">
+ <rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </view>
+ </viewController>
+ <customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="75" y="655"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/examples/objective-c/helloworld_macos/HelloWorld/HelloWorld.entitlements b/examples/objective-c/helloworld_macos/HelloWorld/HelloWorld.entitlements
new file mode 100644
index 0000000000..0c67376eba
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/HelloWorld/HelloWorld.entitlements
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>
diff --git a/examples/objective-c/helloworld_macos/HelloWorld/Info.plist b/examples/objective-c/helloworld_macos/HelloWorld/Info.plist
new file mode 100644
index 0000000000..f7bfdac9d6
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/HelloWorld/Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2018 gRPC. All rights reserved.</string>
+ <key>NSMainStoryboardFile</key>
+ <string>Main</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/examples/objective-c/helloworld_macos/HelloWorld/ViewController.h b/examples/objective-c/helloworld_macos/HelloWorld/ViewController.h
new file mode 100644
index 0000000000..ff620dc426
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/HelloWorld/ViewController.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@interface ViewController : NSViewController
+
+
+@end
+
diff --git a/examples/objective-c/helloworld_macos/HelloWorld/ViewController.m b/examples/objective-c/helloworld_macos/HelloWorld/ViewController.m
new file mode 100644
index 0000000000..fc9e95f9a6
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/HelloWorld/ViewController.m
@@ -0,0 +1,37 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#import "ViewController.h"
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ // Do any additional setup after loading the view.
+}
+
+
+- (void)setRepresentedObject:(id)representedObject {
+ [super setRepresentedObject:representedObject];
+
+ // Update the view, if already loaded.
+}
+
+
+@end
diff --git a/examples/objective-c/helloworld_macos/Podfile b/examples/objective-c/helloworld_macos/Podfile
new file mode 100644
index 0000000000..bf1488cba6
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/Podfile
@@ -0,0 +1,9 @@
+source 'https://github.com/CocoaPods/Specs.git'
+platform :macos, '10.9'
+
+install! 'cocoapods', :deterministic_uuids => false
+
+target 'HelloWorld' do
+ # Depend on the generated HelloWorld library.
+ pod 'HelloWorld', :path => '.'
+end
diff --git a/examples/objective-c/helloworld_macos/README.md b/examples/objective-c/helloworld_macos/README.md
new file mode 100644
index 0000000000..295701b5e6
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/README.md
@@ -0,0 +1,6 @@
+# gRPC Objective-C Mac OS Hello World Example
+
+A hello world example app on Mac OS. Note that Mac OS is not a first class supported platform of gRPC
+Objective-C library. This example is only for reference.
+
+Refer to [Hello World Example](../helloworld) for instructions on installation and running.
diff --git a/examples/objective-c/helloworld_macos/main.m b/examples/objective-c/helloworld_macos/main.m
new file mode 100644
index 0000000000..2a98ec4966
--- /dev/null
+++ b/examples/objective-c/helloworld_macos/main.m
@@ -0,0 +1,43 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import <GRPCClient/GRPCCall+ChannelArg.h>
+#import <GRPCClient/GRPCCall+Tests.h>
+#import <HelloWorld/Helloworld.pbrpc.h>
+
+static NSString * const kHostAddress = @"localhost:50051";
+
+int main(int argc, const char * argv[]) {
+ @autoreleasepool {
+ [GRPCCall useInsecureConnectionsForHost:kHostAddress];
+ [GRPCCall setUserAgentPrefix:@"HelloWorld/1.0" forHost:kHostAddress];
+
+ HLWGreeter *client = [[HLWGreeter alloc] initWithHost:kHostAddress];
+
+ HLWHelloRequest *request = [HLWHelloRequest message];
+ request.name = @"Objective-C";
+
+ [client sayHelloWithRequest:request handler:^(HLWHelloReply *response, NSError *error) {
+ NSLog(@"%@", response.message);
+ }];
+ }
+
+ return NSApplicationMain(argc, argv);
+}
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
index 187551da87..5fd3945efb 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',
@@ -185,6 +195,7 @@ Pod::Spec.new do |s|
'src/cpp/server/secure_server_credentials.cc',
'src/cpp/client/channel_cc.cc',
'src/cpp/client/client_context.cc',
+ 'src/cpp/client/client_interceptor.cc',
'src/cpp/client/create_channel.cc',
'src/cpp/client/create_channel_internal.cc',
'src/cpp/client/create_channel_posix.cc',
@@ -303,7 +314,7 @@ Pod::Spec.new do |s|
'src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h',
'src/core/tsi/alts/frame_protector/frame_handler.h',
'src/core/tsi/alts/handshaker/alts_handshaker_client.h',
- 'src/core/tsi/alts/handshaker/alts_tsi_event.h',
+ 'src/core/tsi/alts/handshaker/alts_shared_resource.h',
'src/core/tsi/alts/handshaker/alts_tsi_handshaker.h',
'src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h',
'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h',
@@ -336,20 +347,18 @@ Pod::Spec.new do |s|
'src/core/ext/filters/client_channel/lb_policy.h',
'src/core/ext/filters/client_channel/lb_policy_factory.h',
'src/core/ext/filters/client_channel/lb_policy_registry.h',
- 'src/core/ext/filters/client_channel/method_params.h',
'src/core/ext/filters/client_channel/parse_address.h',
'src/core/ext/filters/client_channel/proxy_mapper.h',
'src/core/ext/filters/client_channel/proxy_mapper_registry.h',
'src/core/ext/filters/client_channel/resolver.h',
'src/core/ext/filters/client_channel/resolver_factory.h',
'src/core/ext/filters/client_channel/resolver_registry.h',
+ 'src/core/ext/filters/client_channel/resolver_result_parsing.h',
'src/core/ext/filters/client_channel/retry_throttle.h',
'src/core/ext/filters/client_channel/subchannel.h',
'src/core/ext/filters/client_channel/subchannel_index.h',
- 'src/core/ext/filters/client_channel/uri_parser.h',
'src/core/ext/filters/deadline/deadline_filter.h',
'src/core/ext/filters/client_channel/health/health.pb.h',
- 'src/core/tsi/alts_transport_security.h',
'src/core/tsi/fake_transport_security.h',
'src/core/tsi/local_transport_security.h',
'src/core/tsi/ssl/session_cache/ssl_session.h',
@@ -494,6 +503,7 @@ Pod::Spec.new do |s|
'src/core/lib/transport/timeout_encoding.h',
'src/core/lib/transport/transport.h',
'src/core/lib/transport/transport_impl.h',
+ 'src/core/lib/uri/uri_parser.h',
'src/core/lib/debug/trace.h',
'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h',
'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h',
@@ -685,6 +695,7 @@ Pod::Spec.new do |s|
'src/core/lib/transport/timeout_encoding.h',
'src/core/lib/transport/transport.h',
'src/core/lib/transport/transport_impl.h',
+ 'src/core/lib/uri/uri_parser.h',
'src/core/lib/debug/trace.h',
'src/core/ext/transport/inproc/inproc_transport.h',
'src/core/ext/filters/client_channel/health/health.pb.h'
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index bbd5d7e961..f0a715cb58 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -312,7 +312,7 @@ Pod::Spec.new do |s|
'src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h',
'src/core/tsi/alts/frame_protector/frame_handler.h',
'src/core/tsi/alts/handshaker/alts_handshaker_client.h',
- 'src/core/tsi/alts/handshaker/alts_tsi_event.h',
+ 'src/core/tsi/alts/handshaker/alts_shared_resource.h',
'src/core/tsi/alts/handshaker/alts_tsi_handshaker.h',
'src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h',
'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h',
@@ -345,20 +345,18 @@ Pod::Spec.new do |s|
'src/core/ext/filters/client_channel/lb_policy.h',
'src/core/ext/filters/client_channel/lb_policy_factory.h',
'src/core/ext/filters/client_channel/lb_policy_registry.h',
- 'src/core/ext/filters/client_channel/method_params.h',
'src/core/ext/filters/client_channel/parse_address.h',
'src/core/ext/filters/client_channel/proxy_mapper.h',
'src/core/ext/filters/client_channel/proxy_mapper_registry.h',
'src/core/ext/filters/client_channel/resolver.h',
'src/core/ext/filters/client_channel/resolver_factory.h',
'src/core/ext/filters/client_channel/resolver_registry.h',
+ 'src/core/ext/filters/client_channel/resolver_result_parsing.h',
'src/core/ext/filters/client_channel/retry_throttle.h',
'src/core/ext/filters/client_channel/subchannel.h',
'src/core/ext/filters/client_channel/subchannel_index.h',
- 'src/core/ext/filters/client_channel/uri_parser.h',
'src/core/ext/filters/deadline/deadline_filter.h',
'src/core/ext/filters/client_channel/health/health.pb.h',
- 'src/core/tsi/alts_transport_security.h',
'src/core/tsi/fake_transport_security.h',
'src/core/tsi/local_transport_security.h',
'src/core/tsi/ssl/session_cache/ssl_session.h',
@@ -503,6 +501,7 @@ Pod::Spec.new do |s|
'src/core/lib/transport/timeout_encoding.h',
'src/core/lib/transport/transport.h',
'src/core/lib/transport/transport_impl.h',
+ 'src/core/lib/uri/uri_parser.h',
'src/core/lib/debug/trace.h',
'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h',
'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h',
@@ -674,6 +673,7 @@ Pod::Spec.new do |s|
'src/core/lib/transport/timeout_encoding.cc',
'src/core/lib/transport/transport.cc',
'src/core/lib/transport/transport_op_string.cc',
+ 'src/core/lib/uri/uri_parser.cc',
'src/core/lib/debug/trace.cc',
'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc',
'src/core/ext/transport/chttp2/transport/bin_decoder.cc',
@@ -746,7 +746,7 @@ Pod::Spec.new do |s|
'src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc',
'src/core/tsi/alts/frame_protector/frame_handler.cc',
'src/core/tsi/alts/handshaker/alts_handshaker_client.cc',
- 'src/core/tsi/alts/handshaker/alts_tsi_event.cc',
+ 'src/core/tsi/alts/handshaker/alts_shared_resource.cc',
'src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc',
'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc',
'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc',
@@ -785,19 +785,17 @@ Pod::Spec.new do |s|
'src/core/ext/filters/client_channel/lb_policy.cc',
'src/core/ext/filters/client_channel/lb_policy_factory.cc',
'src/core/ext/filters/client_channel/lb_policy_registry.cc',
- 'src/core/ext/filters/client_channel/method_params.cc',
'src/core/ext/filters/client_channel/parse_address.cc',
'src/core/ext/filters/client_channel/proxy_mapper.cc',
'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
'src/core/ext/filters/client_channel/resolver.cc',
'src/core/ext/filters/client_channel/resolver_registry.cc',
+ 'src/core/ext/filters/client_channel/resolver_result_parsing.cc',
'src/core/ext/filters/client_channel/retry_throttle.cc',
'src/core/ext/filters/client_channel/subchannel.cc',
'src/core/ext/filters/client_channel/subchannel_index.cc',
- 'src/core/ext/filters/client_channel/uri_parser.cc',
'src/core/ext/filters/deadline/deadline_filter.cc',
'src/core/ext/filters/client_channel/health/health.pb.c',
- 'src/core/tsi/alts_transport_security.cc',
'src/core/tsi/fake_transport_security.cc',
'src/core/tsi/local_transport_security.cc',
'src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc',
@@ -931,7 +929,7 @@ Pod::Spec.new do |s|
'src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h',
'src/core/tsi/alts/frame_protector/frame_handler.h',
'src/core/tsi/alts/handshaker/alts_handshaker_client.h',
- 'src/core/tsi/alts/handshaker/alts_tsi_event.h',
+ 'src/core/tsi/alts/handshaker/alts_shared_resource.h',
'src/core/tsi/alts/handshaker/alts_tsi_handshaker.h',
'src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h',
'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h',
@@ -964,20 +962,18 @@ Pod::Spec.new do |s|
'src/core/ext/filters/client_channel/lb_policy.h',
'src/core/ext/filters/client_channel/lb_policy_factory.h',
'src/core/ext/filters/client_channel/lb_policy_registry.h',
- 'src/core/ext/filters/client_channel/method_params.h',
'src/core/ext/filters/client_channel/parse_address.h',
'src/core/ext/filters/client_channel/proxy_mapper.h',
'src/core/ext/filters/client_channel/proxy_mapper_registry.h',
'src/core/ext/filters/client_channel/resolver.h',
'src/core/ext/filters/client_channel/resolver_factory.h',
'src/core/ext/filters/client_channel/resolver_registry.h',
+ 'src/core/ext/filters/client_channel/resolver_result_parsing.h',
'src/core/ext/filters/client_channel/retry_throttle.h',
'src/core/ext/filters/client_channel/subchannel.h',
'src/core/ext/filters/client_channel/subchannel_index.h',
- 'src/core/ext/filters/client_channel/uri_parser.h',
'src/core/ext/filters/deadline/deadline_filter.h',
'src/core/ext/filters/client_channel/health/health.pb.h',
- 'src/core/tsi/alts_transport_security.h',
'src/core/tsi/fake_transport_security.h',
'src/core/tsi/local_transport_security.h',
'src/core/tsi/ssl/session_cache/ssl_session.h',
@@ -1122,6 +1118,7 @@ Pod::Spec.new do |s|
'src/core/lib/transport/timeout_encoding.h',
'src/core/lib/transport/transport.h',
'src/core/lib/transport/transport_impl.h',
+ 'src/core/lib/uri/uri_parser.h',
'src/core/lib/debug/trace.h',
'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h',
'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.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/grpc.gemspec b/grpc.gemspec
index a3924d0d93..04f3066899 100644
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -244,7 +244,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h )
s.files += %w( src/core/tsi/alts/frame_protector/frame_handler.h )
s.files += %w( src/core/tsi/alts/handshaker/alts_handshaker_client.h )
- s.files += %w( src/core/tsi/alts/handshaker/alts_tsi_event.h )
+ s.files += %w( src/core/tsi/alts/handshaker/alts_shared_resource.h )
s.files += %w( src/core/tsi/alts/handshaker/alts_tsi_handshaker.h )
s.files += %w( src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h )
s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h )
@@ -281,20 +281,18 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/filters/client_channel/lb_policy.h )
s.files += %w( src/core/ext/filters/client_channel/lb_policy_factory.h )
s.files += %w( src/core/ext/filters/client_channel/lb_policy_registry.h )
- s.files += %w( src/core/ext/filters/client_channel/method_params.h )
s.files += %w( src/core/ext/filters/client_channel/parse_address.h )
s.files += %w( src/core/ext/filters/client_channel/proxy_mapper.h )
s.files += %w( src/core/ext/filters/client_channel/proxy_mapper_registry.h )
s.files += %w( src/core/ext/filters/client_channel/resolver.h )
s.files += %w( src/core/ext/filters/client_channel/resolver_factory.h )
s.files += %w( src/core/ext/filters/client_channel/resolver_registry.h )
+ s.files += %w( src/core/ext/filters/client_channel/resolver_result_parsing.h )
s.files += %w( src/core/ext/filters/client_channel/retry_throttle.h )
s.files += %w( src/core/ext/filters/client_channel/subchannel.h )
s.files += %w( src/core/ext/filters/client_channel/subchannel_index.h )
- s.files += %w( src/core/ext/filters/client_channel/uri_parser.h )
s.files += %w( src/core/ext/filters/deadline/deadline_filter.h )
s.files += %w( src/core/ext/filters/client_channel/health/health.pb.h )
- s.files += %w( src/core/tsi/alts_transport_security.h )
s.files += %w( src/core/tsi/fake_transport_security.h )
s.files += %w( src/core/tsi/local_transport_security.h )
s.files += %w( src/core/tsi/ssl/session_cache/ssl_session.h )
@@ -439,6 +437,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/transport/timeout_encoding.h )
s.files += %w( src/core/lib/transport/transport.h )
s.files += %w( src/core/lib/transport/transport_impl.h )
+ s.files += %w( src/core/lib/uri/uri_parser.h )
s.files += %w( src/core/lib/debug/trace.h )
s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h )
s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h )
@@ -610,6 +609,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/transport/timeout_encoding.cc )
s.files += %w( src/core/lib/transport/transport.cc )
s.files += %w( src/core/lib/transport/transport_op_string.cc )
+ s.files += %w( src/core/lib/uri/uri_parser.cc )
s.files += %w( src/core/lib/debug/trace.cc )
s.files += %w( src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc )
s.files += %w( src/core/ext/transport/chttp2/transport/bin_decoder.cc )
@@ -682,7 +682,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc )
s.files += %w( src/core/tsi/alts/frame_protector/frame_handler.cc )
s.files += %w( src/core/tsi/alts/handshaker/alts_handshaker_client.cc )
- s.files += %w( src/core/tsi/alts/handshaker/alts_tsi_event.cc )
+ s.files += %w( src/core/tsi/alts/handshaker/alts_shared_resource.cc )
s.files += %w( src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc )
s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc )
s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc )
@@ -724,19 +724,17 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/filters/client_channel/lb_policy.cc )
s.files += %w( src/core/ext/filters/client_channel/lb_policy_factory.cc )
s.files += %w( src/core/ext/filters/client_channel/lb_policy_registry.cc )
- s.files += %w( src/core/ext/filters/client_channel/method_params.cc )
s.files += %w( src/core/ext/filters/client_channel/parse_address.cc )
s.files += %w( src/core/ext/filters/client_channel/proxy_mapper.cc )
s.files += %w( src/core/ext/filters/client_channel/proxy_mapper_registry.cc )
s.files += %w( src/core/ext/filters/client_channel/resolver.cc )
s.files += %w( src/core/ext/filters/client_channel/resolver_registry.cc )
+ s.files += %w( src/core/ext/filters/client_channel/resolver_result_parsing.cc )
s.files += %w( src/core/ext/filters/client_channel/retry_throttle.cc )
s.files += %w( src/core/ext/filters/client_channel/subchannel.cc )
s.files += %w( src/core/ext/filters/client_channel/subchannel_index.cc )
- s.files += %w( src/core/ext/filters/client_channel/uri_parser.cc )
s.files += %w( src/core/ext/filters/deadline/deadline_filter.cc )
s.files += %w( src/core/ext/filters/client_channel/health/health.pb.c )
- s.files += %w( src/core/tsi/alts_transport_security.cc )
s.files += %w( src/core/tsi/fake_transport_security.cc )
s.files += %w( src/core/tsi/local_transport_security.cc )
s.files += %w( src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc )
diff --git a/grpc.gyp b/grpc.gyp
index 963b8b22b1..b7352f820a 100644
--- a/grpc.gyp
+++ b/grpc.gyp
@@ -426,6 +426,7 @@
'src/core/lib/transport/timeout_encoding.cc',
'src/core/lib/transport/transport.cc',
'src/core/lib/transport/transport_op_string.cc',
+ 'src/core/lib/uri/uri_parser.cc',
'src/core/lib/debug/trace.cc',
'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc',
'src/core/ext/transport/chttp2/transport/bin_decoder.cc',
@@ -498,7 +499,7 @@
'src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc',
'src/core/tsi/alts/frame_protector/frame_handler.cc',
'src/core/tsi/alts/handshaker/alts_handshaker_client.cc',
- 'src/core/tsi/alts/handshaker/alts_tsi_event.cc',
+ 'src/core/tsi/alts/handshaker/alts_shared_resource.cc',
'src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc',
'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc',
'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc',
@@ -540,19 +541,17 @@
'src/core/ext/filters/client_channel/lb_policy.cc',
'src/core/ext/filters/client_channel/lb_policy_factory.cc',
'src/core/ext/filters/client_channel/lb_policy_registry.cc',
- 'src/core/ext/filters/client_channel/method_params.cc',
'src/core/ext/filters/client_channel/parse_address.cc',
'src/core/ext/filters/client_channel/proxy_mapper.cc',
'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
'src/core/ext/filters/client_channel/resolver.cc',
'src/core/ext/filters/client_channel/resolver_registry.cc',
+ 'src/core/ext/filters/client_channel/resolver_result_parsing.cc',
'src/core/ext/filters/client_channel/retry_throttle.cc',
'src/core/ext/filters/client_channel/subchannel.cc',
'src/core/ext/filters/client_channel/subchannel_index.cc',
- 'src/core/ext/filters/client_channel/uri_parser.cc',
'src/core/ext/filters/deadline/deadline_filter.cc',
'src/core/ext/filters/client_channel/health/health.pb.c',
- 'src/core/tsi/alts_transport_security.cc',
'src/core/tsi/fake_transport_security.cc',
'src/core/tsi/local_transport_security.cc',
'src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc',
@@ -786,6 +785,7 @@
'src/core/lib/transport/timeout_encoding.cc',
'src/core/lib/transport/transport.cc',
'src/core/lib/transport/transport_op_string.cc',
+ 'src/core/lib/uri/uri_parser.cc',
'src/core/lib/debug/trace.cc',
'src/core/ext/filters/client_channel/backup_poller.cc',
'src/core/ext/filters/client_channel/channel_connectivity.cc',
@@ -800,16 +800,15 @@
'src/core/ext/filters/client_channel/lb_policy.cc',
'src/core/ext/filters/client_channel/lb_policy_factory.cc',
'src/core/ext/filters/client_channel/lb_policy_registry.cc',
- 'src/core/ext/filters/client_channel/method_params.cc',
'src/core/ext/filters/client_channel/parse_address.cc',
'src/core/ext/filters/client_channel/proxy_mapper.cc',
'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
'src/core/ext/filters/client_channel/resolver.cc',
'src/core/ext/filters/client_channel/resolver_registry.cc',
+ 'src/core/ext/filters/client_channel/resolver_result_parsing.cc',
'src/core/ext/filters/client_channel/retry_throttle.cc',
'src/core/ext/filters/client_channel/subchannel.cc',
'src/core/ext/filters/client_channel/subchannel_index.cc',
- 'src/core/ext/filters/client_channel/uri_parser.cc',
'src/core/ext/filters/deadline/deadline_filter.cc',
'src/core/ext/filters/client_channel/health/health.pb.c',
'third_party/nanopb/pb_common.c',
@@ -1025,6 +1024,7 @@
'src/core/lib/transport/timeout_encoding.cc',
'src/core/lib/transport/transport.cc',
'src/core/lib/transport/transport_op_string.cc',
+ 'src/core/lib/uri/uri_parser.cc',
'src/core/lib/debug/trace.cc',
'src/core/ext/filters/client_channel/backup_poller.cc',
'src/core/ext/filters/client_channel/channel_connectivity.cc',
@@ -1039,16 +1039,15 @@
'src/core/ext/filters/client_channel/lb_policy.cc',
'src/core/ext/filters/client_channel/lb_policy_factory.cc',
'src/core/ext/filters/client_channel/lb_policy_registry.cc',
- 'src/core/ext/filters/client_channel/method_params.cc',
'src/core/ext/filters/client_channel/parse_address.cc',
'src/core/ext/filters/client_channel/proxy_mapper.cc',
'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
'src/core/ext/filters/client_channel/resolver.cc',
'src/core/ext/filters/client_channel/resolver_registry.cc',
+ 'src/core/ext/filters/client_channel/resolver_result_parsing.cc',
'src/core/ext/filters/client_channel/retry_throttle.cc',
'src/core/ext/filters/client_channel/subchannel.cc',
'src/core/ext/filters/client_channel/subchannel_index.cc',
- 'src/core/ext/filters/client_channel/uri_parser.cc',
'src/core/ext/filters/deadline/deadline_filter.cc',
'src/core/ext/filters/client_channel/health/health.pb.c',
'third_party/nanopb/pb_common.c',
@@ -1242,6 +1241,7 @@
'src/core/lib/transport/timeout_encoding.cc',
'src/core/lib/transport/transport.cc',
'src/core/lib/transport/transport_op_string.cc',
+ 'src/core/lib/uri/uri_parser.cc',
'src/core/lib/debug/trace.cc',
'src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc',
'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc',
@@ -1290,16 +1290,15 @@
'src/core/ext/filters/client_channel/lb_policy.cc',
'src/core/ext/filters/client_channel/lb_policy_factory.cc',
'src/core/ext/filters/client_channel/lb_policy_registry.cc',
- 'src/core/ext/filters/client_channel/method_params.cc',
'src/core/ext/filters/client_channel/parse_address.cc',
'src/core/ext/filters/client_channel/proxy_mapper.cc',
'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
'src/core/ext/filters/client_channel/resolver.cc',
'src/core/ext/filters/client_channel/resolver_registry.cc',
+ 'src/core/ext/filters/client_channel/resolver_result_parsing.cc',
'src/core/ext/filters/client_channel/retry_throttle.cc',
'src/core/ext/filters/client_channel/subchannel.cc',
'src/core/ext/filters/client_channel/subchannel_index.cc',
- 'src/core/ext/filters/client_channel/uri_parser.cc',
'src/core/ext/filters/deadline/deadline_filter.cc',
'src/core/ext/filters/client_channel/health/health.pb.c',
'third_party/nanopb/pb_common.c',
@@ -1386,6 +1385,7 @@
'src/cpp/server/secure_server_credentials.cc',
'src/cpp/client/channel_cc.cc',
'src/cpp/client/client_context.cc',
+ 'src/cpp/client/client_interceptor.cc',
'src/cpp/client/create_channel.cc',
'src/cpp/client/create_channel_internal.cc',
'src/cpp/client/create_channel_posix.cc',
@@ -1536,6 +1536,7 @@
'src/cpp/server/insecure_server_credentials.cc',
'src/cpp/client/channel_cc.cc',
'src/cpp/client/client_context.cc',
+ 'src/cpp/client/client_interceptor.cc',
'src/cpp/client/create_channel.cc',
'src/cpp/client/create_channel_internal.cc',
'src/cpp/client/create_channel_posix.cc',
@@ -1756,6 +1757,7 @@
'test/cpp/qps/qps_worker.cc',
'test/cpp/qps/report.cc',
'test/cpp/qps/server_async.cc',
+ 'test/cpp/qps/server_callback.cc',
'test/cpp/qps/server_sync.cc',
'test/cpp/qps/usage_timer.cc',
],
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..4502b94b17 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,
+ size_t 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..b4c34a01c9
--- /dev/null
+++ b/include/grpcpp/impl/codegen/call_op_set.h
@@ -0,0 +1,919 @@
+/*
+ *
+ * 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(
+ InterceptorBatchMethodsImpl* interceptor_methods) {}
+ void SetFinishInterceptionHookPoint(
+ InterceptorBatchMethodsImpl* interceptor_methods) {}
+ void SetHijackingState(InterceptorBatchMethodsImpl* 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(
+ InterceptorBatchMethodsImpl* interceptor_methods) {
+ if (!send_) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA);
+ interceptor_methods->SetSendInitialMetadata(metadata_map_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InterceptorBatchMethodsImpl* interceptor_methods) {}
+
+ void SetHijackingState(InterceptorBatchMethodsImpl* 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(
+ InterceptorBatchMethodsImpl* interceptor_methods) {
+ if (!send_buf_.Valid()) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_MESSAGE);
+ interceptor_methods->SetSendMessage(&send_buf_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InterceptorBatchMethodsImpl* interceptor_methods) {}
+
+ void SetHijackingState(InterceptorBatchMethodsImpl* 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(
+ InterceptorBatchMethodsImpl* interceptor_methods) {
+ interceptor_methods->SetRecvMessage(message_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InterceptorBatchMethodsImpl* interceptor_methods) {
+ if (!got_message) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+ }
+ void SetHijackingState(InterceptorBatchMethodsImpl* 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(
+ InterceptorBatchMethodsImpl* interceptor_methods) {
+ interceptor_methods->SetRecvMessage(message_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InterceptorBatchMethodsImpl* interceptor_methods) {
+ if (!got_message) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+ }
+ void SetHijackingState(InterceptorBatchMethodsImpl* 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(
+ InterceptorBatchMethodsImpl* interceptor_methods) {
+ if (!send_) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_CLOSE);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InterceptorBatchMethodsImpl* interceptor_methods) {}
+
+ void SetHijackingState(InterceptorBatchMethodsImpl* 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(
+ InterceptorBatchMethodsImpl* 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(
+ InterceptorBatchMethodsImpl* interceptor_methods) {}
+
+ void SetHijackingState(InterceptorBatchMethodsImpl* 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(
+ InterceptorBatchMethodsImpl* interceptor_methods) {
+ interceptor_methods->SetRecvInitialMetadata(metadata_map_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InterceptorBatchMethodsImpl* interceptor_methods) {
+ if (metadata_map_ == nullptr) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
+ metadata_map_ = nullptr;
+ }
+
+ void SetHijackingState(InterceptorBatchMethodsImpl* 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(
+ InterceptorBatchMethodsImpl* interceptor_methods) {
+ interceptor_methods->SetRecvStatus(recv_status_);
+ interceptor_methods->SetRecvTrailingMetadata(metadata_map_);
+ }
+
+ void SetFinishInterceptionHookPoint(
+ InterceptorBatchMethodsImpl* interceptor_methods) {
+ if (recv_status_ == nullptr) return;
+ interceptor_methods->AddInterceptionHookPoint(
+ experimental::InterceptionHookPoints::POST_RECV_STATUS);
+ recv_status_ = nullptr;
+ }
+
+ void SetHijackingState(InterceptorBatchMethodsImpl* 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..51367cf550 100644
--- a/include/grpcpp/impl/codegen/callback_common.h
+++ b/include/grpcpp/impl/codegen/callback_common.h
@@ -94,18 +94,25 @@ 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_);
}
};
+/// CallbackWithSuccessTag can be reused multiple times, and will be used in
+/// this fashion for streaming operations. As a result, it shouldn't clear
+/// anything up until its destructor
class CallbackWithSuccessTag
: public grpc_experimental_completion_queue_functor {
public:
@@ -121,13 +128,39 @@ class CallbackWithSuccessTag
// there are no tests catching the compiler warning.
static void operator delete(void*, void*) { assert(0); }
+ CallbackWithSuccessTag() : call_(nullptr) {}
+
CallbackWithSuccessTag(grpc_call* call, std::function<void(bool)> f,
- CompletionQueueTag* ops)
- : call_(call), func_(std::move(f)), ops_(ops) {
+ CompletionQueueTag* ops) {
+ Set(call, f, ops);
+ }
+
+ CallbackWithSuccessTag(const CallbackWithSuccessTag&) = delete;
+ CallbackWithSuccessTag& operator=(const CallbackWithSuccessTag&) = delete;
+
+ ~CallbackWithSuccessTag() { Clear(); }
+
+ // Set can only be called on a default-constructed or Clear'ed tag.
+ // It should never be called on a tag that was constructed with arguments
+ // or on a tag that has been Set before unless the tag has been cleared.
+ void Set(grpc_call* call, std::function<void(bool)> f,
+ CompletionQueueTag* ops) {
+ call_ = call;
+ func_ = std::move(f);
+ ops_ = ops;
g_core_codegen_interface->grpc_call_ref(call);
functor_run = &CallbackWithSuccessTag::StaticRun;
}
+ void Clear() {
+ if (call_ != nullptr) {
+ func_ = nullptr;
+ grpc_call* call = call_;
+ call_ = nullptr;
+ g_core_codegen_interface->grpc_call_unref(call);
+ }
+ }
+
CompletionQueueTag* ops() { return ops_; }
// force_run can not be performed on a tag if operations using this tag
@@ -135,6 +168,9 @@ class CallbackWithSuccessTag
// that are detected before the operations are internally processed.
void force_run(bool ok) { Run(ok); }
+ /// check if this tag is currently set
+ operator bool() const { return call_ != nullptr; }
+
private:
grpc_call* call_;
std::function<void(bool)> func_;
@@ -147,14 +183,14 @@ 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
- g_core_codegen_interface->grpc_call_unref(call_);
+ if (do_callback) {
+ CatchingCallback(func_, ok);
+ }
}
};
diff --git a/include/grpcpp/impl/codegen/channel_interface.h b/include/grpcpp/impl/codegen/channel_interface.h
index b257acc1ab..6ec1ffb8c7 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,
+ size_t 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..75b955e760 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) |
@@ -414,6 +426,8 @@ class ClientContext {
grpc::string authority() { return authority_; }
+ void SendCancelToInterceptors();
+
bool initial_metadata_received_;
bool wait_for_ready_;
bool wait_for_ready_explicitly_set_;
@@ -439,6 +453,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..f69c99ab22 100644
--- a/include/grpcpp/impl/codegen/client_interceptor.h
+++ b/include/grpcpp/impl/codegen/client_interceptor.h
@@ -19,25 +19,108 @@
#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_INTERCEPTOR_H
#define GRPCPP_IMPL_CODEGEN_CLIENT_INTERCEPTOR_H
+#include <memory>
+#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;
+
+namespace internal {
+class InterceptorBatchMethodsImpl;
+}
-class ClientRpcInfo {};
+namespace experimental {
+class ClientRpcInfo;
class ClientInterceptorFactoryInterface {
public:
virtual ~ClientInterceptorFactoryInterface() {}
- virtual ClientInterceptor* CreateClientInterceptor(ClientRpcInfo* info) = 0;
+ virtual Interceptor* CreateClientInterceptor(ClientRpcInfo* info) = 0;
+};
+} // namespace experimental
+
+namespace internal {
+extern experimental::ClientInterceptorFactoryInterface*
+ g_global_client_interceptor_factory;
+}
+
+namespace experimental {
+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,
+ size_t interceptor_pos) {
+ if (interceptor_pos > creators.size()) {
+ // No interceptors to register
+ return;
+ }
+ for (auto it = creators.begin() + interceptor_pos; it != creators.end();
+ ++it) {
+ interceptors_.push_back(std::unique_ptr<experimental::Interceptor>(
+ (*it)->CreateClientInterceptor(this)));
+ }
+ if (internal::g_global_client_interceptor_factory != nullptr) {
+ interceptors_.push_back(std::unique_ptr<experimental::Interceptor>(
+ internal::g_global_client_interceptor_factory
+ ->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;
};
+// PLEASE DO NOT USE THIS. ALWAYS PREFER PER CHANNEL INTERCEPTORS OVER A GLOBAL
+// INTERCEPTOR. IF USAGE IS ABSOLUTELY NECESSARY, PLEASE READ THE SAFETY NOTES.
+// Registers a global client interceptor factory object, which is used for all
+// RPCs made in this process. If the argument is nullptr, the global
+// interceptor factory is deregistered. The application is responsible for
+// maintaining the life of the object while gRPC operations are in progress. It
+// is unsafe to try to register/deregister if any gRPC operation is in progress.
+// For safety, it is in the best interests of the developer to register the
+// global interceptor factory once at the start of the process before any gRPC
+// operations have begun. Deregistration is optional since gRPC does not
+// maintain any references to the object.
+void RegisterGlobalClientInterceptorFactory(
+ ClientInterceptorFactoryInterface* factory);
+
} // namespace experimental
} // namespace grpc
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..20a5b3300c 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,
@@ -142,6 +145,15 @@ extern CoreCodegenInterface* g_core_codegen_interface;
} \
} while (0)
+/// Codegen specific version of \a GPR_DEBUG_ASSERT.
+#ifndef NDEBUG
+#define GPR_CODEGEN_DEBUG_ASSERT(x) GPR_CODEGEN_ASSERT(x)
+#else
+#define GPR_CODEGEN_DEBUG_ASSERT(x) \
+ do { \
+ } while (0)
+#endif
+
} // namespace grpc
#endif // GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
diff --git a/include/grpcpp/impl/codegen/intercepted_channel.h b/include/grpcpp/impl/codegen/intercepted_channel.h
new file mode 100644
index 0000000000..5255a6d147
--- /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, size_t 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_;
+ size_t 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..e449e44a23 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,
@@ -45,22 +56,77 @@ enum class InterceptionHookPoints {
POST_RECV_MESSAGE,
POST_RECV_STATUS /* client only */,
POST_RECV_CLOSE /* server only */,
+ /* This is a special hook point available to both clients and servers when
+ TryCancel() is performed.
+ - No other hook points will be present along with this.
+ - It is illegal for an interceptor to block/delay this operation.
+ - ALL interceptors see this hook point irrespective of whether the RPC was
+ hijacked or not. */
+ PRE_SEND_CANCEL,
NUM_INTERCEPTION_HOOKS
};
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;
// Calling this will signal that the interceptor is done intercepting the
- // current batch of the RPC
+ // current batch of the RPC.
+ // Proceed is a no-op if the batch contains PRE_SEND_CANCEL. Simply returning
+ // from the Intercept method does the job of continuing the RPC in this case.
virtual void Proceed() = 0;
// 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..d0aa23cb0a
--- /dev/null
+++ b/include/grpcpp/impl/codegen/interceptor_common.h
@@ -0,0 +1,458 @@
+/*
+ *
+ * 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 <array>
+#include <functional>
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/call_op_set_interface.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/impl/codegen/intercepted_channel.h>
+#include <grpcpp/impl/codegen/server_interceptor.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+namespace grpc {
+namespace internal {
+
+class InterceptorBatchMethodsImpl
+ : public experimental::InterceptorBatchMethods {
+ 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 {
+ 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) {
+ 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) { send_message_ = buf; }
+
+ void SetSendInitialMetadata(
+ std::multimap<grpc::string, grpc::string>* metadata) {
+ send_initial_metadata_ = metadata;
+ }
+
+ void SetSendStatus(grpc_status_code* code, grpc::string* error_details,
+ grpc::string* error_message) {
+ code_ = code;
+ error_details_ = error_details;
+ error_message_ = error_message;
+ }
+
+ void SetSendTrailingMetadata(
+ std::multimap<grpc::string, grpc::string>* metadata) {
+ send_trailing_metadata_ = metadata;
+ }
+
+ void SetRecvMessage(void* message) { recv_message_ = message; }
+
+ void SetRecvInitialMetadata(MetadataMap* map) {
+ recv_initial_metadata_ = map;
+ }
+
+ void SetRecvStatus(Status* status) { recv_status_ = status; }
+
+ void SetRecvTrailingMetadata(MetadataMap* map) {
+ 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;
+};
+
+// A special implementation of InterceptorBatchMethods to send a Cancel
+// notification down the interceptor stack
+class CancelInterceptorBatchMethods
+ : public experimental::InterceptorBatchMethods {
+ public:
+ bool QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints type) override {
+ if (type == experimental::InterceptionHookPoints::PRE_SEND_CANCEL) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void Proceed() override {
+ // This is a no-op. For actual continuation of the RPC simply needs to
+ // return from the Intercept method
+ }
+
+ void Hijack() override {
+ // Only the client can hijack when sending down initial metadata
+ GPR_CODEGEN_ASSERT(false &&
+ "It is illegal to call Hijack on a method which has a "
+ "Cancel notification");
+ }
+
+ ByteBuffer* GetSendMessage() override {
+ GPR_CODEGEN_ASSERT(false &&
+ "It is illegal to call GetSendMessage on a method which "
+ "has a Cancel notification");
+ return nullptr;
+ }
+
+ std::multimap<grpc::string, grpc::string>* GetSendInitialMetadata() override {
+ GPR_CODEGEN_ASSERT(false &&
+ "It is illegal to call GetSendInitialMetadata on a "
+ "method which has a Cancel notification");
+ return nullptr;
+ }
+
+ Status GetSendStatus() override {
+ GPR_CODEGEN_ASSERT(false &&
+ "It is illegal to call GetSendStatus on a method which "
+ "has a Cancel notification");
+ return Status();
+ }
+
+ void ModifySendStatus(const Status& status) override {
+ GPR_CODEGEN_ASSERT(false &&
+ "It is illegal to call ModifySendStatus on a method "
+ "which has a Cancel notification");
+ return;
+ }
+
+ std::multimap<grpc::string, grpc::string>* GetSendTrailingMetadata()
+ override {
+ GPR_CODEGEN_ASSERT(false &&
+ "It is illegal to call GetSendTrailingMetadata on a "
+ "method which has a Cancel notification");
+ return nullptr;
+ }
+
+ void* GetRecvMessage() override {
+ GPR_CODEGEN_ASSERT(false &&
+ "It is illegal to call GetRecvMessage on a method which "
+ "has a Cancel notification");
+ return nullptr;
+ }
+
+ std::multimap<grpc::string_ref, grpc::string_ref>* GetRecvInitialMetadata()
+ override {
+ GPR_CODEGEN_ASSERT(false &&
+ "It is illegal to call GetRecvInitialMetadata on a "
+ "method which has a Cancel notification");
+ return nullptr;
+ }
+
+ Status* GetRecvStatus() override {
+ GPR_CODEGEN_ASSERT(false &&
+ "It is illegal to call GetRecvStatus on a method which "
+ "has a Cancel notification");
+ return nullptr;
+ }
+
+ std::multimap<grpc::string_ref, grpc::string_ref>* GetRecvTrailingMetadata()
+ override {
+ GPR_CODEGEN_ASSERT(false &&
+ "It is illegal to call GetRecvTrailingMetadata on a "
+ "method which has a Cancel notification");
+ return nullptr;
+ }
+
+ std::unique_ptr<ChannelInterface> GetInterceptedChannel() override {
+ GPR_CODEGEN_ASSERT(false &&
+ "It is illegal to call GetInterceptedChannel on a "
+ "method which has a Cancel notification");
+ return std::unique_ptr<ChannelInterface>(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..9cec54d9f0 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>
@@ -30,11 +32,9 @@ const char kBinaryErrorDetailsKey[] = "grpc-status-details-bin";
class MetadataMap {
public:
- MetadataMap() { memset(&arr_, 0, sizeof(arr_)); }
+ MetadataMap() { Setup(); }
- ~MetadataMap() {
- g_core_codegen_interface->grpc_metadata_array_destroy(&arr_);
- }
+ ~MetadataMap() { Destroy(); }
grpc::string GetBinaryErrorDetails() {
// if filled_, extract from the multimap for O(log(n))
@@ -69,11 +69,24 @@ class MetadataMap {
}
grpc_metadata_array* arr() { return &arr_; }
+ void Reset() {
+ filled_ = false;
+ map_.clear();
+ Destroy();
+ Setup();
+ }
+
private:
bool filled_ = false;
grpc_metadata_array arr_;
std::multimap<grpc::string_ref, grpc::string_ref> map_;
+ void Destroy() {
+ g_core_codegen_interface->grpc_metadata_array_destroy(&arr_);
+ }
+
+ void Setup() { memset(&arr_, 0, sizeof(arr_)); }
+
void FillMap() {
if (filled_) return;
filled_ = true;
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..b866fc16dc
--- /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/call_op_set.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_.Set(
+ 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_.Set(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..55c94f4d2f 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 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 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/package.xml b/package.xml
index c90cee8fe9..3044cbf862 100644
--- a/package.xml
+++ b/package.xml
@@ -249,7 +249,7 @@
<file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h" role="src" />
<file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/frame_handler.h" role="src" />
<file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_handshaker_client.h" role="src" />
- <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_tsi_event.h" role="src" />
+ <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_shared_resource.h" role="src" />
<file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_tsi_handshaker.h" role="src" />
<file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h" role="src" />
<file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h" role="src" />
@@ -286,20 +286,18 @@
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy_factory.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy_registry.h" role="src" />
- <file baseinstalldir="/" name="src/core/ext/filters/client_channel/method_params.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/parse_address.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/proxy_mapper.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/proxy_mapper_registry.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver_factory.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver_registry.h" role="src" />
+ <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver_result_parsing.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/retry_throttle.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/subchannel.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/subchannel_index.h" role="src" />
- <file baseinstalldir="/" name="src/core/ext/filters/client_channel/uri_parser.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/deadline/deadline_filter.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/health/health.pb.h" role="src" />
- <file baseinstalldir="/" name="src/core/tsi/alts_transport_security.h" role="src" />
<file baseinstalldir="/" name="src/core/tsi/fake_transport_security.h" role="src" />
<file baseinstalldir="/" name="src/core/tsi/local_transport_security.h" role="src" />
<file baseinstalldir="/" name="src/core/tsi/ssl/session_cache/ssl_session.h" role="src" />
@@ -444,6 +442,7 @@
<file baseinstalldir="/" name="src/core/lib/transport/timeout_encoding.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/transport.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/transport_impl.h" role="src" />
+ <file baseinstalldir="/" name="src/core/lib/uri/uri_parser.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/debug/trace.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" role="src" />
@@ -615,6 +614,7 @@
<file baseinstalldir="/" name="src/core/lib/transport/timeout_encoding.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/transport.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/transport_op_string.cc" role="src" />
+ <file baseinstalldir="/" name="src/core/lib/uri/uri_parser.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/debug/trace.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/bin_decoder.cc" role="src" />
@@ -687,7 +687,7 @@
<file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc" role="src" />
<file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/frame_handler.cc" role="src" />
<file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_handshaker_client.cc" role="src" />
- <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_tsi_event.cc" role="src" />
+ <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_shared_resource.cc" role="src" />
<file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc" role="src" />
<file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc" role="src" />
<file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc" role="src" />
@@ -729,19 +729,17 @@
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy_factory.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy_registry.cc" role="src" />
- <file baseinstalldir="/" name="src/core/ext/filters/client_channel/method_params.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/parse_address.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/proxy_mapper.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/proxy_mapper_registry.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver_registry.cc" role="src" />
+ <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver_result_parsing.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/retry_throttle.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/subchannel.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/subchannel_index.cc" role="src" />
- <file baseinstalldir="/" name="src/core/ext/filters/client_channel/uri_parser.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/deadline/deadline_filter.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/health/health.pb.c" role="src" />
- <file baseinstalldir="/" name="src/core/tsi/alts_transport_security.cc" role="src" />
<file baseinstalldir="/" name="src/core/tsi/fake_transport_security.cc" role="src" />
<file baseinstalldir="/" name="src/core/tsi/local_transport_security.cc" role="src" />
<file baseinstalldir="/" name="src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc" role="src" />
diff --git a/requirements.bazel.txt b/requirements.bazel.txt
index 16f31f9e94..61e529a6ec 100644
--- a/requirements.bazel.txt
+++ b/requirements.bazel.txt
@@ -8,3 +8,8 @@ wheel>=0.29
futures>=2.2.0
google-auth>=1.0.0
oauth2client==4.1.0
+requests>=2.14.2
+urllib3==1.22
+chardet==3.0.4
+certifi==2017.4.17
+idna==2.7
diff --git a/setup.py b/setup.py
index 388e629ec2..ae86e6c9fb 100644
--- a/setup.py
+++ b/setup.py
@@ -57,11 +57,13 @@ os.chdir(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.abspath(PYTHON_STEM))
# Break import-style to ensure we can actually find our in-repo dependencies.
+import _parallel_compile_patch
import _spawn_patch
import commands
import grpc_core_dependencies
import grpc_version
+_parallel_compile_patch.monkeypatch_compile_maybe()
_spawn_patch.monkeypatch_spawn()
LICENSE = 'Apache License 2.0'
@@ -87,22 +89,26 @@ 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)
+# If this environmental variable is set, GRPC will not try to be compatible with
+# libc versions old than the one it was compiled against.
+DISABLE_LIBC_COMPATIBILITY = os.environ.get('GRPC_PYTHON_DISABLE_LIBC_COMPATIBILITY', False)
+
# Environment variable to determine whether or not to enable coverage analysis
# in Cython modules.
ENABLE_CYTHON_TRACING = os.environ.get(
@@ -198,11 +204,11 @@ if BUILD_WITH_SYSTEM_ZLIB:
if BUILD_WITH_SYSTEM_CARES:
EXTENSION_LIBRARIES += ('cares',)
-DEFINE_MACROS = (
- ('OPENSSL_NO_ASM', 1), ('_WIN32_WINNT', 0x600),
- ('GPR_BACKWARDS_COMPATIBILITY_MODE', 1))
+DEFINE_MACROS = (('OPENSSL_NO_ASM', 1), ('_WIN32_WINNT', 0x600))
+if not DISABLE_LIBC_COMPATIBILITY:
+ DEFINE_MACROS += (('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),
@@ -283,8 +289,7 @@ if not PY3:
INSTALL_REQUIRES += ('futures>=2.2.0', 'enum34>=1.0.4')
SETUP_REQUIRES = INSTALL_REQUIRES + (
- 'sphinx>=1.3',
- 'sphinx_rtd_theme>=0.1.8',
+ 'Sphinx~=1.8.1',
'six>=1.10',
) if ENABLE_DOCUMENTATION_BUILD else ()
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/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc
index a923ce8e38..59ddbd82f6 100644
--- a/src/compiler/csharp_generator.cc
+++ b/src/compiler/csharp_generator.cc
@@ -609,6 +609,42 @@ void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor* service) {
out->Print("\n");
}
+void GenerateBindServiceWithBinderMethod(Printer* out,
+ const ServiceDescriptor* service) {
+ out->Print(
+ "/// <summary>Register service method implementations with a service "
+ "binder. Useful when customizing the service binding logic.\n"
+ "/// Note: this method is part of an experimental API that can change or "
+ "be "
+ "removed without any prior notice.</summary>\n");
+ out->Print(
+ "/// <param name=\"serviceBinder\">Service methods will be bound by "
+ "calling <c>AddMethod</c> on this object."
+ "</param>\n");
+ out->Print(
+ "/// <param name=\"serviceImpl\">An object implementing the server-side"
+ " handling logic.</param>\n");
+ out->Print(
+ "public static void BindService(grpc::ServiceBinderBase serviceBinder, "
+ "$implclass$ "
+ "serviceImpl)\n",
+ "implclass", GetServerClassName(service));
+ out->Print("{\n");
+ out->Indent();
+
+ for (int i = 0; i < service->method_count(); i++) {
+ const MethodDescriptor* method = service->method(i);
+ out->Print(
+ "serviceBinder.AddMethod($methodfield$, serviceImpl.$methodname$);\n",
+ "methodfield", GetMethodFieldName(method), "methodname",
+ method->name());
+ }
+
+ out->Outdent();
+ out->Print("}\n");
+ out->Print("\n");
+}
+
void GenerateService(Printer* out, const ServiceDescriptor* service,
bool generate_client, bool generate_server,
bool internal_access) {
@@ -637,6 +673,7 @@ void GenerateService(Printer* out, const ServiceDescriptor* service,
}
if (generate_server) {
GenerateBindServiceMethod(out, service);
+ GenerateBindServiceWithBinderMethod(out, service);
}
out->Outdent();
diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc
index daf1b89b09..be7962261b 100644
--- a/src/core/ext/filters/client_channel/client_channel.cc
+++ b/src/core/ext/filters/client_channel/client_channel.cc
@@ -34,9 +34,9 @@
#include "src/core/ext/filters/client_channel/backup_poller.h"
#include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
-#include "src/core/ext/filters/client_channel/method_params.h"
#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
#include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/ext/filters/client_channel/resolver_result_parsing.h"
#include "src/core/ext/filters/client_channel/retry_throttle.h"
#include "src/core/ext/filters/client_channel/subchannel.h"
#include "src/core/ext/filters/deadline/deadline_filter.h"
@@ -63,6 +63,8 @@
#include "src/core/lib/transport/status_metadata.h"
using grpc_core::internal::ClientChannelMethodParams;
+using grpc_core::internal::ClientChannelMethodParamsTable;
+using grpc_core::internal::ProcessedResolverResult;
using grpc_core::internal::ServerRetryThrottleData;
/* Client channel implementation */
@@ -83,10 +85,6 @@ grpc_core::TraceFlag grpc_client_channel_trace(false, "client_channel");
struct external_connectivity_watcher;
-typedef grpc_core::SliceHashTable<
- grpc_core::RefCountedPtr<ClientChannelMethodParams>>
- MethodParamsTable;
-
typedef struct client_channel_channel_data {
grpc_core::OrphanablePtr<grpc_core::Resolver> resolver;
bool started_resolving;
@@ -102,7 +100,7 @@ typedef struct client_channel_channel_data {
/** retry throttle data */
grpc_core::RefCountedPtr<ServerRetryThrottleData> retry_throttle_data;
/** maps method names to method_parameters structs */
- grpc_core::RefCountedPtr<MethodParamsTable> method_params_table;
+ grpc_core::RefCountedPtr<ClientChannelMethodParamsTable> method_params_table;
/** incoming resolver result - set by resolver.next() */
grpc_channel_args* resolver_result;
/** a list of closures that are all waiting for resolver result to come in */
@@ -251,66 +249,6 @@ static void start_resolving_locked(channel_data* chand) {
&chand->on_resolver_result_changed);
}
-typedef struct {
- char* server_name;
- grpc_core::RefCountedPtr<ServerRetryThrottleData> retry_throttle_data;
-} service_config_parsing_state;
-
-static void parse_retry_throttle_params(
- const grpc_json* field, service_config_parsing_state* parsing_state) {
- if (strcmp(field->key, "retryThrottling") == 0) {
- if (parsing_state->retry_throttle_data != nullptr) return; // Duplicate.
- if (field->type != GRPC_JSON_OBJECT) return;
- int max_milli_tokens = 0;
- int milli_token_ratio = 0;
- for (grpc_json* sub_field = field->child; sub_field != nullptr;
- sub_field = sub_field->next) {
- if (sub_field->key == nullptr) return;
- if (strcmp(sub_field->key, "maxTokens") == 0) {
- if (max_milli_tokens != 0) return; // Duplicate.
- if (sub_field->type != GRPC_JSON_NUMBER) return;
- max_milli_tokens = gpr_parse_nonnegative_int(sub_field->value);
- if (max_milli_tokens == -1) return;
- max_milli_tokens *= 1000;
- } else if (strcmp(sub_field->key, "tokenRatio") == 0) {
- if (milli_token_ratio != 0) return; // Duplicate.
- if (sub_field->type != GRPC_JSON_NUMBER) return;
- // We support up to 3 decimal digits.
- size_t whole_len = strlen(sub_field->value);
- uint32_t multiplier = 1;
- uint32_t decimal_value = 0;
- const char* decimal_point = strchr(sub_field->value, '.');
- if (decimal_point != nullptr) {
- whole_len = static_cast<size_t>(decimal_point - sub_field->value);
- multiplier = 1000;
- size_t decimal_len = strlen(decimal_point + 1);
- if (decimal_len > 3) decimal_len = 3;
- if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len,
- &decimal_value)) {
- return;
- }
- uint32_t decimal_multiplier = 1;
- for (size_t i = 0; i < (3 - decimal_len); ++i) {
- decimal_multiplier *= 10;
- }
- decimal_value *= decimal_multiplier;
- }
- uint32_t whole_value;
- if (!gpr_parse_bytes_to_uint32(sub_field->value, whole_len,
- &whole_value)) {
- return;
- }
- milli_token_ratio =
- static_cast<int>((whole_value * multiplier) + decimal_value);
- if (milli_token_ratio <= 0) return;
- }
- }
- parsing_state->retry_throttle_data =
- grpc_core::internal::ServerRetryThrottleMap::GetDataForServer(
- parsing_state->server_name, max_milli_tokens, milli_token_ratio);
- }
-}
-
// Invoked from the resolver NextLocked() callback when the resolver
// is shutting down.
static void on_resolver_shutdown_locked(channel_data* chand,
@@ -352,37 +290,6 @@ static void on_resolver_shutdown_locked(channel_data* chand,
GRPC_ERROR_UNREF(error);
}
-// Returns the LB policy name from the resolver result.
-static grpc_core::UniquePtr<char>
-get_lb_policy_name_from_resolver_result_locked(channel_data* chand) {
- // Find LB policy name in channel args.
- const grpc_arg* channel_arg =
- grpc_channel_args_find(chand->resolver_result, GRPC_ARG_LB_POLICY_NAME);
- const char* lb_policy_name = grpc_channel_arg_get_string(channel_arg);
- // Special case: If at least one balancer address is present, we use
- // the grpclb policy, regardless of what the resolver actually specified.
- channel_arg =
- grpc_channel_args_find(chand->resolver_result, GRPC_ARG_LB_ADDRESSES);
- if (channel_arg != nullptr && channel_arg->type == GRPC_ARG_POINTER) {
- grpc_lb_addresses* addresses =
- static_cast<grpc_lb_addresses*>(channel_arg->value.pointer.p);
- if (grpc_lb_addresses_contains_balancer_address(*addresses)) {
- if (lb_policy_name != nullptr &&
- gpr_stricmp(lb_policy_name, "grpclb") != 0) {
- gpr_log(GPR_INFO,
- "resolver requested LB policy %s but provided at least one "
- "balancer address -- forcing use of grpclb LB policy",
- lb_policy_name);
- }
- lb_policy_name = "grpclb";
- }
- }
- // Use pick_first if nothing was specified and we didn't select grpclb
- // above.
- if (lb_policy_name == nullptr) lb_policy_name = "pick_first";
- return grpc_core::UniquePtr<char>(gpr_strdup(lb_policy_name));
-}
-
static void request_reresolution_locked(void* arg, grpc_error* error) {
reresolution_request_args* args =
static_cast<reresolution_request_args*>(arg);
@@ -410,13 +317,14 @@ using TraceStringVector = grpc_core::InlinedVector<char*, 3>;
// *connectivity_error to its initial connectivity state; otherwise,
// leaves them unchanged.
static void create_new_lb_policy_locked(
- channel_data* chand, char* lb_policy_name,
+ channel_data* chand, char* lb_policy_name, grpc_json* lb_config,
grpc_connectivity_state* connectivity_state,
grpc_error** connectivity_error, TraceStringVector* trace_strings) {
grpc_core::LoadBalancingPolicy::Args lb_policy_args;
lb_policy_args.combiner = chand->combiner;
lb_policy_args.client_channel_factory = chand->client_channel_factory;
lb_policy_args.args = chand->resolver_result;
+ lb_policy_args.lb_config = lb_config;
grpc_core::OrphanablePtr<grpc_core::LoadBalancingPolicy> new_lb_policy =
grpc_core::LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
lb_policy_name, lb_policy_args);
@@ -473,44 +381,6 @@ static void create_new_lb_policy_locked(
}
}
-// Returns the service config (as a JSON string) from the resolver result.
-// Also updates state in chand.
-static grpc_core::UniquePtr<char>
-get_service_config_from_resolver_result_locked(channel_data* chand) {
- const grpc_arg* channel_arg =
- grpc_channel_args_find(chand->resolver_result, GRPC_ARG_SERVICE_CONFIG);
- const char* service_config_json = grpc_channel_arg_get_string(channel_arg);
- if (service_config_json != nullptr) {
- if (grpc_client_channel_trace.enabled()) {
- gpr_log(GPR_INFO, "chand=%p: resolver returned service config: \"%s\"",
- chand, service_config_json);
- }
- grpc_core::UniquePtr<grpc_core::ServiceConfig> service_config =
- grpc_core::ServiceConfig::Create(service_config_json);
- if (service_config != nullptr) {
- if (chand->enable_retries) {
- channel_arg =
- grpc_channel_args_find(chand->resolver_result, GRPC_ARG_SERVER_URI);
- const char* server_uri = grpc_channel_arg_get_string(channel_arg);
- GPR_ASSERT(server_uri != nullptr);
- grpc_uri* uri = grpc_uri_parse(server_uri, true);
- GPR_ASSERT(uri->path[0] != '\0');
- service_config_parsing_state parsing_state;
- parsing_state.server_name =
- uri->path[0] == '/' ? uri->path + 1 : uri->path;
- service_config->ParseGlobalParams(parse_retry_throttle_params,
- &parsing_state);
- grpc_uri_destroy(uri);
- chand->retry_throttle_data =
- std::move(parsing_state.retry_throttle_data);
- }
- chand->method_params_table = service_config->CreateMethodConfigTable(
- ClientChannelMethodParams::CreateFromJson);
- }
- }
- return grpc_core::UniquePtr<char>(gpr_strdup(service_config_json));
-}
-
static void maybe_add_trace_message_for_address_changes_locked(
channel_data* chand, TraceStringVector* trace_strings) {
int resolution_contains_addresses = false;
@@ -597,9 +467,23 @@ static void on_resolver_result_changed_locked(void* arg, grpc_error* error) {
if (grpc_client_channel_trace.enabled()) {
gpr_log(GPR_INFO, "chand=%p: resolver transient failure", chand);
}
+ // Don't override connectivity state if we already have an LB policy.
+ if (chand->lb_policy != nullptr) set_connectivity_state = false;
} else {
+ // Parse the resolver result.
+ ProcessedResolverResult resolver_result(chand->resolver_result,
+ chand->enable_retries);
+ chand->retry_throttle_data = resolver_result.retry_throttle_data();
+ chand->method_params_table = resolver_result.method_params_table();
+ grpc_core::UniquePtr<char> service_config_json =
+ resolver_result.service_config_json();
+ if (service_config_json != nullptr && grpc_client_channel_trace.enabled()) {
+ gpr_log(GPR_INFO, "chand=%p: resolver returned service config: \"%s\"",
+ chand, service_config_json.get());
+ }
grpc_core::UniquePtr<char> lb_policy_name =
- get_lb_policy_name_from_resolver_result_locked(chand);
+ resolver_result.lb_policy_name();
+ grpc_json* lb_policy_config = resolver_result.lb_policy_config();
// Check to see if we're already using the right LB policy.
// Note: It's safe to use chand->info_lb_policy_name here without
// taking a lock on chand->info_mu, because this function is the
@@ -614,19 +498,16 @@ static void on_resolver_result_changed_locked(void* arg, grpc_error* error) {
gpr_log(GPR_INFO, "chand=%p: updating existing LB policy \"%s\" (%p)",
chand, lb_policy_name.get(), chand->lb_policy.get());
}
- chand->lb_policy->UpdateLocked(*chand->resolver_result);
+ chand->lb_policy->UpdateLocked(*chand->resolver_result, lb_policy_config);
// No need to set the channel's connectivity state; the existing
// watch on the LB policy will take care of that.
set_connectivity_state = false;
} else {
// Instantiate new LB policy.
- create_new_lb_policy_locked(chand, lb_policy_name.get(),
+ create_new_lb_policy_locked(chand, lb_policy_name.get(), lb_policy_config,
&connectivity_state, &connectivity_error,
&trace_strings);
}
- // Find service config.
- grpc_core::UniquePtr<char> service_config_json =
- get_service_config_from_resolver_result_locked(chand);
// Note: It's safe to use chand->info_service_config_json here without
// taking a lock on chand->info_mu, because this function is the
// only thing that modifies its value, and it can only be invoked
@@ -938,12 +819,26 @@ static void cc_destroy_channel_elem(grpc_channel_element* elem) {
// (census filter is on top of this one)
// - add census stats for retries
+namespace {
+struct call_data;
+
// State used for starting a retryable batch on a subchannel call.
// This provides its own grpc_transport_stream_op_batch and other data
// structures needed to populate the ops in the batch.
// We allocate one struct on the arena for each attempt at starting a
// batch on a given subchannel call.
-typedef struct {
+struct subchannel_batch_data {
+ subchannel_batch_data(grpc_call_element* elem, call_data* calld, int refcount,
+ bool set_on_complete);
+ // All dtor code must be added in `destroy`. This is because we may
+ // call closures in `subchannel_batch_data` after they are unrefed by
+ // `batch_data_unref`, and msan would complain about accessing this class
+ // after calling dtor. As a result we cannot call the `dtor` in
+ // `batch_data_unref`.
+ // TODO(soheil): We should try to call the dtor in `batch_data_unref`.
+ ~subchannel_batch_data() { destroy(); }
+ void destroy();
+
gpr_refcount refs;
grpc_call_element* elem;
grpc_subchannel_call* subchannel_call; // Holds a ref.
@@ -952,11 +847,23 @@ typedef struct {
grpc_transport_stream_op_batch batch;
// For intercepting on_complete.
grpc_closure on_complete;
-} subchannel_batch_data;
+};
// Retry state associated with a subchannel call.
// Stored in the parent_data of the subchannel call object.
-typedef struct {
+struct subchannel_call_retry_state {
+ explicit subchannel_call_retry_state(grpc_call_context_element* context)
+ : batch_payload(context),
+ started_send_initial_metadata(false),
+ completed_send_initial_metadata(false),
+ started_send_trailing_metadata(false),
+ completed_send_trailing_metadata(false),
+ started_recv_initial_metadata(false),
+ completed_recv_initial_metadata(false),
+ started_recv_trailing_metadata(false),
+ completed_recv_trailing_metadata(false),
+ retry_dispatched(false) {}
+
// subchannel_batch_data.batch.payload points to this.
grpc_transport_stream_op_batch_payload batch_payload;
// For send_initial_metadata.
@@ -975,7 +882,7 @@ typedef struct {
// For intercepting recv_initial_metadata.
grpc_metadata_batch recv_initial_metadata;
grpc_closure recv_initial_metadata_ready;
- bool trailing_metadata_available;
+ bool trailing_metadata_available = false;
// For intercepting recv_message.
grpc_closure recv_message_ready;
grpc_core::OrphanablePtr<grpc_core::ByteStream> recv_message;
@@ -985,10 +892,10 @@ typedef struct {
grpc_closure recv_trailing_metadata_ready;
// These fields indicate which ops have been started and completed on
// this subchannel call.
- size_t started_send_message_count;
- size_t completed_send_message_count;
- size_t started_recv_message_count;
- size_t completed_recv_message_count;
+ size_t started_send_message_count = 0;
+ size_t completed_send_message_count = 0;
+ size_t started_recv_message_count = 0;
+ size_t completed_recv_message_count = 0;
bool started_send_initial_metadata : 1;
bool completed_send_initial_metadata : 1;
bool started_send_trailing_metadata : 1;
@@ -997,14 +904,18 @@ typedef struct {
bool completed_recv_initial_metadata : 1;
bool started_recv_trailing_metadata : 1;
bool completed_recv_trailing_metadata : 1;
+ subchannel_batch_data* recv_initial_metadata_ready_deferred_batch = nullptr;
+ grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
+ subchannel_batch_data* recv_message_ready_deferred_batch = nullptr;
+ grpc_error* recv_message_error = GRPC_ERROR_NONE;
+ subchannel_batch_data* recv_trailing_metadata_internal_batch = nullptr;
// State for callback processing.
+ // NOTE: Do not move this next to the metadata bitfields above. That would
+ // save space but will also result in a data race because compiler will
+ // generate a 2 byte store which overwrites the meta-data fields upon
+ // setting this field.
bool retry_dispatched : 1;
- subchannel_batch_data* recv_initial_metadata_ready_deferred_batch;
- grpc_error* recv_initial_metadata_error;
- subchannel_batch_data* recv_message_ready_deferred_batch;
- grpc_error* recv_message_error;
- subchannel_batch_data* recv_trailing_metadata_internal_batch;
-} subchannel_call_retry_state;
+};
// Pending batches stored in call data.
typedef struct {
@@ -1019,7 +930,44 @@ typedef struct {
Handles queueing of stream ops until a call object is ready, waiting
for initial metadata before trying to create a call object,
and handling cancellation gracefully. */
-typedef struct client_channel_call_data {
+struct call_data {
+ call_data(grpc_call_element* elem, const channel_data& chand,
+ const grpc_call_element_args& args)
+ : deadline_state(elem, args.call_stack, args.call_combiner,
+ GPR_LIKELY(chand.deadline_checking_enabled)
+ ? args.deadline
+ : GRPC_MILLIS_INF_FUTURE),
+ path(grpc_slice_ref_internal(args.path)),
+ call_start_time(args.start_time),
+ deadline(args.deadline),
+ arena(args.arena),
+ owning_call(args.call_stack),
+ call_combiner(args.call_combiner),
+ pending_send_initial_metadata(false),
+ pending_send_message(false),
+ pending_send_trailing_metadata(false),
+ enable_retries(chand.enable_retries),
+ retry_committed(false),
+ last_attempt_got_server_pushback(false) {}
+
+ ~call_data() {
+ if (GPR_LIKELY(subchannel_call != nullptr)) {
+ GRPC_SUBCHANNEL_CALL_UNREF(subchannel_call,
+ "client_channel_destroy_call");
+ }
+ grpc_slice_unref_internal(path);
+ GRPC_ERROR_UNREF(cancel_error);
+ for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches); ++i) {
+ GPR_ASSERT(pending_batches[i].batch == nullptr);
+ }
+ for (size_t i = 0; i < GRPC_CONTEXT_COUNT; ++i) {
+ if (pick.subchannel_call_context[i].value != nullptr) {
+ pick.subchannel_call_context[i].destroy(
+ pick.subchannel_call_context[i].value);
+ }
+ }
+ }
+
// State for handling deadlines.
// The code in deadline_filter.c requires this to be the first field.
// TODO(roth): This is slightly sub-optimal in that grpc_deadline_state
@@ -1038,24 +986,24 @@ typedef struct client_channel_call_data {
grpc_core::RefCountedPtr<ServerRetryThrottleData> retry_throttle_data;
grpc_core::RefCountedPtr<ClientChannelMethodParams> method_params;
- grpc_subchannel_call* subchannel_call;
+ grpc_subchannel_call* subchannel_call = nullptr;
// Set when we get a cancel_stream op.
- grpc_error* cancel_error;
+ grpc_error* cancel_error = GRPC_ERROR_NONE;
grpc_core::LoadBalancingPolicy::PickState pick;
grpc_closure pick_closure;
grpc_closure pick_cancel_closure;
- grpc_polling_entity* pollent;
- bool pollent_added_to_interested_parties;
+ grpc_polling_entity* pollent = nullptr;
+ bool pollent_added_to_interested_parties = false;
// Batches are added to this list when received from above.
// They are removed when we are done handling the batch (i.e., when
// either we have invoked all of the batch's callbacks or we have
// passed the batch down to the subchannel call and are not
// intercepting any of its callbacks).
- pending_batch pending_batches[MAX_PENDING_BATCHES];
+ pending_batch pending_batches[MAX_PENDING_BATCHES] = {};
bool pending_send_initial_metadata : 1;
bool pending_send_message : 1;
bool pending_send_trailing_metadata : 1;
@@ -1064,8 +1012,8 @@ typedef struct client_channel_call_data {
bool enable_retries : 1;
bool retry_committed : 1;
bool last_attempt_got_server_pushback : 1;
- int num_attempts_completed;
- size_t bytes_buffered_for_retry;
+ int num_attempts_completed = 0;
+ size_t bytes_buffered_for_retry = 0;
grpc_core::ManualConstructor<grpc_core::BackOff> retry_backoff;
grpc_timer retry_timer;
@@ -1076,12 +1024,12 @@ typedef struct client_channel_call_data {
// until all of these batches have completed.
// Note that we actually only need to track replay batches, but it's
// easier to track all batches with send ops.
- int num_pending_retriable_subchannel_send_batches;
+ int num_pending_retriable_subchannel_send_batches = 0;
// Cached data for retrying send ops.
// send_initial_metadata
- bool seen_send_initial_metadata;
- grpc_linked_mdelem* send_initial_metadata_storage;
+ bool seen_send_initial_metadata = false;
+ grpc_linked_mdelem* send_initial_metadata_storage = nullptr;
grpc_metadata_batch send_initial_metadata;
uint32_t send_initial_metadata_flags;
gpr_atm* peer_string;
@@ -1092,14 +1040,13 @@ typedef struct client_channel_call_data {
// Note: We inline the cache for the first 3 send_message ops and use
// dynamic allocation after that. This number was essentially picked
// at random; it could be changed in the future to tune performance.
- grpc_core::ManualConstructor<
- grpc_core::InlinedVector<grpc_core::ByteStreamCache*, 3>>
- send_messages;
+ grpc_core::InlinedVector<grpc_core::ByteStreamCache*, 3> send_messages;
// send_trailing_metadata
- bool seen_send_trailing_metadata;
- grpc_linked_mdelem* send_trailing_metadata_storage;
+ bool seen_send_trailing_metadata = false;
+ grpc_linked_mdelem* send_trailing_metadata_storage = nullptr;
grpc_metadata_batch send_trailing_metadata;
-} call_data;
+};
+} // namespace
// Forward declarations.
static void retry_commit(grpc_call_element* elem,
@@ -1143,7 +1090,7 @@ static void maybe_cache_send_ops_for_batch(call_data* calld,
gpr_arena_alloc(calld->arena, sizeof(grpc_core::ByteStreamCache)));
new (cache) grpc_core::ByteStreamCache(
std::move(batch->payload->send_message.send_message));
- calld->send_messages->push_back(cache);
+ calld->send_messages.push_back(cache);
}
// Save metadata batch for send_trailing_metadata ops.
if (batch->send_trailing_metadata) {
@@ -1180,7 +1127,7 @@ static void free_cached_send_message(channel_data* chand, call_data* calld,
"chand=%p calld=%p: destroying calld->send_messages[%" PRIuPTR "]",
chand, calld, idx);
}
- (*calld->send_messages)[idx]->Destroy();
+ calld->send_messages[idx]->Destroy();
}
// Frees cached send_trailing_metadata.
@@ -1650,55 +1597,66 @@ static bool maybe_retry(grpc_call_element* elem,
// subchannel_batch_data
//
-// Creates a subchannel_batch_data object on the call's arena with the
-// specified refcount. If set_on_complete is true, the batch's
-// on_complete callback will be set to point to on_complete();
-// otherwise, the batch's on_complete callback will be null.
-static subchannel_batch_data* batch_data_create(grpc_call_element* elem,
- int refcount,
- bool set_on_complete) {
- call_data* calld = static_cast<call_data*>(elem->call_data);
+namespace {
+subchannel_batch_data::subchannel_batch_data(grpc_call_element* elem,
+ call_data* calld, int refcount,
+ bool set_on_complete)
+ : elem(elem),
+ subchannel_call(GRPC_SUBCHANNEL_CALL_REF(calld->subchannel_call,
+ "batch_data_create")) {
subchannel_call_retry_state* retry_state =
static_cast<subchannel_call_retry_state*>(
grpc_connected_subchannel_call_get_parent_data(
calld->subchannel_call));
- subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(
- gpr_arena_alloc(calld->arena, sizeof(*batch_data)));
- batch_data->elem = elem;
- batch_data->subchannel_call =
- GRPC_SUBCHANNEL_CALL_REF(calld->subchannel_call, "batch_data_create");
- batch_data->batch.payload = &retry_state->batch_payload;
- gpr_ref_init(&batch_data->refs, refcount);
+ batch.payload = &retry_state->batch_payload;
+ gpr_ref_init(&refs, refcount);
if (set_on_complete) {
- GRPC_CLOSURE_INIT(&batch_data->on_complete, on_complete, batch_data,
+ GRPC_CLOSURE_INIT(&on_complete, ::on_complete, this,
grpc_schedule_on_exec_ctx);
- batch_data->batch.on_complete = &batch_data->on_complete;
+ batch.on_complete = &on_complete;
}
GRPC_CALL_STACK_REF(calld->owning_call, "batch_data");
+}
+
+void subchannel_batch_data::destroy() {
+ subchannel_call_retry_state* retry_state =
+ static_cast<subchannel_call_retry_state*>(
+ grpc_connected_subchannel_call_get_parent_data(subchannel_call));
+ if (batch.send_initial_metadata) {
+ grpc_metadata_batch_destroy(&retry_state->send_initial_metadata);
+ }
+ if (batch.send_trailing_metadata) {
+ grpc_metadata_batch_destroy(&retry_state->send_trailing_metadata);
+ }
+ if (batch.recv_initial_metadata) {
+ grpc_metadata_batch_destroy(&retry_state->recv_initial_metadata);
+ }
+ if (batch.recv_trailing_metadata) {
+ grpc_metadata_batch_destroy(&retry_state->recv_trailing_metadata);
+ }
+ GRPC_SUBCHANNEL_CALL_UNREF(subchannel_call, "batch_data_unref");
+ call_data* calld = static_cast<call_data*>(elem->call_data);
+ GRPC_CALL_STACK_UNREF(calld->owning_call, "batch_data");
+}
+} // namespace
+
+// Creates a subchannel_batch_data object on the call's arena with the
+// specified refcount. If set_on_complete is true, the batch's
+// on_complete callback will be set to point to on_complete();
+// otherwise, the batch's on_complete callback will be null.
+static subchannel_batch_data* batch_data_create(grpc_call_element* elem,
+ int refcount,
+ bool set_on_complete) {
+ call_data* calld = static_cast<call_data*>(elem->call_data);
+ subchannel_batch_data* batch_data =
+ new (gpr_arena_alloc(calld->arena, sizeof(*batch_data)))
+ subchannel_batch_data(elem, calld, refcount, set_on_complete);
return batch_data;
}
static void batch_data_unref(subchannel_batch_data* batch_data) {
if (gpr_unref(&batch_data->refs)) {
- subchannel_call_retry_state* retry_state =
- static_cast<subchannel_call_retry_state*>(
- grpc_connected_subchannel_call_get_parent_data(
- batch_data->subchannel_call));
- if (batch_data->batch.send_initial_metadata) {
- grpc_metadata_batch_destroy(&retry_state->send_initial_metadata);
- }
- if (batch_data->batch.send_trailing_metadata) {
- grpc_metadata_batch_destroy(&retry_state->send_trailing_metadata);
- }
- if (batch_data->batch.recv_initial_metadata) {
- grpc_metadata_batch_destroy(&retry_state->recv_initial_metadata);
- }
- if (batch_data->batch.recv_trailing_metadata) {
- grpc_metadata_batch_destroy(&retry_state->recv_trailing_metadata);
- }
- GRPC_SUBCHANNEL_CALL_UNREF(batch_data->subchannel_call, "batch_data_unref");
- call_data* calld = static_cast<call_data*>(batch_data->elem->call_data);
- GRPC_CALL_STACK_UNREF(calld->owning_call, "batch_data");
+ batch_data->destroy();
}
}
@@ -1996,7 +1954,7 @@ static bool pending_batch_is_unstarted(
return true;
}
if (pending->batch->send_message &&
- retry_state->started_send_message_count < calld->send_messages->size()) {
+ retry_state->started_send_message_count < calld->send_messages.size()) {
return true;
}
if (pending->batch->send_trailing_metadata &&
@@ -2152,7 +2110,7 @@ static void add_closures_for_replay_or_pending_send_ops(
channel_data* chand = static_cast<channel_data*>(elem->channel_data);
call_data* calld = static_cast<call_data*>(elem->call_data);
bool have_pending_send_message_ops =
- retry_state->started_send_message_count < calld->send_messages->size();
+ retry_state->started_send_message_count < calld->send_messages.size();
bool have_pending_send_trailing_metadata_op =
calld->seen_send_trailing_metadata &&
!retry_state->started_send_trailing_metadata;
@@ -2344,7 +2302,7 @@ static void add_retriable_send_message_op(
chand, calld, retry_state->started_send_message_count);
}
grpc_core::ByteStreamCache* cache =
- (*calld->send_messages)[retry_state->started_send_message_count];
+ calld->send_messages[retry_state->started_send_message_count];
++retry_state->started_send_message_count;
retry_state->send_message.Init(cache);
batch_data->batch.send_message = true;
@@ -2476,7 +2434,7 @@ static subchannel_batch_data* maybe_create_subchannel_batch_for_replay(
}
// send_message.
// Note that we can only have one send_message op in flight at a time.
- if (retry_state->started_send_message_count < calld->send_messages->size() &&
+ if (retry_state->started_send_message_count < calld->send_messages.size() &&
retry_state->started_send_message_count ==
retry_state->completed_send_message_count &&
!calld->pending_send_message) {
@@ -2497,7 +2455,7 @@ static subchannel_batch_data* maybe_create_subchannel_batch_for_replay(
// to start, since we can't send down any more send_message ops after
// send_trailing_metadata.
if (calld->seen_send_trailing_metadata &&
- retry_state->started_send_message_count == calld->send_messages->size() &&
+ retry_state->started_send_message_count == calld->send_messages.size() &&
!retry_state->started_send_trailing_metadata &&
!calld->pending_send_trailing_metadata) {
if (grpc_client_channel_trace.enabled()) {
@@ -2549,7 +2507,7 @@ static void add_subchannel_batches_for_pending_batches(
// send_message ops after send_trailing_metadata.
if (batch->send_trailing_metadata &&
(retry_state->started_send_message_count + batch->send_message <
- calld->send_messages->size() ||
+ calld->send_messages.size() ||
retry_state->started_send_trailing_metadata)) {
continue;
}
@@ -2715,17 +2673,10 @@ static void create_subchannel_call(grpc_call_element* elem, grpc_error* error) {
new_error = grpc_error_add_child(new_error, error);
pending_batches_fail(elem, new_error, true /* yield_call_combiner */);
} else {
- grpc_core::channelz::SubchannelNode* channelz_subchannel =
- calld->pick.connected_subchannel->channelz_subchannel();
- if (channelz_subchannel != nullptr) {
- channelz_subchannel->RecordCallStarted();
- }
if (parent_data_size > 0) {
- subchannel_call_retry_state* retry_state =
- static_cast<subchannel_call_retry_state*>(
- grpc_connected_subchannel_call_get_parent_data(
- calld->subchannel_call));
- retry_state->batch_payload.context = calld->pick.subchannel_call_context;
+ new (grpc_connected_subchannel_call_get_parent_data(
+ calld->subchannel_call))
+ subchannel_call_retry_state(calld->pick.subchannel_call_context);
}
pending_batches_resume(elem);
}
@@ -2951,6 +2902,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 +2930,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 +3102,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);
@@ -3231,21 +3216,8 @@ static void cc_start_transport_stream_op_batch(
/* Constructor for call_data */
static grpc_error* cc_init_call_elem(grpc_call_element* elem,
const grpc_call_element_args* args) {
- call_data* calld = static_cast<call_data*>(elem->call_data);
channel_data* chand = static_cast<channel_data*>(elem->channel_data);
- // Initialize data members.
- calld->path = grpc_slice_ref_internal(args->path);
- calld->call_start_time = args->start_time;
- calld->deadline = args->deadline;
- calld->arena = args->arena;
- calld->owning_call = args->call_stack;
- calld->call_combiner = args->call_combiner;
- if (GPR_LIKELY(chand->deadline_checking_enabled)) {
- grpc_deadline_state_init(elem, args->call_stack, args->call_combiner,
- calld->deadline);
- }
- calld->enable_retries = chand->enable_retries;
- calld->send_messages.Init();
+ new (elem->call_data) call_data(elem, *chand, *args);
return GRPC_ERROR_NONE;
}
@@ -3254,34 +3226,12 @@ static void cc_destroy_call_elem(grpc_call_element* elem,
const grpc_call_final_info* final_info,
grpc_closure* then_schedule_closure) {
call_data* calld = static_cast<call_data*>(elem->call_data);
- channel_data* chand = static_cast<channel_data*>(elem->channel_data);
- if (GPR_LIKELY(chand->deadline_checking_enabled)) {
- grpc_deadline_state_destroy(elem);
- }
- grpc_slice_unref_internal(calld->path);
- calld->retry_throttle_data.reset();
- calld->method_params.reset();
- GRPC_ERROR_UNREF(calld->cancel_error);
if (GPR_LIKELY(calld->subchannel_call != nullptr)) {
grpc_subchannel_call_set_cleanup_closure(calld->subchannel_call,
then_schedule_closure);
then_schedule_closure = nullptr;
- GRPC_SUBCHANNEL_CALL_UNREF(calld->subchannel_call,
- "client_channel_destroy_call");
- }
- for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
- GPR_ASSERT(calld->pending_batches[i].batch == nullptr);
- }
- if (GPR_LIKELY(calld->pick.connected_subchannel != nullptr)) {
- calld->pick.connected_subchannel.reset();
- }
- for (size_t i = 0; i < GRPC_CONTEXT_COUNT; ++i) {
- if (calld->pick.subchannel_call_context[i].value != nullptr) {
- calld->pick.subchannel_call_context[i].destroy(
- calld->pick.subchannel_call_context[i].value);
- }
}
- calld->send_messages.Destroy();
+ calld->~call_data();
GRPC_CLOSURE_SCHED(then_schedule_closure, GRPC_ERROR_NONE);
}
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..587919596f 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"
@@ -285,10 +286,9 @@ HealthCheckClient::CallState::CallState(
health_check_client_(std::move(health_check_client)),
pollent_(grpc_polling_entity_create_from_pollset_set(interested_parties)),
arena_(gpr_arena_create(health_check_client_->connected_subchannel_
- ->GetInitialCallSizeEstimate(0))) {
- memset(&call_combiner_, 0, sizeof(call_combiner_));
+ ->GetInitialCallSizeEstimate(0))),
+ payload_(context_) {
grpc_call_combiner_init(&call_combiner_);
- memset(context_, 0, sizeof(context_));
gpr_atm_rel_store(&seen_response_, static_cast<gpr_atm>(0));
}
@@ -298,6 +298,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 +350,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/filters/client_channel/health/health_check_client.h b/src/core/ext/filters/client_channel/health/health_check_client.h
index 7f77348f18..f6babef7d6 100644
--- a/src/core/ext/filters/client_channel/health/health_check_client.h
+++ b/src/core/ext/filters/client_channel/health/health_check_client.h
@@ -97,7 +97,7 @@ class HealthCheckClient
gpr_arena* arena_;
grpc_call_combiner call_combiner_;
- grpc_call_context_element context_[GRPC_CONTEXT_COUNT];
+ grpc_call_context_element context_[GRPC_CONTEXT_COUNT] = {};
// The streaming call to the backend. Always non-NULL.
grpc_subchannel_call* call_;
diff --git a/src/core/ext/filters/client_channel/http_connect_handshaker.cc b/src/core/ext/filters/client_channel/http_connect_handshaker.cc
index bfabc68c66..0716e46818 100644
--- a/src/core/ext/filters/client_channel/http_connect_handshaker.cc
+++ b/src/core/ext/filters/client_channel/http_connect_handshaker.cc
@@ -29,7 +29,6 @@
#include "src/core/ext/filters/client_channel/client_channel.h"
#include "src/core/ext/filters/client_channel/resolver_registry.h"
-#include "src/core/ext/filters/client_channel/uri_parser.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/handshaker_registry.h"
#include "src/core/lib/gpr/env.h"
@@ -37,6 +36,7 @@
#include "src/core/lib/http/format_request.h"
#include "src/core/lib/http/parser.h"
#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/uri/uri_parser.h"
typedef struct http_connect_handshaker {
// Base class. Must be first.
diff --git a/src/core/ext/filters/client_channel/http_proxy.cc b/src/core/ext/filters/client_channel/http_proxy.cc
index 26d3f479b7..8951a2920c 100644
--- a/src/core/ext/filters/client_channel/http_proxy.cc
+++ b/src/core/ext/filters/client_channel/http_proxy.cc
@@ -29,12 +29,12 @@
#include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
-#include "src/core/ext/filters/client_channel/uri_parser.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gpr/env.h"
#include "src/core/lib/gpr/host_port.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/slice/b64.h"
+#include "src/core/lib/uri/uri_parser.h"
/**
* Parses the 'https_proxy' env var (fallback on 'http_proxy') and returns the
diff --git a/src/core/ext/filters/client_channel/lb_policy.h b/src/core/ext/filters/client_channel/lb_policy.h
index 21f80b7b94..6733fdca81 100644
--- a/src/core/ext/filters/client_channel/lb_policy.h
+++ b/src/core/ext/filters/client_channel/lb_policy.h
@@ -58,44 +58,47 @@ class LoadBalancingPolicy
/// Note that the LB policy gets the set of addresses from the
/// GRPC_ARG_LB_ADDRESSES channel arg.
grpc_channel_args* args = nullptr;
+ /// Load balancing config from the resolver.
+ grpc_json* lb_config = nullptr;
};
/// State used for an LB pick.
struct PickState {
/// Initial metadata associated with the picking call.
- grpc_metadata_batch* initial_metadata;
+ grpc_metadata_batch* initial_metadata = nullptr;
/// Bitmask used for selective cancelling. See
/// \a CancelMatchingPicksLocked() and \a GRPC_INITIAL_METADATA_* in
/// grpc_types.h.
- uint32_t initial_metadata_flags;
+ uint32_t initial_metadata_flags = 0;
/// Storage for LB token in \a initial_metadata, or nullptr if not used.
grpc_linked_mdelem lb_token_mdelem_storage;
/// Closure to run when pick is complete, if not completed synchronously.
/// If null, pick will fail if a result is not available synchronously.
- grpc_closure* on_complete;
+ grpc_closure* on_complete = nullptr;
/// Will be set to the selected subchannel, or nullptr on failure or when
/// the LB policy decides to drop the call.
RefCountedPtr<ConnectedSubchannel> connected_subchannel;
/// Will be populated with context to pass to the subchannel call, if
/// needed.
- grpc_call_context_element subchannel_call_context[GRPC_CONTEXT_COUNT];
+ grpc_call_context_element subchannel_call_context[GRPC_CONTEXT_COUNT] = {};
/// Upon success, \a *user_data will be set to whatever opaque information
/// may need to be propagated from the LB policy, or nullptr if not needed.
// TODO(roth): As part of revamping our metadata APIs, try to find a
// way to clean this up and C++-ify it.
- void** user_data;
+ void** user_data = nullptr;
/// Next pointer. For internal use by LB policy.
- PickState* next;
+ PickState* next = nullptr;
};
// Not copyable nor movable.
LoadBalancingPolicy(const LoadBalancingPolicy&) = delete;
LoadBalancingPolicy& operator=(const LoadBalancingPolicy&) = delete;
- /// Updates the policy with a new set of \a args from the resolver.
- /// Note that the LB policy gets the set of addresses from the
+ /// Updates the policy with a new set of \a args and a new \a lb_config from
+ /// the resolver. Note that the LB policy gets the set of addresses from the
/// GRPC_ARG_LB_ADDRESSES channel arg.
- virtual void UpdateLocked(const grpc_channel_args& args) GRPC_ABSTRACT;
+ virtual void UpdateLocked(const grpc_channel_args& args,
+ grpc_json* lb_config) GRPC_ABSTRACT;
/// Finds an appropriate subchannel for a call, based on data in \a pick.
/// \a pick must remain alive until the pick is complete.
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc
index cc259bcdbf..399bb452f4 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc
@@ -37,16 +37,27 @@ static void destroy_channel_elem(grpc_channel_element* elem) {}
namespace {
struct call_data {
+ call_data(const grpc_call_element_args& args) {
+ if (args.context[GRPC_GRPCLB_CLIENT_STATS].value != nullptr) {
+ // Get stats object from context and take a ref.
+ client_stats = static_cast<grpc_core::GrpcLbClientStats*>(
+ args.context[GRPC_GRPCLB_CLIENT_STATS].value)
+ ->Ref();
+ // Record call started.
+ client_stats->AddCallStarted();
+ }
+ }
+
// Stats object to update.
grpc_core::RefCountedPtr<grpc_core::GrpcLbClientStats> client_stats;
// State for intercepting send_initial_metadata.
grpc_closure on_complete_for_send;
grpc_closure* original_on_complete_for_send;
- bool send_initial_metadata_succeeded;
+ bool send_initial_metadata_succeeded = false;
// State for intercepting recv_initial_metadata.
grpc_closure recv_initial_metadata_ready;
grpc_closure* original_recv_initial_metadata_ready;
- bool recv_initial_metadata_succeeded;
+ bool recv_initial_metadata_succeeded = false;
};
} // namespace
@@ -70,16 +81,8 @@ static void recv_initial_metadata_ready(void* arg, grpc_error* error) {
static grpc_error* init_call_elem(grpc_call_element* elem,
const grpc_call_element_args* args) {
- call_data* calld = static_cast<call_data*>(elem->call_data);
- // Get stats object from context and take a ref.
GPR_ASSERT(args->context != nullptr);
- if (args->context[GRPC_GRPCLB_CLIENT_STATS].value != nullptr) {
- calld->client_stats = static_cast<grpc_core::GrpcLbClientStats*>(
- args->context[GRPC_GRPCLB_CLIENT_STATS].value)
- ->Ref();
- // Record call started.
- calld->client_stats->AddCallStarted();
- }
+ new (elem->call_data) call_data(*args);
return GRPC_ERROR_NONE;
}
@@ -97,6 +100,7 @@ static void destroy_call_elem(grpc_call_element* elem,
// TODO(roth): Eliminate this once filter stack is converted to C++.
calld->client_stats.reset();
}
+ calld->~call_data();
}
static void start_transport_stream_op_batch(
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
index 17e0d26875..dc0e1f89ce 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
@@ -123,7 +123,8 @@ class GrpcLb : public LoadBalancingPolicy {
public:
GrpcLb(const grpc_lb_addresses* addresses, const Args& args);
- void UpdateLocked(const grpc_channel_args& args) override;
+ void UpdateLocked(const grpc_channel_args& args,
+ grpc_json* lb_config) override;
bool PickLocked(PickState* pick, grpc_error** error) override;
void CancelPickLocked(PickState* pick, grpc_error* error) override;
void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
@@ -1331,13 +1332,10 @@ void GrpcLb::ProcessChannelArgsLocked(const grpc_channel_args& args) {
grpc_channel_args_destroy(lb_channel_args);
}
-void GrpcLb::UpdateLocked(const grpc_channel_args& args) {
+void GrpcLb::UpdateLocked(const grpc_channel_args& args, grpc_json* lb_config) {
ProcessChannelArgsLocked(args);
- // If fallback is configured and the RR policy already exists, update
- // it with the new fallback addresses.
- if (lb_fallback_timeout_ms_ > 0 && rr_policy_ != nullptr) {
- CreateOrUpdateRoundRobinPolicyLocked();
- }
+ // Update the existing RR policy.
+ if (rr_policy_ != nullptr) CreateOrUpdateRoundRobinPolicyLocked();
// Start watching the LB channel connectivity for connection, if not
// already doing so.
if (!watching_lb_channel_) {
@@ -1730,7 +1728,7 @@ void GrpcLb::CreateOrUpdateRoundRobinPolicyLocked() {
gpr_log(GPR_INFO, "[grpclb %p] Updating RR policy %p", this,
rr_policy_.get());
}
- rr_policy_->UpdateLocked(*args);
+ rr_policy_->UpdateLocked(*args, nullptr);
} else {
LoadBalancingPolicy::Args lb_policy_args;
lb_policy_args.combiner = combiner();
diff --git a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
index eb494486b9..d454401a66 100644
--- a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
@@ -46,7 +46,8 @@ class PickFirst : public LoadBalancingPolicy {
public:
explicit PickFirst(const Args& args);
- void UpdateLocked(const grpc_channel_args& args) override;
+ void UpdateLocked(const grpc_channel_args& args,
+ grpc_json* lb_config) override;
bool PickLocked(PickState* pick, grpc_error** error) override;
void CancelPickLocked(PickState* pick, grpc_error* error) override;
void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
@@ -159,7 +160,7 @@ PickFirst::PickFirst(const Args& args) : LoadBalancingPolicy(args) {
if (grpc_lb_pick_first_trace.enabled()) {
gpr_log(GPR_INFO, "Pick First %p created.", this);
}
- UpdateLocked(*args.args);
+ UpdateLocked(*args.args, args.lb_config);
grpc_subchannel_index_ref();
}
@@ -333,7 +334,8 @@ void PickFirst::UpdateChildRefsLocked() {
child_subchannels_ = std::move(cs);
}
-void PickFirst::UpdateLocked(const grpc_channel_args& args) {
+void PickFirst::UpdateLocked(const grpc_channel_args& args,
+ grpc_json* lb_config) {
AutoChildRefsUpdater guard(this);
const grpc_arg* arg = grpc_channel_args_find(&args, GRPC_ARG_LB_ADDRESSES);
if (arg == nullptr || arg->type != GRPC_ARG_POINTER) {
diff --git a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
index e9ed85cf66..2a16975131 100644
--- a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
@@ -57,7 +57,8 @@ class RoundRobin : public LoadBalancingPolicy {
public:
explicit RoundRobin(const Args& args);
- void UpdateLocked(const grpc_channel_args& args) override;
+ void UpdateLocked(const grpc_channel_args& args,
+ grpc_json* lb_config) override;
bool PickLocked(PickState* pick, grpc_error** error) override;
void CancelPickLocked(PickState* pick, grpc_error* error) override;
void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
@@ -232,7 +233,7 @@ RoundRobin::RoundRobin(const Args& args) : LoadBalancingPolicy(args) {
gpr_mu_init(&child_refs_mu_);
grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE,
"round_robin");
- UpdateLocked(*args.args);
+ UpdateLocked(*args.args, args.lb_config);
if (grpc_lb_round_robin_trace.enabled()) {
gpr_log(GPR_INFO, "[RR %p] Created with %" PRIuPTR " subchannels", this,
subchannel_list_->num_subchannels());
@@ -664,7 +665,8 @@ void RoundRobin::NotifyOnStateChangeLocked(grpc_connectivity_state* current,
notify);
}
-void RoundRobin::UpdateLocked(const grpc_channel_args& args) {
+void RoundRobin::UpdateLocked(const grpc_channel_args& args,
+ grpc_json* lb_config) {
const grpc_arg* arg = grpc_channel_args_find(&args, GRPC_ARG_LB_ADDRESSES);
AutoChildRefsUpdater guard(this);
if (GPR_UNLIKELY(arg == nullptr || arg->type != GRPC_ARG_POINTER)) {
diff --git a/src/core/ext/filters/client_channel/lb_policy/xds/xds.cc b/src/core/ext/filters/client_channel/lb_policy/xds/xds.cc
index 7fb4cbdcd2..29cd904375 100644
--- a/src/core/ext/filters/client_channel/lb_policy/xds/xds.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/xds/xds.cc
@@ -26,30 +26,26 @@
/// channel that uses pick_first to select from the list of balancer
/// addresses.
///
-/// The first time the policy gets a request for a pick, a ping, or to exit
-/// the idle state, \a StartPickingLocked() is called. This method is
-/// responsible for instantiating the internal *streaming* call to the LB
-/// server (whichever address pick_first chose). The call will be complete
-/// when either the balancer sends status or when we cancel the call (e.g.,
-/// because we are shutting down). In needed, we retry the call. If we
-/// received at least one valid message from the server, a new call attempt
-/// will be made immediately; otherwise, we apply back-off delays between
-/// attempts.
+/// The first time the xDS policy gets a request for a pick or to exit the idle
+/// state, \a StartPickingLocked() is called. This method is responsible for
+/// instantiating the internal *streaming* call to the LB server (whichever
+/// address pick_first chose). The call will be complete when either the
+/// balancer sends status or when we cancel the call (e.g., because we are
+/// shutting down). In needed, we retry the call. If we received at least one
+/// valid message from the server, a new call attempt will be made immediately;
+/// otherwise, we apply back-off delays between attempts.
///
-/// We maintain an internal round_robin policy instance for distributing
+/// We maintain an internal child policy (round_robin) instance for distributing
/// requests across backends. Whenever we receive a new serverlist from
-/// the balancer, we update the round_robin policy with the new list of
-/// addresses. If we cannot communicate with the balancer on startup,
-/// however, we may enter fallback mode, in which case we will populate
-/// the RR policy's addresses from the backend addresses returned by the
-/// resolver.
+/// the balancer, we update the child policy with the new list of
+/// addresses.
///
-/// Once an RR policy instance is in place (and getting updated as described),
-/// calls for a pick, a ping, or a cancellation will be serviced right
-/// away by forwarding them to the RR instance. Any time there's no RR
-/// policy available (i.e., right after the creation of the gRPCLB policy),
-/// pick and ping requests are added to a list of pending picks and pings
-/// to be flushed and serviced when the RR policy instance becomes available.
+/// Once a child policy instance is in place (and getting updated as
+/// described), calls for a pick, or a cancellation will be serviced right away
+/// by forwarding them to the child policy instance. Any time there's no child
+/// policy available (i.e., right after the creation of the xDS policy), pick
+/// requests are added to a list of pending picks to be flushed and serviced
+/// when the child policy instance becomes available.
///
/// \see https://github.com/grpc/grpc/blob/master/doc/load-balancing.md for the
/// high level design and details.
@@ -122,7 +118,8 @@ class XdsLb : public LoadBalancingPolicy {
public:
XdsLb(const grpc_lb_addresses* addresses, const Args& args);
- void UpdateLocked(const grpc_channel_args& args) override;
+ void UpdateLocked(const grpc_channel_args& args,
+ grpc_json* lb_config) override;
bool PickLocked(PickState* pick, grpc_error** error) override;
void CancelPickLocked(PickState* pick, grpc_error* error) override;
void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
@@ -141,10 +138,10 @@ class XdsLb : public LoadBalancingPolicy {
private:
/// Linked list of pending pick requests. It stores all information needed to
- /// eventually call (Round Robin's) pick() on them. They mainly stay pending
- /// waiting for the RR policy to be created.
+ /// eventually call pick() on them. They mainly stay pending waiting for the
+ /// child policy to be created.
///
- /// Note that when a pick is sent to the RR policy, we inject our own
+ /// Note that when a pick is sent to the child policy, we inject our own
/// on_complete callback, so that we can intercept the result before
/// invoking the original on_complete callback. This allows us to set the
/// LB token metadata and add client_stats to the call context.
@@ -266,18 +263,18 @@ class XdsLb : public LoadBalancingPolicy {
void AddPendingPick(PendingPick* pp);
static void OnPendingPickComplete(void* arg, grpc_error* error);
- // Methods for dealing with the RR policy.
- void CreateOrUpdateRoundRobinPolicyLocked();
- grpc_channel_args* CreateRoundRobinPolicyArgsLocked();
- void CreateRoundRobinPolicyLocked(const Args& args);
- bool PickFromRoundRobinPolicyLocked(bool force_async, PendingPick* pp,
- grpc_error** error);
- void UpdateConnectivityStateFromRoundRobinPolicyLocked(
- grpc_error* rr_state_error);
- static void OnRoundRobinConnectivityChangedLocked(void* arg,
- grpc_error* error);
- static void OnRoundRobinRequestReresolutionLocked(void* arg,
- grpc_error* error);
+ // Methods for dealing with the child policy.
+ void CreateOrUpdateChildPolicyLocked();
+ grpc_channel_args* CreateChildPolicyArgsLocked();
+ void CreateChildPolicyLocked(const Args& args);
+ bool PickFromChildPolicyLocked(bool force_async, PendingPick* pp,
+ grpc_error** error);
+ void UpdateConnectivityStateFromChildPolicyLocked(
+ grpc_error* child_state_error);
+ static void OnChildPolicyConnectivityChangedLocked(void* arg,
+ grpc_error* error);
+ static void OnChildPolicyRequestReresolutionLocked(void* arg,
+ grpc_error* error);
// Who the client is trying to communicate with.
const char* server_name_ = nullptr;
@@ -319,10 +316,6 @@ class XdsLb : public LoadBalancingPolicy {
// The deserialized response from the balancer. May be nullptr until one
// such response has arrived.
xds_grpclb_serverlist* serverlist_ = nullptr;
- // Index into serverlist for next pick.
- // If the server at this index is a drop, we return a drop.
- // Otherwise, we delegate to the RR policy.
- size_t serverlist_index_ = 0;
// Timeout in milliseconds for before using fallback backend addresses.
// 0 means not using fallback.
@@ -334,14 +327,14 @@ class XdsLb : public LoadBalancingPolicy {
grpc_timer lb_fallback_timer_;
grpc_closure lb_on_fallback_;
- // Pending picks that are waiting on the RR policy's connectivity.
+ // Pending picks that are waiting on the xDS policy's connectivity.
PendingPick* pending_picks_ = nullptr;
- // The RR policy to use for the backends.
- OrphanablePtr<LoadBalancingPolicy> rr_policy_;
- grpc_connectivity_state rr_connectivity_state_;
- grpc_closure on_rr_connectivity_changed_;
- grpc_closure on_rr_request_reresolution_;
+ // The policy to use for the backends.
+ OrphanablePtr<LoadBalancingPolicy> child_policy_;
+ grpc_connectivity_state child_connectivity_state_;
+ grpc_closure on_child_connectivity_changed_;
+ grpc_closure on_child_request_reresolution_;
};
//
@@ -448,7 +441,7 @@ grpc_lb_addresses* ProcessServerlist(const xds_grpclb_serverlist* serverlist) {
grpc_lb_addresses* lb_addresses =
grpc_lb_addresses_create(num_valid, &lb_token_vtable);
/* second pass: actually populate the addresses and LB tokens (aka user data
- * to the outside world) to be read by the RR policy during its creation.
+ * to the outside world) to be read by the child policy during its creation.
* Given that the validity tests are very cheap, they are performed again
* instead of marking the valid ones during the first pass, as this would
* incurr in an allocation due to the arbitrary number of server */
@@ -837,8 +830,7 @@ void XdsLb::BalancerCallState::OnBalancerMessageReceivedLocked(
// serverlist instance will be destroyed either upon the next
// update or when the XdsLb instance is destroyed.
xdslb_policy->serverlist_ = serverlist;
- xdslb_policy->serverlist_index_ = 0;
- xdslb_policy->CreateOrUpdateRoundRobinPolicyLocked();
+ xdslb_policy->CreateOrUpdateChildPolicyLocked();
}
} else {
if (grpc_lb_xds_trace.enabled()) {
@@ -871,7 +863,7 @@ void XdsLb::BalancerCallState::OnBalancerMessageReceivedLocked(
&lb_calld->lb_on_balancer_message_received_);
GPR_ASSERT(GRPC_CALL_OK == call_error);
} else {
- lb_calld->Unref(DEBUG_LOCATION, "on_message_received+grpclb_shutdown");
+ lb_calld->Unref(DEBUG_LOCATION, "on_message_received+xds_shutdown");
}
}
@@ -949,7 +941,7 @@ grpc_lb_addresses* ExtractBalancerAddresses(
* - \a addresses: corresponding to the balancers.
* - \a response_generator: in order to propagate updates from the resolver
* above the grpclb policy.
- * - \a args: other args inherited from the grpclb policy. */
+ * - \a args: other args inherited from the xds policy. */
grpc_channel_args* BuildBalancerChannelArgs(
const grpc_lb_addresses* addresses,
FakeResolverResponseGenerator* response_generator,
@@ -971,10 +963,10 @@ grpc_channel_args* BuildBalancerChannelArgs(
// resolver will have is_balancer=false, whereas our own addresses have
// is_balancer=true. We need the LB channel to return addresses with
// is_balancer=false so that it does not wind up recursively using the
- // grpclb LB policy, as per the special case logic in client_channel.c.
+ // xds LB policy, as per the special case logic in client_channel.c.
GRPC_ARG_LB_ADDRESSES,
// The fake resolver response generator, because we are replacing it
- // with the one from the grpclb policy, used to propagate updates to
+ // with the one from the xds policy, used to propagate updates to
// the LB channel.
GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
// The LB channel should use the authority indicated by the target
@@ -996,7 +988,7 @@ grpc_channel_args* BuildBalancerChannelArgs(
// address updates into the LB channel.
grpc_core::FakeResolverResponseGenerator::MakeChannelArg(
response_generator),
- // A channel arg indicating the target is a grpclb load balancer.
+ // A channel arg indicating the target is a xds load balancer.
grpc_channel_arg_integer_create(
const_cast<char*>(GRPC_ARG_ADDRESS_IS_XDS_LOAD_BALANCER), 1),
// A channel arg indicating this is an internal channels, aka it is
@@ -1019,6 +1011,7 @@ grpc_channel_args* BuildBalancerChannelArgs(
// ctor and dtor
//
+// TODO(vishalpowar): Use lb_config in args to configure LB policy.
XdsLb::XdsLb(const grpc_lb_addresses* addresses,
const LoadBalancingPolicy::Args& args)
: LoadBalancingPolicy(args),
@@ -1036,11 +1029,11 @@ XdsLb::XdsLb(const grpc_lb_addresses* addresses,
GRPC_CLOSURE_INIT(&lb_channel_on_connectivity_changed_,
&XdsLb::OnBalancerChannelConnectivityChangedLocked, this,
grpc_combiner_scheduler(args.combiner));
- GRPC_CLOSURE_INIT(&on_rr_connectivity_changed_,
- &XdsLb::OnRoundRobinConnectivityChangedLocked, this,
+ GRPC_CLOSURE_INIT(&on_child_connectivity_changed_,
+ &XdsLb::OnChildPolicyConnectivityChangedLocked, this,
grpc_combiner_scheduler(args.combiner));
- GRPC_CLOSURE_INIT(&on_rr_request_reresolution_,
- &XdsLb::OnRoundRobinRequestReresolutionLocked, this,
+ GRPC_CLOSURE_INIT(&on_child_request_reresolution_,
+ &XdsLb::OnChildPolicyRequestReresolutionLocked, this,
grpc_combiner_scheduler(args.combiner));
grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE, "xds");
// Record server name.
@@ -1092,7 +1085,7 @@ void XdsLb::ShutdownLocked() {
if (fallback_timer_callback_pending_) {
grpc_timer_cancel(&lb_fallback_timer_);
}
- rr_policy_.reset();
+ child_policy_.reset();
TryReresolutionLocked(&grpc_lb_xds_trace, GRPC_ERROR_CANCELLED);
// We destroy the LB channel here instead of in our destructor because
// destroying the channel triggers a last callback to
@@ -1105,7 +1098,7 @@ void XdsLb::ShutdownLocked() {
gpr_mu_unlock(&lb_channel_mu_);
}
grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_SHUTDOWN,
- GRPC_ERROR_REF(error), "grpclb_shutdown");
+ GRPC_ERROR_REF(error), "xds_shutdown");
// Clear pending picks.
PendingPick* pp;
while ((pp = pending_picks_) != nullptr) {
@@ -1138,13 +1131,13 @@ void XdsLb::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) {
// Cancel a specific pending pick.
//
-// A grpclb pick progresses as follows:
-// - If there's a Round Robin policy (rr_policy_) available, it'll be
-// handed over to the RR policy (in CreateRoundRobinPolicyLocked()). From
-// that point onwards, it'll be RR's responsibility. For cancellations, that
-// implies the pick needs also be cancelled by the RR instance.
-// - Otherwise, without an RR instance, picks stay pending at this policy's
-// level (grpclb), inside the pending_picks_ list. To cancel these,
+// A pick progresses as follows:
+// - If there's a child policy available, it'll be handed over to child policy
+// (in CreateChildPolicyLocked()). From that point onwards, it'll be the
+// child policy's responsibility. For cancellations, that implies the pick
+// needs to be also cancelled by the child policy instance.
+// - Otherwise, without a child policy instance, picks stay pending at this
+// policy's level (xds), inside the pending_picks_ list. To cancel these,
// we invoke the completion closure and set the pick's connected
// subchannel to nullptr right here.
void XdsLb::CancelPickLocked(PickState* pick, grpc_error* error) {
@@ -1164,21 +1157,21 @@ void XdsLb::CancelPickLocked(PickState* pick, grpc_error* error) {
}
pp = next;
}
- if (rr_policy_ != nullptr) {
- rr_policy_->CancelPickLocked(pick, GRPC_ERROR_REF(error));
+ if (child_policy_ != nullptr) {
+ child_policy_->CancelPickLocked(pick, GRPC_ERROR_REF(error));
}
GRPC_ERROR_UNREF(error);
}
// Cancel all pending picks.
//
-// A grpclb pick progresses as follows:
-// - If there's a Round Robin policy (rr_policy_) available, it'll be
-// handed over to the RR policy (in CreateRoundRobinPolicyLocked()). From
-// that point onwards, it'll be RR's responsibility. For cancellations, that
-// implies the pick needs also be cancelled by the RR instance.
-// - Otherwise, without an RR instance, picks stay pending at this policy's
-// level (grpclb), inside the pending_picks_ list. To cancel these,
+// A pick progresses as follows:
+// - If there's a child policy available, it'll be handed over to child policy
+// (in CreateChildPolicyLocked()). From that point onwards, it'll be the
+// child policy's responsibility. For cancellations, that implies the pick
+// needs to be also cancelled by the child policy instance.
+// - Otherwise, without a child policy instance, picks stay pending at this
+// policy's level (xds), inside the pending_picks_ list. To cancel these,
// we invoke the completion closure and set the pick's connected
// subchannel to nullptr right here.
void XdsLb::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
@@ -1200,10 +1193,10 @@ void XdsLb::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
}
pp = next;
}
- if (rr_policy_ != nullptr) {
- rr_policy_->CancelMatchingPicksLocked(initial_metadata_flags_mask,
- initial_metadata_flags_eq,
- GRPC_ERROR_REF(error));
+ if (child_policy_ != nullptr) {
+ child_policy_->CancelMatchingPicksLocked(initial_metadata_flags_mask,
+ initial_metadata_flags_eq,
+ GRPC_ERROR_REF(error));
}
GRPC_ERROR_UNREF(error);
}
@@ -1218,22 +1211,21 @@ void XdsLb::ResetBackoffLocked() {
if (lb_channel_ != nullptr) {
grpc_channel_reset_connect_backoff(lb_channel_);
}
- if (rr_policy_ != nullptr) {
- rr_policy_->ResetBackoffLocked();
+ if (child_policy_ != nullptr) {
+ child_policy_->ResetBackoffLocked();
}
}
bool XdsLb::PickLocked(PickState* pick, grpc_error** error) {
PendingPick* pp = PendingPickCreate(pick);
bool pick_done = false;
- if (rr_policy_ != nullptr) {
+ if (child_policy_ != nullptr) {
if (grpc_lb_xds_trace.enabled()) {
- gpr_log(GPR_INFO, "[xdslb %p] about to PICK from RR %p", this,
- rr_policy_.get());
+ gpr_log(GPR_INFO, "[xdslb %p] about to PICK from policy %p", this,
+ child_policy_.get());
}
- pick_done =
- PickFromRoundRobinPolicyLocked(false /* force_async */, pp, error);
- } else { // rr_policy_ == NULL
+ pick_done = PickFromChildPolicyLocked(false /* force_async */, pp, error);
+ } else { // child_policy_ == NULL
if (pick->on_complete == nullptr) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No pick result available but synchronous result required.");
@@ -1241,7 +1233,7 @@ bool XdsLb::PickLocked(PickState* pick, grpc_error** error) {
} else {
if (grpc_lb_xds_trace.enabled()) {
gpr_log(GPR_INFO,
- "[xdslb %p] No RR policy. Adding to grpclb's pending picks",
+ "[xdslb %p] No child policy. Adding to xds's pending picks",
this);
}
AddPendingPick(pp);
@@ -1256,8 +1248,8 @@ bool XdsLb::PickLocked(PickState* pick, grpc_error** error) {
void XdsLb::FillChildRefsForChannelz(channelz::ChildRefsList* child_subchannels,
channelz::ChildRefsList* child_channels) {
- // delegate to the RoundRobin to fill the children subchannels.
- rr_policy_->FillChildRefsForChannelz(child_subchannels, child_channels);
+ // delegate to the child_policy_ to fill the children subchannels.
+ child_policy_->FillChildRefsForChannelz(child_subchannels, child_channels);
MutexLock lock(&lb_channel_mu_);
if (lb_channel_ != nullptr) {
grpc_core::channelz::ChannelNode* channel_node =
@@ -1324,13 +1316,15 @@ void XdsLb::ProcessChannelArgsLocked(const grpc_channel_args& args) {
grpc_channel_args_destroy(lb_channel_args);
}
-void XdsLb::UpdateLocked(const grpc_channel_args& args) {
+// TODO(vishalpowar): Use lb_config to configure LB policy.
+void XdsLb::UpdateLocked(const grpc_channel_args& args, grpc_json* lb_config) {
ProcessChannelArgsLocked(args);
- // If fallback is configured and the RR policy already exists, update
- // it with the new fallback addresses.
- if (lb_fallback_timeout_ms_ > 0 && rr_policy_ != nullptr) {
- CreateOrUpdateRoundRobinPolicyLocked();
- }
+ // Update the existing child policy.
+ // Note: We have disabled fallback mode in the code, so this child policy must
+ // have been created from a serverlist.
+ // TODO(vpowar): Handle the fallback_address changes when we add support for
+ // fallback in xDS.
+ if (child_policy_ != nullptr) CreateOrUpdateChildPolicyLocked();
// Start watching the LB channel connectivity for connection, if not
// already doing so.
if (!watching_lb_channel_) {
@@ -1398,11 +1392,10 @@ void XdsLb::OnFallbackTimerLocked(void* arg, grpc_error* error) {
if (xdslb_policy->serverlist_ == nullptr && !xdslb_policy->shutting_down_ &&
error == GRPC_ERROR_NONE) {
if (grpc_lb_xds_trace.enabled()) {
- gpr_log(GPR_INFO, "[xdslb %p] Falling back to use backends from resolver",
+ gpr_log(GPR_INFO,
+ "[xdslb %p] Fallback timer fired. Not using fallback backends",
xdslb_policy);
}
- GPR_ASSERT(xdslb_policy->fallback_backend_addresses_ != nullptr);
- xdslb_policy->CreateOrUpdateRoundRobinPolicyLocked();
}
xdslb_policy->Unref(DEBUG_LOCATION, "on_fallback_timer");
}
@@ -1452,8 +1445,8 @@ void XdsLb::OnBalancerChannelConnectivityChangedLocked(void* arg,
XdsLb* xdslb_policy = static_cast<XdsLb*>(arg);
if (xdslb_policy->shutting_down_) goto done;
// Re-initialize the lb_call. This should also take care of updating the
- // embedded RR policy. Note that the current RR policy, if any, will stay in
- // effect until an update from the new lb_call is received.
+ // child policy. Note that the current child policy, if any, will
+ // stay in effect until an update from the new lb_call is received.
switch (xdslb_policy->lb_channel_connectivity_) {
case GRPC_CHANNEL_CONNECTING:
case GRPC_CHANNEL_TRANSIENT_FAILURE: {
@@ -1512,8 +1505,8 @@ void DestroyClientStats(void* arg) {
}
void XdsLb::PendingPickSetMetadataAndContext(PendingPick* pp) {
- /* if connected_subchannel is nullptr, no pick has been made by the RR
- * policy (e.g., all addresses failed to connect). There won't be any
+ /* if connected_subchannel is nullptr, no pick has been made by the
+ * child policy (e.g., all addresses failed to connect). There won't be any
* user_data/token available */
if (pp->pick->connected_subchannel != nullptr) {
if (GPR_LIKELY(!GRPC_MDISNULL(pp->lb_token))) {
@@ -1539,8 +1532,8 @@ void XdsLb::PendingPickSetMetadataAndContext(PendingPick* pp) {
}
/* The \a on_complete closure passed as part of the pick requires keeping a
- * reference to its associated round robin instance. We wrap this closure in
- * order to unref the round robin instance upon its invocation */
+ * reference to its associated child policy instance. We wrap this closure in
+ * order to unref the child policy instance upon its invocation */
void XdsLb::OnPendingPickComplete(void* arg, grpc_error* error) {
PendingPick* pp = static_cast<PendingPick*>(arg);
PendingPickSetMetadataAndContext(pp);
@@ -1565,50 +1558,24 @@ void XdsLb::AddPendingPick(PendingPick* pp) {
}
//
-// code for interacting with the RR policy
+// code for interacting with the child policy
//
-// Performs a pick over \a rr_policy_. Given that a pick can return
+// Performs a pick over \a child_policy_. Given that a pick can return
// immediately (ignoring its completion callback), we need to perform the
// cleanups this callback would otherwise be responsible for.
// If \a force_async is true, then we will manually schedule the
// completion callback even if the pick is available immediately.
-bool XdsLb::PickFromRoundRobinPolicyLocked(bool force_async, PendingPick* pp,
- grpc_error** error) {
- // Check for drops if we are not using fallback backend addresses.
- if (serverlist_ != nullptr) {
- // Look at the index into the serverlist to see if we should drop this call.
- xds_grpclb_server* server = serverlist_->servers[serverlist_index_++];
- if (serverlist_index_ == serverlist_->num_servers) {
- serverlist_index_ = 0; // Wrap-around.
- }
- if (server->drop) {
- // Update client load reporting stats to indicate the number of
- // dropped calls. Note that we have to do this here instead of in
- // the client_load_reporting filter, because we do not create a
- // subchannel call (and therefore no client_load_reporting filter)
- // for dropped calls.
- if (lb_calld_ != nullptr && lb_calld_->client_stats() != nullptr) {
- lb_calld_->client_stats()->AddCallDroppedLocked(
- server->load_balance_token);
- }
- if (force_async) {
- GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_NONE);
- Delete(pp);
- return false;
- }
- Delete(pp);
- return true;
- }
- }
+bool XdsLb::PickFromChildPolicyLocked(bool force_async, PendingPick* pp,
+ grpc_error** error) {
// Set client_stats and user_data.
if (lb_calld_ != nullptr && lb_calld_->client_stats() != nullptr) {
pp->client_stats = lb_calld_->client_stats()->Ref();
}
GPR_ASSERT(pp->pick->user_data == nullptr);
pp->pick->user_data = (void**)&pp->lb_token;
- // Pick via the RR policy.
- bool pick_done = rr_policy_->PickLocked(pp->pick, error);
+ // Pick via the child policy.
+ bool pick_done = child_policy_->PickLocked(pp->pick, error);
if (pick_done) {
PendingPickSetMetadataAndContext(pp);
if (force_async) {
@@ -1619,72 +1586,67 @@ bool XdsLb::PickFromRoundRobinPolicyLocked(bool force_async, PendingPick* pp,
Delete(pp);
}
// else, the pending pick will be registered and taken care of by the
- // pending pick list inside the RR policy. Eventually,
+ // pending pick list inside the child policy. Eventually,
// OnPendingPickComplete() will be called, which will (among other
// things) add the LB token to the call's initial metadata.
return pick_done;
}
-void XdsLb::CreateRoundRobinPolicyLocked(const Args& args) {
- GPR_ASSERT(rr_policy_ == nullptr);
- rr_policy_ = LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
+void XdsLb::CreateChildPolicyLocked(const Args& args) {
+ GPR_ASSERT(child_policy_ == nullptr);
+ child_policy_ = LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
"round_robin", args);
- if (GPR_UNLIKELY(rr_policy_ == nullptr)) {
- gpr_log(GPR_ERROR, "[xdslb %p] Failure creating a RoundRobin policy", this);
+ if (GPR_UNLIKELY(child_policy_ == nullptr)) {
+ gpr_log(GPR_ERROR, "[xdslb %p] Failure creating a child policy", this);
return;
}
// TODO(roth): We currently track this ref manually. Once the new
// ClosureRef API is done, pass the RefCountedPtr<> along with the closure.
- auto self = Ref(DEBUG_LOCATION, "on_rr_reresolution_requested");
+ auto self = Ref(DEBUG_LOCATION, "on_child_reresolution_requested");
self.release();
- rr_policy_->SetReresolutionClosureLocked(&on_rr_request_reresolution_);
- grpc_error* rr_state_error = nullptr;
- rr_connectivity_state_ = rr_policy_->CheckConnectivityLocked(&rr_state_error);
- // Connectivity state is a function of the RR policy updated/created.
- UpdateConnectivityStateFromRoundRobinPolicyLocked(rr_state_error);
- // Add the gRPC LB's interested_parties pollset_set to that of the newly
- // created RR policy. This will make the RR policy progress upon activity on
- // gRPC LB, which in turn is tied to the application's call.
- grpc_pollset_set_add_pollset_set(rr_policy_->interested_parties(),
+ child_policy_->SetReresolutionClosureLocked(&on_child_request_reresolution_);
+ grpc_error* child_state_error = nullptr;
+ child_connectivity_state_ =
+ child_policy_->CheckConnectivityLocked(&child_state_error);
+ // Connectivity state is a function of the child policy updated/created.
+ UpdateConnectivityStateFromChildPolicyLocked(child_state_error);
+ // Add the xDS's interested_parties pollset_set to that of the newly created
+ // child policy. This will make the child policy progress upon activity on
+ // xDS LB, which in turn is tied to the application's call.
+ grpc_pollset_set_add_pollset_set(child_policy_->interested_parties(),
interested_parties());
- // Subscribe to changes to the connectivity of the new RR.
+ // Subscribe to changes to the connectivity of the new child policy.
// TODO(roth): We currently track this ref manually. Once the new
// ClosureRef API is done, pass the RefCountedPtr<> along with the closure.
- self = Ref(DEBUG_LOCATION, "on_rr_connectivity_changed");
+ self = Ref(DEBUG_LOCATION, "on_child_connectivity_changed");
self.release();
- rr_policy_->NotifyOnStateChangeLocked(&rr_connectivity_state_,
- &on_rr_connectivity_changed_);
- rr_policy_->ExitIdleLocked();
- // Send pending picks to RR policy.
+ child_policy_->NotifyOnStateChangeLocked(&child_connectivity_state_,
+ &on_child_connectivity_changed_);
+ child_policy_->ExitIdleLocked();
+ // Send pending picks to child policy.
PendingPick* pp;
while ((pp = pending_picks_)) {
pending_picks_ = pp->next;
if (grpc_lb_xds_trace.enabled()) {
- gpr_log(GPR_INFO,
- "[xdslb %p] Pending pick about to (async) PICK from RR %p", this,
- rr_policy_.get());
+ gpr_log(
+ GPR_INFO,
+ "[xdslb %p] Pending pick about to (async) PICK from child policy %p",
+ this, child_policy_.get());
}
grpc_error* error = GRPC_ERROR_NONE;
- PickFromRoundRobinPolicyLocked(true /* force_async */, pp, &error);
+ PickFromChildPolicyLocked(true /* force_async */, pp, &error);
}
}
-grpc_channel_args* XdsLb::CreateRoundRobinPolicyArgsLocked() {
+grpc_channel_args* XdsLb::CreateChildPolicyArgsLocked() {
grpc_lb_addresses* addresses;
bool is_backend_from_grpclb_load_balancer = false;
- if (serverlist_ != nullptr) {
- GPR_ASSERT(serverlist_->num_servers > 0);
- addresses = ProcessServerlist(serverlist_);
- is_backend_from_grpclb_load_balancer = true;
- } else {
- // If CreateOrUpdateRoundRobinPolicyLocked() is invoked when we haven't
- // received any serverlist from the balancer, we use the fallback backends
- // returned by the resolver. Note that the fallback backend list may be
- // empty, in which case the new round_robin policy will keep the requested
- // picks pending.
- GPR_ASSERT(fallback_backend_addresses_ != nullptr);
- addresses = grpc_lb_addresses_copy(fallback_backend_addresses_);
- }
+ // This should never be invoked if we do not have serverlist_, as fallback
+ // mode is disabled for xDS plugin.
+ GPR_ASSERT(serverlist_ != nullptr);
+ GPR_ASSERT(serverlist_->num_servers > 0);
+ addresses = ProcessServerlist(serverlist_);
+ is_backend_from_grpclb_load_balancer = true;
GPR_ASSERT(addresses != nullptr);
// Replace the LB addresses in the channel args that we pass down to
// the subchannel.
@@ -1704,66 +1666,68 @@ grpc_channel_args* XdsLb::CreateRoundRobinPolicyArgsLocked() {
return args;
}
-void XdsLb::CreateOrUpdateRoundRobinPolicyLocked() {
+void XdsLb::CreateOrUpdateChildPolicyLocked() {
if (shutting_down_) return;
- grpc_channel_args* args = CreateRoundRobinPolicyArgsLocked();
+ grpc_channel_args* args = CreateChildPolicyArgsLocked();
GPR_ASSERT(args != nullptr);
- if (rr_policy_ != nullptr) {
+ if (child_policy_ != nullptr) {
if (grpc_lb_xds_trace.enabled()) {
- gpr_log(GPR_INFO, "[xdslb %p] Updating RR policy %p", this,
- rr_policy_.get());
+ gpr_log(GPR_INFO, "[xdslb %p] Updating the child policy %p", this,
+ child_policy_.get());
}
- rr_policy_->UpdateLocked(*args);
+ // TODO(vishalpowar): Pass the correct LB config.
+ child_policy_->UpdateLocked(*args, nullptr);
} else {
LoadBalancingPolicy::Args lb_policy_args;
lb_policy_args.combiner = combiner();
lb_policy_args.client_channel_factory = client_channel_factory();
lb_policy_args.args = args;
- CreateRoundRobinPolicyLocked(lb_policy_args);
+ CreateChildPolicyLocked(lb_policy_args);
if (grpc_lb_xds_trace.enabled()) {
- gpr_log(GPR_INFO, "[xdslb %p] Created new RR policy %p", this,
- rr_policy_.get());
+ gpr_log(GPR_INFO, "[xdslb %p] Created a new child policy %p", this,
+ child_policy_.get());
}
}
grpc_channel_args_destroy(args);
}
-void XdsLb::OnRoundRobinRequestReresolutionLocked(void* arg,
- grpc_error* error) {
+void XdsLb::OnChildPolicyRequestReresolutionLocked(void* arg,
+ grpc_error* error) {
XdsLb* xdslb_policy = static_cast<XdsLb*>(arg);
if (xdslb_policy->shutting_down_ || error != GRPC_ERROR_NONE) {
- xdslb_policy->Unref(DEBUG_LOCATION, "on_rr_reresolution_requested");
+ xdslb_policy->Unref(DEBUG_LOCATION, "on_child_reresolution_requested");
return;
}
if (grpc_lb_xds_trace.enabled()) {
- gpr_log(
- GPR_INFO,
- "[xdslb %p] Re-resolution requested from the internal RR policy (%p).",
- xdslb_policy, xdslb_policy->rr_policy_.get());
+ gpr_log(GPR_INFO,
+ "[xdslb %p] Re-resolution requested from child policy "
+ "(%p).",
+ xdslb_policy, xdslb_policy->child_policy_.get());
}
// If we are talking to a balancer, we expect to get updated addresses form
- // the balancer, so we can ignore the re-resolution request from the RR
- // policy. Otherwise, handle the re-resolution request using the
- // grpclb policy's original re-resolution closure.
+ // the balancer, so we can ignore the re-resolution request from the child
+ // policy.
+ // Otherwise, handle the re-resolution request using the xds policy's
+ // original re-resolution closure.
if (xdslb_policy->lb_calld_ == nullptr ||
!xdslb_policy->lb_calld_->seen_initial_response()) {
xdslb_policy->TryReresolutionLocked(&grpc_lb_xds_trace, GRPC_ERROR_NONE);
}
- // Give back the wrapper closure to the RR policy.
- xdslb_policy->rr_policy_->SetReresolutionClosureLocked(
- &xdslb_policy->on_rr_request_reresolution_);
+ // Give back the wrapper closure to the child policy.
+ xdslb_policy->child_policy_->SetReresolutionClosureLocked(
+ &xdslb_policy->on_child_request_reresolution_);
}
-void XdsLb::UpdateConnectivityStateFromRoundRobinPolicyLocked(
- grpc_error* rr_state_error) {
+void XdsLb::UpdateConnectivityStateFromChildPolicyLocked(
+ grpc_error* child_state_error) {
const grpc_connectivity_state curr_glb_state =
grpc_connectivity_state_check(&state_tracker_);
/* The new connectivity status is a function of the previous one and the new
- * input coming from the status of the RR policy.
+ * input coming from the status of the child policy.
*
- * current state (grpclb's)
+ * current state (xds's)
* |
- * v || I | C | R | TF | SD | <- new state (RR's)
+ * v || I | C | R | TF | SD | <- new state (child policy's)
* ===++====+=====+=====+======+======+
* I || I | C | R | [I] | [I] |
* ---++----+-----+-----+------+------+
@@ -1776,52 +1740,51 @@ void XdsLb::UpdateConnectivityStateFromRoundRobinPolicyLocked(
* SD || NA | NA | NA | NA | NA | (*)
* ---++----+-----+-----+------+------+
*
- * A [STATE] indicates that the old RR policy is kept. In those cases, STATE
- * is the current state of grpclb, which is left untouched.
+ * A [STATE] indicates that the old child policy is kept. In those cases,
+ * STATE is the current state of xds, which is left untouched.
*
* In summary, if the new state is TRANSIENT_FAILURE or SHUTDOWN, stick to
- * the previous RR instance.
+ * the previous child policy instance.
*
* Note that the status is never updated to SHUTDOWN as a result of calling
* this function. Only glb_shutdown() has the power to set that state.
*
* (*) This function mustn't be called during shutting down. */
GPR_ASSERT(curr_glb_state != GRPC_CHANNEL_SHUTDOWN);
- switch (rr_connectivity_state_) {
+ switch (child_connectivity_state_) {
case GRPC_CHANNEL_TRANSIENT_FAILURE:
case GRPC_CHANNEL_SHUTDOWN:
- GPR_ASSERT(rr_state_error != GRPC_ERROR_NONE);
+ GPR_ASSERT(child_state_error != GRPC_ERROR_NONE);
break;
case GRPC_CHANNEL_IDLE:
case GRPC_CHANNEL_CONNECTING:
case GRPC_CHANNEL_READY:
- GPR_ASSERT(rr_state_error == GRPC_ERROR_NONE);
+ GPR_ASSERT(child_state_error == GRPC_ERROR_NONE);
}
if (grpc_lb_xds_trace.enabled()) {
- gpr_log(
- GPR_INFO,
- "[xdslb %p] Setting grpclb's state to %s from new RR policy %p state.",
- this, grpc_connectivity_state_name(rr_connectivity_state_),
- rr_policy_.get());
+ gpr_log(GPR_INFO,
+ "[xdslb %p] Setting xds's state to %s from child policy %p state.",
+ this, grpc_connectivity_state_name(child_connectivity_state_),
+ child_policy_.get());
}
- grpc_connectivity_state_set(&state_tracker_, rr_connectivity_state_,
- rr_state_error,
+ grpc_connectivity_state_set(&state_tracker_, child_connectivity_state_,
+ child_state_error,
"update_lb_connectivity_status_locked");
}
-void XdsLb::OnRoundRobinConnectivityChangedLocked(void* arg,
- grpc_error* error) {
+void XdsLb::OnChildPolicyConnectivityChangedLocked(void* arg,
+ grpc_error* error) {
XdsLb* xdslb_policy = static_cast<XdsLb*>(arg);
if (xdslb_policy->shutting_down_) {
- xdslb_policy->Unref(DEBUG_LOCATION, "on_rr_connectivity_changed");
+ xdslb_policy->Unref(DEBUG_LOCATION, "on_child_connectivity_changed");
return;
}
- xdslb_policy->UpdateConnectivityStateFromRoundRobinPolicyLocked(
+ xdslb_policy->UpdateConnectivityStateFromChildPolicyLocked(
GRPC_ERROR_REF(error));
- // Resubscribe. Reuse the "on_rr_connectivity_changed" ref.
- xdslb_policy->rr_policy_->NotifyOnStateChangeLocked(
- &xdslb_policy->rr_connectivity_state_,
- &xdslb_policy->on_rr_connectivity_changed_);
+ // Resubscribe. Reuse the "on_child_connectivity_changed" ref.
+ xdslb_policy->child_policy_->NotifyOnStateChangeLocked(
+ &xdslb_policy->child_connectivity_state_,
+ &xdslb_policy->on_child_connectivity_changed_);
}
//
diff --git a/src/core/ext/filters/client_channel/lb_policy_factory.h b/src/core/ext/filters/client_channel/lb_policy_factory.h
index 62bdbf2689..a59deadb26 100644
--- a/src/core/ext/filters/client_channel/lb_policy_factory.h
+++ b/src/core/ext/filters/client_channel/lb_policy_factory.h
@@ -25,7 +25,7 @@
#include "src/core/ext/filters/client_channel/client_channel_factory.h"
#include "src/core/ext/filters/client_channel/lb_policy.h"
-#include "src/core/ext/filters/client_channel/uri_parser.h"
+#include "src/core/lib/uri/uri_parser.h"
//
// representation of an LB address
diff --git a/src/core/ext/filters/client_channel/lb_policy_registry.cc b/src/core/ext/filters/client_channel/lb_policy_registry.cc
index d651b1120d..ad459c9c8c 100644
--- a/src/core/ext/filters/client_channel/lb_policy_registry.cc
+++ b/src/core/ext/filters/client_channel/lb_policy_registry.cc
@@ -94,4 +94,9 @@ LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
return factory->CreateLoadBalancingPolicy(args);
}
+bool LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(const char* name) {
+ GPR_ASSERT(g_state != nullptr);
+ return g_state->GetLoadBalancingPolicyFactory(name) != nullptr;
+}
+
} // namespace grpc_core
diff --git a/src/core/ext/filters/client_channel/lb_policy_registry.h b/src/core/ext/filters/client_channel/lb_policy_registry.h
index 2e9bb061ed..338f7c9f69 100644
--- a/src/core/ext/filters/client_channel/lb_policy_registry.h
+++ b/src/core/ext/filters/client_channel/lb_policy_registry.h
@@ -47,6 +47,10 @@ class LoadBalancingPolicyRegistry {
/// Creates an LB policy of the type specified by \a name.
static OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
const char* name, const LoadBalancingPolicy::Args& args);
+
+ /// Returns true if the LB policy factory specified by \a name exists in this
+ /// registry.
+ static bool LoadBalancingPolicyExists(const char* name);
};
} // namespace grpc_core
diff --git a/src/core/ext/filters/client_channel/method_params.cc b/src/core/ext/filters/client_channel/method_params.cc
deleted file mode 100644
index 1f116bb67d..0000000000
--- a/src/core/ext/filters/client_channel/method_params.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- *
- * Copyright 2015 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#include <grpc/support/port_platform.h>
-
-#include <stdio.h>
-#include <string.h>
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-#include <grpc/support/string_util.h>
-
-#include "src/core/ext/filters/client_channel/method_params.h"
-#include "src/core/lib/channel/status_util.h"
-#include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gprpp/memory.h"
-
-// As per the retry design, we do not allow more than 5 retry attempts.
-#define MAX_MAX_RETRY_ATTEMPTS 5
-
-namespace grpc_core {
-namespace internal {
-
-namespace {
-
-bool ParseWaitForReady(
- grpc_json* field, ClientChannelMethodParams::WaitForReady* wait_for_ready) {
- if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) {
- return false;
- }
- *wait_for_ready = field->type == GRPC_JSON_TRUE
- ? ClientChannelMethodParams::WAIT_FOR_READY_TRUE
- : ClientChannelMethodParams::WAIT_FOR_READY_FALSE;
- return true;
-}
-
-// Parses a JSON field of the form generated for a google.proto.Duration
-// proto message, as per:
-// https://developers.google.com/protocol-buffers/docs/proto3#json
-bool ParseDuration(grpc_json* field, grpc_millis* duration) {
- if (field->type != GRPC_JSON_STRING) return false;
- size_t len = strlen(field->value);
- if (field->value[len - 1] != 's') return false;
- UniquePtr<char> buf(gpr_strdup(field->value));
- *(buf.get() + len - 1) = '\0'; // Remove trailing 's'.
- char* decimal_point = strchr(buf.get(), '.');
- int nanos = 0;
- if (decimal_point != nullptr) {
- *decimal_point = '\0';
- nanos = gpr_parse_nonnegative_int(decimal_point + 1);
- if (nanos == -1) {
- return false;
- }
- int num_digits = static_cast<int>(strlen(decimal_point + 1));
- if (num_digits > 9) { // We don't accept greater precision than nanos.
- return false;
- }
- for (int i = 0; i < (9 - num_digits); ++i) {
- nanos *= 10;
- }
- }
- int seconds =
- decimal_point == buf.get() ? 0 : gpr_parse_nonnegative_int(buf.get());
- if (seconds == -1) return false;
- *duration = seconds * GPR_MS_PER_SEC + nanos / GPR_NS_PER_MS;
- return true;
-}
-
-UniquePtr<ClientChannelMethodParams::RetryPolicy> ParseRetryPolicy(
- grpc_json* field) {
- auto retry_policy = MakeUnique<ClientChannelMethodParams::RetryPolicy>();
- if (field->type != GRPC_JSON_OBJECT) return nullptr;
- for (grpc_json* sub_field = field->child; sub_field != nullptr;
- sub_field = sub_field->next) {
- if (sub_field->key == nullptr) return nullptr;
- if (strcmp(sub_field->key, "maxAttempts") == 0) {
- if (retry_policy->max_attempts != 0) return nullptr; // Duplicate.
- if (sub_field->type != GRPC_JSON_NUMBER) return nullptr;
- retry_policy->max_attempts = gpr_parse_nonnegative_int(sub_field->value);
- if (retry_policy->max_attempts <= 1) return nullptr;
- if (retry_policy->max_attempts > MAX_MAX_RETRY_ATTEMPTS) {
- gpr_log(GPR_ERROR,
- "service config: clamped retryPolicy.maxAttempts at %d",
- MAX_MAX_RETRY_ATTEMPTS);
- retry_policy->max_attempts = MAX_MAX_RETRY_ATTEMPTS;
- }
- } else if (strcmp(sub_field->key, "initialBackoff") == 0) {
- if (retry_policy->initial_backoff > 0) return nullptr; // Duplicate.
- if (!ParseDuration(sub_field, &retry_policy->initial_backoff)) {
- return nullptr;
- }
- if (retry_policy->initial_backoff == 0) return nullptr;
- } else if (strcmp(sub_field->key, "maxBackoff") == 0) {
- if (retry_policy->max_backoff > 0) return nullptr; // Duplicate.
- if (!ParseDuration(sub_field, &retry_policy->max_backoff)) {
- return nullptr;
- }
- if (retry_policy->max_backoff == 0) return nullptr;
- } else if (strcmp(sub_field->key, "backoffMultiplier") == 0) {
- if (retry_policy->backoff_multiplier != 0) return nullptr; // Duplicate.
- if (sub_field->type != GRPC_JSON_NUMBER) return nullptr;
- if (sscanf(sub_field->value, "%f", &retry_policy->backoff_multiplier) !=
- 1) {
- return nullptr;
- }
- if (retry_policy->backoff_multiplier <= 0) return nullptr;
- } else if (strcmp(sub_field->key, "retryableStatusCodes") == 0) {
- if (!retry_policy->retryable_status_codes.Empty()) {
- return nullptr; // Duplicate.
- }
- if (sub_field->type != GRPC_JSON_ARRAY) return nullptr;
- for (grpc_json* element = sub_field->child; element != nullptr;
- element = element->next) {
- if (element->type != GRPC_JSON_STRING) return nullptr;
- grpc_status_code status;
- if (!grpc_status_code_from_string(element->value, &status)) {
- return nullptr;
- }
- retry_policy->retryable_status_codes.Add(status);
- }
- if (retry_policy->retryable_status_codes.Empty()) return nullptr;
- }
- }
- // Make sure required fields are set.
- if (retry_policy->max_attempts == 0 || retry_policy->initial_backoff == 0 ||
- retry_policy->max_backoff == 0 || retry_policy->backoff_multiplier == 0 ||
- retry_policy->retryable_status_codes.Empty()) {
- return nullptr;
- }
- return retry_policy;
-}
-
-} // namespace
-
-RefCountedPtr<ClientChannelMethodParams>
-ClientChannelMethodParams::CreateFromJson(const grpc_json* json) {
- RefCountedPtr<ClientChannelMethodParams> method_params =
- MakeRefCounted<ClientChannelMethodParams>();
- for (grpc_json* field = json->child; field != nullptr; field = field->next) {
- if (field->key == nullptr) continue;
- if (strcmp(field->key, "waitForReady") == 0) {
- if (method_params->wait_for_ready_ != WAIT_FOR_READY_UNSET) {
- return nullptr; // Duplicate.
- }
- if (!ParseWaitForReady(field, &method_params->wait_for_ready_)) {
- return nullptr;
- }
- } else if (strcmp(field->key, "timeout") == 0) {
- if (method_params->timeout_ > 0) return nullptr; // Duplicate.
- if (!ParseDuration(field, &method_params->timeout_)) return nullptr;
- } else if (strcmp(field->key, "retryPolicy") == 0) {
- if (method_params->retry_policy_ != nullptr) {
- return nullptr; // Duplicate.
- }
- method_params->retry_policy_ = ParseRetryPolicy(field);
- if (method_params->retry_policy_ == nullptr) return nullptr;
- }
- }
- return method_params;
-}
-
-} // namespace internal
-} // namespace grpc_core
diff --git a/src/core/ext/filters/client_channel/method_params.h b/src/core/ext/filters/client_channel/method_params.h
deleted file mode 100644
index a31d360f17..0000000000
--- a/src/core/ext/filters/client_channel/method_params.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- *
- * Copyright 2015 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_METHOD_PARAMS_H
-#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_METHOD_PARAMS_H
-
-#include <grpc/support/port_platform.h>
-
-#include "src/core/lib/channel/status_util.h"
-#include "src/core/lib/gprpp/ref_counted.h"
-#include "src/core/lib/gprpp/ref_counted_ptr.h"
-#include "src/core/lib/iomgr/exec_ctx.h" // for grpc_millis
-#include "src/core/lib/json/json.h"
-
-namespace grpc_core {
-namespace internal {
-
-class ClientChannelMethodParams : public RefCounted<ClientChannelMethodParams> {
- public:
- enum WaitForReady {
- WAIT_FOR_READY_UNSET = 0,
- WAIT_FOR_READY_FALSE,
- WAIT_FOR_READY_TRUE
- };
-
- struct RetryPolicy {
- int max_attempts = 0;
- grpc_millis initial_backoff = 0;
- grpc_millis max_backoff = 0;
- float backoff_multiplier = 0;
- StatusCodeSet retryable_status_codes;
- };
-
- /// Creates a method_parameters object from \a json.
- /// Intended for use with ServiceConfig::CreateMethodConfigTable().
- static RefCountedPtr<ClientChannelMethodParams> CreateFromJson(
- const grpc_json* json);
-
- grpc_millis timeout() const { return timeout_; }
- WaitForReady wait_for_ready() const { return wait_for_ready_; }
- const RetryPolicy* retry_policy() const { return retry_policy_.get(); }
-
- private:
- // So New() can call our private ctor.
- template <typename T, typename... Args>
- friend T* grpc_core::New(Args&&... args);
-
- // So Delete() can call our private dtor.
- template <typename T>
- friend void grpc_core::Delete(T*);
-
- ClientChannelMethodParams() {}
- virtual ~ClientChannelMethodParams() {}
-
- grpc_millis timeout_ = 0;
- WaitForReady wait_for_ready_ = WAIT_FOR_READY_UNSET;
- UniquePtr<RetryPolicy> retry_policy_;
-};
-
-} // namespace internal
-} // namespace grpc_core
-
-#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_METHOD_PARAMS_H */
diff --git a/src/core/ext/filters/client_channel/parse_address.h b/src/core/ext/filters/client_channel/parse_address.h
index c2af0e6c49..5c050a2333 100644
--- a/src/core/ext/filters/client_channel/parse_address.h
+++ b/src/core/ext/filters/client_channel/parse_address.h
@@ -23,8 +23,8 @@
#include <stddef.h>
-#include "src/core/ext/filters/client_channel/uri_parser.h"
#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/uri/uri_parser.h"
/** Populate \a resolved_addr from \a uri, whose path is expected to contain a
* unix socket path. Returns true upon success. */
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
index 01796ca08f..90bc88961d 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
+++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
@@ -120,6 +120,8 @@ class AresDnsResolver : public Resolver {
grpc_lb_addresses* lb_addresses_ = nullptr;
/// currently resolving service config
char* service_config_json_ = nullptr;
+ // has shutdown been initiated
+ bool shutdown_initiated_ = false;
};
AresDnsResolver::AresDnsResolver(const ResolverArgs& args)
@@ -197,6 +199,7 @@ void AresDnsResolver::ResetBackoffLocked() {
}
void AresDnsResolver::ShutdownLocked() {
+ shutdown_initiated_ = true;
if (have_next_resolution_timer_) {
grpc_timer_cancel(&next_resolution_timer_);
}
@@ -213,9 +216,13 @@ void AresDnsResolver::ShutdownLocked() {
void AresDnsResolver::OnNextResolutionLocked(void* arg, grpc_error* error) {
AresDnsResolver* r = static_cast<AresDnsResolver*>(arg);
+ GRPC_CARES_TRACE_LOG(
+ "%p re-resolution timer fired. error: %s. shutdown_initiated_: %d", r,
+ grpc_error_string(error), r->shutdown_initiated_);
r->have_next_resolution_timer_ = false;
- if (error == GRPC_ERROR_NONE) {
+ if (error == GRPC_ERROR_NONE && !r->shutdown_initiated_) {
if (!r->resolving_) {
+ GRPC_CARES_TRACE_LOG("%p start resolving due to re-resolution timer", r);
r->StartResolvingLocked();
}
}
@@ -301,13 +308,12 @@ void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) {
gpr_free(r->pending_request_);
r->pending_request_ = nullptr;
if (r->lb_addresses_ != nullptr) {
- static const char* args_to_remove[2];
+ static const char* args_to_remove[1];
size_t num_args_to_remove = 0;
- grpc_arg new_args[3];
+ grpc_arg args_to_add[2];
size_t num_args_to_add = 0;
- new_args[num_args_to_add++] =
+ args_to_add[num_args_to_add++] =
grpc_lb_addresses_create_channel_arg(r->lb_addresses_);
- grpc_core::UniquePtr<grpc_core::ServiceConfig> service_config;
char* service_config_string = nullptr;
if (r->service_config_json_ != nullptr) {
service_config_string = ChooseServiceConfig(r->service_config_json_);
@@ -316,31 +322,19 @@ void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) {
gpr_log(GPR_INFO, "selected service config choice: %s",
service_config_string);
args_to_remove[num_args_to_remove++] = GRPC_ARG_SERVICE_CONFIG;
- new_args[num_args_to_add++] = grpc_channel_arg_string_create(
+ args_to_add[num_args_to_add++] = grpc_channel_arg_string_create(
(char*)GRPC_ARG_SERVICE_CONFIG, service_config_string);
- service_config =
- grpc_core::ServiceConfig::Create(service_config_string);
- if (service_config != nullptr) {
- const char* lb_policy_name =
- service_config->GetLoadBalancingPolicyName();
- if (lb_policy_name != nullptr) {
- args_to_remove[num_args_to_remove++] = GRPC_ARG_LB_POLICY_NAME;
- new_args[num_args_to_add++] = grpc_channel_arg_string_create(
- (char*)GRPC_ARG_LB_POLICY_NAME,
- const_cast<char*>(lb_policy_name));
- }
- }
}
}
result = grpc_channel_args_copy_and_add_and_remove(
- r->channel_args_, args_to_remove, num_args_to_remove, new_args,
+ r->channel_args_, args_to_remove, num_args_to_remove, args_to_add,
num_args_to_add);
gpr_free(service_config_string);
grpc_lb_addresses_destroy(r->lb_addresses_);
// Reset backoff state so that we start from the beginning when the
// next request gets triggered.
r->backoff_.Reset();
- } else {
+ } else if (!r->shutdown_initiated_) {
const char* msg = grpc_error_string(error);
gpr_log(GPR_DEBUG, "dns resolution failed: %s", msg);
grpc_millis next_try = r->backoff_.NextAttemptTime();
diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
index 144ac24a56..3aa690bea4 100644
--- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
+++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
@@ -103,7 +103,7 @@ void FakeResolver::NextLocked(grpc_channel_args** target_result,
}
void FakeResolver::RequestReresolutionLocked() {
- if (reresolution_results_ != nullptr) {
+ if (reresolution_results_ != nullptr || return_failure_) {
grpc_channel_args_destroy(next_results_);
next_results_ = grpc_channel_args_copy(reresolution_results_);
MaybeFinishNextLocked();
@@ -141,6 +141,7 @@ struct SetResponseClosureArg {
grpc_closure set_response_closure;
FakeResolverResponseGenerator* generator;
grpc_channel_args* response;
+ bool immediate = true;
};
void FakeResolverResponseGenerator::SetResponseLocked(void* arg,
@@ -194,7 +195,7 @@ void FakeResolverResponseGenerator::SetFailureLocked(void* arg,
SetResponseClosureArg* closure_arg = static_cast<SetResponseClosureArg*>(arg);
FakeResolver* resolver = closure_arg->generator->resolver_;
resolver->return_failure_ = true;
- resolver->MaybeFinishNextLocked();
+ if (closure_arg->immediate) resolver->MaybeFinishNextLocked();
Delete(closure_arg);
}
@@ -209,6 +210,18 @@ void FakeResolverResponseGenerator::SetFailure() {
GRPC_ERROR_NONE);
}
+void FakeResolverResponseGenerator::SetFailureOnReresolution() {
+ GPR_ASSERT(resolver_ != nullptr);
+ SetResponseClosureArg* closure_arg = New<SetResponseClosureArg>();
+ closure_arg->generator = this;
+ closure_arg->immediate = false;
+ GRPC_CLOSURE_SCHED(
+ GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetFailureLocked,
+ closure_arg,
+ grpc_combiner_scheduler(resolver_->combiner())),
+ GRPC_ERROR_NONE);
+}
+
namespace {
static void* response_generator_arg_copy(void* p) {
diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h
index 708eaf1147..7f69059351 100644
--- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h
+++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h
@@ -20,9 +20,9 @@
#include <grpc/support/port_platform.h>
#include "src/core/ext/filters/client_channel/lb_policy_factory.h"
-#include "src/core/ext/filters/client_channel/uri_parser.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/uri/uri_parser.h"
#define GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR \
"grpc.fake_resolver.response_generator"
@@ -61,6 +61,10 @@ class FakeResolverResponseGenerator
// returning a null result with no error).
void SetFailure();
+ // Same as SetFailure(), but instead of returning the error
+ // immediately, waits for the next call to RequestReresolutionLocked().
+ void SetFailureOnReresolution();
+
// Returns a channel arg containing \a generator.
static grpc_arg MakeChannelArg(FakeResolverResponseGenerator* generator);
diff --git a/src/core/ext/filters/client_channel/resolver_factory.h b/src/core/ext/filters/client_channel/resolver_factory.h
index ee3cfeeb9b..d891ef62e1 100644
--- a/src/core/ext/filters/client_channel/resolver_factory.h
+++ b/src/core/ext/filters/client_channel/resolver_factory.h
@@ -24,11 +24,11 @@
#include <grpc/support/string_util.h>
#include "src/core/ext/filters/client_channel/resolver.h"
-#include "src/core/ext/filters/client_channel/uri_parser.h"
#include "src/core/lib/gprpp/abstract.h"
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/uri/uri_parser.h"
namespace grpc_core {
diff --git a/src/core/ext/filters/client_channel/resolver_result_parsing.cc b/src/core/ext/filters/client_channel/resolver_result_parsing.cc
new file mode 100644
index 0000000000..82a26ace63
--- /dev/null
+++ b/src/core/ext/filters/client_channel/resolver_result_parsing.cc
@@ -0,0 +1,384 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/resolver_result_parsing.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/lib/channel/status_util.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/memory.h"
+
+// As per the retry design, we do not allow more than 5 retry attempts.
+#define MAX_MAX_RETRY_ATTEMPTS 5
+
+namespace grpc_core {
+namespace internal {
+
+namespace {
+
+// Converts string format from JSON to proto.
+grpc_core::UniquePtr<char> ConvertCamelToSnake(const char* camel) {
+ const size_t size = strlen(camel);
+ char* snake = static_cast<char*>(gpr_malloc(size * 2));
+ size_t j = 0;
+ for (size_t i = 0; i < size; ++i) {
+ if (isupper(camel[i])) {
+ snake[j++] = '_';
+ snake[j++] = tolower(camel[i]);
+ } else {
+ snake[j++] = camel[i];
+ }
+ }
+ snake[j] = '\0';
+ return grpc_core::UniquePtr<char>(snake);
+}
+
+} // namespace
+
+ProcessedResolverResult::ProcessedResolverResult(
+ const grpc_channel_args* resolver_result, bool parse_retry) {
+ ProcessServiceConfig(resolver_result, parse_retry);
+ // If no LB config was found above, just find the LB policy name then.
+ if (lb_policy_config_ == nullptr) ProcessLbPolicyName(resolver_result);
+}
+
+void ProcessedResolverResult::ProcessServiceConfig(
+ const grpc_channel_args* resolver_result, bool parse_retry) {
+ const grpc_arg* channel_arg =
+ grpc_channel_args_find(resolver_result, GRPC_ARG_SERVICE_CONFIG);
+ const char* service_config_json = grpc_channel_arg_get_string(channel_arg);
+ if (service_config_json != nullptr) {
+ service_config_json_.reset(gpr_strdup(service_config_json));
+ service_config_ = grpc_core::ServiceConfig::Create(service_config_json);
+ if (service_config_ != nullptr) {
+ if (parse_retry) {
+ channel_arg =
+ grpc_channel_args_find(resolver_result, GRPC_ARG_SERVER_URI);
+ const char* server_uri = grpc_channel_arg_get_string(channel_arg);
+ GPR_ASSERT(server_uri != nullptr);
+ grpc_uri* uri = grpc_uri_parse(server_uri, true);
+ GPR_ASSERT(uri->path[0] != '\0');
+ server_name_ = uri->path[0] == '/' ? uri->path + 1 : uri->path;
+ service_config_->ParseGlobalParams(ParseServiceConfig, this);
+ grpc_uri_destroy(uri);
+ } else {
+ service_config_->ParseGlobalParams(ParseServiceConfig, this);
+ }
+ method_params_table_ = service_config_->CreateMethodConfigTable(
+ ClientChannelMethodParams::CreateFromJson);
+ }
+ }
+}
+
+void ProcessedResolverResult::ProcessLbPolicyName(
+ const grpc_channel_args* resolver_result) {
+ const char* lb_policy_name = nullptr;
+ // Prefer the LB policy name found in the service config. Note that this is
+ // checking the deprecated loadBalancingPolicy field, rather than the new
+ // loadBalancingConfig field.
+ if (service_config_ != nullptr) {
+ lb_policy_name = service_config_->GetLoadBalancingPolicyName();
+ }
+ // Otherwise, find the LB policy name set by the client API.
+ if (lb_policy_name == nullptr) {
+ const grpc_arg* channel_arg =
+ grpc_channel_args_find(resolver_result, GRPC_ARG_LB_POLICY_NAME);
+ lb_policy_name = grpc_channel_arg_get_string(channel_arg);
+ }
+ // Special case: If at least one balancer address is present, we use
+ // the grpclb policy, regardless of what the resolver has returned.
+ const grpc_arg* channel_arg =
+ grpc_channel_args_find(resolver_result, GRPC_ARG_LB_ADDRESSES);
+ if (channel_arg != nullptr && channel_arg->type == GRPC_ARG_POINTER) {
+ grpc_lb_addresses* addresses =
+ static_cast<grpc_lb_addresses*>(channel_arg->value.pointer.p);
+ if (grpc_lb_addresses_contains_balancer_address(*addresses)) {
+ if (lb_policy_name != nullptr &&
+ gpr_stricmp(lb_policy_name, "grpclb") != 0) {
+ gpr_log(GPR_INFO,
+ "resolver requested LB policy %s but provided at least one "
+ "balancer address -- forcing use of grpclb LB policy",
+ lb_policy_name);
+ }
+ lb_policy_name = "grpclb";
+ }
+ }
+ // Use pick_first if nothing was specified and we didn't select grpclb
+ // above.
+ if (lb_policy_name == nullptr) lb_policy_name = "pick_first";
+ lb_policy_name_.reset(gpr_strdup(lb_policy_name));
+}
+
+void ProcessedResolverResult::ParseServiceConfig(
+ const grpc_json* field, ProcessedResolverResult* parsing_state) {
+ parsing_state->ParseLbConfigFromServiceConfig(field);
+ if (parsing_state->server_name_ != nullptr) {
+ parsing_state->ParseRetryThrottleParamsFromServiceConfig(field);
+ }
+}
+
+void ProcessedResolverResult::ParseLbConfigFromServiceConfig(
+ const grpc_json* field) {
+ if (lb_policy_config_ != nullptr) return; // Already found.
+ // Find the LB config global parameter.
+ if (field->key == nullptr || strcmp(field->key, "loadBalancingConfig") != 0 ||
+ field->type != GRPC_JSON_ARRAY) {
+ return; // Not valid lb config array.
+ }
+ // Find the first LB policy that this client supports.
+ for (grpc_json* lb_config = field->child; lb_config != nullptr;
+ lb_config = lb_config->next) {
+ if (lb_config->type != GRPC_JSON_OBJECT) return;
+ // Find the policy object.
+ grpc_json* policy = nullptr;
+ for (grpc_json* field = lb_config->child; field != nullptr;
+ field = field->next) {
+ if (field->key == nullptr || strcmp(field->key, "policy") != 0 ||
+ field->type != GRPC_JSON_OBJECT) {
+ return;
+ }
+ if (policy != nullptr) return; // Duplicate.
+ policy = field;
+ }
+ // Find the specific policy content since the policy object is of type
+ // "oneof".
+ grpc_json* policy_content = nullptr;
+ for (grpc_json* field = policy->child; field != nullptr;
+ field = field->next) {
+ if (field->key == nullptr || field->type != GRPC_JSON_OBJECT) return;
+ if (policy_content != nullptr) return; // Violate "oneof" type.
+ policy_content = field;
+ }
+ grpc_core::UniquePtr<char> lb_policy_name =
+ ConvertCamelToSnake(policy_content->key);
+ if (!grpc_core::LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(
+ lb_policy_name.get())) {
+ continue;
+ }
+ lb_policy_name_ = std::move(lb_policy_name);
+ lb_policy_config_ = policy_content->child;
+ return;
+ }
+}
+
+void ProcessedResolverResult::ParseRetryThrottleParamsFromServiceConfig(
+ const grpc_json* field) {
+ if (strcmp(field->key, "retryThrottling") == 0) {
+ if (retry_throttle_data_ != nullptr) return; // Duplicate.
+ if (field->type != GRPC_JSON_OBJECT) return;
+ int max_milli_tokens = 0;
+ int milli_token_ratio = 0;
+ for (grpc_json* sub_field = field->child; sub_field != nullptr;
+ sub_field = sub_field->next) {
+ if (sub_field->key == nullptr) return;
+ if (strcmp(sub_field->key, "maxTokens") == 0) {
+ if (max_milli_tokens != 0) return; // Duplicate.
+ if (sub_field->type != GRPC_JSON_NUMBER) return;
+ max_milli_tokens = gpr_parse_nonnegative_int(sub_field->value);
+ if (max_milli_tokens == -1) return;
+ max_milli_tokens *= 1000;
+ } else if (strcmp(sub_field->key, "tokenRatio") == 0) {
+ if (milli_token_ratio != 0) return; // Duplicate.
+ if (sub_field->type != GRPC_JSON_NUMBER) return;
+ // We support up to 3 decimal digits.
+ size_t whole_len = strlen(sub_field->value);
+ uint32_t multiplier = 1;
+ uint32_t decimal_value = 0;
+ const char* decimal_point = strchr(sub_field->value, '.');
+ if (decimal_point != nullptr) {
+ whole_len = static_cast<size_t>(decimal_point - sub_field->value);
+ multiplier = 1000;
+ size_t decimal_len = strlen(decimal_point + 1);
+ if (decimal_len > 3) decimal_len = 3;
+ if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len,
+ &decimal_value)) {
+ return;
+ }
+ uint32_t decimal_multiplier = 1;
+ for (size_t i = 0; i < (3 - decimal_len); ++i) {
+ decimal_multiplier *= 10;
+ }
+ decimal_value *= decimal_multiplier;
+ }
+ uint32_t whole_value;
+ if (!gpr_parse_bytes_to_uint32(sub_field->value, whole_len,
+ &whole_value)) {
+ return;
+ }
+ milli_token_ratio =
+ static_cast<int>((whole_value * multiplier) + decimal_value);
+ if (milli_token_ratio <= 0) return;
+ }
+ }
+ retry_throttle_data_ =
+ grpc_core::internal::ServerRetryThrottleMap::GetDataForServer(
+ server_name_, max_milli_tokens, milli_token_ratio);
+ }
+}
+
+namespace {
+
+bool ParseWaitForReady(
+ grpc_json* field, ClientChannelMethodParams::WaitForReady* wait_for_ready) {
+ if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) {
+ return false;
+ }
+ *wait_for_ready = field->type == GRPC_JSON_TRUE
+ ? ClientChannelMethodParams::WAIT_FOR_READY_TRUE
+ : ClientChannelMethodParams::WAIT_FOR_READY_FALSE;
+ return true;
+}
+
+// Parses a JSON field of the form generated for a google.proto.Duration
+// proto message, as per:
+// https://developers.google.com/protocol-buffers/docs/proto3#json
+bool ParseDuration(grpc_json* field, grpc_millis* duration) {
+ if (field->type != GRPC_JSON_STRING) return false;
+ size_t len = strlen(field->value);
+ if (field->value[len - 1] != 's') return false;
+ UniquePtr<char> buf(gpr_strdup(field->value));
+ *(buf.get() + len - 1) = '\0'; // Remove trailing 's'.
+ char* decimal_point = strchr(buf.get(), '.');
+ int nanos = 0;
+ if (decimal_point != nullptr) {
+ *decimal_point = '\0';
+ nanos = gpr_parse_nonnegative_int(decimal_point + 1);
+ if (nanos == -1) {
+ return false;
+ }
+ int num_digits = static_cast<int>(strlen(decimal_point + 1));
+ if (num_digits > 9) { // We don't accept greater precision than nanos.
+ return false;
+ }
+ for (int i = 0; i < (9 - num_digits); ++i) {
+ nanos *= 10;
+ }
+ }
+ int seconds =
+ decimal_point == buf.get() ? 0 : gpr_parse_nonnegative_int(buf.get());
+ if (seconds == -1) return false;
+ *duration = seconds * GPR_MS_PER_SEC + nanos / GPR_NS_PER_MS;
+ return true;
+}
+
+UniquePtr<ClientChannelMethodParams::RetryPolicy> ParseRetryPolicy(
+ grpc_json* field) {
+ auto retry_policy = MakeUnique<ClientChannelMethodParams::RetryPolicy>();
+ if (field->type != GRPC_JSON_OBJECT) return nullptr;
+ for (grpc_json* sub_field = field->child; sub_field != nullptr;
+ sub_field = sub_field->next) {
+ if (sub_field->key == nullptr) return nullptr;
+ if (strcmp(sub_field->key, "maxAttempts") == 0) {
+ if (retry_policy->max_attempts != 0) return nullptr; // Duplicate.
+ if (sub_field->type != GRPC_JSON_NUMBER) return nullptr;
+ retry_policy->max_attempts = gpr_parse_nonnegative_int(sub_field->value);
+ if (retry_policy->max_attempts <= 1) return nullptr;
+ if (retry_policy->max_attempts > MAX_MAX_RETRY_ATTEMPTS) {
+ gpr_log(GPR_ERROR,
+ "service config: clamped retryPolicy.maxAttempts at %d",
+ MAX_MAX_RETRY_ATTEMPTS);
+ retry_policy->max_attempts = MAX_MAX_RETRY_ATTEMPTS;
+ }
+ } else if (strcmp(sub_field->key, "initialBackoff") == 0) {
+ if (retry_policy->initial_backoff > 0) return nullptr; // Duplicate.
+ if (!ParseDuration(sub_field, &retry_policy->initial_backoff)) {
+ return nullptr;
+ }
+ if (retry_policy->initial_backoff == 0) return nullptr;
+ } else if (strcmp(sub_field->key, "maxBackoff") == 0) {
+ if (retry_policy->max_backoff > 0) return nullptr; // Duplicate.
+ if (!ParseDuration(sub_field, &retry_policy->max_backoff)) {
+ return nullptr;
+ }
+ if (retry_policy->max_backoff == 0) return nullptr;
+ } else if (strcmp(sub_field->key, "backoffMultiplier") == 0) {
+ if (retry_policy->backoff_multiplier != 0) return nullptr; // Duplicate.
+ if (sub_field->type != GRPC_JSON_NUMBER) return nullptr;
+ if (sscanf(sub_field->value, "%f", &retry_policy->backoff_multiplier) !=
+ 1) {
+ return nullptr;
+ }
+ if (retry_policy->backoff_multiplier <= 0) return nullptr;
+ } else if (strcmp(sub_field->key, "retryableStatusCodes") == 0) {
+ if (!retry_policy->retryable_status_codes.Empty()) {
+ return nullptr; // Duplicate.
+ }
+ if (sub_field->type != GRPC_JSON_ARRAY) return nullptr;
+ for (grpc_json* element = sub_field->child; element != nullptr;
+ element = element->next) {
+ if (element->type != GRPC_JSON_STRING) return nullptr;
+ grpc_status_code status;
+ if (!grpc_status_code_from_string(element->value, &status)) {
+ return nullptr;
+ }
+ retry_policy->retryable_status_codes.Add(status);
+ }
+ if (retry_policy->retryable_status_codes.Empty()) return nullptr;
+ }
+ }
+ // Make sure required fields are set.
+ if (retry_policy->max_attempts == 0 || retry_policy->initial_backoff == 0 ||
+ retry_policy->max_backoff == 0 || retry_policy->backoff_multiplier == 0 ||
+ retry_policy->retryable_status_codes.Empty()) {
+ return nullptr;
+ }
+ return retry_policy;
+}
+
+} // namespace
+
+RefCountedPtr<ClientChannelMethodParams>
+ClientChannelMethodParams::CreateFromJson(const grpc_json* json) {
+ RefCountedPtr<ClientChannelMethodParams> method_params =
+ MakeRefCounted<ClientChannelMethodParams>();
+ for (grpc_json* field = json->child; field != nullptr; field = field->next) {
+ if (field->key == nullptr) continue;
+ if (strcmp(field->key, "waitForReady") == 0) {
+ if (method_params->wait_for_ready_ != WAIT_FOR_READY_UNSET) {
+ return nullptr; // Duplicate.
+ }
+ if (!ParseWaitForReady(field, &method_params->wait_for_ready_)) {
+ return nullptr;
+ }
+ } else if (strcmp(field->key, "timeout") == 0) {
+ if (method_params->timeout_ > 0) return nullptr; // Duplicate.
+ if (!ParseDuration(field, &method_params->timeout_)) return nullptr;
+ } else if (strcmp(field->key, "retryPolicy") == 0) {
+ if (method_params->retry_policy_ != nullptr) {
+ return nullptr; // Duplicate.
+ }
+ method_params->retry_policy_ = ParseRetryPolicy(field);
+ if (method_params->retry_policy_ == nullptr) return nullptr;
+ }
+ }
+ return method_params;
+}
+
+} // namespace internal
+} // namespace grpc_core
diff --git a/src/core/ext/filters/client_channel/resolver_result_parsing.h b/src/core/ext/filters/client_channel/resolver_result_parsing.h
new file mode 100644
index 0000000000..f1fb7406bc
--- /dev/null
+++ b/src/core/ext/filters/client_channel/resolver_result_parsing.h
@@ -0,0 +1,146 @@
+/*
+ *
+ * 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 GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/retry_throttle.h"
+#include "src/core/lib/channel/status_util.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/exec_ctx.h" // for grpc_millis
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/slice/slice_hash_table.h"
+#include "src/core/lib/transport/service_config.h"
+
+namespace grpc_core {
+namespace internal {
+
+class ClientChannelMethodParams;
+
+// A table mapping from a method name to its method parameters.
+typedef grpc_core::SliceHashTable<
+ grpc_core::RefCountedPtr<ClientChannelMethodParams>>
+ ClientChannelMethodParamsTable;
+
+// A container of processed fields from the resolver result. Simplifies the
+// usage of resolver result.
+class ProcessedResolverResult {
+ public:
+ // Processes the resolver result and populates the relative members
+ // for later consumption. Tries to parse retry parameters only if parse_retry
+ // is true.
+ ProcessedResolverResult(const grpc_channel_args* resolver_result,
+ bool parse_retry);
+
+ // Getters. Any managed object's ownership is transferred.
+ grpc_core::UniquePtr<char> service_config_json() {
+ return std::move(service_config_json_);
+ }
+ grpc_core::RefCountedPtr<ServerRetryThrottleData> retry_throttle_data() {
+ return std::move(retry_throttle_data_);
+ }
+ grpc_core::RefCountedPtr<ClientChannelMethodParamsTable>
+ method_params_table() {
+ return std::move(method_params_table_);
+ }
+ grpc_core::UniquePtr<char> lb_policy_name() {
+ return std::move(lb_policy_name_);
+ }
+ grpc_json* lb_policy_config() { return lb_policy_config_; }
+
+ private:
+ // Finds the service config; extracts LB config and (maybe) retry throttle
+ // params from it.
+ void ProcessServiceConfig(const grpc_channel_args* resolver_result,
+ bool parse_retry);
+
+ // Finds the LB policy name (when no LB config was found).
+ void ProcessLbPolicyName(const grpc_channel_args* resolver_result);
+
+ // Parses the service config. Intended to be used by
+ // ServiceConfig::ParseGlobalParams.
+ static void ParseServiceConfig(const grpc_json* field,
+ ProcessedResolverResult* parsing_state);
+ // Parses the LB config from service config.
+ void ParseLbConfigFromServiceConfig(const grpc_json* field);
+ // Parses the retry throttle parameters from service config.
+ void ParseRetryThrottleParamsFromServiceConfig(const grpc_json* field);
+
+ // Service config.
+ grpc_core::UniquePtr<char> service_config_json_;
+ grpc_core::UniquePtr<grpc_core::ServiceConfig> service_config_;
+ // LB policy.
+ grpc_json* lb_policy_config_ = nullptr;
+ grpc_core::UniquePtr<char> lb_policy_name_;
+ // Retry throttle data.
+ char* server_name_ = nullptr;
+ grpc_core::RefCountedPtr<ServerRetryThrottleData> retry_throttle_data_;
+ // Method params table.
+ grpc_core::RefCountedPtr<ClientChannelMethodParamsTable> method_params_table_;
+};
+
+// The parameters of a method.
+class ClientChannelMethodParams : public RefCounted<ClientChannelMethodParams> {
+ public:
+ enum WaitForReady {
+ WAIT_FOR_READY_UNSET = 0,
+ WAIT_FOR_READY_FALSE,
+ WAIT_FOR_READY_TRUE
+ };
+
+ struct RetryPolicy {
+ int max_attempts = 0;
+ grpc_millis initial_backoff = 0;
+ grpc_millis max_backoff = 0;
+ float backoff_multiplier = 0;
+ StatusCodeSet retryable_status_codes;
+ };
+
+ /// Creates a method_parameters object from \a json.
+ /// Intended for use with ServiceConfig::CreateMethodConfigTable().
+ static RefCountedPtr<ClientChannelMethodParams> CreateFromJson(
+ const grpc_json* json);
+
+ grpc_millis timeout() const { return timeout_; }
+ WaitForReady wait_for_ready() const { return wait_for_ready_; }
+ const RetryPolicy* retry_policy() const { return retry_policy_.get(); }
+
+ private:
+ // So New() can call our private ctor.
+ template <typename T, typename... Args>
+ friend T* grpc_core::New(Args&&... args);
+
+ // So Delete() can call our private dtor.
+ template <typename T>
+ friend void grpc_core::Delete(T*);
+
+ ClientChannelMethodParams() {}
+ virtual ~ClientChannelMethodParams() {}
+
+ grpc_millis timeout_ = 0;
+ WaitForReady wait_for_ready_ = WAIT_FOR_READY_UNSET;
+ UniquePtr<RetryPolicy> retry_policy_;
+};
+
+} // namespace internal
+} // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H */
diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc
index e4c6efe862..a56db0201b 100644
--- a/src/core/ext/filters/client_channel/subchannel.cc
+++ b/src/core/ext/filters/client_channel/subchannel.cc
@@ -34,7 +34,6 @@
#include "src/core/ext/filters/client_channel/parse_address.h"
#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
#include "src/core/ext/filters/client_channel/subchannel_index.h"
-#include "src/core/ext/filters/client_channel/uri_parser.h"
#include "src/core/lib/backoff/backoff.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/connected_channel.h"
@@ -54,6 +53,7 @@
#include "src/core/lib/transport/error_utils.h"
#include "src/core/lib/transport/service_config.h"
#include "src/core/lib/transport/status_metadata.h"
+#include "src/core/lib/uri/uri_parser.h"
#define INTERNAL_REF_BITS 16
#define STRONG_REF_MASK (~(gpr_atm)((1 << INTERNAL_REF_BITS) - 1))
@@ -162,12 +162,16 @@ struct grpc_subchannel {
};
struct grpc_subchannel_call {
+ grpc_subchannel_call(grpc_core::ConnectedSubchannel* connection,
+ const grpc_core::ConnectedSubchannel::CallArgs& args)
+ : connection(connection), deadline(args.deadline) {}
+
grpc_core::ConnectedSubchannel* connection;
- grpc_closure* schedule_closure_after_destroy;
+ grpc_closure* schedule_closure_after_destroy = nullptr;
// state needed to support channelz interception of recv trailing metadata.
grpc_closure recv_trailing_metadata_ready;
grpc_closure* original_recv_trailing_metadata;
- grpc_metadata_batch* recv_trailing_metadata;
+ grpc_metadata_batch* recv_trailing_metadata = nullptr;
grpc_millis deadline;
};
@@ -905,6 +909,7 @@ static void subchannel_call_destroy(void* call, grpc_error* error) {
grpc_call_stack_destroy(SUBCHANNEL_CALL_TO_CALL_STACK(c), nullptr,
c->schedule_closure_after_destroy);
connection->Unref(DEBUG_LOCATION, "subchannel_call");
+ c->~grpc_subchannel_call();
}
void grpc_subchannel_call_set_cleanup_closure(grpc_subchannel_call* call,
@@ -1102,14 +1107,12 @@ grpc_error* ConnectedSubchannel::CreateCall(const CallArgs& args,
grpc_subchannel_call** call) {
const size_t allocation_size =
GetInitialCallSizeEstimate(args.parent_data_size);
- *call = static_cast<grpc_subchannel_call*>(
- gpr_arena_alloc(args.arena, allocation_size));
+ *call = new (gpr_arena_alloc(args.arena, allocation_size))
+ grpc_subchannel_call(this, args);
grpc_call_stack* callstk = SUBCHANNEL_CALL_TO_CALL_STACK(*call);
RefCountedPtr<ConnectedSubchannel> connection =
Ref(DEBUG_LOCATION, "subchannel_call");
connection.release(); // Ref is passed to the grpc_subchannel_call object.
- (*call)->connection = this;
- (*call)->deadline = args.deadline;
const grpc_call_element_args call_args = {
callstk, /* call_stack */
nullptr, /* server_transport_data */
@@ -1128,6 +1131,9 @@ grpc_error* ConnectedSubchannel::CreateCall(const CallArgs& args,
return error;
}
grpc_call_stack_set_pollset_or_pollset_set(callstk, args.pollent);
+ if (channelz_subchannel_ != nullptr) {
+ channelz_subchannel_->RecordCallStarted();
+ }
return GRPC_ERROR_NONE;
}
diff --git a/src/core/ext/filters/deadline/deadline_filter.cc b/src/core/ext/filters/deadline/deadline_filter.cc
index d23ad67ad5..b4cb07f0f9 100644
--- a/src/core/ext/filters/deadline/deadline_filter.cc
+++ b/src/core/ext/filters/deadline/deadline_filter.cc
@@ -27,6 +27,7 @@
#include <grpc/support/time.h>
#include "src/core/lib/channel/channel_stack_builder.h"
+#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/iomgr/timer.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/surface/channel_init.h"
@@ -152,7 +153,11 @@ static void inject_recv_trailing_metadata_ready(
// Callback and associated state for starting the timer after call stack
// initialization has been completed.
struct start_timer_after_init_state {
- bool in_call_combiner;
+ start_timer_after_init_state(grpc_call_element* elem, grpc_millis deadline)
+ : elem(elem), deadline(deadline) {}
+ ~start_timer_after_init_state() { start_timer_if_needed(elem, deadline); }
+
+ bool in_call_combiner = false;
grpc_call_element* elem;
grpc_millis deadline;
grpc_closure closure;
@@ -171,20 +176,16 @@ static void start_timer_after_init(void* arg, grpc_error* error) {
"scheduling deadline timer");
return;
}
- start_timer_if_needed(state->elem, state->deadline);
- gpr_free(state);
+ grpc_core::Delete(state);
GRPC_CALL_COMBINER_STOP(deadline_state->call_combiner,
"done scheduling deadline timer");
}
-void grpc_deadline_state_init(grpc_call_element* elem,
- grpc_call_stack* call_stack,
- grpc_call_combiner* call_combiner,
- grpc_millis deadline) {
- grpc_deadline_state* deadline_state =
- static_cast<grpc_deadline_state*>(elem->call_data);
- deadline_state->call_stack = call_stack;
- deadline_state->call_combiner = call_combiner;
+grpc_deadline_state::grpc_deadline_state(grpc_call_element* elem,
+ grpc_call_stack* call_stack,
+ grpc_call_combiner* call_combiner,
+ grpc_millis deadline)
+ : call_stack(call_stack), call_combiner(call_combiner) {
// Deadline will always be infinite on servers, so the timer will only be
// set on clients with a finite deadline.
if (deadline != GRPC_MILLIS_INF_FUTURE) {
@@ -196,21 +197,14 @@ void grpc_deadline_state_init(grpc_call_element* elem,
// create a closure to start the timer, and we schedule that closure
// to be run after call stack initialization is done.
struct start_timer_after_init_state* state =
- static_cast<struct start_timer_after_init_state*>(
- gpr_zalloc(sizeof(*state)));
- state->elem = elem;
- state->deadline = deadline;
+ grpc_core::New<start_timer_after_init_state>(elem, deadline);
GRPC_CLOSURE_INIT(&state->closure, start_timer_after_init, state,
grpc_schedule_on_exec_ctx);
GRPC_CLOSURE_SCHED(&state->closure, GRPC_ERROR_NONE);
}
}
-void grpc_deadline_state_destroy(grpc_call_element* elem) {
- grpc_deadline_state* deadline_state =
- static_cast<grpc_deadline_state*>(elem->call_data);
- cancel_timer_if_needed(deadline_state);
-}
+grpc_deadline_state::~grpc_deadline_state() { cancel_timer_if_needed(this); }
void grpc_deadline_state_reset(grpc_call_element* elem,
grpc_millis new_deadline) {
@@ -269,8 +263,8 @@ typedef struct server_call_data {
// Constructor for call_data. Used for both client and server filters.
static grpc_error* init_call_elem(grpc_call_element* elem,
const grpc_call_element_args* args) {
- grpc_deadline_state_init(elem, args->call_stack, args->call_combiner,
- args->deadline);
+ new (elem->call_data) grpc_deadline_state(
+ elem, args->call_stack, args->call_combiner, args->deadline);
return GRPC_ERROR_NONE;
}
@@ -278,7 +272,9 @@ static grpc_error* init_call_elem(grpc_call_element* elem,
static void destroy_call_elem(grpc_call_element* elem,
const grpc_call_final_info* final_info,
grpc_closure* ignored) {
- grpc_deadline_state_destroy(elem);
+ grpc_deadline_state* deadline_state =
+ static_cast<grpc_deadline_state*>(elem->call_data);
+ deadline_state->~grpc_deadline_state();
}
// Method for starting a call op for client filter.
diff --git a/src/core/ext/filters/deadline/deadline_filter.h b/src/core/ext/filters/deadline/deadline_filter.h
index 1d797f445a..e37032999c 100644
--- a/src/core/ext/filters/deadline/deadline_filter.h
+++ b/src/core/ext/filters/deadline/deadline_filter.h
@@ -22,19 +22,23 @@
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/iomgr/timer.h"
-typedef enum grpc_deadline_timer_state {
+enum grpc_deadline_timer_state {
GRPC_DEADLINE_STATE_INITIAL,
GRPC_DEADLINE_STATE_PENDING,
GRPC_DEADLINE_STATE_FINISHED
-} grpc_deadline_timer_state;
+};
// State used for filters that enforce call deadlines.
// Must be the first field in the filter's call_data.
-typedef struct grpc_deadline_state {
+struct grpc_deadline_state {
+ grpc_deadline_state(grpc_call_element* elem, grpc_call_stack* call_stack,
+ grpc_call_combiner* call_combiner, grpc_millis deadline);
+ ~grpc_deadline_state();
+
// We take a reference to the call stack for the timer callback.
grpc_call_stack* call_stack;
grpc_call_combiner* call_combiner;
- grpc_deadline_timer_state timer_state;
+ grpc_deadline_timer_state timer_state = GRPC_DEADLINE_STATE_INITIAL;
grpc_timer timer;
grpc_closure timer_callback;
// Closure to invoke when we receive trailing metadata.
@@ -43,21 +47,13 @@ typedef struct grpc_deadline_state {
// The original recv_trailing_metadata_ready closure, which we chain to
// after our own closure is invoked.
grpc_closure* original_recv_trailing_metadata_ready;
-} grpc_deadline_state;
+};
//
// NOTE: All of these functions require that the first field in
// elem->call_data is a grpc_deadline_state.
//
-// assumes elem->call_data is zero'd
-void grpc_deadline_state_init(grpc_call_element* elem,
- grpc_call_stack* call_stack,
- grpc_call_combiner* call_combiner,
- grpc_millis deadline);
-
-void grpc_deadline_state_destroy(grpc_call_element* elem);
-
// Cancels the existing timer and starts a new one with new_deadline.
//
// Note: It is generally safe to call this with an earlier deadline
diff --git a/src/core/ext/filters/http/client/http_client_filter.cc b/src/core/ext/filters/http/client/http_client_filter.cc
index cd459e47cd..bf9a01f659 100644
--- a/src/core/ext/filters/http/client/http_client_filter.cc
+++ b/src/core/ext/filters/http/client/http_client_filter.cc
@@ -37,10 +37,31 @@
#define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1
/* default maximum size of payload eligable for GET request */
-static const size_t kMaxPayloadSizeForGet = 2048;
+static constexpr size_t kMaxPayloadSizeForGet = 2048;
+
+static void recv_initial_metadata_ready(void* user_data, grpc_error* error);
+static void recv_trailing_metadata_ready(void* user_data, grpc_error* error);
+static void on_send_message_next_done(void* arg, grpc_error* error);
+static void send_message_on_complete(void* arg, grpc_error* error);
namespace {
struct call_data {
+ call_data(grpc_call_element* elem, const grpc_call_element_args& args)
+ : call_combiner(args.call_combiner) {
+ GRPC_CLOSURE_INIT(&recv_initial_metadata_ready,
+ ::recv_initial_metadata_ready, elem,
+ grpc_schedule_on_exec_ctx);
+ GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
+ ::recv_trailing_metadata_ready, elem,
+ grpc_schedule_on_exec_ctx);
+ GRPC_CLOSURE_INIT(&on_send_message_next_done, ::on_send_message_next_done,
+ elem, grpc_schedule_on_exec_ctx);
+ GRPC_CLOSURE_INIT(&send_message_on_complete, ::send_message_on_complete,
+ elem, grpc_schedule_on_exec_ctx);
+ }
+
+ ~call_data() { GRPC_ERROR_UNREF(recv_initial_metadata_error); }
+
grpc_call_combiner* call_combiner;
// State for handling send_initial_metadata ops.
grpc_linked_mdelem method;
@@ -51,18 +72,18 @@ struct call_data {
grpc_linked_mdelem user_agent;
// State for handling recv_initial_metadata ops.
grpc_metadata_batch* recv_initial_metadata;
- grpc_error* recv_initial_metadata_error;
- grpc_closure* original_recv_initial_metadata_ready;
+ grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
+ grpc_closure* original_recv_initial_metadata_ready = nullptr;
grpc_closure recv_initial_metadata_ready;
// State for handling recv_trailing_metadata ops.
grpc_metadata_batch* recv_trailing_metadata;
grpc_closure* original_recv_trailing_metadata_ready;
grpc_closure recv_trailing_metadata_ready;
- grpc_error* recv_trailing_metadata_error;
- bool seen_recv_trailing_metadata_ready;
+ grpc_error* recv_trailing_metadata_error = GRPC_ERROR_NONE;
+ bool seen_recv_trailing_metadata_ready = false;
// State for handling send_message ops.
grpc_transport_stream_op_batch* send_message_batch;
- size_t send_message_bytes_read;
+ size_t send_message_bytes_read = 0;
grpc_core::ManualConstructor<grpc_core::ByteStreamCache> send_message_cache;
grpc_core::ManualConstructor<grpc_core::ByteStreamCache::CachingByteStream>
send_message_caching_stream;
@@ -442,18 +463,7 @@ done:
/* Constructor for call_data */
static grpc_error* init_call_elem(grpc_call_element* elem,
const grpc_call_element_args* args) {
- call_data* calld = static_cast<call_data*>(elem->call_data);
- calld->call_combiner = args->call_combiner;
- GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready,
- recv_initial_metadata_ready, elem,
- grpc_schedule_on_exec_ctx);
- GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready,
- recv_trailing_metadata_ready, elem,
- grpc_schedule_on_exec_ctx);
- GRPC_CLOSURE_INIT(&calld->send_message_on_complete, send_message_on_complete,
- elem, grpc_schedule_on_exec_ctx);
- GRPC_CLOSURE_INIT(&calld->on_send_message_next_done,
- on_send_message_next_done, elem, grpc_schedule_on_exec_ctx);
+ new (elem->call_data) call_data(elem, *args);
return GRPC_ERROR_NONE;
}
@@ -462,7 +472,7 @@ static void destroy_call_elem(grpc_call_element* elem,
const grpc_call_final_info* final_info,
grpc_closure* ignored) {
call_data* calld = static_cast<call_data*>(elem->call_data);
- GRPC_ERROR_UNREF(calld->recv_initial_metadata_error);
+ calld->~call_data();
}
static grpc_mdelem scheme_from_args(const grpc_channel_args* args) {
diff --git a/src/core/ext/filters/http/message_compress/message_compress_filter.cc b/src/core/ext/filters/http/message_compress/message_compress_filter.cc
index 933fe3c77b..9c8c8d9e18 100644
--- a/src/core/ext/filters/http/message_compress/message_compress_filter.cc
+++ b/src/core/ext/filters/http/message_compress/message_compress_filter.cc
@@ -39,6 +39,10 @@
#include "src/core/lib/surface/call.h"
#include "src/core/lib/transport/static_metadata.h"
+static void start_send_message_batch(void* arg, grpc_error* unused);
+static void send_message_on_complete(void* arg, grpc_error* error);
+static void on_send_message_next_done(void* arg, grpc_error* error);
+
namespace {
enum initial_metadata_state {
// Initial metadata not yet seen.
@@ -50,6 +54,23 @@ enum initial_metadata_state {
};
struct call_data {
+ call_data(grpc_call_element* elem, const grpc_call_element_args& args)
+ : call_combiner(args.call_combiner) {
+ GRPC_CLOSURE_INIT(&start_send_message_batch_in_call_combiner,
+ start_send_message_batch, elem,
+ grpc_schedule_on_exec_ctx);
+ grpc_slice_buffer_init(&slices);
+ GRPC_CLOSURE_INIT(&send_message_on_complete, ::send_message_on_complete,
+ elem, grpc_schedule_on_exec_ctx);
+ GRPC_CLOSURE_INIT(&on_send_message_next_done, ::on_send_message_next_done,
+ elem, grpc_schedule_on_exec_ctx);
+ }
+
+ ~call_data() {
+ grpc_slice_buffer_destroy_internal(&slices);
+ GRPC_ERROR_UNREF(cancel_error);
+ }
+
grpc_call_combiner* call_combiner;
grpc_linked_mdelem compression_algorithm_storage;
grpc_linked_mdelem stream_compression_algorithm_storage;
@@ -57,11 +78,12 @@ struct call_data {
grpc_linked_mdelem accept_stream_encoding_storage;
/** Compression algorithm we'll try to use. It may be given by incoming
* metadata, or by the channel's default compression settings. */
- grpc_message_compression_algorithm message_compression_algorithm;
- initial_metadata_state send_initial_metadata_state;
- grpc_error* cancel_error;
+ grpc_message_compression_algorithm message_compression_algorithm =
+ GRPC_MESSAGE_COMPRESS_NONE;
+ initial_metadata_state send_initial_metadata_state = INITIAL_METADATA_UNSEEN;
+ grpc_error* cancel_error = GRPC_ERROR_NONE;
grpc_closure start_send_message_batch_in_call_combiner;
- grpc_transport_stream_op_batch* send_message_batch;
+ grpc_transport_stream_op_batch* send_message_batch = nullptr;
grpc_slice_buffer slices; /**< Buffers up input slices to be compressed */
grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream>
replacement_stream;
@@ -424,16 +446,7 @@ static void compress_start_transport_stream_op_batch(
/* Constructor for call_data */
static grpc_error* init_call_elem(grpc_call_element* elem,
const grpc_call_element_args* args) {
- call_data* calld = static_cast<call_data*>(elem->call_data);
- calld->call_combiner = args->call_combiner;
- calld->cancel_error = GRPC_ERROR_NONE;
- grpc_slice_buffer_init(&calld->slices);
- GRPC_CLOSURE_INIT(&calld->start_send_message_batch_in_call_combiner,
- start_send_message_batch, elem, grpc_schedule_on_exec_ctx);
- GRPC_CLOSURE_INIT(&calld->on_send_message_next_done,
- on_send_message_next_done, elem, grpc_schedule_on_exec_ctx);
- GRPC_CLOSURE_INIT(&calld->send_message_on_complete, send_message_on_complete,
- elem, grpc_schedule_on_exec_ctx);
+ new (elem->call_data) call_data(elem, *args);
return GRPC_ERROR_NONE;
}
@@ -442,8 +455,7 @@ static void destroy_call_elem(grpc_call_element* elem,
const grpc_call_final_info* final_info,
grpc_closure* ignored) {
call_data* calld = static_cast<call_data*>(elem->call_data);
- grpc_slice_buffer_destroy_internal(&calld->slices);
- GRPC_ERROR_UNREF(calld->cancel_error);
+ calld->~call_data();
}
/* Constructor for channel_data */
diff --git a/src/core/ext/filters/http/server/http_server_filter.cc b/src/core/ext/filters/http/server/http_server_filter.cc
index 436ea09d94..ce1be8370c 100644
--- a/src/core/ext/filters/http/server/http_server_filter.cc
+++ b/src/core/ext/filters/http/server/http_server_filter.cc
@@ -35,9 +35,32 @@
#define EXPECTED_CONTENT_TYPE "application/grpc"
#define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1
+static void hs_recv_initial_metadata_ready(void* user_data, grpc_error* err);
+static void hs_recv_trailing_metadata_ready(void* user_data, grpc_error* err);
+static void hs_recv_message_ready(void* user_data, grpc_error* err);
+
namespace {
struct call_data {
+ call_data(grpc_call_element* elem, const grpc_call_element_args& args)
+ : call_combiner(args.call_combiner) {
+ GRPC_CLOSURE_INIT(&recv_initial_metadata_ready,
+ hs_recv_initial_metadata_ready, elem,
+ grpc_schedule_on_exec_ctx);
+ GRPC_CLOSURE_INIT(&recv_message_ready, hs_recv_message_ready, elem,
+ grpc_schedule_on_exec_ctx);
+ GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
+ hs_recv_trailing_metadata_ready, elem,
+ grpc_schedule_on_exec_ctx);
+ }
+
+ ~call_data() {
+ GRPC_ERROR_UNREF(recv_initial_metadata_ready_error);
+ if (have_read_stream) {
+ read_stream->Orphan();
+ }
+ }
+
grpc_call_combiner* call_combiner;
// Outgoing headers to add to send_initial_metadata.
@@ -47,27 +70,27 @@ struct call_data {
// If we see the recv_message contents in the GET query string, we
// store it here.
grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> read_stream;
- bool have_read_stream;
+ bool have_read_stream = false;
// State for intercepting recv_initial_metadata.
grpc_closure recv_initial_metadata_ready;
- grpc_error* recv_initial_metadata_ready_error;
+ grpc_error* recv_initial_metadata_ready_error = GRPC_ERROR_NONE;
grpc_closure* original_recv_initial_metadata_ready;
- grpc_metadata_batch* recv_initial_metadata;
+ grpc_metadata_batch* recv_initial_metadata = nullptr;
uint32_t* recv_initial_metadata_flags;
- bool seen_recv_initial_metadata_ready;
+ bool seen_recv_initial_metadata_ready = false;
// State for intercepting recv_message.
grpc_closure* original_recv_message_ready;
grpc_closure recv_message_ready;
grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message;
- bool seen_recv_message_ready;
+ bool seen_recv_message_ready = false;
// State for intercepting recv_trailing_metadata
grpc_closure recv_trailing_metadata_ready;
grpc_closure* original_recv_trailing_metadata_ready;
grpc_error* recv_trailing_metadata_ready_error;
- bool seen_recv_trailing_metadata_ready;
+ bool seen_recv_trailing_metadata_ready = false;
};
struct channel_data {
@@ -431,16 +454,7 @@ static void hs_start_transport_stream_op_batch(
/* Constructor for call_data */
static grpc_error* hs_init_call_elem(grpc_call_element* elem,
const grpc_call_element_args* args) {
- call_data* calld = static_cast<call_data*>(elem->call_data);
- calld->call_combiner = args->call_combiner;
- GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready,
- hs_recv_initial_metadata_ready, elem,
- grpc_schedule_on_exec_ctx);
- GRPC_CLOSURE_INIT(&calld->recv_message_ready, hs_recv_message_ready, elem,
- grpc_schedule_on_exec_ctx);
- GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready,
- hs_recv_trailing_metadata_ready, elem,
- grpc_schedule_on_exec_ctx);
+ new (elem->call_data) call_data(elem, *args);
return GRPC_ERROR_NONE;
}
@@ -449,10 +463,7 @@ static void hs_destroy_call_elem(grpc_call_element* elem,
const grpc_call_final_info* final_info,
grpc_closure* ignored) {
call_data* calld = static_cast<call_data*>(elem->call_data);
- GRPC_ERROR_UNREF(calld->recv_initial_metadata_ready_error);
- if (calld->have_read_stream) {
- calld->read_stream->Orphan();
- }
+ calld->~call_data();
}
/* Constructor for channel_data */
diff --git a/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc b/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc
index 8ac34c629f..6a7231ff7d 100644
--- a/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc
+++ b/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc
@@ -25,7 +25,6 @@
#include <grpc/support/string_util.h>
#include "src/core/ext/filters/client_channel/parse_address.h"
-#include "src/core/ext/filters/client_channel/uri_parser.h"
#include "src/core/ext/filters/load_reporting/registered_opencensus_objects.h"
#include "src/core/ext/filters/load_reporting/server_load_reporting_filter.h"
#include "src/core/lib/channel/channel_args.h"
@@ -36,6 +35,7 @@
#include "src/core/lib/security/context/security_context.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/surface/call.h"
+#include "src/core/lib/uri/uri_parser.h"
namespace grpc {
diff --git a/src/core/ext/filters/message_size/message_size_filter.cc b/src/core/ext/filters/message_size/message_size_filter.cc
index 2d3b16d992..94d6942aa4 100644
--- a/src/core/ext/filters/message_size/message_size_filter.cc
+++ b/src/core/ext/filters/message_size/message_size_filter.cc
@@ -90,9 +90,53 @@ RefCountedPtr<MessageSizeLimits> MessageSizeLimits::CreateFromJson(
} // namespace
} // namespace grpc_core
+static void recv_message_ready(void* user_data, grpc_error* error);
+static void recv_trailing_metadata_ready(void* user_data, grpc_error* error);
+
namespace {
+struct channel_data {
+ message_size_limits limits;
+ // Maps path names to refcounted_message_size_limits structs.
+ grpc_core::RefCountedPtr<grpc_core::SliceHashTable<
+ grpc_core::RefCountedPtr<grpc_core::MessageSizeLimits>>>
+ method_limit_table;
+};
+
struct call_data {
+ call_data(grpc_call_element* elem, const channel_data& chand,
+ const grpc_call_element_args& args)
+ : call_combiner(args.call_combiner), limits(chand.limits) {
+ GRPC_CLOSURE_INIT(&recv_message_ready, ::recv_message_ready, elem,
+ grpc_schedule_on_exec_ctx);
+ GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
+ ::recv_trailing_metadata_ready, elem,
+ grpc_schedule_on_exec_ctx);
+ // Get max sizes from channel data, then merge in per-method config values.
+ // Note: Per-method config is only available on the client, so we
+ // apply the max request size to the send limit and the max response
+ // size to the receive limit.
+ if (chand.method_limit_table != nullptr) {
+ grpc_core::RefCountedPtr<grpc_core::MessageSizeLimits> limits =
+ grpc_core::ServiceConfig::MethodConfigTableLookup(
+ *chand.method_limit_table, args.path);
+ if (limits != nullptr) {
+ if (limits->limits().max_send_size >= 0 &&
+ (limits->limits().max_send_size < this->limits.max_send_size ||
+ this->limits.max_send_size < 0)) {
+ this->limits.max_send_size = limits->limits().max_send_size;
+ }
+ if (limits->limits().max_recv_size >= 0 &&
+ (limits->limits().max_recv_size < this->limits.max_recv_size ||
+ this->limits.max_recv_size < 0)) {
+ this->limits.max_recv_size = limits->limits().max_recv_size;
+ }
+ }
+ }
+ }
+
+ ~call_data() { GRPC_ERROR_UNREF(error); }
+
grpc_call_combiner* call_combiner;
message_size_limits limits;
// Receive closures are chained: we inject this closure as the
@@ -101,25 +145,17 @@ struct call_data {
grpc_closure recv_message_ready;
grpc_closure recv_trailing_metadata_ready;
// The error caused by a message that is too large, or GRPC_ERROR_NONE
- grpc_error* error;
+ grpc_error* error = GRPC_ERROR_NONE;
// Used by recv_message_ready.
- grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message;
+ grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message = nullptr;
// Original recv_message_ready callback, invoked after our own.
- grpc_closure* next_recv_message_ready;
+ grpc_closure* next_recv_message_ready = nullptr;
// Original recv_trailing_metadata callback, invoked after our own.
grpc_closure* original_recv_trailing_metadata_ready;
- bool seen_recv_trailing_metadata;
+ bool seen_recv_trailing_metadata = false;
grpc_error* recv_trailing_metadata_error;
};
-struct channel_data {
- message_size_limits limits;
- // Maps path names to refcounted_message_size_limits structs.
- grpc_core::RefCountedPtr<grpc_core::SliceHashTable<
- grpc_core::RefCountedPtr<grpc_core::MessageSizeLimits>>>
- method_limit_table;
-};
-
} // namespace
// Callback invoked when we receive a message. Here we check the max
@@ -228,38 +264,7 @@ static void start_transport_stream_op_batch(
static grpc_error* init_call_elem(grpc_call_element* elem,
const grpc_call_element_args* args) {
channel_data* chand = static_cast<channel_data*>(elem->channel_data);
- call_data* calld = static_cast<call_data*>(elem->call_data);
- calld->call_combiner = args->call_combiner;
- calld->next_recv_message_ready = nullptr;
- calld->original_recv_trailing_metadata_ready = nullptr;
- calld->error = GRPC_ERROR_NONE;
- GRPC_CLOSURE_INIT(&calld->recv_message_ready, recv_message_ready, elem,
- grpc_schedule_on_exec_ctx);
- GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready,
- recv_trailing_metadata_ready, elem,
- grpc_schedule_on_exec_ctx);
- // Get max sizes from channel data, then merge in per-method config values.
- // Note: Per-method config is only available on the client, so we
- // apply the max request size to the send limit and the max response
- // size to the receive limit.
- calld->limits = chand->limits;
- if (chand->method_limit_table != nullptr) {
- grpc_core::RefCountedPtr<grpc_core::MessageSizeLimits> limits =
- grpc_core::ServiceConfig::MethodConfigTableLookup(
- *chand->method_limit_table, args->path);
- if (limits != nullptr) {
- if (limits->limits().max_send_size >= 0 &&
- (limits->limits().max_send_size < calld->limits.max_send_size ||
- calld->limits.max_send_size < 0)) {
- calld->limits.max_send_size = limits->limits().max_send_size;
- }
- if (limits->limits().max_recv_size >= 0 &&
- (limits->limits().max_recv_size < calld->limits.max_recv_size ||
- calld->limits.max_recv_size < 0)) {
- calld->limits.max_recv_size = limits->limits().max_recv_size;
- }
- }
- }
+ new (elem->call_data) call_data(elem, *chand, *args);
return GRPC_ERROR_NONE;
}
@@ -268,7 +273,7 @@ static void destroy_call_elem(grpc_call_element* elem,
const grpc_call_final_info* final_info,
grpc_closure* ignored) {
call_data* calld = (call_data*)elem->call_data;
- GRPC_ERROR_UNREF(calld->error);
+ calld->~call_data();
}
static int default_size(const grpc_channel_args* args,
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/client/secure/secure_channel_create.cc b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc
index 5ce73a95d7..e73eee4353 100644
--- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc
+++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc
@@ -27,7 +27,6 @@
#include "src/core/ext/filters/client_channel/client_channel.h"
#include "src/core/ext/filters/client_channel/resolver_registry.h"
-#include "src/core/ext/filters/client_channel/uri_parser.h"
#include "src/core/ext/transport/chttp2/client/chttp2_connector.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gprpp/memory.h"
@@ -39,6 +38,7 @@
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/uri/uri_parser.h"
static void client_channel_factory_ref(
grpc_client_channel_factory* cc_factory) {}
diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.cc b/src/core/ext/transport/chttp2/server/chttp2_server.cc
index 07b304b320..33d2b22aa5 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);
@@ -338,9 +366,10 @@ grpc_error* grpc_chttp2_server_add_port(grpc_server* server, const char* addr,
grpc_resolved_addresses_destroy(resolved);
arg = grpc_channel_args_find(args, GRPC_ARG_ENABLE_CHANNELZ);
- if (grpc_channel_arg_get_bool(arg, false)) {
+ if (grpc_channel_arg_get_bool(arg, GRPC_ENABLE_CHANNELZ_DEFAULT)) {
state->channelz_listen_socket =
- grpc_core::MakeRefCounted<grpc_core::channelz::ListenSocketNode>();
+ grpc_core::MakeRefCounted<grpc_core::channelz::ListenSocketNode>(
+ grpc_core::UniquePtr<char>(gpr_strdup(addr)));
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..978ecd59e4 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
@@ -53,6 +53,7 @@
#include "src/core/lib/transport/timeout_encoding.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/transport/transport_impl.h"
+#include "src/core/lib/uri/uri_parser.h"
#define DEFAULT_CONNECTION_WINDOW_TARGET (1024 * 1024)
#define MAX_WINDOW 0x7fffffffu
@@ -153,53 +154,52 @@ bool g_flow_control_enabled = true;
/*******************************************************************************
* CONSTRUCTION/DESTRUCTION/REFCOUNTING
*/
-
-static void destruct_transport(grpc_chttp2_transport* t) {
+grpc_chttp2_transport::~grpc_chttp2_transport() {
size_t i;
- if (t->channelz_socket != nullptr) {
- t->channelz_socket.reset();
+ if (channelz_socket != nullptr) {
+ channelz_socket.reset();
}
- grpc_endpoint_destroy(t->ep);
+ grpc_endpoint_destroy(ep);
- grpc_slice_buffer_destroy_internal(&t->qbuf);
+ grpc_slice_buffer_destroy_internal(&qbuf);
- grpc_slice_buffer_destroy_internal(&t->outbuf);
- grpc_chttp2_hpack_compressor_destroy(&t->hpack_compressor);
+ grpc_slice_buffer_destroy_internal(&outbuf);
+ grpc_chttp2_hpack_compressor_destroy(&hpack_compressor);
- grpc_slice_buffer_destroy_internal(&t->read_buffer);
- grpc_chttp2_hpack_parser_destroy(&t->hpack_parser);
- grpc_chttp2_goaway_parser_destroy(&t->goaway_parser);
+ grpc_slice_buffer_destroy_internal(&read_buffer);
+ grpc_chttp2_hpack_parser_destroy(&hpack_parser);
+ grpc_chttp2_goaway_parser_destroy(&goaway_parser);
for (i = 0; i < STREAM_LIST_COUNT; i++) {
- GPR_ASSERT(t->lists[i].head == nullptr);
- GPR_ASSERT(t->lists[i].tail == nullptr);
+ GPR_ASSERT(lists[i].head == nullptr);
+ GPR_ASSERT(lists[i].tail == nullptr);
}
- GRPC_ERROR_UNREF(t->goaway_error);
+ GRPC_ERROR_UNREF(goaway_error);
- GPR_ASSERT(grpc_chttp2_stream_map_size(&t->stream_map) == 0);
+ GPR_ASSERT(grpc_chttp2_stream_map_size(&stream_map) == 0);
- grpc_chttp2_stream_map_destroy(&t->stream_map);
- grpc_connectivity_state_destroy(&t->channel_callback.state_tracker);
+ grpc_chttp2_stream_map_destroy(&stream_map);
+ grpc_connectivity_state_destroy(&channel_callback.state_tracker);
- GRPC_COMBINER_UNREF(t->combiner, "chttp2_transport");
+ GRPC_COMBINER_UNREF(combiner, "chttp2_transport");
- cancel_pings(t, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed"));
+ cancel_pings(this,
+ GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed"));
- while (t->write_cb_pool) {
- grpc_chttp2_write_cb* next = t->write_cb_pool->next;
- gpr_free(t->write_cb_pool);
- t->write_cb_pool = next;
+ while (write_cb_pool) {
+ grpc_chttp2_write_cb* next = write_cb_pool->next;
+ gpr_free(write_cb_pool);
+ write_cb_pool = next;
}
- t->flow_control.Destroy();
+ flow_control.Destroy();
- GRPC_ERROR_UNREF(t->closed_with_error);
- gpr_free(t->ping_acks);
- gpr_free(t->peer_string);
- gpr_free(t);
+ GRPC_ERROR_UNREF(closed_with_error);
+ gpr_free(ping_acks);
+ gpr_free(peer_string);
}
#ifndef NDEBUG
@@ -211,7 +211,8 @@ void grpc_chttp2_unref_transport(grpc_chttp2_transport* t, const char* reason,
t, val, val - 1, reason, file, line);
}
if (!gpr_unref(&t->refs)) return;
- destruct_transport(t);
+ t->~grpc_chttp2_transport();
+ gpr_free(t);
}
void grpc_chttp2_ref_transport(grpc_chttp2_transport* t, const char* reason,
@@ -226,7 +227,8 @@ void grpc_chttp2_ref_transport(grpc_chttp2_transport* t, const char* reason,
#else
void grpc_chttp2_unref_transport(grpc_chttp2_transport* t) {
if (!gpr_unref(&t->refs)) return;
- destruct_transport(t);
+ t->~grpc_chttp2_transport();
+ gpr_free(t);
}
void grpc_chttp2_ref_transport(grpc_chttp2_transport* t) { gpr_ref(&t->refs); }
@@ -395,8 +397,12 @@ static bool read_channel_args(grpc_chttp2_transport* t,
}
}
if (channelz_enabled) {
+ // TODO(ncteisen): add an API to endpoint to query for local addr, and pass
+ // it in here, so SocketNode knows its own address.
t->channelz_socket =
- grpc_core::MakeRefCounted<grpc_core::channelz::SocketNode>();
+ grpc_core::MakeRefCounted<grpc_core::channelz::SocketNode>(
+ grpc_core::UniquePtr<char>(),
+ grpc_core::UniquePtr<char>(gpr_strdup(t->peer_string)));
}
return enable_bdp;
}
@@ -476,113 +482,97 @@ 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_chttp2_transport::grpc_chttp2_transport(
+ const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client,
+ grpc_resource_user* resource_user)
+ : ep(ep),
+ peer_string(grpc_endpoint_get_peer(ep)),
+ resource_user(resource_user),
+ combiner(grpc_combiner_create()),
+ is_client(is_client),
+ next_stream_id(is_client ? 1 : 2),
+ deframe_state(is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0) {
GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) ==
GRPC_CHTTP2_CLIENT_CONNECT_STRLEN);
-
- t->base.vtable = get_vtable();
- t->ep = ep;
+ base.vtable = get_vtable();
/* one ref is for destroy */
- gpr_ref_init(&t->refs, 1);
- t->combiner = grpc_combiner_create();
- t->peer_string = grpc_endpoint_get_peer(ep);
- t->endpoint_reading = 1;
- t->next_stream_id = is_client ? 1 : 2;
- t->is_client = is_client;
- t->deframe_state = is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0;
- t->is_first_frame = true;
- grpc_connectivity_state_init(
- &t->channel_callback.state_tracker, GRPC_CHANNEL_READY,
- is_client ? "client_transport" : "server_transport");
-
- grpc_slice_buffer_init(&t->qbuf);
- grpc_slice_buffer_init(&t->outbuf);
- grpc_chttp2_hpack_compressor_init(&t->hpack_compressor);
-
- init_transport_closures(t);
-
- t->goaway_error = GRPC_ERROR_NONE;
- grpc_chttp2_goaway_parser_init(&t->goaway_parser);
- grpc_chttp2_hpack_parser_init(&t->hpack_parser);
-
- grpc_slice_buffer_init(&t->read_buffer);
-
+ gpr_ref_init(&refs, 1);
/* 8 is a random stab in the dark as to a good initial size: it's small enough
that it shouldn't waste memory for infrequently used connections, yet
large enough that the exponential growth should happen nicely when it's
needed.
TODO(ctiller): tune this */
- grpc_chttp2_stream_map_init(&t->stream_map, 8);
+ grpc_chttp2_stream_map_init(&stream_map, 8);
+ grpc_slice_buffer_init(&read_buffer);
+ grpc_connectivity_state_init(
+ &channel_callback.state_tracker, GRPC_CHANNEL_READY,
+ is_client ? "client_transport" : "server_transport");
+ grpc_slice_buffer_init(&outbuf);
+ if (is_client) {
+ grpc_slice_buffer_add(&outbuf, grpc_slice_from_copied_string(
+ GRPC_CHTTP2_CLIENT_CONNECT_STRING));
+ }
+ grpc_chttp2_hpack_compressor_init(&hpack_compressor);
+ grpc_slice_buffer_init(&qbuf);
/* copy in initial settings to all setting sets */
size_t i;
int j;
for (i = 0; i < GRPC_CHTTP2_NUM_SETTINGS; i++) {
for (j = 0; j < GRPC_NUM_SETTING_SETS; j++) {
- t->settings[j][i] = grpc_chttp2_settings_parameters[i].default_value;
+ settings[j][i] = grpc_chttp2_settings_parameters[i].default_value;
}
}
- t->dirtied_local_settings = 1;
- /* Hack: it's common for implementations to assume 65536 bytes initial send
- window -- this should by rights be 0 */
- t->force_send_settings = 1 << GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
- t->sent_local_settings = 0;
- t->write_buffer_size = grpc_core::chttp2::kDefaultWindow;
+ grpc_chttp2_hpack_parser_init(&hpack_parser);
+ grpc_chttp2_goaway_parser_init(&goaway_parser);
- if (is_client) {
- grpc_slice_buffer_add(&t->outbuf, grpc_slice_from_copied_string(
- GRPC_CHTTP2_CLIENT_CONNECT_STRING));
- }
+ init_transport_closures(this);
/* configure http2 the way we like it */
if (is_client) {
- queue_setting_update(t, GRPC_CHTTP2_SETTINGS_ENABLE_PUSH, 0);
- queue_setting_update(t, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 0);
+ queue_setting_update(this, GRPC_CHTTP2_SETTINGS_ENABLE_PUSH, 0);
+ queue_setting_update(this, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 0);
}
- queue_setting_update(t, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
+ queue_setting_update(this, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
DEFAULT_MAX_HEADER_LIST_SIZE);
- queue_setting_update(t, GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA,
- 1);
-
- configure_transport_ping_policy(t);
- init_transport_keepalive_settings(t);
+ queue_setting_update(this,
+ GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA, 1);
- t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY;
+ configure_transport_ping_policy(this);
+ init_transport_keepalive_settings(this);
bool enable_bdp = true;
if (channel_args) {
- enable_bdp = read_channel_args(t, channel_args, is_client);
+ enable_bdp = read_channel_args(this, channel_args, is_client);
}
if (g_flow_control_enabled) {
- t->flow_control.Init<grpc_core::chttp2::TransportFlowControl>(t,
- enable_bdp);
+ flow_control.Init<grpc_core::chttp2::TransportFlowControl>(this,
+ enable_bdp);
} else {
- t->flow_control.Init<grpc_core::chttp2::TransportFlowControlDisabled>(t);
+ flow_control.Init<grpc_core::chttp2::TransportFlowControlDisabled>(this);
enable_bdp = false;
}
/* No pings allowed before receiving a header or data frame. */
- t->ping_state.pings_before_data_required = 0;
- t->ping_state.is_delayed_ping_timer_set = false;
- t->ping_state.last_ping_sent_time = GRPC_MILLIS_INF_PAST;
+ ping_state.pings_before_data_required = 0;
+ ping_state.is_delayed_ping_timer_set = false;
+ ping_state.last_ping_sent_time = GRPC_MILLIS_INF_PAST;
- t->ping_recv_state.last_ping_recv_time = GRPC_MILLIS_INF_PAST;
- t->ping_recv_state.ping_strikes = 0;
+ ping_recv_state.last_ping_recv_time = GRPC_MILLIS_INF_PAST;
+ ping_recv_state.ping_strikes = 0;
- init_keepalive_pings_if_enabled(t);
+ init_keepalive_pings_if_enabled(this);
if (enable_bdp) {
- GRPC_CHTTP2_REF_TRANSPORT(t, "bdp_ping");
- schedule_bdp_ping_locked(t);
- grpc_chttp2_act_on_flowctl_action(t->flow_control->PeriodicUpdate(), t,
+ GRPC_CHTTP2_REF_TRANSPORT(this, "bdp_ping");
+ schedule_bdp_ping_locked(this);
+ grpc_chttp2_act_on_flowctl_action(flow_control->PeriodicUpdate(), this,
nullptr);
}
- grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE);
- post_benign_reclaimer(t);
+ grpc_chttp2_initiate_write(this, GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE);
+ post_benign_reclaimer(this);
}
static void destroy_transport_locked(void* tp, grpc_error* error) {
@@ -592,6 +582,7 @@ static void destroy_transport_locked(void* tp, grpc_error* error) {
t, grpc_error_set_int(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed"),
GRPC_ERROR_INT_OCCURRED_DURING_WRITE, t->write_state));
+ // Must be the last line.
GRPC_CHTTP2_UNREF_TRANSPORT(t, "destroy");
}
@@ -676,111 +667,108 @@ void grpc_chttp2_stream_unref(grpc_chttp2_stream* s) {
}
#endif
-static int init_stream(grpc_transport* gt, grpc_stream* gs,
- grpc_stream_refcount* refcount, const void* server_data,
- gpr_arena* arena) {
- GPR_TIMER_SCOPE("init_stream", 0);
- grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
- grpc_chttp2_stream* s = reinterpret_cast<grpc_chttp2_stream*>(gs);
-
- s->t = t;
- s->refcount = refcount;
+grpc_chttp2_stream::grpc_chttp2_stream(grpc_chttp2_transport* t,
+ grpc_stream_refcount* refcount,
+ const void* server_data,
+ gpr_arena* arena)
+ : t(t), refcount(refcount), metadata_buffer{{arena}, {arena}} {
/* We reserve one 'active stream' that's dropped when the stream is
read-closed. The others are for Chttp2IncomingByteStreams that are
actively reading */
- GRPC_CHTTP2_STREAM_REF(s, "chttp2");
-
- grpc_chttp2_incoming_metadata_buffer_init(&s->metadata_buffer[0], arena);
- grpc_chttp2_incoming_metadata_buffer_init(&s->metadata_buffer[1], arena);
- grpc_chttp2_data_parser_init(&s->data_parser);
- grpc_slice_buffer_init(&s->flow_controlled_buffer);
- s->deadline = GRPC_MILLIS_INF_FUTURE;
- GRPC_CLOSURE_INIT(&s->complete_fetch_locked, complete_fetch_locked, s,
- grpc_schedule_on_exec_ctx);
- grpc_slice_buffer_init(&s->unprocessed_incoming_frames_buffer);
- s->unprocessed_incoming_frames_buffer_cached_length = 0;
- grpc_slice_buffer_init(&s->frame_storage);
- grpc_slice_buffer_init(&s->compressed_data_buffer);
- grpc_slice_buffer_init(&s->decompressed_data_buffer);
- s->pending_byte_stream = false;
- s->decompressed_header_bytes = 0;
- GRPC_CLOSURE_INIT(&s->reset_byte_stream, reset_byte_stream, s,
- grpc_combiner_scheduler(t->combiner));
-
+ GRPC_CHTTP2_STREAM_REF(this, "chttp2");
GRPC_CHTTP2_REF_TRANSPORT(t, "stream");
if (server_data) {
- s->id = static_cast<uint32_t>((uintptr_t)server_data);
- *t->accepting_stream = s;
- grpc_chttp2_stream_map_add(&t->stream_map, s->id, s);
+ id = static_cast<uint32_t>((uintptr_t)server_data);
+ *t->accepting_stream = this;
+ grpc_chttp2_stream_map_add(&t->stream_map, id, this);
post_destructive_reclaimer(t);
}
-
if (t->flow_control->flow_control_enabled()) {
- s->flow_control.Init<grpc_core::chttp2::StreamFlowControl>(
+ flow_control.Init<grpc_core::chttp2::StreamFlowControl>(
static_cast<grpc_core::chttp2::TransportFlowControl*>(
t->flow_control.get()),
- s);
+ this);
} else {
- s->flow_control.Init<grpc_core::chttp2::StreamFlowControlDisabled>();
+ flow_control.Init<grpc_core::chttp2::StreamFlowControlDisabled>();
}
- return 0;
-}
+ grpc_slice_buffer_init(&frame_storage);
+ grpc_slice_buffer_init(&unprocessed_incoming_frames_buffer);
+ grpc_slice_buffer_init(&flow_controlled_buffer);
+ grpc_slice_buffer_init(&compressed_data_buffer);
+ grpc_slice_buffer_init(&decompressed_data_buffer);
-static void destroy_stream_locked(void* sp, grpc_error* error) {
- GPR_TIMER_SCOPE("destroy_stream", 0);
- grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(sp);
- grpc_chttp2_transport* t = s->t;
+ GRPC_CLOSURE_INIT(&complete_fetch_locked, ::complete_fetch_locked, this,
+ grpc_schedule_on_exec_ctx);
+ GRPC_CLOSURE_INIT(&reset_byte_stream, ::reset_byte_stream, this,
+ grpc_combiner_scheduler(t->combiner));
+}
+grpc_chttp2_stream::~grpc_chttp2_stream() {
if (t->channelz_socket != nullptr) {
- if ((t->is_client && s->eos_received) || (!t->is_client && s->eos_sent)) {
+ if ((t->is_client && eos_received) || (!t->is_client && eos_sent)) {
t->channelz_socket->RecordStreamSucceeded();
} else {
t->channelz_socket->RecordStreamFailed();
}
}
- GPR_ASSERT((s->write_closed && s->read_closed) || s->id == 0);
- if (s->id != 0) {
- GPR_ASSERT(grpc_chttp2_stream_map_find(&t->stream_map, s->id) == nullptr);
+ GPR_ASSERT((write_closed && read_closed) || id == 0);
+ if (id != 0) {
+ GPR_ASSERT(grpc_chttp2_stream_map_find(&t->stream_map, id) == nullptr);
}
- grpc_slice_buffer_destroy_internal(&s->unprocessed_incoming_frames_buffer);
- grpc_slice_buffer_destroy_internal(&s->frame_storage);
- grpc_slice_buffer_destroy_internal(&s->compressed_data_buffer);
- grpc_slice_buffer_destroy_internal(&s->decompressed_data_buffer);
+ grpc_slice_buffer_destroy_internal(&unprocessed_incoming_frames_buffer);
+ grpc_slice_buffer_destroy_internal(&frame_storage);
+ grpc_slice_buffer_destroy_internal(&compressed_data_buffer);
+ grpc_slice_buffer_destroy_internal(&decompressed_data_buffer);
- grpc_chttp2_list_remove_stalled_by_transport(t, s);
- grpc_chttp2_list_remove_stalled_by_stream(t, s);
+ grpc_chttp2_list_remove_stalled_by_transport(t, this);
+ grpc_chttp2_list_remove_stalled_by_stream(t, this);
for (int i = 0; i < STREAM_LIST_COUNT; i++) {
- if (GPR_UNLIKELY(s->included[i])) {
+ if (GPR_UNLIKELY(included[i])) {
gpr_log(GPR_ERROR, "%s stream %d still included in list %d",
- t->is_client ? "client" : "server", s->id, i);
+ t->is_client ? "client" : "server", id, i);
abort();
}
}
- GPR_ASSERT(s->send_initial_metadata_finished == nullptr);
- GPR_ASSERT(s->fetching_send_message == nullptr);
- GPR_ASSERT(s->send_trailing_metadata_finished == nullptr);
- GPR_ASSERT(s->recv_initial_metadata_ready == nullptr);
- GPR_ASSERT(s->recv_message_ready == nullptr);
- GPR_ASSERT(s->recv_trailing_metadata_finished == nullptr);
- grpc_chttp2_data_parser_destroy(&s->data_parser);
- grpc_chttp2_incoming_metadata_buffer_destroy(&s->metadata_buffer[0]);
- grpc_chttp2_incoming_metadata_buffer_destroy(&s->metadata_buffer[1]);
- grpc_slice_buffer_destroy_internal(&s->flow_controlled_buffer);
- GRPC_ERROR_UNREF(s->read_closed_error);
- GRPC_ERROR_UNREF(s->write_closed_error);
- GRPC_ERROR_UNREF(s->byte_stream_error);
+ GPR_ASSERT(send_initial_metadata_finished == nullptr);
+ GPR_ASSERT(fetching_send_message == nullptr);
+ GPR_ASSERT(send_trailing_metadata_finished == nullptr);
+ GPR_ASSERT(recv_initial_metadata_ready == nullptr);
+ GPR_ASSERT(recv_message_ready == nullptr);
+ GPR_ASSERT(recv_trailing_metadata_finished == nullptr);
+ grpc_slice_buffer_destroy_internal(&flow_controlled_buffer);
+ GRPC_ERROR_UNREF(read_closed_error);
+ GRPC_ERROR_UNREF(write_closed_error);
+ GRPC_ERROR_UNREF(byte_stream_error);
- s->flow_control.Destroy();
+ 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(destroy_stream_arg, GRPC_ERROR_NONE);
+}
- GRPC_CLOSURE_SCHED(s->destroy_stream_arg, GRPC_ERROR_NONE);
+static int init_stream(grpc_transport* gt, grpc_stream* gs,
+ grpc_stream_refcount* refcount, const void* server_data,
+ gpr_arena* arena) {
+ GPR_TIMER_SCOPE("init_stream", 0);
+ grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
+ new (gs) grpc_chttp2_stream(t, refcount, server_data, arena);
+ return 0;
+}
+
+static void destroy_stream_locked(void* sp, grpc_error* error) {
+ GPR_TIMER_SCOPE("destroy_stream", 0);
+ grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(sp);
+ s->~grpc_chttp2_stream();
}
static void destroy_stream(grpc_transport* gt, grpc_stream* gs,
@@ -816,7 +804,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,
@@ -1701,8 +1703,8 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
gpr_free(str);
}
- op->handler_private.extra_arg = gs;
GRPC_CHTTP2_STREAM_REF(s, "perform_stream_op");
+ op->handler_private.extra_arg = gs;
GRPC_CLOSURE_SCHED(
GRPC_CLOSURE_INIT(&op->handler_private.closure, perform_stream_op_locked,
op, grpc_combiner_scheduler(t->combiner)),
@@ -2708,6 +2710,7 @@ static void init_keepalive_ping_locked(void* arg, grpc_error* error) {
grpc_chttp2_stream_map_size(&t->stream_map) > 0) {
t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_PINGING;
GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive ping end");
+ grpc_timer_init_unset(&t->keepalive_watchdog_timer);
send_keepalive_ping_locked(t);
grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING);
} else {
@@ -3185,10 +3188,10 @@ 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) {
- grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(
- gpr_zalloc(sizeof(grpc_chttp2_transport)));
- init_transport(t, channel_args, ep, is_client);
+ const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client,
+ grpc_resource_user* resource_user) {
+ auto t = new (gpr_malloc(sizeof(grpc_chttp2_transport)))
+ grpc_chttp2_transport(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_data.cc b/src/core/ext/transport/chttp2/transport/frame_data.cc
index 933b32c03c..1de00735cf 100644
--- a/src/core/ext/transport/chttp2/transport/frame_data.cc
+++ b/src/core/ext/transport/chttp2/transport/frame_data.cc
@@ -32,18 +32,12 @@
#include "src/core/lib/slice/slice_string_helpers.h"
#include "src/core/lib/transport/transport.h"
-grpc_error* grpc_chttp2_data_parser_init(grpc_chttp2_data_parser* parser) {
- parser->state = GRPC_CHTTP2_DATA_FH_0;
- parser->parsing_frame = nullptr;
- return GRPC_ERROR_NONE;
-}
-
-void grpc_chttp2_data_parser_destroy(grpc_chttp2_data_parser* parser) {
- if (parser->parsing_frame != nullptr) {
- GRPC_ERROR_UNREF(parser->parsing_frame->Finished(
+grpc_chttp2_data_parser::~grpc_chttp2_data_parser() {
+ if (parsing_frame != nullptr) {
+ GRPC_ERROR_UNREF(parsing_frame->Finished(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Parser destroyed"), false));
}
- GRPC_ERROR_UNREF(parser->error);
+ GRPC_ERROR_UNREF(error);
}
grpc_error* grpc_chttp2_data_parser_begin_frame(grpc_chttp2_data_parser* parser,
diff --git a/src/core/ext/transport/chttp2/transport/frame_data.h b/src/core/ext/transport/chttp2/transport/frame_data.h
index e5d01f764e..2c5da99fa6 100644
--- a/src/core/ext/transport/chttp2/transport/frame_data.h
+++ b/src/core/ext/transport/chttp2/transport/frame_data.h
@@ -43,20 +43,18 @@ namespace grpc_core {
class Chttp2IncomingByteStream;
} // namespace grpc_core
-typedef struct {
- grpc_chttp2_stream_state state;
- uint8_t frame_type;
- uint32_t frame_size;
- grpc_error* error;
+struct grpc_chttp2_data_parser {
+ grpc_chttp2_data_parser() = default;
+ ~grpc_chttp2_data_parser();
- bool is_frame_compressed;
- grpc_core::Chttp2IncomingByteStream* parsing_frame;
-} grpc_chttp2_data_parser;
+ grpc_chttp2_stream_state state = GRPC_CHTTP2_DATA_FH_0;
+ uint8_t frame_type = 0;
+ uint32_t frame_size = 0;
+ grpc_error* error = GRPC_ERROR_NONE;
-/* initialize per-stream state for data frame parsing */
-grpc_error* grpc_chttp2_data_parser_init(grpc_chttp2_data_parser* parser);
-
-void grpc_chttp2_data_parser_destroy(grpc_chttp2_data_parser* parser);
+ bool is_frame_compressed = false;
+ grpc_core::Chttp2IncomingByteStream* parsing_frame = nullptr;
+};
/* start processing a new data frame */
grpc_error* grpc_chttp2_data_parser_begin_frame(grpc_chttp2_data_parser* parser,
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/hpack_encoder.cc b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
index 920d52770f..dbe9df6ae3 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
@@ -212,10 +212,6 @@ static uint32_t prepare_space_for_new_elem(grpc_chttp2_hpack_compressor* c,
return new_index;
}
-/* dummy function */
-static void add_nothing(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem,
- size_t elem_size) {}
-
// Add a key to the dynamic table. Both key and value will be added to table at
// the decoder.
static void add_key_with_index(grpc_chttp2_hpack_compressor* c,
@@ -524,17 +520,22 @@ static void hpack_enc(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem,
uint32_t indices_key;
/* should this elem be in the table? */
- size_t decoder_space_usage =
+ const size_t decoder_space_usage =
grpc_chttp2_get_size_in_hpack_table(elem, st->use_true_binary_metadata);
- bool should_add_elem = elem_interned &&
- decoder_space_usage < MAX_DECODER_SPACE_USAGE &&
- c->filter_elems[HASH_FRAGMENT_1(elem_hash)] >=
- c->filter_elems_sum / ONE_ON_ADD_PROBABILITY;
- void (*maybe_add)(grpc_chttp2_hpack_compressor*, grpc_mdelem, size_t) =
- should_add_elem ? add_elem : add_nothing;
- void (*emit)(grpc_chttp2_hpack_compressor*, uint32_t, grpc_mdelem,
- framer_state*) =
- should_add_elem ? emit_lithdr_incidx : emit_lithdr_noidx;
+ const bool should_add_elem = elem_interned &&
+ decoder_space_usage < MAX_DECODER_SPACE_USAGE &&
+ c->filter_elems[HASH_FRAGMENT_1(elem_hash)] >=
+ c->filter_elems_sum / ONE_ON_ADD_PROBABILITY;
+
+ auto emit_maybe_add = [&should_add_elem, &elem, &st, &c, &indices_key,
+ &decoder_space_usage] {
+ if (should_add_elem) {
+ emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st);
+ add_elem(c, elem, decoder_space_usage);
+ } else {
+ emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st);
+ }
+ };
/* no hits for the elem... maybe there's a key? */
indices_key = c->indices_keys[HASH_FRAGMENT_2(key_hash)];
@@ -542,8 +543,7 @@ static void hpack_enc(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem,
GRPC_MDKEY(elem)) &&
indices_key > c->tail_remote_index) {
/* HIT: key (first cuckoo hash) */
- emit(c, dynidx(c, indices_key), elem, st);
- maybe_add(c, elem, decoder_space_usage);
+ emit_maybe_add();
return;
}
@@ -552,20 +552,23 @@ static void hpack_enc(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem,
GRPC_MDKEY(elem)) &&
indices_key > c->tail_remote_index) {
/* HIT: key (first cuckoo hash) */
- emit(c, dynidx(c, indices_key), elem, st);
- maybe_add(c, elem, decoder_space_usage);
+ emit_maybe_add();
return;
}
/* no elem, key in the table... fall back to literal emission */
- bool should_add_key =
+ const bool should_add_key =
!elem_interned && decoder_space_usage < MAX_DECODER_SPACE_USAGE;
- emit = (should_add_elem || should_add_key) ? emit_lithdr_incidx_v
- : emit_lithdr_noidx_v;
- maybe_add =
- should_add_elem ? add_elem : (should_add_key ? add_key : add_nothing);
- emit(c, 0, elem, st);
- maybe_add(c, elem, decoder_space_usage);
+ if (should_add_elem || should_add_key) {
+ emit_lithdr_incidx_v(c, 0, elem, st);
+ } else {
+ emit_lithdr_noidx_v(c, 0, elem, st);
+ }
+ if (should_add_elem) {
+ add_elem(c, elem, decoder_space_usage);
+ } else if (should_add_key) {
+ add_key(c, elem, decoder_space_usage);
+ }
}
#define STRLEN_LIT(x) (sizeof(x) - 1)
diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.cc b/src/core/ext/transport/chttp2/transport/incoming_metadata.cc
index 4d7dfd900f..dca15e7680 100644
--- a/src/core/ext/transport/chttp2/transport/incoming_metadata.cc
+++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.cc
@@ -27,18 +27,6 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-void grpc_chttp2_incoming_metadata_buffer_init(
- grpc_chttp2_incoming_metadata_buffer* buffer, gpr_arena* arena) {
- buffer->arena = arena;
- grpc_metadata_batch_init(&buffer->batch);
- buffer->batch.deadline = GRPC_MILLIS_INF_FUTURE;
-}
-
-void grpc_chttp2_incoming_metadata_buffer_destroy(
- grpc_chttp2_incoming_metadata_buffer* buffer) {
- grpc_metadata_batch_destroy(&buffer->batch);
-}
-
grpc_error* grpc_chttp2_incoming_metadata_buffer_add(
grpc_chttp2_incoming_metadata_buffer* buffer, grpc_mdelem elem) {
buffer->size += GRPC_MDELEM_LENGTH(elem);
diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.h b/src/core/ext/transport/chttp2/transport/incoming_metadata.h
index d029cf00d4..c551b3cc8b 100644
--- a/src/core/ext/transport/chttp2/transport/incoming_metadata.h
+++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.h
@@ -23,17 +23,20 @@
#include "src/core/lib/transport/transport.h"
-typedef struct {
+struct grpc_chttp2_incoming_metadata_buffer {
+ grpc_chttp2_incoming_metadata_buffer(gpr_arena* arena) : arena(arena) {
+ grpc_metadata_batch_init(&batch);
+ batch.deadline = GRPC_MILLIS_INF_FUTURE;
+ }
+ ~grpc_chttp2_incoming_metadata_buffer() {
+ grpc_metadata_batch_destroy(&batch);
+ }
+
gpr_arena* arena;
grpc_metadata_batch batch;
- size_t size; // total size of metadata
-} grpc_chttp2_incoming_metadata_buffer;
-
-/** assumes everything initially zeroed */
-void grpc_chttp2_incoming_metadata_buffer_init(
- grpc_chttp2_incoming_metadata_buffer* buffer, gpr_arena* arena);
-void grpc_chttp2_incoming_metadata_buffer_destroy(
- grpc_chttp2_incoming_metadata_buffer* buffer);
+ size_t size = 0; // total size of metadata
+};
+
void grpc_chttp2_incoming_metadata_buffer_publish(
grpc_chttp2_incoming_metadata_buffer* buffer, grpc_metadata_batch* batch);
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index ff26dd9255..3ee408c103 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -103,8 +103,8 @@ const char* grpc_chttp2_initiate_write_reason_string(
grpc_chttp2_initiate_write_reason reason);
typedef struct {
- grpc_closure_list lists[GRPC_CHTTP2_PCL_COUNT];
- uint64_t inflight_id;
+ grpc_closure_list lists[GRPC_CHTTP2_PCL_COUNT] = {};
+ uint64_t inflight_id = 0;
} grpc_chttp2_ping_queue;
typedef struct {
@@ -280,34 +280,41 @@ typedef enum {
} grpc_chttp2_keepalive_state;
struct grpc_chttp2_transport {
+ grpc_chttp2_transport(const grpc_channel_args* channel_args,
+ grpc_endpoint* ep, bool is_client,
+ grpc_resource_user* resource_user);
+ ~grpc_chttp2_transport();
+
grpc_transport base; /* must be first */
gpr_refcount refs;
grpc_endpoint* ep;
char* peer_string;
+ grpc_resource_user* resource_user;
+
grpc_combiner* combiner;
- grpc_closure* notify_on_receive_settings;
+ grpc_closure* notify_on_receive_settings = nullptr;
/** write execution state of the transport */
- grpc_chttp2_write_state write_state;
+ grpc_chttp2_write_state write_state = GRPC_CHTTP2_WRITE_STATE_IDLE;
/** is this the first write in a series of writes?
set when we initiate writing from idle, cleared when we
initiate writing from writing+more */
- bool is_first_write_in_batch;
+ bool is_first_write_in_batch = false;
/** is the transport destroying itself? */
- uint8_t destroying;
+ uint8_t destroying = false;
/** has the upper layer closed the transport? */
- grpc_error* closed_with_error;
+ grpc_error* closed_with_error = GRPC_ERROR_NONE;
/** is there a read request to the endpoint outstanding? */
- uint8_t endpoint_reading;
+ uint8_t endpoint_reading = 1;
- grpc_chttp2_optimization_target opt_target;
+ grpc_chttp2_optimization_target opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY;
/** various lists of streams */
- grpc_chttp2_stream_list lists[STREAM_LIST_COUNT];
+ grpc_chttp2_stream_list lists[STREAM_LIST_COUNT] = {};
/** maps stream id to grpc_chttp2_stream objects */
grpc_chttp2_stream_map stream_map;
@@ -324,7 +331,7 @@ struct grpc_chttp2_transport {
/** address to place a newly accepted stream - set and unset by
grpc_chttp2_parsing_accept_stream; used by init_stream to
publish the accepted server stream */
- grpc_chttp2_stream** accepting_stream;
+ grpc_chttp2_stream** accepting_stream = nullptr;
struct {
/* accept stream callback */
@@ -348,41 +355,43 @@ struct grpc_chttp2_transport {
/** how much data are we willing to buffer when the WRITE_BUFFER_HINT is set?
*/
- uint32_t write_buffer_size;
+ uint32_t write_buffer_size = grpc_core::chttp2::kDefaultWindow;
/** Set to a grpc_error object if a goaway frame is received. By default, set
* to GRPC_ERROR_NONE */
- grpc_error* goaway_error;
+ grpc_error* goaway_error = GRPC_ERROR_NONE;
- grpc_chttp2_sent_goaway_state sent_goaway_state;
+ grpc_chttp2_sent_goaway_state sent_goaway_state = GRPC_CHTTP2_NO_GOAWAY_SEND;
/** are the local settings dirty and need to be sent? */
- bool dirtied_local_settings;
+ bool dirtied_local_settings = true;
/** have local settings been sent? */
- bool sent_local_settings;
- /** bitmask of setting indexes to send out */
- uint32_t force_send_settings;
+ bool sent_local_settings = false;
+ /** bitmask of setting indexes to send out
+ Hack: it's common for implementations to assume 65536 bytes initial send
+ window -- this should by rights be 0 */
+ uint32_t force_send_settings = 1 << GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
/** settings values */
uint32_t settings[GRPC_NUM_SETTING_SETS][GRPC_CHTTP2_NUM_SETTINGS];
/** what is the next stream id to be allocated by this peer?
copied to next_stream_id in parsing when parsing commences */
- uint32_t next_stream_id;
+ uint32_t next_stream_id = 0;
/** last new stream id */
- uint32_t last_new_stream_id;
+ uint32_t last_new_stream_id = 0;
/** ping queues for various ping insertion points */
- grpc_chttp2_ping_queue ping_queue;
+ grpc_chttp2_ping_queue ping_queue = grpc_chttp2_ping_queue();
grpc_chttp2_repeated_ping_policy ping_policy;
grpc_chttp2_repeated_ping_state ping_state;
- uint64_t ping_ctr; /* unique id for pings */
+ uint64_t ping_ctr = 0; /* unique id for pings */
grpc_closure retry_initiate_ping_locked;
/** ping acks */
- size_t ping_ack_count;
- size_t ping_ack_capacity;
- uint64_t* ping_acks;
+ size_t ping_ack_count = 0;
+ size_t ping_ack_capacity = 0;
+ uint64_t* ping_acks = nullptr;
grpc_chttp2_server_ping_recv_state ping_recv_state;
/** parser for headers */
@@ -408,22 +417,22 @@ struct grpc_chttp2_transport {
int64_t initial_window_update = 0;
/* deframing */
- grpc_chttp2_deframe_transport_state deframe_state;
- uint8_t incoming_frame_type;
- uint8_t incoming_frame_flags;
- uint8_t header_eof;
- bool is_first_frame;
- uint32_t expect_continuation_stream_id;
- uint32_t incoming_frame_size;
- uint32_t incoming_stream_id;
+ grpc_chttp2_deframe_transport_state deframe_state = GRPC_DTS_CLIENT_PREFIX_0;
+ uint8_t incoming_frame_type = 0;
+ uint8_t incoming_frame_flags = 0;
+ uint8_t header_eof = 0;
+ bool is_first_frame = true;
+ uint32_t expect_continuation_stream_id = 0;
+ uint32_t incoming_frame_size = 0;
+ uint32_t incoming_stream_id = 0;
/* active parser */
- void* parser_data;
- grpc_chttp2_stream* incoming_stream;
+ void* parser_data = nullptr;
+ grpc_chttp2_stream* incoming_stream = nullptr;
grpc_error* (*parser)(void* parser_user_data, grpc_chttp2_transport* t,
grpc_chttp2_stream* s, grpc_slice slice, int is_last);
- grpc_chttp2_write_cb* write_cb_pool;
+ grpc_chttp2_write_cb* write_cb_pool = nullptr;
/* bdp estimator */
grpc_closure next_bdp_ping_timer_expired_locked;
@@ -432,23 +441,23 @@ struct grpc_chttp2_transport {
/* if non-NULL, close the transport with this error when writes are finished
*/
- grpc_error* close_transport_on_writes_finished;
+ grpc_error* close_transport_on_writes_finished = GRPC_ERROR_NONE;
/* a list of closures to run after writes are finished */
- grpc_closure_list run_after_write;
+ grpc_closure_list run_after_write = GRPC_CLOSURE_LIST_INIT;
/* buffer pool state */
/** have we scheduled a benign cleanup? */
- bool benign_reclaimer_registered;
+ bool benign_reclaimer_registered = false;
/** have we scheduled a destructive cleanup? */
- bool destructive_reclaimer_registered;
+ bool destructive_reclaimer_registered = false;
/** benign cleanup closure */
grpc_closure benign_reclaimer_locked;
/** destructive cleanup closure */
grpc_closure destructive_reclaimer_locked;
/* next bdp ping timer */
- bool have_next_bdp_ping_timer;
+ bool have_next_bdp_ping_timer = false;
grpc_timer next_bdp_ping_timer;
/* keep-alive ping support */
@@ -469,12 +478,12 @@ struct grpc_chttp2_transport {
/** grace period for a ping to complete before watchdog kicks in */
grpc_millis keepalive_timeout;
/** if keepalive pings are allowed when there's no outstanding streams */
- bool keepalive_permit_without_calls;
+ bool keepalive_permit_without_calls = false;
/** keep-alive state machine state */
grpc_chttp2_keepalive_state keepalive_state;
grpc_core::RefCountedPtr<grpc_core::channelz::SocketNode> channelz_socket;
- uint32_t num_messages_in_next_write;
+ uint32_t num_messages_in_next_write = 0;
};
typedef enum {
@@ -485,6 +494,10 @@ typedef enum {
} grpc_published_metadata_method;
struct grpc_chttp2_stream {
+ grpc_chttp2_stream(grpc_chttp2_transport* t, grpc_stream_refcount* refcount,
+ const void* server_data, gpr_arena* arena);
+ ~grpc_chttp2_stream();
+
grpc_chttp2_transport* t;
grpc_stream_refcount* refcount;
@@ -492,63 +505,63 @@ struct grpc_chttp2_stream {
grpc_closure* destroy_stream_arg;
grpc_chttp2_stream_link links[STREAM_LIST_COUNT];
- uint8_t included[STREAM_LIST_COUNT];
+ uint8_t included[STREAM_LIST_COUNT] = {};
/** HTTP2 stream id for this stream, or zero if one has not been assigned */
- uint32_t id;
+ uint32_t id = 0;
/** things the upper layers would like to send */
- grpc_metadata_batch* send_initial_metadata;
- grpc_closure* send_initial_metadata_finished;
- grpc_metadata_batch* send_trailing_metadata;
- grpc_closure* send_trailing_metadata_finished;
+ grpc_metadata_batch* send_initial_metadata = nullptr;
+ grpc_closure* send_initial_metadata_finished = nullptr;
+ grpc_metadata_batch* send_trailing_metadata = nullptr;
+ grpc_closure* send_trailing_metadata_finished = nullptr;
grpc_core::OrphanablePtr<grpc_core::ByteStream> fetching_send_message;
- uint32_t fetched_send_message_length;
- grpc_slice fetching_slice;
+ uint32_t fetched_send_message_length = 0;
+ grpc_slice fetching_slice = grpc_empty_slice();
int64_t next_message_end_offset;
- int64_t flow_controlled_bytes_written;
- int64_t flow_controlled_bytes_flowed;
+ int64_t flow_controlled_bytes_written = 0;
+ int64_t flow_controlled_bytes_flowed = 0;
grpc_closure complete_fetch_locked;
- grpc_closure* fetching_send_message_finished;
+ grpc_closure* fetching_send_message_finished = nullptr;
grpc_metadata_batch* recv_initial_metadata;
- grpc_closure* recv_initial_metadata_ready;
- bool* trailing_metadata_available;
+ grpc_closure* recv_initial_metadata_ready = nullptr;
+ bool* trailing_metadata_available = nullptr;
grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message;
- grpc_closure* recv_message_ready;
+ grpc_closure* recv_message_ready = nullptr;
grpc_metadata_batch* recv_trailing_metadata;
- grpc_closure* recv_trailing_metadata_finished;
+ grpc_closure* recv_trailing_metadata_finished = nullptr;
- grpc_transport_stream_stats* collecting_stats;
- grpc_transport_stream_stats stats;
+ grpc_transport_stream_stats* collecting_stats = nullptr;
+ grpc_transport_stream_stats stats = grpc_transport_stream_stats();
/** Is this stream closed for writing. */
- bool write_closed;
+ bool write_closed = false;
/** Is this stream reading half-closed. */
- bool read_closed;
+ bool read_closed = false;
/** Are all published incoming byte streams closed. */
- bool all_incoming_byte_streams_finished;
+ bool all_incoming_byte_streams_finished = false;
/** Has this stream seen an error.
If true, then pending incoming frames can be thrown away. */
- bool seen_error;
+ bool seen_error = false;
/** Are we buffering writes on this stream? If yes, we won't become writable
until there's enough queued up in the flow_controlled_buffer */
- bool write_buffering;
+ bool write_buffering = false;
/** Has trailing metadata been received. */
- bool received_trailing_metadata;
+ bool received_trailing_metadata = false;
/* have we sent or received the EOS bit? */
- bool eos_received;
- bool eos_sent;
+ bool eos_received = false;
+ bool eos_sent = false;
/** the error that resulted in this stream being read-closed */
- grpc_error* read_closed_error;
+ grpc_error* read_closed_error = GRPC_ERROR_NONE;
/** the error that resulted in this stream being write-closed */
- grpc_error* write_closed_error;
+ grpc_error* write_closed_error = GRPC_ERROR_NONE;
- grpc_published_metadata_method published_metadata[2];
- bool final_metadata_requested;
+ grpc_published_metadata_method published_metadata[2] = {};
+ bool final_metadata_requested = false;
grpc_chttp2_incoming_metadata_buffer metadata_buffer[2];
@@ -558,33 +571,33 @@ struct grpc_chttp2_stream {
* Accessed only by application thread when stream->pending_byte_stream ==
* true */
grpc_slice_buffer unprocessed_incoming_frames_buffer;
- grpc_closure* on_next; /* protected by t combiner */
- bool pending_byte_stream; /* protected by t combiner */
+ grpc_closure* on_next = nullptr; /* protected by t combiner */
+ bool pending_byte_stream = false; /* protected by t combiner */
// cached length of buffer to be used by the transport thread in cases where
// stream->pending_byte_stream == true. The value is saved before
// application threads are allowed to modify
// unprocessed_incoming_frames_buffer
- size_t unprocessed_incoming_frames_buffer_cached_length;
+ size_t unprocessed_incoming_frames_buffer_cached_length = 0;
grpc_closure reset_byte_stream;
- grpc_error* byte_stream_error; /* protected by t combiner */
- bool received_last_frame; /* protected by t combiner */
+ grpc_error* byte_stream_error = GRPC_ERROR_NONE; /* protected by t combiner */
+ bool received_last_frame = false; /* protected by t combiner */
- grpc_millis deadline;
+ grpc_millis deadline = GRPC_MILLIS_INF_FUTURE;
/** saw some stream level error */
- grpc_error* forced_close_error;
+ grpc_error* forced_close_error = GRPC_ERROR_NONE;
/** how many header frames have we received? */
- uint8_t header_frames_received;
+ uint8_t header_frames_received = 0;
/** parsing state for data frames */
/* Accessed only by transport thread when stream->pending_byte_stream == false
* Accessed only by application thread when stream->pending_byte_stream ==
* true */
grpc_chttp2_data_parser data_parser;
/** number of bytes received - reset at end of parse thread execution */
- int64_t received_bytes;
+ int64_t received_bytes = 0;
- bool sent_initial_metadata;
- bool sent_trailing_metadata;
+ bool sent_initial_metadata = false;
+ bool sent_trailing_metadata = false;
grpc_core::PolymorphicManualConstructor<
grpc_core::chttp2::StreamFlowControlBase,
@@ -594,32 +607,34 @@ struct grpc_chttp2_stream {
grpc_slice_buffer flow_controlled_buffer;
- grpc_chttp2_write_cb* on_flow_controlled_cbs;
- grpc_chttp2_write_cb* on_write_finished_cbs;
- grpc_chttp2_write_cb* finish_after_write;
- size_t sending_bytes;
+ grpc_chttp2_write_cb* on_flow_controlled_cbs = nullptr;
+ grpc_chttp2_write_cb* on_write_finished_cbs = nullptr;
+ grpc_chttp2_write_cb* finish_after_write = nullptr;
+ size_t sending_bytes = 0;
/* Stream compression method to be used. */
- grpc_stream_compression_method stream_compression_method;
+ grpc_stream_compression_method stream_compression_method =
+ GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS;
/* Stream decompression method to be used. */
- grpc_stream_compression_method stream_decompression_method;
+ grpc_stream_compression_method stream_decompression_method =
+ GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS;
/** Stream compression decompress context */
- grpc_stream_compression_context* stream_decompression_ctx;
+ grpc_stream_compression_context* stream_decompression_ctx = nullptr;
/** Stream compression compress context */
- grpc_stream_compression_context* stream_compression_ctx;
+ grpc_stream_compression_context* stream_compression_ctx = nullptr;
/** Buffer storing data that is compressed but not sent */
grpc_slice_buffer compressed_data_buffer;
/** Amount of uncompressed bytes sent out when compressed_data_buffer is
* emptied */
- size_t uncompressed_data_size;
+ size_t uncompressed_data_size = 0;
/** Temporary buffer storing decompressed data */
grpc_slice_buffer decompressed_data_buffer;
/** Whether bytes stored in unprocessed_incoming_byte_stream is decompressed
*/
- bool unprocessed_incoming_frames_decompressed;
+ bool unprocessed_incoming_frames_decompressed = false;
/** gRPC header bytes that are already decompressed */
- size_t decompressed_header_bytes;
+ size_t decompressed_header_bytes = 0;
};
/** Transport writing call flow:
diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.cc b/src/core/ext/transport/cronet/transport/cronet_transport.cc
index 81e2634e3a..349d8681d5 100644
--- a/src/core/ext/transport/cronet/transport/cronet_transport.cc
+++ b/src/core/ext/transport/cronet/transport/cronet_transport.cc
@@ -111,16 +111,21 @@ typedef struct grpc_cronet_transport grpc_cronet_transport;
/* TODO (makdharma): reorder structure for memory efficiency per
http://www.catb.org/esr/structure-packing/#_structure_reordering: */
struct read_state {
+ read_state(gpr_arena* arena)
+ : trailing_metadata(arena), initial_metadata(arena) {
+ grpc_slice_buffer_init(&read_slice_buffer);
+ }
+
/* vars to store data coming from server */
- char* read_buffer;
- bool length_field_received;
- int received_bytes;
- int remaining_bytes;
- int length_field;
- bool compressed;
- char grpc_header_bytes[GRPC_HEADER_SIZE_IN_BYTES];
- char* payload_field;
- bool read_stream_closed;
+ char* read_buffer = nullptr;
+ bool length_field_received = false;
+ int received_bytes = 0;
+ int remaining_bytes = 0;
+ int length_field = 0;
+ bool compressed = 0;
+ char grpc_header_bytes[GRPC_HEADER_SIZE_IN_BYTES] = {};
+ char* payload_field = nullptr;
+ bool read_stream_closed = 0;
/* vars for holding data destined for the application */
grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> sbs;
@@ -128,59 +133,71 @@ struct read_state {
/* vars for trailing metadata */
grpc_chttp2_incoming_metadata_buffer trailing_metadata;
- bool trailing_metadata_valid;
+ bool trailing_metadata_valid = false;
/* vars for initial metadata */
grpc_chttp2_incoming_metadata_buffer initial_metadata;
};
struct write_state {
- char* write_buffer;
+ char* write_buffer = nullptr;
};
/* track state of one stream op */
struct op_state {
- bool state_op_done[OP_NUM_OPS];
- bool state_callback_received[OP_NUM_OPS];
+ op_state(gpr_arena* arena) : rs(arena) {}
+
+ bool state_op_done[OP_NUM_OPS] = {};
+ bool state_callback_received[OP_NUM_OPS] = {};
/* A non-zero gRPC status code has been seen */
- bool fail_state;
+ bool fail_state = false;
/* Transport is discarding all buffered messages */
- bool flush_read;
- bool flush_cronet_when_ready;
- bool pending_write_for_trailer;
- bool pending_send_message;
+ bool flush_read = false;
+ bool flush_cronet_when_ready = false;
+ bool pending_write_for_trailer = false;
+ bool pending_send_message = false;
/* User requested RECV_TRAILING_METADATA */
- bool pending_recv_trailing_metadata;
+ bool pending_recv_trailing_metadata = false;
/* Cronet has not issued a callback of a bidirectional read */
- bool pending_read_from_cronet;
- grpc_error* cancel_error;
+ bool pending_read_from_cronet = false;
+ grpc_error* cancel_error = GRPC_ERROR_NONE;
/* data structure for storing data coming from server */
struct read_state rs;
/* data structure for storing data going to the server */
struct write_state ws;
};
+struct stream_obj;
+
struct op_and_state {
+ op_and_state(stream_obj* s, const grpc_transport_stream_op_batch& op);
+
grpc_transport_stream_op_batch op;
struct op_state state;
- bool done;
- struct stream_obj* s; /* Pointer back to the stream object */
- struct op_and_state* next; /* next op_and_state in the linked list */
+ bool done = false;
+ struct stream_obj* s; /* Pointer back to the stream object */
+ /* next op_and_state in the linked list */
+ struct op_and_state* next;
};
struct op_storage {
- int num_pending_ops;
- struct op_and_state* head;
+ int num_pending_ops = 0;
+ struct op_and_state* head = nullptr;
};
struct stream_obj {
+ stream_obj(grpc_transport* gt, grpc_stream* gs,
+ grpc_stream_refcount* refcount, gpr_arena* arena);
+ ~stream_obj();
+
gpr_arena* arena;
- struct op_and_state* oas;
- grpc_transport_stream_op_batch* curr_op;
+ struct op_and_state* oas = nullptr;
+ grpc_transport_stream_op_batch* curr_op = nullptr;
grpc_cronet_transport* curr_ct;
grpc_stream* curr_gs;
- bidirectional_stream* cbs;
- bidirectional_stream_header_array header_array;
+ bidirectional_stream* cbs = nullptr;
+ bidirectional_stream_header_array header_array =
+ bidirectional_stream_header_array(); // Zero-initialize the structure.
/* Stream level state. Some state will be tracked both at stream and stream_op
* level */
@@ -195,7 +212,6 @@ struct stream_obj {
/* Refcount object of the stream */
grpc_stream_refcount* refcount;
};
-typedef struct stream_obj stream_obj;
#ifndef NDEBUG
#define GRPC_CRONET_STREAM_REF(stream, reason) \
@@ -306,6 +322,10 @@ static grpc_error* make_error_with_desc(int error_code, const char* desc) {
return error;
}
+inline op_and_state::op_and_state(stream_obj* s,
+ const grpc_transport_stream_op_batch& op)
+ : op(op), state(s->arena), s(s), next(s->storage.head) {}
+
/*
Add a new stream op to op storage.
*/
@@ -314,14 +334,8 @@ static void add_to_storage(struct stream_obj* s,
struct op_storage* storage = &s->storage;
/* add new op at the beginning of the linked list. The memory is freed
in remove_from_storage */
- struct op_and_state* new_op = static_cast<struct op_and_state*>(
- gpr_malloc(sizeof(struct op_and_state)));
- memcpy(&new_op->op, op, sizeof(grpc_transport_stream_op_batch));
- memset(&new_op->state, 0, sizeof(new_op->state));
- new_op->s = s;
- new_op->done = false;
+ op_and_state* new_op = grpc_core::New<op_and_state>(s, *op);
gpr_mu_lock(&s->mu);
- new_op->next = storage->head;
storage->head = new_op;
storage->num_pending_ops++;
if (op->send_message) {
@@ -347,7 +361,7 @@ static void remove_from_storage(struct stream_obj* s,
}
if (s->storage.head == oas) {
s->storage.head = oas->next;
- gpr_free(oas);
+ grpc_core::Delete(oas);
s->storage.num_pending_ops--;
CRONET_LOG(GPR_DEBUG, "Freed %p. Now %d in the queue", oas,
s->storage.num_pending_ops);
@@ -358,7 +372,7 @@ static void remove_from_storage(struct stream_obj* s,
s->storage.num_pending_ops--;
CRONET_LOG(GPR_DEBUG, "Freed %p. Now %d in the queue", oas,
s->storage.num_pending_ops);
- gpr_free(oas);
+ grpc_core::Delete(oas);
break;
} else if (GPR_UNLIKELY(curr->next == nullptr)) {
CRONET_LOG(GPR_ERROR, "Reached end of LL and did not find op to free");
@@ -540,10 +554,6 @@ static void on_response_headers_received(
}
gpr_mu_lock(&s->mu);
- memset(&s->state.rs.initial_metadata, 0,
- sizeof(s->state.rs.initial_metadata));
- grpc_chttp2_incoming_metadata_buffer_init(&s->state.rs.initial_metadata,
- s->arena);
convert_cronet_array_to_metadata(headers, &s->state.rs.initial_metadata);
s->state.state_callback_received[OP_RECV_INITIAL_METADATA] = true;
if (!(s->state.state_op_done[OP_CANCEL_ERROR] ||
@@ -634,11 +644,7 @@ static void on_response_trailers_received(
stream_obj* s = static_cast<stream_obj*>(stream->annotation);
grpc_cronet_transport* t = s->curr_ct;
gpr_mu_lock(&s->mu);
- memset(&s->state.rs.trailing_metadata, 0,
- sizeof(s->state.rs.trailing_metadata));
s->state.rs.trailing_metadata_valid = false;
- grpc_chttp2_incoming_metadata_buffer_init(&s->state.rs.trailing_metadata,
- s->arena);
convert_cronet_array_to_metadata(trailers, &s->state.rs.trailing_metadata);
if (trailers->count > 0) {
s->state.rs.trailing_metadata_valid = true;
@@ -1354,36 +1360,28 @@ static enum e_op_result execute_stream_op(struct op_and_state* oas) {
Functions used by upper layers to access transport functionality.
*/
+inline stream_obj::stream_obj(grpc_transport* gt, grpc_stream* gs,
+ grpc_stream_refcount* refcount, gpr_arena* arena)
+ : arena(arena),
+ curr_ct(reinterpret_cast<grpc_cronet_transport*>(gt)),
+ curr_gs(gs),
+ state(arena),
+ refcount(refcount) {
+ GRPC_CRONET_STREAM_REF(this, "cronet transport");
+ gpr_mu_init(&mu);
+}
+
+inline stream_obj::~stream_obj() {
+ null_and_maybe_free_read_buffer(this);
+ /* Clean up read_slice_buffer in case there is unread data. */
+ grpc_slice_buffer_destroy_internal(&state.rs.read_slice_buffer);
+ GRPC_ERROR_UNREF(state.cancel_error);
+}
+
static int init_stream(grpc_transport* gt, grpc_stream* gs,
grpc_stream_refcount* refcount, const void* server_data,
gpr_arena* arena) {
- stream_obj* s = reinterpret_cast<stream_obj*>(gs);
-
- s->refcount = refcount;
- GRPC_CRONET_STREAM_REF(s, "cronet transport");
- memset(&s->storage, 0, sizeof(s->storage));
- s->storage.head = nullptr;
- memset(&s->state, 0, sizeof(s->state));
- s->curr_op = nullptr;
- s->cbs = nullptr;
- memset(&s->header_array, 0, sizeof(s->header_array));
- memset(&s->state.rs, 0, sizeof(s->state.rs));
- memset(&s->state.ws, 0, sizeof(s->state.ws));
- memset(s->state.state_op_done, 0, sizeof(s->state.state_op_done));
- memset(s->state.state_callback_received, 0,
- sizeof(s->state.state_callback_received));
- s->state.fail_state = s->state.flush_read = false;
- s->state.cancel_error = nullptr;
- s->state.flush_cronet_when_ready = s->state.pending_write_for_trailer = false;
- s->state.pending_send_message = false;
- s->state.pending_recv_trailing_metadata = false;
- s->state.pending_read_from_cronet = false;
-
- s->curr_gs = gs;
- s->curr_ct = reinterpret_cast<grpc_cronet_transport*>(gt);
- s->arena = arena;
-
- gpr_mu_init(&s->mu);
+ new (gs) stream_obj(gt, gs, refcount, arena);
return 0;
}
@@ -1426,10 +1424,7 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
static void destroy_stream(grpc_transport* gt, grpc_stream* gs,
grpc_closure* then_schedule_closure) {
stream_obj* s = reinterpret_cast<stream_obj*>(gs);
- null_and_maybe_free_read_buffer(s);
- /* Clean up read_slice_buffer in case there is unread data. */
- grpc_slice_buffer_destroy_internal(&s->state.rs.read_slice_buffer);
- GRPC_ERROR_UNREF(s->state.cancel_error);
+ s->~stream_obj();
GRPC_CLOSURE_SCHED(then_schedule_closure, GRPC_ERROR_NONE);
}
diff --git a/src/core/ext/transport/inproc/inproc_transport.cc b/src/core/ext/transport/inproc/inproc_transport.cc
index 9dbd095843..61968de4d5 100644
--- a/src/core/ext/transport/inproc/inproc_transport.cc
+++ b/src/core/ext/transport/inproc/inproc_transport.cc
@@ -40,18 +40,68 @@
if (grpc_inproc_trace.enabled()) gpr_log(__VA_ARGS__); \
} while (0)
-static grpc_slice g_empty_slice;
-static grpc_slice g_fake_path_key;
-static grpc_slice g_fake_path_value;
-static grpc_slice g_fake_auth_key;
-static grpc_slice g_fake_auth_value;
+namespace {
+grpc_slice g_empty_slice;
+grpc_slice g_fake_path_key;
+grpc_slice g_fake_path_value;
+grpc_slice g_fake_auth_key;
+grpc_slice g_fake_auth_value;
+
+struct inproc_stream;
+bool cancel_stream_locked(inproc_stream* s, grpc_error* error);
+void op_state_machine(void* arg, grpc_error* error);
+void log_metadata(const grpc_metadata_batch* md_batch, bool is_client,
+ bool is_initial);
+grpc_error* fill_in_metadata(inproc_stream* s,
+ const grpc_metadata_batch* metadata,
+ uint32_t flags, grpc_metadata_batch* out_md,
+ uint32_t* outflags, bool* markfilled);
+
+struct shared_mu {
+ shared_mu() {
+ // Share one lock between both sides since both sides get affected
+ gpr_mu_init(&mu);
+ gpr_ref_init(&refs, 2);
+ }
-typedef struct {
gpr_mu mu;
gpr_refcount refs;
-} shared_mu;
+};
+
+struct inproc_transport {
+ inproc_transport(const grpc_transport_vtable* vtable, shared_mu* mu,
+ bool is_client)
+ : mu(mu), is_client(is_client) {
+ base.vtable = vtable;
+ // Start each side of transport with 2 refs since they each have a ref
+ // to the other
+ gpr_ref_init(&refs, 2);
+ grpc_connectivity_state_init(&connectivity, GRPC_CHANNEL_READY,
+ is_client ? "inproc_client" : "inproc_server");
+ }
+
+ ~inproc_transport() {
+ grpc_connectivity_state_destroy(&connectivity);
+ if (gpr_unref(&mu->refs)) {
+ gpr_free(mu);
+ }
+ }
+
+ void ref() {
+ INPROC_LOG(GPR_INFO, "ref_transport %p", this);
+ gpr_ref(&refs);
+ }
+
+ void unref() {
+ INPROC_LOG(GPR_INFO, "unref_transport %p", this);
+ if (!gpr_unref(&refs)) {
+ return;
+ }
+ INPROC_LOG(GPR_INFO, "really_destroy_transport %p", this);
+ this->~inproc_transport();
+ gpr_free(this);
+ }
-typedef struct inproc_transport {
grpc_transport base;
shared_mu* mu;
gpr_refcount refs;
@@ -60,128 +110,174 @@ typedef struct inproc_transport {
void (*accept_stream_cb)(void* user_data, grpc_transport* transport,
const void* server_data);
void* accept_stream_data;
- bool is_closed;
+ bool is_closed = false;
struct inproc_transport* other_side;
- struct inproc_stream* stream_list;
-} inproc_transport;
+ struct inproc_stream* stream_list = nullptr;
+};
+
+struct inproc_stream {
+ inproc_stream(inproc_transport* t, grpc_stream_refcount* refcount,
+ const void* server_data, gpr_arena* arena)
+ : t(t), refs(refcount), arena(arena) {
+ // Ref this stream right now for ctor and list.
+ ref("inproc_init_stream:init");
+ ref("inproc_init_stream:list");
+
+ grpc_metadata_batch_init(&to_read_initial_md);
+ grpc_metadata_batch_init(&to_read_trailing_md);
+ GRPC_CLOSURE_INIT(&op_closure, op_state_machine, this,
+ grpc_schedule_on_exec_ctx);
+ grpc_metadata_batch_init(&write_buffer_initial_md);
+ grpc_metadata_batch_init(&write_buffer_trailing_md);
+
+ stream_list_prev = nullptr;
+ gpr_mu_lock(&t->mu->mu);
+ stream_list_next = t->stream_list;
+ if (t->stream_list) {
+ t->stream_list->stream_list_prev = this;
+ }
+ t->stream_list = this;
+ gpr_mu_unlock(&t->mu->mu);
+
+ if (!server_data) {
+ t->ref();
+ inproc_transport* st = t->other_side;
+ st->ref();
+ other_side = nullptr; // will get filled in soon
+ // Pass the client-side stream address to the server-side for a ref
+ ref("inproc_init_stream:clt"); // ref it now on behalf of server
+ // side to avoid destruction
+ INPROC_LOG(GPR_INFO, "calling accept stream cb %p %p",
+ st->accept_stream_cb, st->accept_stream_data);
+ (*st->accept_stream_cb)(st->accept_stream_data, &st->base, (void*)this);
+ } else {
+ // This is the server-side and is being called through accept_stream_cb
+ inproc_stream* cs = (inproc_stream*)server_data;
+ other_side = cs;
+ // Ref the server-side stream on behalf of the client now
+ ref("inproc_init_stream:srv");
+
+ // Now we are about to affect the other side, so lock the transport
+ // to make sure that it doesn't get destroyed
+ gpr_mu_lock(&t->mu->mu);
+ cs->other_side = this;
+ // Now transfer from the other side's write_buffer if any to the to_read
+ // buffer
+ if (cs->write_buffer_initial_md_filled) {
+ fill_in_metadata(this, &cs->write_buffer_initial_md,
+ cs->write_buffer_initial_md_flags, &to_read_initial_md,
+ &to_read_initial_md_flags, &to_read_initial_md_filled);
+ deadline = GPR_MIN(deadline, cs->write_buffer_deadline);
+ grpc_metadata_batch_clear(&cs->write_buffer_initial_md);
+ cs->write_buffer_initial_md_filled = false;
+ }
+ if (cs->write_buffer_trailing_md_filled) {
+ fill_in_metadata(this, &cs->write_buffer_trailing_md, 0,
+ &to_read_trailing_md, nullptr,
+ &to_read_trailing_md_filled);
+ grpc_metadata_batch_clear(&cs->write_buffer_trailing_md);
+ cs->write_buffer_trailing_md_filled = false;
+ }
+ if (cs->write_buffer_cancel_error != GRPC_ERROR_NONE) {
+ cancel_other_error = cs->write_buffer_cancel_error;
+ cs->write_buffer_cancel_error = GRPC_ERROR_NONE;
+ }
+
+ gpr_mu_unlock(&t->mu->mu);
+ }
+ }
+
+ ~inproc_stream() {
+ GRPC_ERROR_UNREF(write_buffer_cancel_error);
+ GRPC_ERROR_UNREF(cancel_self_error);
+ GRPC_ERROR_UNREF(cancel_other_error);
+
+ if (recv_inited) {
+ grpc_slice_buffer_destroy_internal(&recv_message);
+ }
+
+ t->unref();
+
+ if (closure_at_destroy) {
+ GRPC_CLOSURE_SCHED(closure_at_destroy, GRPC_ERROR_NONE);
+ }
+ }
+
+#ifndef NDEBUG
+#define STREAM_REF(refs, reason) grpc_stream_ref(refs, reason)
+#define STREAM_UNREF(refs, reason) grpc_stream_unref(refs, reason)
+#else
+#define STREAM_REF(refs, reason) grpc_stream_ref(refs)
+#define STREAM_UNREF(refs, reason) grpc_stream_unref(refs)
+#endif
+ void ref(const char* reason) {
+ INPROC_LOG(GPR_INFO, "ref_stream %p %s", this, reason);
+ STREAM_REF(refs, reason);
+ }
+
+ void unref(const char* reason) {
+ INPROC_LOG(GPR_INFO, "unref_stream %p %s", this, reason);
+ STREAM_UNREF(refs, reason);
+ }
+#undef STREAM_REF
+#undef STREAM_UNREF
-typedef struct inproc_stream {
inproc_transport* t;
grpc_metadata_batch to_read_initial_md;
- uint32_t to_read_initial_md_flags;
- bool to_read_initial_md_filled;
+ uint32_t to_read_initial_md_flags = 0;
+ bool to_read_initial_md_filled = false;
grpc_metadata_batch to_read_trailing_md;
- bool to_read_trailing_md_filled;
- bool ops_needed;
- bool op_closure_scheduled;
+ bool to_read_trailing_md_filled = false;
+ bool ops_needed = false;
+ bool op_closure_scheduled = false;
grpc_closure op_closure;
// Write buffer used only during gap at init time when client-side
// stream is set up but server side stream is not yet set up
grpc_metadata_batch write_buffer_initial_md;
- bool write_buffer_initial_md_filled;
- uint32_t write_buffer_initial_md_flags;
- grpc_millis write_buffer_deadline;
+ bool write_buffer_initial_md_filled = false;
+ uint32_t write_buffer_initial_md_flags = 0;
+ grpc_millis write_buffer_deadline = GRPC_MILLIS_INF_FUTURE;
grpc_metadata_batch write_buffer_trailing_md;
- bool write_buffer_trailing_md_filled;
- grpc_error* write_buffer_cancel_error;
+ bool write_buffer_trailing_md_filled = false;
+ grpc_error* write_buffer_cancel_error = GRPC_ERROR_NONE;
struct inproc_stream* other_side;
- bool other_side_closed; // won't talk anymore
- bool write_buffer_other_side_closed; // on hold
+ bool other_side_closed = false; // won't talk anymore
+ bool write_buffer_other_side_closed = false; // on hold
grpc_stream_refcount* refs;
- grpc_closure* closure_at_destroy;
+ grpc_closure* closure_at_destroy = nullptr;
gpr_arena* arena;
- grpc_transport_stream_op_batch* send_message_op;
- grpc_transport_stream_op_batch* send_trailing_md_op;
- grpc_transport_stream_op_batch* recv_initial_md_op;
- grpc_transport_stream_op_batch* recv_message_op;
- grpc_transport_stream_op_batch* recv_trailing_md_op;
+ grpc_transport_stream_op_batch* send_message_op = nullptr;
+ grpc_transport_stream_op_batch* send_trailing_md_op = nullptr;
+ grpc_transport_stream_op_batch* recv_initial_md_op = nullptr;
+ grpc_transport_stream_op_batch* recv_message_op = nullptr;
+ grpc_transport_stream_op_batch* recv_trailing_md_op = nullptr;
grpc_slice_buffer recv_message;
grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> recv_stream;
- bool recv_inited;
+ bool recv_inited = false;
- bool initial_md_sent;
- bool trailing_md_sent;
- bool initial_md_recvd;
- bool trailing_md_recvd;
+ bool initial_md_sent = false;
+ bool trailing_md_sent = false;
+ bool initial_md_recvd = false;
+ bool trailing_md_recvd = false;
- bool closed;
+ bool closed = false;
- grpc_error* cancel_self_error;
- grpc_error* cancel_other_error;
+ grpc_error* cancel_self_error = GRPC_ERROR_NONE;
+ grpc_error* cancel_other_error = GRPC_ERROR_NONE;
- grpc_millis deadline;
+ grpc_millis deadline = GRPC_MILLIS_INF_FUTURE;
- bool listed;
+ bool listed = true;
struct inproc_stream* stream_list_prev;
struct inproc_stream* stream_list_next;
-} inproc_stream;
-
-static bool cancel_stream_locked(inproc_stream* s, grpc_error* error);
-static void op_state_machine(void* arg, grpc_error* error);
-
-static void ref_transport(inproc_transport* t) {
- INPROC_LOG(GPR_INFO, "ref_transport %p", t);
- gpr_ref(&t->refs);
-}
-
-static void really_destroy_transport(inproc_transport* t) {
- INPROC_LOG(GPR_INFO, "really_destroy_transport %p", t);
- grpc_connectivity_state_destroy(&t->connectivity);
- if (gpr_unref(&t->mu->refs)) {
- gpr_free(t->mu);
- }
- gpr_free(t);
-}
-
-static void unref_transport(inproc_transport* t) {
- INPROC_LOG(GPR_INFO, "unref_transport %p", t);
- if (gpr_unref(&t->refs)) {
- really_destroy_transport(t);
- }
-}
-
-#ifndef NDEBUG
-#define STREAM_REF(refs, reason) grpc_stream_ref(refs, reason)
-#define STREAM_UNREF(refs, reason) grpc_stream_unref(refs, reason)
-#else
-#define STREAM_REF(refs, reason) grpc_stream_ref(refs)
-#define STREAM_UNREF(refs, reason) grpc_stream_unref(refs)
-#endif
-
-static void ref_stream(inproc_stream* s, const char* reason) {
- INPROC_LOG(GPR_INFO, "ref_stream %p %s", s, reason);
- STREAM_REF(s->refs, reason);
-}
-
-static void unref_stream(inproc_stream* s, const char* reason) {
- INPROC_LOG(GPR_INFO, "unref_stream %p %s", s, reason);
- STREAM_UNREF(s->refs, reason);
-}
-
-static void really_destroy_stream(inproc_stream* s) {
- INPROC_LOG(GPR_INFO, "really_destroy_stream %p", s);
+};
- GRPC_ERROR_UNREF(s->write_buffer_cancel_error);
- GRPC_ERROR_UNREF(s->cancel_self_error);
- GRPC_ERROR_UNREF(s->cancel_other_error);
-
- if (s->recv_inited) {
- grpc_slice_buffer_destroy_internal(&s->recv_message);
- }
-
- unref_transport(s->t);
-
- if (s->closure_at_destroy) {
- GRPC_CLOSURE_SCHED(s->closure_at_destroy, GRPC_ERROR_NONE);
- }
-}
-
-static void log_metadata(const grpc_metadata_batch* md_batch, bool is_client,
- bool is_initial) {
+void log_metadata(const grpc_metadata_batch* md_batch, bool is_client,
+ bool is_initial) {
for (grpc_linked_mdelem* md = md_batch->list.head; md != nullptr;
md = md->next) {
char* key = grpc_slice_to_c_string(GRPC_MDKEY(md->md));
@@ -193,10 +289,10 @@ static void log_metadata(const grpc_metadata_batch* md_batch, bool is_client,
}
}
-static grpc_error* fill_in_metadata(inproc_stream* s,
- const grpc_metadata_batch* metadata,
- uint32_t flags, grpc_metadata_batch* out_md,
- uint32_t* outflags, bool* markfilled) {
+grpc_error* fill_in_metadata(inproc_stream* s,
+ const grpc_metadata_batch* metadata,
+ uint32_t flags, grpc_metadata_batch* out_md,
+ uint32_t* outflags, bool* markfilled) {
if (grpc_inproc_trace.enabled()) {
log_metadata(metadata, s->t->is_client, outflags != nullptr);
}
@@ -221,109 +317,16 @@ static grpc_error* fill_in_metadata(inproc_stream* s,
return error;
}
-static int init_stream(grpc_transport* gt, grpc_stream* gs,
- grpc_stream_refcount* refcount, const void* server_data,
- gpr_arena* arena) {
+int init_stream(grpc_transport* gt, grpc_stream* gs,
+ grpc_stream_refcount* refcount, const void* server_data,
+ gpr_arena* arena) {
INPROC_LOG(GPR_INFO, "init_stream %p %p %p", gt, gs, server_data);
inproc_transport* t = reinterpret_cast<inproc_transport*>(gt);
- inproc_stream* s = reinterpret_cast<inproc_stream*>(gs);
- s->arena = arena;
-
- s->refs = refcount;
- // Ref this stream right now
- ref_stream(s, "inproc_init_stream:init");
-
- grpc_metadata_batch_init(&s->to_read_initial_md);
- s->to_read_initial_md_flags = 0;
- s->to_read_initial_md_filled = false;
- grpc_metadata_batch_init(&s->to_read_trailing_md);
- s->to_read_trailing_md_filled = false;
- grpc_metadata_batch_init(&s->write_buffer_initial_md);
- s->write_buffer_initial_md_flags = 0;
- s->write_buffer_initial_md_filled = false;
- grpc_metadata_batch_init(&s->write_buffer_trailing_md);
- s->write_buffer_trailing_md_filled = false;
- s->ops_needed = false;
- s->op_closure_scheduled = false;
- GRPC_CLOSURE_INIT(&s->op_closure, op_state_machine, s,
- grpc_schedule_on_exec_ctx);
- s->t = t;
- s->closure_at_destroy = nullptr;
- s->other_side_closed = false;
-
- s->initial_md_sent = s->trailing_md_sent = s->initial_md_recvd =
- s->trailing_md_recvd = false;
-
- s->closed = false;
-
- s->cancel_self_error = GRPC_ERROR_NONE;
- s->cancel_other_error = GRPC_ERROR_NONE;
- s->write_buffer_cancel_error = GRPC_ERROR_NONE;
- s->deadline = GRPC_MILLIS_INF_FUTURE;
- s->write_buffer_deadline = GRPC_MILLIS_INF_FUTURE;
-
- s->stream_list_prev = nullptr;
- gpr_mu_lock(&t->mu->mu);
- s->listed = true;
- ref_stream(s, "inproc_init_stream:list");
- s->stream_list_next = t->stream_list;
- if (t->stream_list) {
- t->stream_list->stream_list_prev = s;
- }
- t->stream_list = s;
- gpr_mu_unlock(&t->mu->mu);
-
- if (!server_data) {
- ref_transport(t);
- inproc_transport* st = t->other_side;
- ref_transport(st);
- s->other_side = nullptr; // will get filled in soon
- // Pass the client-side stream address to the server-side for a ref
- ref_stream(s, "inproc_init_stream:clt"); // ref it now on behalf of server
- // side to avoid destruction
- INPROC_LOG(GPR_INFO, "calling accept stream cb %p %p", st->accept_stream_cb,
- st->accept_stream_data);
- (*st->accept_stream_cb)(st->accept_stream_data, &st->base, (void*)s);
- } else {
- // This is the server-side and is being called through accept_stream_cb
- inproc_stream* cs = (inproc_stream*)server_data;
- s->other_side = cs;
- // Ref the server-side stream on behalf of the client now
- ref_stream(s, "inproc_init_stream:srv");
-
- // Now we are about to affect the other side, so lock the transport
- // to make sure that it doesn't get destroyed
- gpr_mu_lock(&s->t->mu->mu);
- cs->other_side = s;
- // Now transfer from the other side's write_buffer if any to the to_read
- // buffer
- if (cs->write_buffer_initial_md_filled) {
- fill_in_metadata(s, &cs->write_buffer_initial_md,
- cs->write_buffer_initial_md_flags,
- &s->to_read_initial_md, &s->to_read_initial_md_flags,
- &s->to_read_initial_md_filled);
- s->deadline = GPR_MIN(s->deadline, cs->write_buffer_deadline);
- grpc_metadata_batch_clear(&cs->write_buffer_initial_md);
- cs->write_buffer_initial_md_filled = false;
- }
- if (cs->write_buffer_trailing_md_filled) {
- fill_in_metadata(s, &cs->write_buffer_trailing_md, 0,
- &s->to_read_trailing_md, nullptr,
- &s->to_read_trailing_md_filled);
- grpc_metadata_batch_clear(&cs->write_buffer_trailing_md);
- cs->write_buffer_trailing_md_filled = false;
- }
- if (cs->write_buffer_cancel_error != GRPC_ERROR_NONE) {
- s->cancel_other_error = cs->write_buffer_cancel_error;
- cs->write_buffer_cancel_error = GRPC_ERROR_NONE;
- }
-
- gpr_mu_unlock(&s->t->mu->mu);
- }
+ new (gs) inproc_stream(t, refcount, server_data, arena);
return 0; // return value is not important
}
-static void close_stream_locked(inproc_stream* s) {
+void close_stream_locked(inproc_stream* s) {
if (!s->closed) {
// Release the metadata that we would have written out
grpc_metadata_batch_destroy(&s->write_buffer_initial_md);
@@ -341,21 +344,21 @@ static void close_stream_locked(inproc_stream* s) {
n->stream_list_prev = p;
}
s->listed = false;
- unref_stream(s, "close_stream:list");
+ s->unref("close_stream:list");
}
s->closed = true;
- unref_stream(s, "close_stream:closing");
+ s->unref("close_stream:closing");
}
}
// This function means that we are done talking/listening to the other side
-static void close_other_side_locked(inproc_stream* s, const char* reason) {
+void close_other_side_locked(inproc_stream* s, const char* reason) {
if (s->other_side != nullptr) {
// First release the metadata that came from the other side's arena
grpc_metadata_batch_destroy(&s->to_read_initial_md);
grpc_metadata_batch_destroy(&s->to_read_trailing_md);
- unref_stream(s->other_side, reason);
+ s->other_side->unref(reason);
s->other_side_closed = true;
s->other_side = nullptr;
} else if (!s->other_side_closed) {
@@ -367,9 +370,9 @@ static void close_other_side_locked(inproc_stream* s, const char* reason) {
// this stream_op_batch is only one of the pending operations for this
// stream. This is called when one of the pending operations for the stream
// is done and about to be NULLed out
-static void complete_if_batch_end_locked(inproc_stream* s, grpc_error* error,
- grpc_transport_stream_op_batch* op,
- const char* msg) {
+void complete_if_batch_end_locked(inproc_stream* s, grpc_error* error,
+ grpc_transport_stream_op_batch* op,
+ const char* msg) {
int is_sm = static_cast<int>(op == s->send_message_op);
int is_stm = static_cast<int>(op == s->send_trailing_md_op);
// TODO(vjpai): We should not consider the recv ops here, since they
@@ -386,8 +389,7 @@ static void complete_if_batch_end_locked(inproc_stream* s, grpc_error* error,
}
}
-static void maybe_schedule_op_closure_locked(inproc_stream* s,
- grpc_error* error) {
+void maybe_schedule_op_closure_locked(inproc_stream* s, grpc_error* error) {
if (s && s->ops_needed && !s->op_closure_scheduled) {
GRPC_CLOSURE_SCHED(&s->op_closure, GRPC_ERROR_REF(error));
s->op_closure_scheduled = true;
@@ -395,7 +397,7 @@ static void maybe_schedule_op_closure_locked(inproc_stream* s,
}
}
-static void fail_helper_locked(inproc_stream* s, grpc_error* error) {
+void fail_helper_locked(inproc_stream* s, grpc_error* error) {
INPROC_LOG(GPR_INFO, "op_state_machine %p fail_helper", s);
// If we're failing this side, we need to make sure that
// we also send or have already sent trailing metadata
@@ -525,8 +527,7 @@ static void fail_helper_locked(inproc_stream* s, grpc_error* error) {
// that the incoming byte stream's next() call will always return
// synchronously. That assumption is true today but may not always be
// true in the future.
-static void message_transfer_locked(inproc_stream* sender,
- inproc_stream* receiver) {
+void message_transfer_locked(inproc_stream* sender, inproc_stream* receiver) {
size_t remaining =
sender->send_message_op->payload->send_message.send_message->length();
if (receiver->recv_inited) {
@@ -572,7 +573,7 @@ static void message_transfer_locked(inproc_stream* sender,
sender->send_message_op = nullptr;
}
-static void op_state_machine(void* arg, grpc_error* error) {
+void op_state_machine(void* arg, grpc_error* error) {
// This function gets called when we have contents in the unprocessed reads
// Get what we want based on our ops wanted
// Schedule our appropriate closures
@@ -607,10 +608,8 @@ static void op_state_machine(void* arg, grpc_error* error) {
if (other->recv_message_op) {
message_transfer_locked(s, other);
maybe_schedule_op_closure_locked(other, GRPC_ERROR_NONE);
- } else if (!s->t->is_client &&
- (s->trailing_md_sent || other->recv_trailing_md_op)) {
- // A server send will never be matched if the client is waiting
- // for trailing metadata already
+ } else if (!s->t->is_client && s->trailing_md_sent) {
+ // A server send will never be matched if the server already sent status
s->send_message_op->payload->send_message.send_message.reset();
complete_if_batch_end_locked(
s, GRPC_ERROR_NONE, s->send_message_op,
@@ -621,11 +620,15 @@ static void op_state_machine(void* arg, grpc_error* error) {
// Pause a send trailing metadata if there is still an outstanding
// send message unless we know that the send message will never get
// matched to a receive. This happens on the client if the server has
- // already sent status.
+ // already sent status or on the server if the client has requested
+ // status
if (s->send_trailing_md_op &&
(!s->send_message_op ||
(s->t->is_client &&
- (s->trailing_md_recvd || s->to_read_trailing_md_filled)))) {
+ (s->trailing_md_recvd || s->to_read_trailing_md_filled)) ||
+ (!s->t->is_client && other &&
+ (other->trailing_md_recvd || other->to_read_trailing_md_filled ||
+ other->recv_trailing_md_op)))) {
grpc_metadata_batch* dest = (other == nullptr)
? &s->write_buffer_trailing_md
: &other->to_read_trailing_md;
@@ -723,16 +726,6 @@ static void op_state_machine(void* arg, grpc_error* error) {
maybe_schedule_op_closure_locked(other, GRPC_ERROR_NONE);
}
}
- if (s->recv_trailing_md_op && s->t->is_client && other &&
- other->send_message_op) {
- INPROC_LOG(GPR_INFO,
- "op_state_machine %p scheduling trailing-metadata-ready %p", s,
- GRPC_ERROR_NONE);
- GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->payload->recv_trailing_metadata
- .recv_trailing_metadata_ready,
- GRPC_ERROR_NONE);
- maybe_schedule_op_closure_locked(other, GRPC_ERROR_NONE);
- }
if (s->to_read_trailing_md_filled) {
if (s->trailing_md_recvd) {
new_err =
@@ -748,6 +741,7 @@ static void op_state_machine(void* arg, grpc_error* error) {
if (s->recv_message_op != nullptr) {
// This message needs to be wrapped up because it will never be
// satisfied
+ *s->recv_message_op->payload->recv_message.recv_message = nullptr;
INPROC_LOG(GPR_INFO, "op_state_machine %p scheduling message-ready", s);
GRPC_CLOSURE_SCHED(
s->recv_message_op->payload->recv_message.recv_message_ready,
@@ -810,6 +804,7 @@ static void op_state_machine(void* arg, grpc_error* error) {
// No further message will come on this stream, so finish off the
// recv_message_op
INPROC_LOG(GPR_INFO, "op_state_machine %p scheduling message-ready", s);
+ *s->recv_message_op->payload->recv_message.recv_message = nullptr;
GRPC_CLOSURE_SCHED(
s->recv_message_op->payload->recv_message.recv_message_ready,
GRPC_ERROR_NONE);
@@ -847,7 +842,7 @@ done:
GRPC_ERROR_UNREF(new_err);
}
-static bool cancel_stream_locked(inproc_stream* s, grpc_error* error) {
+bool cancel_stream_locked(inproc_stream* s, grpc_error* error) {
bool ret = false; // was the cancel accepted
INPROC_LOG(GPR_INFO, "cancel_stream %p with %s", s, grpc_error_string(error));
if (s->cancel_self_error == GRPC_ERROR_NONE) {
@@ -900,10 +895,10 @@ static bool cancel_stream_locked(inproc_stream* s, grpc_error* error) {
return ret;
}
-static void do_nothing(void* arg, grpc_error* error) {}
+void do_nothing(void* arg, grpc_error* error) {}
-static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
- grpc_transport_stream_op_batch* op) {
+void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
+ grpc_transport_stream_op_batch* op) {
INPROC_LOG(GPR_INFO, "perform_stream_op %p %p %p", gt, gs, op);
inproc_stream* s = reinterpret_cast<inproc_stream*>(gs);
gpr_mu* mu = &s->t->mu->mu; // save aside in case s gets closed
@@ -1012,18 +1007,18 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
}
// We want to initiate the closure if:
- // 1. We want to send a message and the other side wants to receive or end
+ // 1. We want to send a message and the other side wants to receive
// 2. We want to send trailing metadata and there isn't an unmatched send
+ // or the other side wants trailing metadata
// 3. We want initial metadata and the other side has sent it
// 4. We want to receive a message and there is a message ready
// 5. There is trailing metadata, even if nothing specifically wants
// that because that can shut down the receive message as well
- if ((op->send_message && other &&
- ((other->recv_message_op != nullptr) ||
- (other->recv_trailing_md_op != nullptr))) ||
- (op->send_trailing_metadata && !op->send_message) ||
+ if ((op->send_message && other && other->recv_message_op != nullptr) ||
+ (op->send_trailing_metadata &&
+ (!s->send_message_op || (other && other->recv_trailing_md_op))) ||
(op->recv_initial_metadata && s->to_read_initial_md_filled) ||
- (op->recv_message && other && (other->send_message_op != nullptr)) ||
+ (op->recv_message && other && other->send_message_op != nullptr) ||
(s->to_read_trailing_md_filled || s->trailing_md_recvd)) {
if (!s->op_closure_scheduled) {
GRPC_CLOSURE_SCHED(&s->op_closure, GRPC_ERROR_NONE);
@@ -1083,7 +1078,7 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
GRPC_ERROR_UNREF(error);
}
-static void close_transport_locked(inproc_transport* t) {
+void close_transport_locked(inproc_transport* t) {
INPROC_LOG(GPR_INFO, "close_transport %p %d", t, t->is_closed);
grpc_connectivity_state_set(
&t->connectivity, GRPC_CHANNEL_SHUTDOWN,
@@ -1103,7 +1098,7 @@ static void close_transport_locked(inproc_transport* t) {
}
}
-static void perform_transport_op(grpc_transport* gt, grpc_transport_op* op) {
+void perform_transport_op(grpc_transport* gt, grpc_transport_op* op) {
inproc_transport* t = reinterpret_cast<inproc_transport*>(gt);
INPROC_LOG(GPR_INFO, "perform_transport_op %p %p", t, op);
gpr_mu_lock(&t->mu->mu);
@@ -1136,39 +1131,64 @@ static void perform_transport_op(grpc_transport* gt, grpc_transport_op* op) {
gpr_mu_unlock(&t->mu->mu);
}
-static void destroy_stream(grpc_transport* gt, grpc_stream* gs,
- grpc_closure* then_schedule_closure) {
+void destroy_stream(grpc_transport* gt, grpc_stream* gs,
+ grpc_closure* then_schedule_closure) {
INPROC_LOG(GPR_INFO, "destroy_stream %p %p", gs, then_schedule_closure);
inproc_stream* s = reinterpret_cast<inproc_stream*>(gs);
s->closure_at_destroy = then_schedule_closure;
- really_destroy_stream(s);
+ s->~inproc_stream();
}
-static void destroy_transport(grpc_transport* gt) {
+void destroy_transport(grpc_transport* gt) {
inproc_transport* t = reinterpret_cast<inproc_transport*>(gt);
INPROC_LOG(GPR_INFO, "destroy_transport %p", t);
gpr_mu_lock(&t->mu->mu);
close_transport_locked(t);
gpr_mu_unlock(&t->mu->mu);
- unref_transport(t->other_side);
- unref_transport(t);
+ t->other_side->unref();
+ t->unref();
}
/*******************************************************************************
* INTEGRATION GLUE
*/
-static void set_pollset(grpc_transport* gt, grpc_stream* gs,
- grpc_pollset* pollset) {
+void set_pollset(grpc_transport* gt, grpc_stream* gs, grpc_pollset* pollset) {
// Nothing to do here
}
-static void set_pollset_set(grpc_transport* gt, grpc_stream* gs,
- grpc_pollset_set* pollset_set) {
+void set_pollset_set(grpc_transport* gt, grpc_stream* gs,
+ grpc_pollset_set* pollset_set) {
// Nothing to do here
}
-static grpc_endpoint* get_endpoint(grpc_transport* t) { return nullptr; }
+grpc_endpoint* get_endpoint(grpc_transport* t) { return nullptr; }
+
+const grpc_transport_vtable inproc_vtable = {
+ sizeof(inproc_stream), "inproc", init_stream,
+ set_pollset, set_pollset_set, perform_stream_op,
+ perform_transport_op, destroy_stream, destroy_transport,
+ get_endpoint};
+
+/*******************************************************************************
+ * Main inproc transport functions
+ */
+void inproc_transports_create(grpc_transport** server_transport,
+ const grpc_channel_args* server_args,
+ grpc_transport** client_transport,
+ const grpc_channel_args* client_args) {
+ INPROC_LOG(GPR_INFO, "inproc_transports_create");
+ shared_mu* mu = new (gpr_malloc(sizeof(*mu))) shared_mu();
+ inproc_transport* st = new (gpr_malloc(sizeof(*st)))
+ inproc_transport(&inproc_vtable, mu, /*is_client=*/false);
+ inproc_transport* ct = new (gpr_malloc(sizeof(*ct)))
+ inproc_transport(&inproc_vtable, mu, /*is_client=*/true);
+ st->other_side = ct;
+ ct->other_side = st;
+ *server_transport = reinterpret_cast<grpc_transport*>(st);
+ *client_transport = reinterpret_cast<grpc_transport*>(ct);
+}
+} // namespace
/*******************************************************************************
* GLOBAL INIT AND DESTROY
@@ -1190,48 +1210,6 @@ void grpc_inproc_transport_init(void) {
g_fake_auth_value = grpc_slice_from_static_string("inproc-fail");
}
-static const grpc_transport_vtable inproc_vtable = {
- sizeof(inproc_stream), "inproc", init_stream,
- set_pollset, set_pollset_set, perform_stream_op,
- perform_transport_op, destroy_stream, destroy_transport,
- get_endpoint};
-
-/*******************************************************************************
- * Main inproc transport functions
- */
-static void inproc_transports_create(grpc_transport** server_transport,
- const grpc_channel_args* server_args,
- grpc_transport** client_transport,
- const grpc_channel_args* client_args) {
- INPROC_LOG(GPR_INFO, "inproc_transports_create");
- inproc_transport* st =
- static_cast<inproc_transport*>(gpr_zalloc(sizeof(*st)));
- inproc_transport* ct =
- static_cast<inproc_transport*>(gpr_zalloc(sizeof(*ct)));
- // Share one lock between both sides since both sides get affected
- st->mu = ct->mu = static_cast<shared_mu*>(gpr_malloc(sizeof(*st->mu)));
- gpr_mu_init(&st->mu->mu);
- gpr_ref_init(&st->mu->refs, 2);
- st->base.vtable = &inproc_vtable;
- ct->base.vtable = &inproc_vtable;
- // Start each side of transport with 2 refs since they each have a ref
- // to the other
- gpr_ref_init(&st->refs, 2);
- gpr_ref_init(&ct->refs, 2);
- st->is_client = false;
- ct->is_client = true;
- grpc_connectivity_state_init(&st->connectivity, GRPC_CHANNEL_READY,
- "inproc_server");
- grpc_connectivity_state_init(&ct->connectivity, GRPC_CHANNEL_READY,
- "inproc_client");
- st->other_side = ct;
- ct->other_side = st;
- st->stream_list = nullptr;
- ct->stream_list = nullptr;
- *server_transport = reinterpret_cast<grpc_transport*>(st);
- *client_transport = reinterpret_cast<grpc_transport*>(ct);
-}
-
grpc_channel* grpc_inproc_channel_create(grpc_server* server,
grpc_channel_args* args,
void* reserved) {
diff --git a/src/core/lib/channel/channel_stack.cc b/src/core/lib/channel/channel_stack.cc
index 056fcd93de..df956c7176 100644
--- a/src/core/lib/channel/channel_stack.cc
+++ b/src/core/lib/channel/channel_stack.cc
@@ -157,7 +157,6 @@ grpc_error* grpc_call_stack_init(grpc_channel_stack* channel_stack,
size_t count = channel_stack->count;
grpc_call_element* call_elems;
char* user_data;
- size_t i;
elem_args->call_stack->count = count;
GRPC_STREAM_REF_INIT(&elem_args->call_stack->refcount, initial_refs, destroy,
@@ -168,10 +167,14 @@ grpc_error* grpc_call_stack_init(grpc_channel_stack* channel_stack,
/* init per-filter data */
grpc_error* first_error = GRPC_ERROR_NONE;
- for (i = 0; i < count; i++) {
+ for (size_t i = 0; i < count; i++) {
call_elems[i].filter = channel_elems[i].filter;
call_elems[i].channel_data = channel_elems[i].channel_data;
call_elems[i].call_data = user_data;
+ user_data +=
+ GPR_ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data);
+ }
+ for (size_t i = 0; i < count; i++) {
grpc_error* error =
call_elems[i].filter->init_call_elem(&call_elems[i], elem_args);
if (error != GRPC_ERROR_NONE) {
@@ -181,8 +184,6 @@ grpc_error* grpc_call_stack_init(grpc_channel_stack* channel_stack,
GRPC_ERROR_UNREF(error);
}
}
- user_data +=
- GPR_ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data);
}
return first_error;
}
diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h
index 35c3fb01ea..0de8c67079 100644
--- a/src/core/lib/channel/channel_stack.h
+++ b/src/core/lib/channel/channel_stack.h
@@ -79,11 +79,11 @@ typedef struct {
} grpc_call_stats;
/** Information about the call upon completion. */
-typedef struct {
+struct grpc_call_final_info {
grpc_call_stats stats;
- grpc_status_code final_status;
- const char* error_string;
-} grpc_call_final_info;
+ grpc_status_code final_status = GRPC_STATUS_OK;
+ const char* error_string = nullptr;
+};
/* Channel filters specify:
1. the amount of memory needed in the channel & call (via the sizeof_XXX
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..8d589f5983 100644
--- a/src/core/lib/channel/channelz.cc
+++ b/src/core/lib/channel/channelz.cc
@@ -30,15 +30,18 @@
#include "src/core/lib/channel/channelz_registry.h"
#include "src/core/lib/channel/status_util.h"
+#include "src/core/lib/gpr/host_port.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/b64.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/surface/channel.h"
#include "src/core/lib/surface/server.h"
#include "src/core/lib/transport/error_utils.h"
+#include "src/core/lib/uri/uri_parser.h"
namespace grpc_core {
namespace channelz {
@@ -277,7 +280,61 @@ grpc_json* ServerNode::RenderJson() {
return top_level_json;
}
-SocketNode::SocketNode() : BaseNode(EntityType::kSocket) {}
+static void PopulateSocketAddressJson(grpc_json* json, const char* name,
+ const char* addr_str) {
+ if (addr_str == nullptr) return;
+ grpc_json* json_iterator = nullptr;
+ json_iterator = grpc_json_create_child(json_iterator, json, name, nullptr,
+ GRPC_JSON_OBJECT, false);
+ json = json_iterator;
+ json_iterator = nullptr;
+ grpc_uri* uri = grpc_uri_parse(addr_str, true);
+ if ((uri != nullptr) && ((strcmp(uri->scheme, "ipv4") == 0) ||
+ (strcmp(uri->scheme, "ipv6") == 0))) {
+ const char* host_port = uri->path;
+ if (*host_port == '/') ++host_port;
+ char* host = nullptr;
+ char* port = nullptr;
+ GPR_ASSERT(gpr_split_host_port(host_port, &host, &port));
+ int port_num = -1;
+ if (port != nullptr) {
+ port_num = atoi(port);
+ }
+ char* b64_host = grpc_base64_encode(host, strlen(host), false, false);
+ 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_num);
+ json_iterator = grpc_json_create_child(json_iterator, json, "ip_address",
+ b64_host, GRPC_JSON_STRING, true);
+ gpr_free(host);
+ gpr_free(port);
+
+ } else if (uri != nullptr && strcmp(uri->scheme, "unix") == 0) {
+ json_iterator = grpc_json_create_child(json_iterator, json, "uds_address",
+ nullptr, GRPC_JSON_OBJECT, false);
+ json = json_iterator;
+ json_iterator = nullptr;
+ json_iterator =
+ grpc_json_create_child(json_iterator, json, "filename",
+ gpr_strdup(uri->path), GRPC_JSON_STRING, true);
+ } else {
+ json_iterator = grpc_json_create_child(json_iterator, json, "other_address",
+ nullptr, GRPC_JSON_OBJECT, false);
+ json = json_iterator;
+ json_iterator = nullptr;
+ json_iterator = grpc_json_create_child(json_iterator, json, "name",
+ addr_str, GRPC_JSON_STRING, false);
+ }
+ grpc_uri_destroy(uri);
+}
+
+SocketNode::SocketNode(UniquePtr<char> local, UniquePtr<char> remote)
+ : BaseNode(EntityType::kSocket),
+ local_(std::move(local)),
+ remote_(std::move(remote)) {}
void SocketNode::RecordStreamStartedFromLocal() {
gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1));
@@ -315,6 +372,9 @@ grpc_json* SocketNode::RenderJson() {
json_iterator = nullptr;
json_iterator = grpc_json_add_number_string_child(json, json_iterator,
"socketId", uuid());
+ json = top_level_json;
+ PopulateSocketAddressJson(json, "remote", remote_.get());
+ PopulateSocketAddressJson(json, "local", local_.get());
// reset json iterators to top level object
json = top_level_json;
json_iterator = nullptr;
@@ -374,7 +434,8 @@ grpc_json* SocketNode::RenderJson() {
return top_level_json;
}
-ListenSocketNode::ListenSocketNode() : BaseNode(EntityType::kSocket) {}
+ListenSocketNode::ListenSocketNode(UniquePtr<char> local_addr)
+ : BaseNode(EntityType::kSocket), local_addr_(std::move(local_addr)) {}
grpc_json* ListenSocketNode::RenderJson() {
// We need to track these three json objects to build our object
@@ -388,6 +449,9 @@ grpc_json* ListenSocketNode::RenderJson() {
json_iterator = nullptr;
json_iterator = grpc_json_add_number_string_child(json, json_iterator,
"socketId", uuid());
+ json = top_level_json;
+ PopulateSocketAddressJson(json, "local", local_addr_.get());
+
return top_level_json;
}
diff --git a/src/core/lib/channel/channelz.h b/src/core/lib/channel/channelz.h
index 88551befc8..64ab5cb3a6 100644
--- a/src/core/lib/channel/channelz.h
+++ b/src/core/lib/channel/channelz.h
@@ -232,7 +232,7 @@ class ServerNode : public BaseNode {
// Handles channelz bookkeeping for sockets
class SocketNode : public BaseNode {
public:
- SocketNode();
+ SocketNode(UniquePtr<char> local, UniquePtr<char> remote);
~SocketNode() override {}
grpc_json* RenderJson() override;
@@ -262,16 +262,21 @@ class SocketNode : public BaseNode {
gpr_atm last_remote_stream_created_millis_ = 0;
gpr_atm last_message_sent_millis_ = 0;
gpr_atm last_message_received_millis_ = 0;
- UniquePtr<char> peer_string_;
+ UniquePtr<char> local_;
+ UniquePtr<char> remote_;
};
// Handles channelz bookkeeping for listen sockets
class ListenSocketNode : public BaseNode {
public:
- ListenSocketNode();
+ // ListenSocketNode takes ownership of host.
+ explicit ListenSocketNode(UniquePtr<char> local_addr);
~ListenSocketNode() override {}
grpc_json* RenderJson() override;
+
+ private:
+ UniquePtr<char> local_addr_;
};
// Creation functions
diff --git a/src/core/lib/channel/channelz_registry.cc b/src/core/lib/channel/channelz_registry.cc
index 1fe2fad3e1..bc23b90a66 100644
--- a/src/core/lib/channel/channelz_registry.cc
+++ b/src/core/lib/channel/channelz_registry.cc
@@ -210,6 +210,17 @@ char* ChannelzRegistry::InternalGetServers(intptr_t start_server_id) {
return json_str;
}
+void ChannelzRegistry::InternalLogAllEntities() {
+ MutexLock lock(&mu_);
+ for (size_t i = 0; i < entities_.size(); ++i) {
+ if (entities_[i] != nullptr) {
+ char* json = entities_[i]->RenderJsonString();
+ gpr_log(GPR_INFO, "%s", json);
+ gpr_free(json);
+ }
+ }
+}
+
} // namespace channelz
} // namespace grpc_core
@@ -222,6 +233,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/channel/channelz_registry.h b/src/core/lib/channel/channelz_registry.h
index 326f0201c7..73b330785d 100644
--- a/src/core/lib/channel/channelz_registry.h
+++ b/src/core/lib/channel/channelz_registry.h
@@ -62,6 +62,10 @@ class ChannelzRegistry {
return Default()->InternalGetServers(start_server_id);
}
+ // Test only helper function to dump the JSON representation to std out.
+ // This can aid in debugging channelz code.
+ static void LogAllEntities() { Default()->InternalLogAllEntities(); }
+
private:
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
@@ -96,6 +100,8 @@ class ChannelzRegistry {
// Else, will return idx of the first uuid higher than the target.
int FindByUuidLocked(intptr_t uuid, bool direct_hit_needed);
+ void InternalLogAllEntities();
+
// protects members
gpr_mu mu_;
InlinedVector<BaseNode*, 20> entities_;
diff --git a/src/core/lib/channel/context.h b/src/core/lib/channel/context.h
index 5daf48a9a9..763e4ffc9f 100644
--- a/src/core/lib/channel/context.h
+++ b/src/core/lib/channel/context.h
@@ -41,9 +41,9 @@ typedef enum {
GRPC_CONTEXT_COUNT
} grpc_context_index;
-typedef struct {
- void* value;
- void (*destroy)(void*);
-} grpc_call_context_element;
+struct grpc_call_context_element {
+ void* value = nullptr;
+ void (*destroy)(void*) = nullptr;
+};
#endif /* GRPC_CORE_LIB_CHANNEL_CONTEXT_H */
diff --git a/src/core/lib/gpr/arena.cc b/src/core/lib/gpr/arena.cc
index 77f9357146..836a7ca793 100644
--- a/src/core/lib/gpr/arena.cc
+++ b/src/core/lib/gpr/arena.cc
@@ -21,6 +21,7 @@
#include "src/core/lib/gpr/arena.h"
#include <string.h>
+#include <new>
#include <grpc/support/alloc.h>
#include <grpc/support/atm.h>
@@ -28,34 +29,79 @@
#include <grpc/support/sync.h>
#include "src/core/lib/gpr/alloc.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gprpp/memory.h"
+
+namespace {
+enum init_strategy {
+ NO_INIT, // Do not initialize the arena blocks.
+ ZERO_INIT, // Initialize arena blocks with 0.
+ NON_ZERO_INIT, // Initialize arena blocks with a non-zero value.
+};
+
+gpr_once g_init_strategy_once = GPR_ONCE_INIT;
+init_strategy g_init_strategy = NO_INIT;
+} // namespace
+
+static void set_strategy_from_env() {
+ char* str = gpr_getenv("GRPC_ARENA_INIT_STRATEGY");
+ if (str == nullptr) {
+ g_init_strategy = NO_INIT;
+ } else if (strcmp(str, "zero_init") == 0) {
+ g_init_strategy = ZERO_INIT;
+ } else if (strcmp(str, "non_zero_init") == 0) {
+ g_init_strategy = NON_ZERO_INIT;
+ } else {
+ g_init_strategy = NO_INIT;
+ }
+ gpr_free(str);
+}
+
+static void* gpr_arena_alloc_maybe_init(size_t size) {
+ void* mem = gpr_malloc_aligned(size, GPR_MAX_ALIGNMENT);
+ gpr_once_init(&g_init_strategy_once, set_strategy_from_env);
+ if (GPR_UNLIKELY(g_init_strategy != NO_INIT)) {
+ if (g_init_strategy == ZERO_INIT) {
+ memset(mem, 0, size);
+ } else { // NON_ZERO_INIT.
+ memset(mem, 0xFE, size);
+ }
+ }
+ return mem;
+}
+
+void gpr_arena_init() {
+ gpr_once_init(&g_init_strategy_once, set_strategy_from_env);
+}
// Uncomment this to use a simple arena that simply allocates the
// requested amount of memory for each call to gpr_arena_alloc(). This
// effectively eliminates the efficiency gain of using an arena, but it
// may be useful for debugging purposes.
//#define SIMPLE_ARENA_FOR_DEBUGGING
-
#ifdef SIMPLE_ARENA_FOR_DEBUGGING
struct gpr_arena {
+ gpr_arena() { gpr_mu_init(&mu); }
+ ~gpr_arena() {
+ gpr_mu_destroy(&mu);
+ for (size_t i = 0; i < num_ptrs; ++i) {
+ gpr_free_aligned(ptrs[i]);
+ }
+ gpr_free(ptrs);
+ }
+
gpr_mu mu;
- void** ptrs;
- size_t num_ptrs;
+ void** ptrs = nullptr;
+ size_t num_ptrs = 0;
};
gpr_arena* gpr_arena_create(size_t ignored_initial_size) {
- gpr_arena* arena = (gpr_arena*)gpr_zalloc(sizeof(*arena));
- gpr_mu_init(&arena->mu);
- return arena;
+ return grpc_core::New<gpr_arena>();
}
size_t gpr_arena_destroy(gpr_arena* arena) {
- gpr_mu_destroy(&arena->mu);
- for (size_t i = 0; i < arena->num_ptrs; ++i) {
- gpr_free(arena->ptrs[i]);
- }
- gpr_free(arena->ptrs);
- gpr_free(arena);
+ grpc_core::Delete(arena);
return 1; // Value doesn't matter, since it won't be used.
}
@@ -63,7 +109,8 @@ void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
gpr_mu_lock(&arena->mu);
arena->ptrs =
(void**)gpr_realloc(arena->ptrs, sizeof(void*) * (arena->num_ptrs + 1));
- void* retval = arena->ptrs[arena->num_ptrs++] = gpr_zalloc(size);
+ void* retval = arena->ptrs[arena->num_ptrs++] =
+ gpr_arena_alloc_maybe_init(size);
gpr_mu_unlock(&arena->mu);
return retval;
}
@@ -77,45 +124,45 @@ void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
// would allow us to use the alignment actually needed by the caller.
typedef struct zone {
- zone* next;
+ zone* next = nullptr;
} zone;
struct gpr_arena {
+ gpr_arena(size_t initial_size)
+ : initial_zone_size(initial_size), last_zone(&initial_zone) {
+ gpr_mu_init(&arena_growth_mutex);
+ }
+ ~gpr_arena() {
+ gpr_mu_destroy(&arena_growth_mutex);
+ zone* z = initial_zone.next;
+ while (z) {
+ zone* next_z = z->next;
+ z->~zone();
+ gpr_free_aligned(z);
+ z = next_z;
+ }
+ }
+
// Keep track of the total used size. We use this in our call sizing
// historesis.
- gpr_atm total_used;
+ gpr_atm total_used = 0;
size_t initial_zone_size;
zone initial_zone;
zone* last_zone;
gpr_mu arena_growth_mutex;
};
-static void* zalloc_aligned(size_t size) {
- void* ptr = gpr_malloc_aligned(size, GPR_MAX_ALIGNMENT);
- memset(ptr, 0, size);
- return ptr;
-}
-
gpr_arena* gpr_arena_create(size_t initial_size) {
initial_size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(initial_size);
- gpr_arena* a = static_cast<gpr_arena*>(zalloc_aligned(
- GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena)) + initial_size));
- a->initial_zone_size = initial_size;
- a->last_zone = &a->initial_zone;
- gpr_mu_init(&a->arena_growth_mutex);
- return a;
+ return new (gpr_arena_alloc_maybe_init(
+ GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena)) + initial_size))
+ gpr_arena(initial_size);
}
size_t gpr_arena_destroy(gpr_arena* arena) {
- gpr_mu_destroy(&arena->arena_growth_mutex);
- gpr_atm size = gpr_atm_no_barrier_load(&arena->total_used);
- zone* z = arena->initial_zone.next;
+ const gpr_atm size = gpr_atm_no_barrier_load(&arena->total_used);
+ arena->~gpr_arena();
gpr_free_aligned(arena);
- while (z) {
- zone* next_z = z->next;
- gpr_free_aligned(z);
- z = next_z;
- }
return static_cast<size_t>(size);
}
@@ -132,8 +179,8 @@ void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
// sizing historesis (that is, most calls should have a large enough initial
// zone and will not need to grow the arena).
gpr_mu_lock(&arena->arena_growth_mutex);
- zone* z = static_cast<zone*>(
- zalloc_aligned(GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone)) + size));
+ zone* z = new (gpr_arena_alloc_maybe_init(
+ GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone)) + size)) zone();
arena->last_zone->next = z;
arena->last_zone = z;
gpr_mu_unlock(&arena->arena_growth_mutex);
diff --git a/src/core/lib/gpr/arena.h b/src/core/lib/gpr/arena.h
index 6d2a073dd5..069892b228 100644
--- a/src/core/lib/gpr/arena.h
+++ b/src/core/lib/gpr/arena.h
@@ -37,5 +37,7 @@ gpr_arena* gpr_arena_create(size_t initial_size);
void* gpr_arena_alloc(gpr_arena* arena, size_t size);
// Destroy an arena, returning the total number of bytes allocated
size_t gpr_arena_destroy(gpr_arena* arena);
+// Initializes the Arena component.
+void gpr_arena_init();
#endif /* GRPC_CORE_LIB_GPR_ARENA_H */
diff --git a/src/core/lib/gprpp/ref_counted.h b/src/core/lib/gprpp/ref_counted.h
index 03c293f6ed..e366445bff 100644
--- a/src/core/lib/gprpp/ref_counted.h
+++ b/src/core/lib/gprpp/ref_counted.h
@@ -24,6 +24,8 @@
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
+#include <atomic>
+#include <cassert>
#include <cinttypes>
#include "src/core/lib/debug/trace.h"
@@ -34,14 +36,99 @@
namespace grpc_core {
+// PolymorphicRefCount enforces polymorphic destruction of RefCounted.
+class PolymorphicRefCount {
+ public:
+ GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+
+ virtual ~PolymorphicRefCount() = default;
+};
+
+// NonPolymorphicRefCount does not enforce polymorphic destruction of
+// RefCounted. Please refer to grpc_core::RefCounted for more details, and
+// when in doubt use PolymorphicRefCount.
+class NonPolymorphicRefCount {
+ public:
+ GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+
+ ~NonPolymorphicRefCount() = default;
+};
+
+// RefCount is a simple atomic ref-count.
+//
+// This is a C++ implementation of gpr_refcount, with inline functions. Due to
+// inline functions, this class is significantly more efficient than
+// gpr_refcount and should be preferred over gpr_refcount whenever possible.
+//
+// TODO(soheil): Remove gpr_refcount after submitting the GRFC and the paragraph
+// above.
+class RefCount {
+ public:
+ using Value = intptr_t;
+
+ // `init` is the initial refcount stored in this object.
+ constexpr explicit RefCount(Value init = 1) : value_(init) {}
+
+ // Increases the ref-count by `n`.
+ void Ref(Value n = 1) { value_.fetch_add(n, std::memory_order_relaxed); }
+
+ // Similar to Ref() with an assert on the ref-count being non-zero.
+ void RefNonZero() {
+#ifndef NDEBUG
+ const Value prior = value_.fetch_add(1, std::memory_order_relaxed);
+ assert(prior > 0);
+#else
+ Ref();
+#endif
+ }
+
+ // Decrements the ref-count and returns true if the ref-count reaches 0.
+ bool Unref() {
+ const Value prior = value_.fetch_sub(1, std::memory_order_acq_rel);
+ GPR_DEBUG_ASSERT(prior > 0);
+ return prior == 1;
+ }
+
+ Value get() const { return value_.load(std::memory_order_relaxed); }
+
+ private:
+ std::atomic<Value> value_;
+};
+
// A base class for reference-counted objects.
// New objects should be created via New() and start with a refcount of 1.
// When the refcount reaches 0, the object will be deleted via Delete().
//
// This will commonly be used by CRTP (curiously-recurring template pattern)
// e.g., class MyClass : public RefCounted<MyClass>
-template <typename Child>
-class RefCounted {
+//
+// Use PolymorphicRefCount and NonPolymorphicRefCount to select between
+// different implementations of RefCounted.
+//
+// Note that NonPolymorphicRefCount does not support polymorphic destruction.
+// So, use NonPolymorphicRefCount only when both of the following conditions
+// are guaranteed to hold:
+// (a) Child is a concrete leaf class in RefCounted<Child>, and
+// (b) you are gauranteed to call Unref only on concrete leaf classes and not
+// their parents.
+//
+// The following example is illegal, because calling Unref() will not call
+// the dtor of Child.
+//
+// class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {}
+// class Child : public Parent {}
+//
+// Child* ch;
+// ch->Unref();
+//
+template <typename Child, typename Impl = PolymorphicRefCount>
+class RefCounted : public Impl {
public:
RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
IncrementRefCount();
@@ -53,7 +140,7 @@ class RefCounted {
// private, since it will only be used by RefCountedPtr<>, which is a
// friend of this class.
void Unref() {
- if (gpr_unref(&refs_)) {
+ if (refs_.Unref()) {
Delete(static_cast<Child*>(this));
}
}
@@ -67,18 +154,19 @@ class RefCounted {
protected:
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
- RefCounted() { gpr_ref_init(&refs_, 1); }
+ RefCounted() = default;
- virtual ~RefCounted() {}
+ // Note: Depending on the Impl used, this dtor can be implicitly virtual.
+ ~RefCounted() = default;
private:
// Allow RefCountedPtr<> to access IncrementRefCount().
template <typename T>
friend class RefCountedPtr;
- void IncrementRefCount() { gpr_ref(&refs_); }
+ void IncrementRefCount() { refs_.Ref(); }
- gpr_refcount refs_;
+ RefCount refs_;
};
// An alternative version of the RefCounted base class that
@@ -87,8 +175,8 @@ class RefCounted {
// pointers and legacy code that is manually calling Ref() and Unref().
// Once all of our code is converted to idiomatic C++, we may be able to
// eliminate this class.
-template <typename Child>
-class RefCountedWithTracing {
+template <typename Child, typename Impl = PolymorphicRefCount>
+class RefCountedWithTracing : public Impl {
public:
RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
IncrementRefCount();
@@ -98,7 +186,7 @@ class RefCountedWithTracing {
RefCountedPtr<Child> Ref(const DebugLocation& location,
const char* reason) GRPC_MUST_USE_RESULT {
if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) {
- gpr_atm old_refs = gpr_atm_no_barrier_load(&refs_.count);
+ const RefCount::Value old_refs = refs_.get();
gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s",
trace_flag_->name(), this, location.file(), location.line(),
old_refs, old_refs + 1, reason);
@@ -112,14 +200,14 @@ class RefCountedWithTracing {
// friend of this class.
void Unref() {
- if (gpr_unref(&refs_)) {
+ if (refs_.Unref()) {
Delete(static_cast<Child*>(this));
}
}
void Unref(const DebugLocation& location, const char* reason) {
if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) {
- gpr_atm old_refs = gpr_atm_no_barrier_load(&refs_.count);
+ const RefCount::Value old_refs = refs_.get();
gpr_log(GPR_INFO, "%s:%p %s:%d unref %" PRIdPTR " -> %" PRIdPTR " %s",
trace_flag_->name(), this, location.file(), location.line(),
old_refs, old_refs - 1, reason);
@@ -140,26 +228,25 @@ class RefCountedWithTracing {
: RefCountedWithTracing(static_cast<TraceFlag*>(nullptr)) {}
explicit RefCountedWithTracing(TraceFlag* trace_flag)
- : trace_flag_(trace_flag) {
- gpr_ref_init(&refs_, 1);
- }
+ : trace_flag_(trace_flag) {}
#ifdef NDEBUG
explicit RefCountedWithTracing(DebugOnlyTraceFlag* trace_flag)
: RefCountedWithTracing() {}
#endif
- virtual ~RefCountedWithTracing() {}
+ // Note: Depending on the Impl used, this dtor can be implicitly virtual.
+ ~RefCountedWithTracing() = default;
private:
// Allow RefCountedPtr<> to access IncrementRefCount().
template <typename T>
friend class RefCountedPtr;
- void IncrementRefCount() { gpr_ref(&refs_); }
+ void IncrementRefCount() { refs_.Ref(); }
TraceFlag* trace_flag_ = nullptr;
- gpr_refcount refs_;
+ RefCount refs_;
};
} // namespace grpc_core
diff --git a/src/core/lib/gprpp/ref_counted_ptr.h b/src/core/lib/gprpp/ref_counted_ptr.h
index c2dfbdd90f..facd7c6dce 100644
--- a/src/core/lib/gprpp/ref_counted_ptr.h
+++ b/src/core/lib/gprpp/ref_counted_ptr.h
@@ -21,6 +21,7 @@
#include <grpc/support/port_platform.h>
+#include <type_traits>
#include <utility>
#include "src/core/lib/gprpp/memory.h"
@@ -74,6 +75,8 @@ class RefCountedPtr {
}
template <typename Y>
RefCountedPtr(const RefCountedPtr<Y>& other) {
+ static_assert(std::has_virtual_destructor<T>::value,
+ "T does not have a virtual dtor");
if (other.value_ != nullptr) other.value_->IncrementRefCount();
value_ = other.value_;
}
@@ -89,6 +92,8 @@ class RefCountedPtr {
}
template <typename Y>
RefCountedPtr& operator=(const RefCountedPtr<Y>& other) {
+ static_assert(std::has_virtual_destructor<T>::value,
+ "T does not have a virtual dtor");
// Note: Order of reffing and unreffing is important here in case value_
// and other.value_ are the same object.
if (other.value_ != nullptr) other.value_->IncrementRefCount();
@@ -102,8 +107,14 @@ class RefCountedPtr {
}
// If value is non-null, we take ownership of a ref to it.
+ void reset(T* value) {
+ if (value_ != nullptr) value_->Unref();
+ value_ = value;
+ }
template <typename Y>
void reset(Y* value) {
+ static_assert(std::has_virtual_destructor<T>::value,
+ "T does not have a virtual dtor");
if (value_ != nullptr) value_->Unref();
value_ = value;
}
diff --git a/src/core/lib/iomgr/call_combiner.cc b/src/core/lib/iomgr/call_combiner.cc
index 00a839b64c..90dda45ba3 100644
--- a/src/core/lib/iomgr/call_combiner.cc
+++ b/src/core/lib/iomgr/call_combiner.cc
@@ -40,6 +40,8 @@ static gpr_atm encode_cancel_state_error(grpc_error* error) {
}
void grpc_call_combiner_init(grpc_call_combiner* call_combiner) {
+ gpr_atm_no_barrier_store(&call_combiner->cancel_state, 0);
+ gpr_atm_no_barrier_store(&call_combiner->size, 0);
gpr_mpscq_init(&call_combiner->queue);
}
diff --git a/src/core/lib/iomgr/call_combiner.h b/src/core/lib/iomgr/call_combiner.h
index 6f7ddd4043..c943fb1557 100644
--- a/src/core/lib/iomgr/call_combiner.h
+++ b/src/core/lib/iomgr/call_combiner.h
@@ -41,12 +41,12 @@
extern grpc_core::TraceFlag grpc_call_combiner_trace;
typedef struct {
- gpr_atm size; // size_t, num closures in queue or currently executing
+ gpr_atm size = 0; // size_t, num closures in queue or currently executing
gpr_mpscq queue;
// Either 0 (if not cancelled and no cancellation closure set),
// a grpc_closure* (if the lowest bit is 0),
// or a grpc_error* (if the lowest bit is 1).
- gpr_atm cancel_state;
+ gpr_atm cancel_state = 0;
} grpc_call_combiner;
// Assumes memory was initialized to zero.
diff --git a/src/core/lib/iomgr/closure.h b/src/core/lib/iomgr/closure.h
index f14c723844..bde3437c02 100644
--- a/src/core/lib/iomgr/closure.h
+++ b/src/core/lib/iomgr/closure.h
@@ -114,6 +114,7 @@ inline grpc_closure* grpc_closure_init(grpc_closure* closure,
closure->cb = cb;
closure->cb_arg = cb_arg;
closure->scheduler = scheduler;
+ closure->error_data.error = GRPC_ERROR_NONE;
#ifndef NDEBUG
closure->scheduled = false;
closure->file_initiated = nullptr;
diff --git a/src/core/lib/iomgr/polling_entity.h b/src/core/lib/iomgr/polling_entity.h
index a95e08524c..6f4c5bdd66 100644
--- a/src/core/lib/iomgr/polling_entity.h
+++ b/src/core/lib/iomgr/polling_entity.h
@@ -34,13 +34,13 @@ typedef enum grpc_pollset_tag {
* functions that accept a pollset XOR a pollset_set to do so through an
* abstract interface. No ownership is taken. */
-typedef struct grpc_polling_entity {
+struct grpc_polling_entity {
union {
- grpc_pollset* pollset;
+ grpc_pollset* pollset = nullptr;
grpc_pollset_set* pollset_set;
} pollent;
- grpc_pollset_tag tag;
-} grpc_polling_entity;
+ grpc_pollset_tag tag = GRPC_POLLS_NONE;
+};
grpc_polling_entity grpc_polling_entity_create_from_pollset_set(
grpc_pollset_set* pollset_set);
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/socket_utils_common_posix.cc b/src/core/lib/iomgr/socket_utils_common_posix.cc
index bdfc1d70c3..4c337a0521 100644
--- a/src/core/lib/iomgr/socket_utils_common_posix.cc
+++ b/src/core/lib/iomgr/socket_utils_common_posix.cc
@@ -296,14 +296,17 @@ grpc_error* grpc_set_socket_tcp_user_timeout(
socklen_t len = sizeof(newval);
if (0 != setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout,
sizeof(timeout))) {
- return GRPC_OS_ERROR(errno, "setsockopt(TCP_USER_TIMEOUT)");
+ gpr_log(GPR_ERROR, "setsockopt(TCP_USER_TIMEOUT) %s", strerror(errno));
+ return GRPC_ERROR_NONE;
}
if (0 != getsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &newval, &len)) {
- return GRPC_OS_ERROR(errno, "getsockopt(TCP_USER_TIMEOUT)");
+ gpr_log(GPR_ERROR, "getsockopt(TCP_USER_TIMEOUT) %s", strerror(errno));
+ return GRPC_ERROR_NONE;
}
if (newval != timeout) {
- return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
- "Failed to set TCP_USER_TIMEOUT");
+ /* Do not fail on failing to set TCP_USER_TIMEOUT for now. */
+ gpr_log(GPR_ERROR, "Failed to set TCP_USER_TIMEOUT");
+ return GRPC_ERROR_NONE;
}
}
#else
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/iomgr/tcp_client_posix.cc b/src/core/lib/iomgr/tcp_client_posix.cc
index 8553ed0db4..0bff74e88b 100644
--- a/src/core/lib/iomgr/tcp_client_posix.cc
+++ b/src/core/lib/iomgr/tcp_client_posix.cc
@@ -76,6 +76,8 @@ static grpc_error* prepare_socket(const grpc_resolved_address* addr, int fd,
if (!grpc_is_unix_socket(addr)) {
err = grpc_set_socket_low_latency(fd, 1);
if (err != GRPC_ERROR_NONE) goto error;
+ err = grpc_set_socket_reuse_addr(fd, 1);
+ if (err != GRPC_ERROR_NONE) goto error;
err = grpc_set_socket_tcp_user_timeout(fd, channel_args,
true /* is_client */);
if (err != GRPC_ERROR_NONE) goto error;
diff --git a/src/core/lib/security/context/security_context.cc b/src/core/lib/security/context/security_context.cc
index 94c9c69fcd..16f40b4f55 100644
--- a/src/core/lib/security/context/security_context.cc
+++ b/src/core/lib/security/context/security_context.cc
@@ -81,38 +81,45 @@ void grpc_auth_context_release(grpc_auth_context* context) {
}
/* --- grpc_client_security_context --- */
+grpc_client_security_context::~grpc_client_security_context() {
+ grpc_call_credentials_unref(creds);
+ GRPC_AUTH_CONTEXT_UNREF(auth_context, "client_security_context");
+ if (extension.instance != nullptr && extension.destroy != nullptr) {
+ extension.destroy(extension.instance);
+ }
+}
grpc_client_security_context* grpc_client_security_context_create(
gpr_arena* arena) {
- return static_cast<grpc_client_security_context*>(
- gpr_arena_alloc(arena, sizeof(grpc_client_security_context)));
+ return new (gpr_arena_alloc(arena, sizeof(grpc_client_security_context)))
+ grpc_client_security_context();
}
void grpc_client_security_context_destroy(void* ctx) {
grpc_core::ExecCtx exec_ctx;
grpc_client_security_context* c =
static_cast<grpc_client_security_context*>(ctx);
- grpc_call_credentials_unref(c->creds);
- GRPC_AUTH_CONTEXT_UNREF(c->auth_context, "client_security_context");
- if (c->extension.instance != nullptr && c->extension.destroy != nullptr) {
- c->extension.destroy(c->extension.instance);
- }
+ c->~grpc_client_security_context();
}
/* --- grpc_server_security_context --- */
+grpc_server_security_context::~grpc_server_security_context() {
+ GRPC_AUTH_CONTEXT_UNREF(auth_context, "server_security_context");
+ if (extension.instance != nullptr && extension.destroy != nullptr) {
+ extension.destroy(extension.instance);
+ }
+}
+
grpc_server_security_context* grpc_server_security_context_create(
gpr_arena* arena) {
- return static_cast<grpc_server_security_context*>(
- gpr_arena_alloc(arena, sizeof(grpc_server_security_context)));
+ return new (gpr_arena_alloc(arena, sizeof(grpc_server_security_context)))
+ grpc_server_security_context();
}
void grpc_server_security_context_destroy(void* ctx) {
grpc_server_security_context* c =
static_cast<grpc_server_security_context*>(ctx);
- GRPC_AUTH_CONTEXT_UNREF(c->auth_context, "server_security_context");
- if (c->extension.instance != nullptr && c->extension.destroy != nullptr) {
- c->extension.destroy(c->extension.instance);
- }
+ c->~grpc_server_security_context();
}
/* --- grpc_auth_context --- */
diff --git a/src/core/lib/security/context/security_context.h b/src/core/lib/security/context/security_context.h
index a8e1c3fd64..e45415f63b 100644
--- a/src/core/lib/security/context/security_context.h
+++ b/src/core/lib/security/context/security_context.h
@@ -34,18 +34,20 @@ struct gpr_arena;
/* Property names are always NULL terminated. */
-typedef struct {
- grpc_auth_property* array;
- size_t count;
- size_t capacity;
-} grpc_auth_property_array;
+struct grpc_auth_property_array {
+ grpc_auth_property* array = nullptr;
+ size_t count = 0;
+ size_t capacity = 0;
+};
struct grpc_auth_context {
- struct grpc_auth_context* chained;
+ grpc_auth_context() { gpr_ref_init(&refcount, 0); }
+
+ struct grpc_auth_context* chained = nullptr;
grpc_auth_property_array properties;
gpr_refcount refcount;
- const char* peer_identity_property_name;
- grpc_pollset* pollset;
+ const char* peer_identity_property_name = nullptr;
+ grpc_pollset* pollset = nullptr;
};
/* Creation. */
@@ -76,20 +78,23 @@ void grpc_auth_property_reset(grpc_auth_property* property);
Extension to the security context that may be set in a filter and accessed
later by a higher level method on a grpc_call object. */
-typedef struct {
- void* instance;
- void (*destroy)(void*);
-} grpc_security_context_extension;
+struct grpc_security_context_extension {
+ void* instance = nullptr;
+ void (*destroy)(void*) = nullptr;
+};
/* --- grpc_client_security_context ---
Internal client-side security context. */
-typedef struct {
- grpc_call_credentials* creds;
- grpc_auth_context* auth_context;
+struct grpc_client_security_context {
+ grpc_client_security_context() = default;
+ ~grpc_client_security_context();
+
+ grpc_call_credentials* creds = nullptr;
+ grpc_auth_context* auth_context = nullptr;
grpc_security_context_extension extension;
-} grpc_client_security_context;
+};
grpc_client_security_context* grpc_client_security_context_create(
gpr_arena* arena);
@@ -99,10 +104,13 @@ void grpc_client_security_context_destroy(void* ctx);
Internal server-side security context. */
-typedef struct {
- grpc_auth_context* auth_context;
+struct grpc_server_security_context {
+ grpc_server_security_context() = default;
+ ~grpc_server_security_context();
+
+ grpc_auth_context* auth_context = nullptr;
grpc_security_context_extension extension;
-} grpc_server_security_context;
+};
grpc_server_security_context* grpc_server_security_context_create(
gpr_arena* arena);
diff --git a/src/core/lib/security/credentials/credentials.h b/src/core/lib/security/credentials/credentials.h
index b486d25ab2..3878958b38 100644
--- a/src/core/lib/security/credentials/credentials.h
+++ b/src/core/lib/security/credentials/credentials.h
@@ -142,8 +142,8 @@ grpc_channel_credentials* grpc_channel_credentials_find_in_args(
/* --- grpc_credentials_mdelem_array. --- */
typedef struct {
- grpc_mdelem* md;
- size_t size;
+ grpc_mdelem* md = nullptr;
+ size_t size = 0;
} grpc_credentials_mdelem_array;
/// Takes a new ref to \a md.
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/security/transport/client_auth_filter.cc b/src/core/lib/security/transport/client_auth_filter.cc
index e34eacc8d7..6955e8698e 100644
--- a/src/core/lib/security/transport/client_auth_filter.cc
+++ b/src/core/lib/security/transport/client_auth_filter.cc
@@ -43,20 +43,39 @@
namespace {
/* We can have a per-call credentials. */
struct call_data {
+ call_data(grpc_call_element* elem, const grpc_call_element_args& args)
+ : arena(args.arena),
+ owning_call(args.call_stack),
+ call_combiner(args.call_combiner) {}
+
+ // This method is technically the dtor of this class. However, since
+ // `get_request_metadata_cancel_closure` can run in parallel to
+ // `destroy_call_elem`, we cannot call the dtor in them. Otherwise,
+ // fields will be accessed after calling dtor, and msan correctly complains
+ // that the memory is not initialized.
+ void destroy() {
+ grpc_credentials_mdelem_array_destroy(&md_array);
+ grpc_call_credentials_unref(creds);
+ grpc_slice_unref_internal(host);
+ grpc_slice_unref_internal(method);
+ grpc_auth_metadata_context_reset(&auth_md_context);
+ }
+
gpr_arena* arena;
grpc_call_stack* owning_call;
grpc_call_combiner* call_combiner;
- grpc_call_credentials* creds;
- grpc_slice host;
- grpc_slice method;
+ grpc_call_credentials* creds = nullptr;
+ grpc_slice host = grpc_empty_slice();
+ grpc_slice method = grpc_empty_slice();
/* pollset{_set} bound to this call; if we need to make external
network requests, they should be done under a pollset added to this
pollset_set so that work can progress when this call wants work to progress
*/
- grpc_polling_entity* pollent;
+ grpc_polling_entity* pollent = nullptr;
grpc_credentials_mdelem_array md_array;
- grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT];
- grpc_auth_metadata_context auth_md_context;
+ grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT] = {};
+ grpc_auth_metadata_context auth_md_context =
+ grpc_auth_metadata_context(); // Zero-initialize the C struct.
grpc_closure async_result_closure;
grpc_closure check_call_host_cancel_closure;
grpc_closure get_request_metadata_cancel_closure;
@@ -334,12 +353,7 @@ static void auth_start_transport_stream_op_batch(
/* Constructor for call_data */
static grpc_error* init_call_elem(grpc_call_element* elem,
const grpc_call_element_args* args) {
- call_data* calld = static_cast<call_data*>(elem->call_data);
- calld->arena = args->arena;
- calld->owning_call = args->call_stack;
- calld->call_combiner = args->call_combiner;
- calld->host = grpc_empty_slice();
- calld->method = grpc_empty_slice();
+ new (elem->call_data) call_data(elem, *args);
return GRPC_ERROR_NONE;
}
@@ -354,11 +368,7 @@ static void destroy_call_elem(grpc_call_element* elem,
const grpc_call_final_info* final_info,
grpc_closure* ignored) {
call_data* calld = static_cast<call_data*>(elem->call_data);
- grpc_credentials_mdelem_array_destroy(&calld->md_array);
- grpc_call_credentials_unref(calld->creds);
- grpc_slice_unref_internal(calld->host);
- grpc_slice_unref_internal(calld->method);
- grpc_auth_metadata_context_reset(&calld->auth_md_context);
+ calld->destroy();
}
/* Constructor for channel_data */
diff --git a/src/core/lib/security/transport/secure_endpoint.cc b/src/core/lib/security/transport/secure_endpoint.cc
index f40f969bb7..34d8435907 100644
--- a/src/core/lib/security/transport/secure_endpoint.cc
+++ b/src/core/lib/security/transport/secure_endpoint.cc
@@ -22,6 +22,8 @@
headers. Therefore, sockaddr.h must always be included first */
#include <grpc/support/port_platform.h>
+#include <new>
+
#include "src/core/lib/iomgr/sockaddr.h"
#include <grpc/slice.h>
@@ -31,6 +33,7 @@
#include <grpc/support/sync.h>
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/profiling/timers.h"
#include "src/core/lib/security/transport/secure_endpoint.h"
#include "src/core/lib/security/transport/tsi_error.h"
@@ -40,44 +43,68 @@
#define STAGING_BUFFER_SIZE 8192
-typedef struct {
+static void on_read(void* user_data, grpc_error* error);
+
+namespace {
+struct secure_endpoint {
+ secure_endpoint(const grpc_endpoint_vtable* vtable,
+ tsi_frame_protector* protector,
+ tsi_zero_copy_grpc_protector* zero_copy_protector,
+ grpc_endpoint* transport, grpc_slice* leftover_slices,
+ size_t leftover_nslices)
+ : wrapped_ep(transport),
+ protector(protector),
+ zero_copy_protector(zero_copy_protector) {
+ base.vtable = vtable;
+ gpr_mu_init(&protector_mu);
+ GRPC_CLOSURE_INIT(&on_read, ::on_read, this, grpc_schedule_on_exec_ctx);
+ grpc_slice_buffer_init(&source_buffer);
+ grpc_slice_buffer_init(&leftover_bytes);
+ for (size_t i = 0; i < leftover_nslices; i++) {
+ grpc_slice_buffer_add(&leftover_bytes,
+ grpc_slice_ref_internal(leftover_slices[i]));
+ }
+ grpc_slice_buffer_init(&output_buffer);
+ gpr_ref_init(&ref, 1);
+ }
+
+ ~secure_endpoint() {
+ grpc_endpoint_destroy(wrapped_ep);
+ tsi_frame_protector_destroy(protector);
+ tsi_zero_copy_grpc_protector_destroy(zero_copy_protector);
+ grpc_slice_buffer_destroy_internal(&source_buffer);
+ grpc_slice_buffer_destroy_internal(&leftover_bytes);
+ grpc_slice_unref_internal(read_staging_buffer);
+ grpc_slice_unref_internal(write_staging_buffer);
+ grpc_slice_buffer_destroy_internal(&output_buffer);
+ gpr_mu_destroy(&protector_mu);
+ }
+
grpc_endpoint base;
grpc_endpoint* wrapped_ep;
struct tsi_frame_protector* protector;
struct tsi_zero_copy_grpc_protector* zero_copy_protector;
gpr_mu protector_mu;
/* saved upper level callbacks and user_data. */
- grpc_closure* read_cb;
- grpc_closure* write_cb;
+ grpc_closure* read_cb = nullptr;
+ grpc_closure* write_cb = nullptr;
grpc_closure on_read;
- grpc_slice_buffer* read_buffer;
+ grpc_slice_buffer* read_buffer = nullptr;
grpc_slice_buffer source_buffer;
/* saved handshaker leftover data to unprotect. */
grpc_slice_buffer leftover_bytes;
/* buffers for read and write */
- grpc_slice read_staging_buffer;
-
- grpc_slice write_staging_buffer;
+ grpc_slice read_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE);
+ grpc_slice write_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE);
grpc_slice_buffer output_buffer;
gpr_refcount ref;
-} secure_endpoint;
+};
+} // namespace
grpc_core::TraceFlag grpc_trace_secure_endpoint(false, "secure_endpoint");
-static void destroy(secure_endpoint* secure_ep) {
- secure_endpoint* ep = secure_ep;
- grpc_endpoint_destroy(ep->wrapped_ep);
- tsi_frame_protector_destroy(ep->protector);
- tsi_zero_copy_grpc_protector_destroy(ep->zero_copy_protector);
- grpc_slice_buffer_destroy_internal(&ep->leftover_bytes);
- grpc_slice_unref_internal(ep->read_staging_buffer);
- grpc_slice_unref_internal(ep->write_staging_buffer);
- grpc_slice_buffer_destroy_internal(&ep->output_buffer);
- grpc_slice_buffer_destroy_internal(&ep->source_buffer);
- gpr_mu_destroy(&ep->protector_mu);
- gpr_free(ep);
-}
+static void destroy(secure_endpoint* ep) { grpc_core::Delete(ep); }
#ifndef NDEBUG
#define SECURE_ENDPOINT_UNREF(ep, reason) \
@@ -405,25 +432,8 @@ grpc_endpoint* grpc_secure_endpoint_create(
struct tsi_zero_copy_grpc_protector* zero_copy_protector,
grpc_endpoint* transport, grpc_slice* leftover_slices,
size_t leftover_nslices) {
- size_t i;
- secure_endpoint* ep =
- static_cast<secure_endpoint*>(gpr_malloc(sizeof(secure_endpoint)));
- ep->base.vtable = &vtable;
- ep->wrapped_ep = transport;
- ep->protector = protector;
- ep->zero_copy_protector = zero_copy_protector;
- grpc_slice_buffer_init(&ep->leftover_bytes);
- for (i = 0; i < leftover_nslices; i++) {
- grpc_slice_buffer_add(&ep->leftover_bytes,
- grpc_slice_ref_internal(leftover_slices[i]));
- }
- ep->write_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE);
- ep->read_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE);
- grpc_slice_buffer_init(&ep->output_buffer);
- grpc_slice_buffer_init(&ep->source_buffer);
- ep->read_buffer = nullptr;
- GRPC_CLOSURE_INIT(&ep->on_read, on_read, ep, grpc_schedule_on_exec_ctx);
- gpr_mu_init(&ep->protector_mu);
- gpr_ref_init(&ep->ref, 1);
+ secure_endpoint* ep = grpc_core::New<secure_endpoint>(
+ &vtable, protector, zero_copy_protector, transport, leftover_slices,
+ leftover_nslices);
return &ep->base;
}
diff --git a/src/core/lib/security/transport/security_handshaker.cc b/src/core/lib/security/transport/security_handshaker.cc
index 4d6b133809..854a1c4af9 100644
--- a/src/core/lib/security/transport/security_handshaker.cc
+++ b/src/core/lib/security/transport/security_handshaker.cc
@@ -275,9 +275,6 @@ static void on_handshake_next_done_grpc_wrapper(
tsi_result result, void* user_data, const unsigned char* bytes_to_send,
size_t bytes_to_send_size, tsi_handshaker_result* handshaker_result) {
security_handshaker* h = static_cast<security_handshaker*>(user_data);
- // This callback will be invoked by TSI in a non-grpc thread, so it's
- // safe to create our own exec_ctx here.
- grpc_core::ExecCtx exec_ctx;
gpr_mu_lock(&h->mu);
grpc_error* error = on_handshake_next_done_locked(
h, result, bytes_to_send, bytes_to_send_size, handshaker_result);
diff --git a/src/core/lib/security/transport/server_auth_filter.cc b/src/core/lib/security/transport/server_auth_filter.cc
index b99fc5e178..362f49a584 100644
--- a/src/core/lib/security/transport/server_auth_filter.cc
+++ b/src/core/lib/security/transport/server_auth_filter.cc
@@ -28,6 +28,9 @@
#include "src/core/lib/security/transport/auth_filters.h"
#include "src/core/lib/slice/slice_internal.h"
+static void recv_initial_metadata_ready(void* arg, grpc_error* error);
+static void recv_trailing_metadata_ready(void* user_data, grpc_error* error);
+
namespace {
enum async_state {
STATE_INIT = 0,
@@ -35,28 +38,55 @@ enum async_state {
STATE_CANCELLED,
};
+struct channel_data {
+ grpc_auth_context* auth_context;
+ grpc_server_credentials* creds;
+};
+
struct call_data {
+ call_data(grpc_call_element* elem, const grpc_call_element_args& args)
+ : call_combiner(args.call_combiner), owning_call(args.call_stack) {
+ GRPC_CLOSURE_INIT(&recv_initial_metadata_ready,
+ ::recv_initial_metadata_ready, elem,
+ grpc_schedule_on_exec_ctx);
+ GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
+ ::recv_trailing_metadata_ready, elem,
+ grpc_schedule_on_exec_ctx);
+ // Create server security context. Set its auth context from channel
+ // data and save it in the call context.
+ grpc_server_security_context* server_ctx =
+ grpc_server_security_context_create(args.arena);
+ channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+ server_ctx->auth_context =
+ GRPC_AUTH_CONTEXT_REF(chand->auth_context, "server_auth_filter");
+ if (args.context[GRPC_CONTEXT_SECURITY].value != nullptr) {
+ args.context[GRPC_CONTEXT_SECURITY].destroy(
+ args.context[GRPC_CONTEXT_SECURITY].value);
+ }
+ args.context[GRPC_CONTEXT_SECURITY].value = server_ctx;
+ args.context[GRPC_CONTEXT_SECURITY].destroy =
+ grpc_server_security_context_destroy;
+ }
+
+ ~call_data() { GRPC_ERROR_UNREF(recv_initial_metadata_error); }
+
grpc_call_combiner* call_combiner;
grpc_call_stack* owning_call;
grpc_transport_stream_op_batch* recv_initial_metadata_batch;
grpc_closure* original_recv_initial_metadata_ready;
grpc_closure recv_initial_metadata_ready;
- grpc_error* recv_initial_metadata_error;
+ grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
grpc_closure recv_trailing_metadata_ready;
grpc_closure* original_recv_trailing_metadata_ready;
grpc_error* recv_trailing_metadata_error;
- bool seen_recv_trailing_metadata_ready;
+ bool seen_recv_trailing_metadata_ready = false;
grpc_metadata_array md;
const grpc_metadata* consumed_md;
size_t num_consumed_md;
grpc_closure cancel_closure;
- gpr_atm state; // async_state
+ gpr_atm state = STATE_INIT; // async_state
};
-struct channel_data {
- grpc_auth_context* auth_context;
- grpc_server_credentials* creds;
-};
} // namespace
static grpc_metadata_array metadata_batch_to_md_array(
@@ -244,29 +274,7 @@ static void auth_start_transport_stream_op_batch(
/* Constructor for call_data */
static grpc_error* init_call_elem(grpc_call_element* elem,
const grpc_call_element_args* args) {
- call_data* calld = static_cast<call_data*>(elem->call_data);
- channel_data* chand = static_cast<channel_data*>(elem->channel_data);
- calld->call_combiner = args->call_combiner;
- calld->owning_call = args->call_stack;
- GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready,
- recv_initial_metadata_ready, elem,
- grpc_schedule_on_exec_ctx);
- GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready,
- recv_trailing_metadata_ready, elem,
- grpc_schedule_on_exec_ctx);
- // Create server security context. Set its auth context from channel
- // data and save it in the call context.
- grpc_server_security_context* server_ctx =
- grpc_server_security_context_create(args->arena);
- server_ctx->auth_context =
- GRPC_AUTH_CONTEXT_REF(chand->auth_context, "server_auth_filter");
- if (args->context[GRPC_CONTEXT_SECURITY].value != nullptr) {
- args->context[GRPC_CONTEXT_SECURITY].destroy(
- args->context[GRPC_CONTEXT_SECURITY].value);
- }
- args->context[GRPC_CONTEXT_SECURITY].value = server_ctx;
- args->context[GRPC_CONTEXT_SECURITY].destroy =
- grpc_server_security_context_destroy;
+ new (elem->call_data) call_data(elem, *args);
return GRPC_ERROR_NONE;
}
@@ -275,7 +283,7 @@ static void destroy_call_elem(grpc_call_element* elem,
const grpc_call_final_info* final_info,
grpc_closure* ignored) {
call_data* calld = static_cast<call_data*>(elem->call_data);
- GRPC_ERROR_UNREF(calld->recv_initial_metadata_error);
+ calld->~call_data();
}
/* Constructor for channel_data */
diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc
index a9349afa68..89b3f77822 100644
--- a/src/core/lib/surface/call.cc
+++ b/src/core/lib/surface/call.cc
@@ -72,8 +72,11 @@
// Used to create arena for the first call.
#define ESTIMATED_MDELEM_COUNT 16
-typedef struct batch_control {
- grpc_call* call;
+struct batch_control {
+ batch_control() { gpr_ref_init(&steps_to_complete, 0); }
+
+ grpc_call* call = nullptr;
+ grpc_transport_stream_op_batch op;
/* Share memory for cq_completion and notify_tag as they are never needed
simultaneously. Each byte used in this data structure count as six bytes
per call, so any savings we can make are worthwhile,
@@ -96,84 +99,110 @@ typedef struct batch_control {
grpc_closure start_batch;
grpc_closure finish_batch;
gpr_refcount steps_to_complete;
- gpr_atm batch_error;
- grpc_transport_stream_op_batch op;
-} batch_control;
+ gpr_atm batch_error = reinterpret_cast<gpr_atm>(GRPC_ERROR_NONE);
+};
+
+struct parent_call {
+ parent_call() { gpr_mu_init(&child_list_mu); }
+ ~parent_call() { gpr_mu_destroy(&child_list_mu); }
-typedef struct {
gpr_mu child_list_mu;
- grpc_call* first_child;
-} parent_call;
+ grpc_call* first_child = nullptr;
+};
-typedef struct {
+struct child_call {
+ child_call(grpc_call* parent) : parent(parent) {}
grpc_call* parent;
/** siblings: children of the same parent form a list, and this list is
protected under
parent->mu */
- grpc_call* sibling_next;
- grpc_call* sibling_prev;
-} child_call;
+ grpc_call* sibling_next = nullptr;
+ grpc_call* sibling_prev = nullptr;
+};
#define RECV_NONE ((gpr_atm)0)
#define RECV_INITIAL_METADATA_FIRST ((gpr_atm)1)
struct grpc_call {
+ grpc_call(gpr_arena* arena, const grpc_call_create_args& args)
+ : arena(arena),
+ cq(args.cq),
+ channel(args.channel),
+ is_client(args.server_transport_data == nullptr),
+ stream_op_payload(context) {
+ gpr_ref_init(&ext_ref, 1);
+ grpc_call_combiner_init(&call_combiner);
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ metadata_batch[i][j].deadline = GRPC_MILLIS_INF_FUTURE;
+ }
+ }
+ }
+
+ ~grpc_call() {
+ gpr_free(static_cast<void*>(const_cast<char*>(final_info.error_string)));
+ grpc_call_combiner_destroy(&call_combiner);
+ }
+
gpr_refcount ext_ref;
gpr_arena* arena;
grpc_call_combiner call_combiner;
grpc_completion_queue* cq;
grpc_polling_entity pollent;
grpc_channel* channel;
- gpr_timespec start_time;
- /* parent_call* */ gpr_atm parent_call_atm;
- child_call* child;
+ gpr_timespec start_time = gpr_now(GPR_CLOCK_MONOTONIC);
+ /* parent_call* */ gpr_atm parent_call_atm = 0;
+ child_call* child = nullptr;
/* client or server call */
bool is_client;
/** has grpc_call_unref been called */
- bool destroy_called;
+ bool destroy_called = false;
/** flag indicating that cancellation is inherited */
- bool cancellation_is_inherited;
+ bool cancellation_is_inherited = false;
/** which ops are in-flight */
- bool sent_initial_metadata;
- bool sending_message;
- bool sent_final_op;
- bool received_initial_metadata;
- bool receiving_message;
- bool requested_final_op;
- gpr_atm any_ops_sent_atm;
- gpr_atm received_final_op_atm;
-
- batch_control* active_batches[MAX_CONCURRENT_BATCHES];
+ bool sent_initial_metadata = false;
+ bool sending_message = false;
+ bool sent_final_op = false;
+ bool received_initial_metadata = false;
+ bool receiving_message = false;
+ bool requested_final_op = false;
+ gpr_atm any_ops_sent_atm = 0;
+ gpr_atm received_final_op_atm = 0;
+
+ batch_control* active_batches[MAX_CONCURRENT_BATCHES] = {};
grpc_transport_stream_op_batch_payload stream_op_payload;
/* first idx: is_receiving, second idx: is_trailing */
- grpc_metadata_batch metadata_batch[2][2];
+ grpc_metadata_batch metadata_batch[2][2] = {};
/* Buffered read metadata waiting to be returned to the application.
Element 0 is initial metadata, element 1 is trailing metadata. */
- grpc_metadata_array* buffered_metadata[2];
+ grpc_metadata_array* buffered_metadata[2] = {};
grpc_metadata compression_md;
// A char* indicating the peer name.
- gpr_atm peer_string;
+ gpr_atm peer_string = 0;
/* Call data useful used for reporting. Only valid after the call has
* completed */
grpc_call_final_info final_info;
/* Compression algorithm for *incoming* data */
- grpc_message_compression_algorithm incoming_message_compression_algorithm;
+ grpc_message_compression_algorithm incoming_message_compression_algorithm =
+ GRPC_MESSAGE_COMPRESS_NONE;
/* Stream compression algorithm for *incoming* data */
- grpc_stream_compression_algorithm incoming_stream_compression_algorithm;
- /* Supported encodings (compression algorithms), a bitset */
- uint32_t encodings_accepted_by_peer;
+ grpc_stream_compression_algorithm incoming_stream_compression_algorithm =
+ GRPC_STREAM_COMPRESS_NONE;
+ /* Supported encodings (compression algorithms), a bitset.
+ * Always support no compression. */
+ uint32_t encodings_accepted_by_peer = 1 << GRPC_MESSAGE_COMPRESS_NONE;
/* Supported stream encodings (stream compression algorithms), a bitset */
- uint32_t stream_encodings_accepted_by_peer;
+ uint32_t stream_encodings_accepted_by_peer = 0;
/* Contexts for various subsystems (security, tracing, ...). */
- grpc_call_context_element context[GRPC_CONTEXT_COUNT];
+ grpc_call_context_element context[GRPC_CONTEXT_COUNT] = {};
/* for the client, extra metadata is initial metadata; for the
server, it's trailing metadata */
@@ -184,14 +213,14 @@ struct grpc_call {
grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> sending_stream;
grpc_core::OrphanablePtr<grpc_core::ByteStream> receiving_stream;
- grpc_byte_buffer** receiving_buffer;
- grpc_slice receiving_slice;
+ grpc_byte_buffer** receiving_buffer = nullptr;
+ grpc_slice receiving_slice = grpc_empty_slice();
grpc_closure receiving_slice_ready;
grpc_closure receiving_stream_ready;
grpc_closure receiving_initial_metadata_ready;
grpc_closure receiving_trailing_metadata_ready;
- uint32_t test_only_last_message_flags;
- gpr_atm cancelled;
+ uint32_t test_only_last_message_flags = 0;
+ gpr_atm cancelled = 0;
grpc_closure release_call;
@@ -207,7 +236,7 @@ struct grpc_call {
grpc_server* server;
} server;
} final_op;
- gpr_atm status_error;
+ gpr_atm status_error = 0;
/* recv_state can contain one of the following values:
RECV_NONE : : no initial metadata and messages received
@@ -225,7 +254,7 @@ struct grpc_call {
For 1, 4: See receiving_initial_metadata_ready() function
For 2, 3: See receiving_stream_ready() function */
- gpr_atm recv_state;
+ gpr_atm recv_state = 0;
};
grpc_core::TraceFlag grpc_call_error_trace(false, "call_error");
@@ -269,11 +298,10 @@ void* grpc_call_arena_alloc(grpc_call* call, size_t size) {
static parent_call* get_or_create_parent_call(grpc_call* call) {
parent_call* p = (parent_call*)gpr_atm_acq_load(&call->parent_call_atm);
if (p == nullptr) {
- p = static_cast<parent_call*>(gpr_arena_alloc(call->arena, sizeof(*p)));
- gpr_mu_init(&p->child_list_mu);
+ p = new (gpr_arena_alloc(call->arena, sizeof(*p))) parent_call();
if (!gpr_atm_rel_cas(&call->parent_call_atm, (gpr_atm) nullptr,
(gpr_atm)p)) {
- gpr_mu_destroy(&p->child_list_mu);
+ p->~parent_call();
p = (parent_call*)gpr_atm_acq_load(&call->parent_call_atm);
}
}
@@ -292,7 +320,9 @@ size_t grpc_call_get_initial_size_estimate() {
grpc_error* grpc_call_create(const grpc_call_create_args* args,
grpc_call** out_call) {
GPR_TIMER_SCOPE("grpc_call_create", 0);
- size_t i, j;
+
+ GRPC_CHANNEL_INTERNAL_REF(args->channel, "call");
+
grpc_error* error = GRPC_ERROR_NONE;
grpc_channel_stack* channel_stack =
grpc_channel_get_channel_stack(args->channel);
@@ -300,27 +330,19 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args,
size_t initial_size = grpc_channel_get_call_size_estimate(args->channel);
GRPC_STATS_INC_CALL_INITIAL_SIZE(initial_size);
gpr_arena* arena = gpr_arena_create(initial_size);
- call = static_cast<grpc_call*>(
- gpr_arena_alloc(arena, GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)) +
- channel_stack->call_stack_size));
- gpr_ref_init(&call->ext_ref, 1);
- gpr_atm_no_barrier_store(&call->cancelled, 0);
- call->arena = arena;
- grpc_call_combiner_init(&call->call_combiner);
+ call = new (gpr_arena_alloc(
+ arena, GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)) +
+ channel_stack->call_stack_size)) grpc_call(arena, *args);
*out_call = call;
- call->channel = args->channel;
- call->cq = args->cq;
- call->start_time = gpr_now(GPR_CLOCK_MONOTONIC);
- /* Always support no compression */
- GPR_BITSET(&call->encodings_accepted_by_peer, GRPC_MESSAGE_COMPRESS_NONE);
- call->is_client = args->server_transport_data == nullptr;
- call->stream_op_payload.context = call->context;
grpc_slice path = grpc_empty_slice();
if (call->is_client) {
+ call->final_op.client.status_details = nullptr;
+ call->final_op.client.status = nullptr;
+ call->final_op.client.error_string = nullptr;
GRPC_STATS_INC_CLIENT_CALLS_CREATED();
GPR_ASSERT(args->add_initial_metadata_count <
MAX_SEND_EXTRA_METADATA_COUNT);
- for (i = 0; i < args->add_initial_metadata_count; i++) {
+ for (size_t i = 0; i < args->add_initial_metadata_count; i++) {
call->send_extra_metadata[i].md = args->add_initial_metadata[i];
if (grpc_slice_eq(GRPC_MDKEY(args->add_initial_metadata[i]),
GRPC_MDSTR_PATH)) {
@@ -332,23 +354,18 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args,
static_cast<int>(args->add_initial_metadata_count);
} else {
GRPC_STATS_INC_SERVER_CALLS_CREATED();
+ call->final_op.server.cancelled = nullptr;
call->final_op.server.server = args->server;
GPR_ASSERT(args->add_initial_metadata_count == 0);
call->send_extra_metadata_count = 0;
}
- for (i = 0; i < 2; i++) {
- for (j = 0; j < 2; j++) {
- call->metadata_batch[i][j].deadline = GRPC_MILLIS_INF_FUTURE;
- }
- }
- grpc_millis send_deadline = args->send_deadline;
+ grpc_millis send_deadline = args->send_deadline;
bool immediately_cancel = false;
if (args->parent != nullptr) {
- call->child =
- static_cast<child_call*>(gpr_arena_alloc(arena, sizeof(child_call)));
- call->child->parent = args->parent;
+ call->child = new (gpr_arena_alloc(arena, sizeof(child_call)))
+ child_call(args->parent);
GRPC_CALL_INTERNAL_REF(args->parent, "child");
GPR_ASSERT(call->is_client);
@@ -382,10 +399,7 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args,
}
}
}
-
call->send_deadline = send_deadline;
-
- GRPC_CHANNEL_INTERNAL_REF(args->channel, "call");
/* initial refcount dropped by grpc_call_unref */
grpc_call_element_args call_args = {CALL_STACK_FROM_CALL(call),
args->server_transport_data,
@@ -413,6 +427,7 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args,
}
gpr_mu_unlock(&pc->child_list_mu);
}
+
if (error != GRPC_ERROR_NONE) {
cancel_with_error(call, GRPC_ERROR_REF(error));
}
@@ -487,9 +502,9 @@ void grpc_call_internal_unref(grpc_call* c REF_ARG) {
static void release_call(void* call, grpc_error* error) {
grpc_call* c = static_cast<grpc_call*>(call);
grpc_channel* channel = c->channel;
- gpr_free(static_cast<void*>(const_cast<char*>(c->final_info.error_string)));
- grpc_call_combiner_destroy(&c->call_combiner);
- grpc_channel_update_call_size_estimate(channel, gpr_arena_destroy(c->arena));
+ gpr_arena* arena = c->arena;
+ c->~grpc_call();
+ grpc_channel_update_call_size_estimate(channel, gpr_arena_destroy(arena));
GRPC_CHANNEL_INTERNAL_UNREF(channel, "call");
}
@@ -505,7 +520,7 @@ static void destroy_call(void* call, grpc_error* error) {
c->receiving_stream.reset();
parent_call* pc = get_parent_call(c);
if (pc != nullptr) {
- gpr_mu_destroy(&pc->child_list_mu);
+ pc->~parent_call();
}
for (ii = 0; ii < c->send_extra_metadata_count; ii++) {
GRPC_MDELEM_UNREF(c->send_extra_metadata[ii].md);
@@ -679,6 +694,10 @@ static void cancel_with_error(grpc_call* c, grpc_error* error) {
execute_batch(c, op, &state->start_batch);
}
+void grpc_call_cancel_internal(grpc_call* call) {
+ cancel_with_error(call, GRPC_ERROR_CANCELLED);
+}
+
static grpc_error* error_from_status(grpc_status_code status,
const char* description) {
// copying 'description' is needed to ensure the grpc_call_cancel_with_status
@@ -1100,10 +1119,11 @@ static batch_control* reuse_or_allocate_batch_control(grpc_call* call,
if (bctl->call != nullptr) {
return nullptr;
}
- memset(bctl, 0, sizeof(*bctl));
+ bctl->~batch_control();
+ bctl->op = {};
} else {
- bctl = static_cast<batch_control*>(
- gpr_arena_alloc(call->arena, sizeof(batch_control)));
+ bctl = new (gpr_arena_alloc(call->arena, sizeof(batch_control)))
+ batch_control();
*pslot = bctl;
}
bctl->call = call;
diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h
index b34260505a..bd7295fe11 100644
--- a/src/core/lib/surface/call.h
+++ b/src/core/lib/surface/call.h
@@ -81,6 +81,10 @@ grpc_call_error grpc_call_start_batch_and_execute(grpc_call* call,
size_t nops,
grpc_closure* closure);
+/* gRPC core internal version of grpc_call_cancel that does not create
+ * exec_ctx. */
+void grpc_call_cancel_internal(grpc_call* call);
+
/* Given the top call_element, get the call object. */
grpc_call* grpc_call_from_top_element(grpc_call_element* surface_element);
diff --git a/src/core/lib/surface/channel.cc b/src/core/lib/surface/channel.cc
index d7095c24d4..e47cb4360e 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) {
@@ -310,8 +322,8 @@ static grpc_call* grpc_channel_create_call_internal(
}
grpc_call_create_args args;
- memset(&args, 0, sizeof(args));
args.channel = channel;
+ args.server = nullptr;
args.parent = parent_call;
args.propagation_mask = propagation_mask;
args.cq = cq;
@@ -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/completion_queue.cc b/src/core/lib/surface/completion_queue.cc
index b81ae73b4d..661022ec5f 100644
--- a/src/core/lib/surface/completion_queue.cc
+++ b/src/core/lib/surface/completion_queue.cc
@@ -859,8 +859,8 @@ static void cq_end_op_for_callback(
gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1);
if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) {
- cq_finish_shutdown_callback(cq);
gpr_mu_unlock(cq->mu);
+ cq_finish_shutdown_callback(cq);
} else {
gpr_mu_unlock(cq->mu);
}
diff --git a/src/core/lib/surface/init.cc b/src/core/lib/surface/init.cc
index 0ad82fed99..c6198b8ae7 100644
--- a/src/core/lib/surface/init.cc
+++ b/src/core/lib/surface/init.cc
@@ -123,6 +123,7 @@ void grpc_init(void) {
grpc_core::Fork::GlobalInit();
grpc_fork_handlers_auto_register();
gpr_time_init();
+ gpr_arena_init();
grpc_stats_init();
grpc_slice_intern_init();
grpc_mdctx_global_init();
diff --git a/src/core/lib/surface/server.cc b/src/core/lib/surface/server.cc
index 35ab2c3bce..5dc81b29bb 100644
--- a/src/core/lib/surface/server.cc
+++ b/src/core/lib/surface/server.cc
@@ -47,6 +47,10 @@
grpc_core::TraceFlag grpc_server_channel_trace(false, "server_channel");
+static void server_on_recv_initial_metadata(void* ptr, grpc_error* error);
+static void server_recv_trailing_metadata_ready(void* user_data,
+ grpc_error* error);
+
namespace {
struct listener {
void* arg;
@@ -128,38 +132,62 @@ typedef enum {
typedef struct request_matcher request_matcher;
struct call_data {
+ call_data(grpc_call_element* elem, const grpc_call_element_args& args)
+ : call(grpc_call_from_top_element(elem)),
+ call_combiner(args.call_combiner) {
+ GRPC_CLOSURE_INIT(&server_on_recv_initial_metadata,
+ ::server_on_recv_initial_metadata, elem,
+ grpc_schedule_on_exec_ctx);
+ GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
+ server_recv_trailing_metadata_ready, elem,
+ grpc_schedule_on_exec_ctx);
+ }
+ ~call_data() {
+ GPR_ASSERT(state != PENDING);
+ GRPC_ERROR_UNREF(recv_initial_metadata_error);
+ if (host_set) {
+ grpc_slice_unref_internal(host);
+ }
+ if (path_set) {
+ grpc_slice_unref_internal(path);
+ }
+ grpc_metadata_array_destroy(&initial_metadata);
+ grpc_byte_buffer_destroy(payload);
+ }
+
grpc_call* call;
- gpr_atm state;
+ gpr_atm state = NOT_STARTED;
- bool path_set;
- bool host_set;
+ bool path_set = false;
+ bool host_set = false;
grpc_slice path;
grpc_slice host;
- grpc_millis deadline;
+ grpc_millis deadline = GRPC_MILLIS_INF_FUTURE;
- grpc_completion_queue* cq_new;
+ grpc_completion_queue* cq_new = nullptr;
- grpc_metadata_batch* recv_initial_metadata;
- uint32_t recv_initial_metadata_flags;
- grpc_metadata_array initial_metadata;
+ grpc_metadata_batch* recv_initial_metadata = nullptr;
+ uint32_t recv_initial_metadata_flags = 0;
+ grpc_metadata_array initial_metadata =
+ grpc_metadata_array(); // Zero-initialize the C struct.
- request_matcher* matcher;
- grpc_byte_buffer* payload;
+ request_matcher* matcher = nullptr;
+ grpc_byte_buffer* payload = nullptr;
grpc_closure got_initial_metadata;
grpc_closure server_on_recv_initial_metadata;
grpc_closure kill_zombie_closure;
grpc_closure* on_done_recv_initial_metadata;
grpc_closure recv_trailing_metadata_ready;
- grpc_error* recv_initial_metadata_error;
+ grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
grpc_closure* original_recv_trailing_metadata_ready;
- grpc_error* recv_trailing_metadata_error;
- bool seen_recv_trailing_metadata_ready;
+ grpc_error* recv_trailing_metadata_error = GRPC_ERROR_NONE;
+ bool seen_recv_trailing_metadata_ready = false;
grpc_closure publish;
- call_data* pending_next;
+ call_data* pending_next = nullptr;
grpc_call_combiner* call_combiner;
};
@@ -189,6 +217,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;
@@ -822,11 +852,16 @@ static void accept_stream(void* cd, grpc_transport* transport,
channel_data* chand = static_cast<channel_data*>(cd);
/* create a call */
grpc_call_create_args args;
- memset(&args, 0, sizeof(args));
args.channel = chand->channel;
+ args.server = chand->server;
+ args.parent = nullptr;
+ args.propagation_mask = 0;
+ args.cq = nullptr;
+ args.pollset_set_alternative = nullptr;
args.server_transport_data = transport_server_data;
+ args.add_initial_metadata = nullptr;
+ args.add_initial_metadata_count = 0;
args.send_deadline = GRPC_MILLIS_INF_FUTURE;
- args.server = chand->server;
grpc_call* call;
grpc_error* error = grpc_call_create(&args, &call);
grpc_call_element* elem =
@@ -838,8 +873,9 @@ static void accept_stream(void* cd, grpc_transport* transport,
}
call_data* calld = static_cast<call_data*>(elem->call_data);
grpc_op op;
- memset(&op, 0, sizeof(op));
op.op = GRPC_OP_RECV_INITIAL_METADATA;
+ op.flags = 0;
+ op.reserved = nullptr;
op.data.recv_initial_metadata.recv_initial_metadata =
&calld->initial_metadata;
GRPC_CLOSURE_INIT(&calld->got_initial_metadata, got_initial_metadata, elem,
@@ -867,40 +903,18 @@ static void channel_connectivity_changed(void* cd, grpc_error* error) {
static grpc_error* init_call_elem(grpc_call_element* elem,
const grpc_call_element_args* args) {
- call_data* calld = static_cast<call_data*>(elem->call_data);
channel_data* chand = static_cast<channel_data*>(elem->channel_data);
- memset(calld, 0, sizeof(call_data));
- calld->deadline = GRPC_MILLIS_INF_FUTURE;
- calld->call = grpc_call_from_top_element(elem);
- calld->call_combiner = args->call_combiner;
-
- GRPC_CLOSURE_INIT(&calld->server_on_recv_initial_metadata,
- server_on_recv_initial_metadata, elem,
- grpc_schedule_on_exec_ctx);
- GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready,
- server_recv_trailing_metadata_ready, elem,
- grpc_schedule_on_exec_ctx);
server_ref(chand->server);
+ new (elem->call_data) call_data(elem, *args);
return GRPC_ERROR_NONE;
}
static void destroy_call_elem(grpc_call_element* elem,
const grpc_call_final_info* final_info,
grpc_closure* ignored) {
- channel_data* chand = static_cast<channel_data*>(elem->channel_data);
call_data* calld = static_cast<call_data*>(elem->call_data);
-
- GPR_ASSERT(calld->state != PENDING);
- GRPC_ERROR_UNREF(calld->recv_initial_metadata_error);
- if (calld->host_set) {
- grpc_slice_unref_internal(calld->host);
- }
- if (calld->path_set) {
- grpc_slice_unref_internal(calld->path);
- }
- grpc_metadata_array_destroy(&calld->initial_metadata);
- grpc_byte_buffer_destroy(calld->payload);
-
+ calld->~call_data();
+ channel_data* chand = static_cast<channel_data*>(elem->channel_data);
server_unref(chand->server);
}
@@ -1024,6 +1038,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 +1145,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 +1159,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 +1355,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 +1578,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/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h
index 0bcbb32d1f..f6e8bbf205 100644
--- a/src/core/lib/transport/metadata_batch.h
+++ b/src/core/lib/transport/metadata_batch.h
@@ -31,9 +31,11 @@
#include "src/core/lib/transport/static_metadata.h"
typedef struct grpc_linked_mdelem {
+ grpc_linked_mdelem() {}
+
grpc_mdelem md;
- struct grpc_linked_mdelem* next;
- struct grpc_linked_mdelem* prev;
+ struct grpc_linked_mdelem* next = nullptr;
+ struct grpc_linked_mdelem* prev = nullptr;
void* reserved;
} grpc_linked_mdelem;
diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
index 9e784635c6..edfa7030d1 100644
--- a/src/core/lib/transport/transport.h
+++ b/src/core/lib/transport/transport.h
@@ -81,16 +81,16 @@ void grpc_stream_unref(grpc_stream_refcount* refcount);
grpc_slice grpc_slice_from_stream_owned_buffer(grpc_stream_refcount* refcount,
void* buffer, size_t length);
-typedef struct {
- uint64_t framing_bytes;
- uint64_t data_bytes;
- uint64_t header_bytes;
-} grpc_transport_one_way_stats;
+struct grpc_transport_one_way_stats {
+ uint64_t framing_bytes = 0;
+ uint64_t data_bytes = 0;
+ uint64_t header_bytes = 0;
+};
-typedef struct grpc_transport_stream_stats {
+struct grpc_transport_stream_stats {
grpc_transport_one_way_stats incoming;
grpc_transport_one_way_stats outgoing;
-} grpc_transport_stream_stats;
+};
void grpc_transport_move_one_way_stats(grpc_transport_one_way_stats* from,
grpc_transport_one_way_stats* to);
@@ -121,7 +121,16 @@ typedef struct grpc_transport_stream_op_batch_payload
/* Transport stream op: a set of operations to perform on a transport
against a single stream */
-typedef struct grpc_transport_stream_op_batch {
+struct grpc_transport_stream_op_batch {
+ grpc_transport_stream_op_batch()
+ : send_initial_metadata(false),
+ send_trailing_metadata(false),
+ send_message(false),
+ recv_initial_metadata(false),
+ recv_message(false),
+ recv_trailing_metadata(false),
+ cancel_stream(false) {}
+
/** Should be scheduled when all of the non-recv operations in the batch
are complete.
@@ -131,10 +140,10 @@ typedef struct grpc_transport_stream_op_batch {
scheduled as soon as the non-recv ops are complete, regardless of
whether or not the recv ops are complete. If a batch contains
only recv ops, on_complete can be null. */
- grpc_closure* on_complete;
+ grpc_closure* on_complete = nullptr;
/** Values for the stream op (fields set are determined by flags above) */
- grpc_transport_stream_op_batch_payload* payload;
+ grpc_transport_stream_op_batch_payload* payload = nullptr;
/** Send initial metadata to the peer, from the provided metadata batch. */
bool send_initial_metadata : 1;
@@ -163,24 +172,33 @@ typedef struct grpc_transport_stream_op_batch {
* current handler of the op */
grpc_handler_private_op_data handler_private;
-} grpc_transport_stream_op_batch;
+};
struct grpc_transport_stream_op_batch_payload {
+ explicit grpc_transport_stream_op_batch_payload(
+ grpc_call_context_element* context)
+ : context(context) {}
+ ~grpc_transport_stream_op_batch_payload() {
+ // We don't really own `send_message`, so release ownership and let the
+ // owner clean the data.
+ send_message.send_message.release();
+ }
+
struct {
- grpc_metadata_batch* send_initial_metadata;
+ grpc_metadata_batch* send_initial_metadata = nullptr;
/** Iff send_initial_metadata != NULL, flags associated with
send_initial_metadata: a bitfield of GRPC_INITIAL_METADATA_xxx */
- uint32_t send_initial_metadata_flags;
+ uint32_t send_initial_metadata_flags = 0;
// If non-NULL, will be set by the transport to the peer string (a char*).
// The transport retains ownership of the string.
// Note: This pointer may be used by the transport after the
// send_initial_metadata op is completed. It must remain valid
// until the call is destroyed.
- gpr_atm* peer_string;
+ gpr_atm* peer_string = nullptr;
} send_initial_metadata;
struct {
- grpc_metadata_batch* send_trailing_metadata;
+ grpc_metadata_batch* send_trailing_metadata = nullptr;
} send_trailing_metadata;
struct {
@@ -192,39 +210,39 @@ struct grpc_transport_stream_op_batch_payload {
} send_message;
struct {
- grpc_metadata_batch* recv_initial_metadata;
+ grpc_metadata_batch* recv_initial_metadata = nullptr;
// Flags are used only on the server side. If non-null, will be set to
// a bitfield of the GRPC_INITIAL_METADATA_xxx macros (e.g., to
// indicate if the call is idempotent).
- uint32_t* recv_flags;
+ uint32_t* recv_flags = nullptr;
/** Should be enqueued when initial metadata is ready to be processed. */
- grpc_closure* recv_initial_metadata_ready;
+ grpc_closure* recv_initial_metadata_ready = nullptr;
// If not NULL, will be set to true if trailing metadata is
// immediately available. This may be a signal that we received a
// Trailers-Only response.
- bool* trailing_metadata_available;
+ bool* trailing_metadata_available = nullptr;
// If non-NULL, will be set by the transport to the peer string (a char*).
// The transport retains ownership of the string.
// Note: This pointer may be used by the transport after the
// recv_initial_metadata op is completed. It must remain valid
// until the call is destroyed.
- gpr_atm* peer_string;
+ gpr_atm* peer_string = nullptr;
} recv_initial_metadata;
struct {
// Will be set by the transport to point to the byte stream
// containing a received message.
// Will be NULL if trailing metadata is received instead of a message.
- grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message;
+ grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message = nullptr;
/** Should be enqueued when one message is ready to be processed. */
- grpc_closure* recv_message_ready;
+ grpc_closure* recv_message_ready = nullptr;
} recv_message;
struct {
- grpc_metadata_batch* recv_trailing_metadata;
- grpc_transport_stream_stats* collect_stats;
+ grpc_metadata_batch* recv_trailing_metadata = nullptr;
+ grpc_transport_stream_stats* collect_stats = nullptr;
/** Should be enqueued when initial metadata is ready to be processed. */
- grpc_closure* recv_trailing_metadata_ready;
+ grpc_closure* recv_trailing_metadata_ready = nullptr;
} recv_trailing_metadata;
/** Forcefully close this stream.
@@ -240,7 +258,7 @@ struct grpc_transport_stream_op_batch_payload {
struct {
// Error contract: the transport that gets this op must cause cancel_error
// to be unref'ed after processing it
- grpc_error* cancel_error;
+ grpc_error* cancel_error = GRPC_ERROR_NONE;
} cancel_stream;
/* Indexes correspond to grpc_context_index enum values */
diff --git a/src/core/ext/filters/client_channel/uri_parser.cc b/src/core/lib/uri/uri_parser.cc
index 0572034a9c..f212c7d2c0 100644
--- a/src/core/ext/filters/client_channel/uri_parser.cc
+++ b/src/core/lib/uri/uri_parser.cc
@@ -18,7 +18,7 @@
#include <grpc/support/port_platform.h>
-#include "src/core/ext/filters/client_channel/uri_parser.h"
+#include "src/core/lib/uri/uri_parser.h"
#include <string.h>
diff --git a/src/core/ext/filters/client_channel/uri_parser.h b/src/core/lib/uri/uri_parser.h
index d749f23308..b6771bbde3 100644
--- a/src/core/ext/filters/client_channel/uri_parser.h
+++ b/src/core/lib/uri/uri_parser.h
@@ -16,8 +16,8 @@
*
*/
-#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_URI_PARSER_H
-#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_URI_PARSER_H
+#ifndef GRPC_CORE_LIB_URI_URI_PARSER_H
+#define GRPC_CORE_LIB_URI_URI_PARSER_H
#include <grpc/support/port_platform.h>
@@ -47,4 +47,4 @@ const char* grpc_uri_get_query_arg(const grpc_uri* uri, const char* key);
/** destroy a uri */
void grpc_uri_destroy(grpc_uri* uri);
-#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_URI_PARSER_H */
+#endif /* GRPC_CORE_LIB_URI_URI_PARSER_H */
diff --git a/src/core/plugin_registry/grpc_cronet_plugin_registry.cc b/src/core/plugin_registry/grpc_cronet_plugin_registry.cc
index c0c17b0a4b..92085d31d7 100644
--- a/src/core/plugin_registry/grpc_cronet_plugin_registry.cc
+++ b/src/core/plugin_registry/grpc_cronet_plugin_registry.cc
@@ -28,8 +28,6 @@ void grpc_deadline_filter_init(void);
void grpc_deadline_filter_shutdown(void);
void grpc_client_channel_init(void);
void grpc_client_channel_shutdown(void);
-void grpc_tsi_alts_init(void);
-void grpc_tsi_alts_shutdown(void);
void grpc_register_built_in_plugins(void) {
grpc_register_plugin(grpc_http_filters_init,
@@ -40,6 +38,4 @@ void grpc_register_built_in_plugins(void) {
grpc_deadline_filter_shutdown);
grpc_register_plugin(grpc_client_channel_init,
grpc_client_channel_shutdown);
- grpc_register_plugin(grpc_tsi_alts_init,
- grpc_tsi_alts_shutdown);
}
diff --git a/src/core/plugin_registry/grpc_plugin_registry.cc b/src/core/plugin_registry/grpc_plugin_registry.cc
index 94c2493d5e..cde40ef65c 100644
--- a/src/core/plugin_registry/grpc_plugin_registry.cc
+++ b/src/core/plugin_registry/grpc_plugin_registry.cc
@@ -28,8 +28,6 @@ void grpc_deadline_filter_init(void);
void grpc_deadline_filter_shutdown(void);
void grpc_client_channel_init(void);
void grpc_client_channel_shutdown(void);
-void grpc_tsi_alts_init(void);
-void grpc_tsi_alts_shutdown(void);
void grpc_inproc_plugin_init(void);
void grpc_inproc_plugin_shutdown(void);
void grpc_resolver_fake_init(void);
@@ -66,8 +64,6 @@ void grpc_register_built_in_plugins(void) {
grpc_deadline_filter_shutdown);
grpc_register_plugin(grpc_client_channel_init,
grpc_client_channel_shutdown);
- grpc_register_plugin(grpc_tsi_alts_init,
- grpc_tsi_alts_shutdown);
grpc_register_plugin(grpc_inproc_plugin_init,
grpc_inproc_plugin_shutdown);
grpc_register_plugin(grpc_resolver_fake_init,
diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_client.cc b/src/core/tsi/alts/handshaker/alts_handshaker_client.cc
index 17e8026096..1de6264183 100644
--- a/src/core/tsi/alts/handshaker/alts_handshaker_client.cc
+++ b/src/core/tsi/alts/handshaker/alts_handshaker_client.cc
@@ -25,30 +25,165 @@
#include <grpc/support/log.h>
#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/channel.h"
#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h"
+#include "src/core/tsi/alts/handshaker/alts_shared_resource.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_utils.h"
+
+#define TSI_ALTS_INITIAL_BUFFER_SIZE 256
const int kHandshakerClientOpNum = 4;
+struct alts_handshaker_client {
+ const alts_handshaker_client_vtable* vtable;
+};
+
typedef struct alts_grpc_handshaker_client {
alts_handshaker_client base;
+ alts_tsi_handshaker* handshaker;
grpc_call* call;
+ /* A pointer to a function handling the interaction with handshaker service.
+ * That is, it points to grpc_call_start_batch_and_execute when the handshaker
+ * client is used in a non-testing use case and points to a custom function
+ * that validates the data to be sent to handshaker service in a testing use
+ * case. */
alts_grpc_caller grpc_caller;
+ /* A callback function provided by gRPC to handle the response returned from
+ * handshaker service. It also serves to bring the control safely back to
+ * application when dedicated CQ and thread are used. */
+ grpc_iomgr_cb_func grpc_cb;
+ /* A gRPC closure to be scheduled when the response from handshaker service
+ * is received. It will be initialized with grpc_cb. */
+ grpc_closure on_handshaker_service_resp_recv;
+ /* Buffers containing information to be sent (or received) to (or from) the
+ * handshaker service. */
+ grpc_byte_buffer* send_buffer;
+ grpc_byte_buffer* recv_buffer;
+ grpc_status_code status;
+ /* Initial metadata to be received from handshaker service. */
+ grpc_metadata_array recv_initial_metadata;
+ /* A callback function provided by an application to be invoked when response
+ * is received from handshaker service. */
+ tsi_handshaker_on_next_done_cb cb;
+ void* user_data;
+ /* ALTS credential options passed in from the caller. */
+ grpc_alts_credentials_options* options;
+ /* target name information to be passed to handshaker service for server
+ * authorization check. */
+ grpc_slice target_name;
+ /* boolean flag indicating if the handshaker client is used at client
+ * (is_client = true) or server (is_client = false) side. */
+ bool is_client;
+ /* a temporary store for data received from handshaker service used to extract
+ * unused data. */
+ grpc_slice recv_bytes;
+ /* a buffer containing data to be sent to the grpc client or server's peer. */
+ unsigned char* buffer;
+ size_t buffer_size;
} alts_grpc_handshaker_client;
-static grpc_call_error grpc_start_batch(grpc_call* call, const grpc_op* ops,
- size_t nops, void* tag) {
- return grpc_call_start_batch(call, ops, nops, tag, nullptr);
+static void handshaker_client_send_buffer_destroy(
+ alts_grpc_handshaker_client* client) {
+ GPR_ASSERT(client != nullptr);
+ grpc_byte_buffer_destroy(client->send_buffer);
+ client->send_buffer = nullptr;
+}
+
+static bool is_handshake_finished_properly(grpc_gcp_handshaker_resp* resp) {
+ GPR_ASSERT(resp != nullptr);
+ if (resp->has_result) {
+ return true;
+ }
+ return false;
+}
+
+void alts_handshaker_client_handle_response(alts_handshaker_client* c,
+ bool is_ok) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ grpc_byte_buffer* recv_buffer = client->recv_buffer;
+ grpc_status_code status = client->status;
+ tsi_handshaker_on_next_done_cb cb = client->cb;
+ void* user_data = client->user_data;
+ alts_tsi_handshaker* handshaker = client->handshaker;
+
+ /* Invalid input check. */
+ if (cb == nullptr) {
+ gpr_log(GPR_ERROR,
+ "cb is nullptr in alts_tsi_handshaker_handle_response()");
+ return;
+ }
+ if (handshaker == nullptr || recv_buffer == nullptr) {
+ gpr_log(GPR_ERROR,
+ "Invalid arguments to alts_tsi_handshaker_handle_response()");
+ cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
+ return;
+ }
+ if (alts_tsi_handshaker_has_shutdown(handshaker)) {
+ gpr_log(GPR_ERROR, "TSI handshake shutdown");
+ cb(TSI_HANDSHAKE_SHUTDOWN, user_data, nullptr, 0, nullptr);
+ return;
+ }
+ /* Failed grpc call check. */
+ if (!is_ok || status != GRPC_STATUS_OK) {
+ gpr_log(GPR_ERROR, "grpc call made to handshaker service failed");
+ cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
+ return;
+ }
+ grpc_gcp_handshaker_resp* resp =
+ alts_tsi_utils_deserialize_response(recv_buffer);
+ grpc_byte_buffer_destroy(client->recv_buffer);
+ client->recv_buffer = nullptr;
+ /* Invalid handshaker response check. */
+ if (resp == nullptr) {
+ gpr_log(GPR_ERROR, "alts_tsi_utils_deserialize_response() failed");
+ cb(TSI_DATA_CORRUPTED, user_data, nullptr, 0, nullptr);
+ return;
+ }
+ grpc_slice* slice = static_cast<grpc_slice*>(resp->out_frames.arg);
+ unsigned char* bytes_to_send = nullptr;
+ size_t bytes_to_send_size = 0;
+ if (slice != nullptr) {
+ bytes_to_send_size = GRPC_SLICE_LENGTH(*slice);
+ while (bytes_to_send_size > client->buffer_size) {
+ client->buffer_size *= 2;
+ client->buffer = static_cast<unsigned char*>(
+ gpr_realloc(client->buffer, client->buffer_size));
+ }
+ memcpy(client->buffer, GRPC_SLICE_START_PTR(*slice), bytes_to_send_size);
+ bytes_to_send = client->buffer;
+ }
+ tsi_handshaker_result* result = nullptr;
+ if (is_handshake_finished_properly(resp)) {
+ alts_tsi_handshaker_result_create(resp, client->is_client, &result);
+ alts_tsi_handshaker_result_set_unused_bytes(result, &client->recv_bytes,
+ resp->bytes_consumed);
+ }
+ grpc_status_code code = static_cast<grpc_status_code>(resp->status.code);
+ if (code != GRPC_STATUS_OK) {
+ grpc_slice* details = static_cast<grpc_slice*>(resp->status.details.arg);
+ if (details != nullptr) {
+ char* error_details = grpc_slice_to_c_string(*details);
+ gpr_log(GPR_ERROR, "Error from handshaker service:%s", error_details);
+ gpr_free(error_details);
+ }
+ }
+ grpc_gcp_handshaker_resp_destroy(resp);
+ cb(alts_tsi_utils_convert_to_tsi_result(code), user_data, bytes_to_send,
+ bytes_to_send_size, result);
}
/**
- * Populate grpc operation data with the fields of ALTS TSI event and make a
- * grpc call.
+ * Populate grpc operation data with the fields of ALTS handshaker client and
+ * make a grpc call.
*/
-static tsi_result make_grpc_call(alts_handshaker_client* client,
- alts_tsi_event* event, bool is_start) {
- GPR_ASSERT(client != nullptr && event != nullptr);
- alts_grpc_handshaker_client* grpc_client =
- reinterpret_cast<alts_grpc_handshaker_client*>(client);
+static tsi_result make_grpc_call(alts_handshaker_client* c, bool is_start) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
grpc_op ops[kHandshakerClientOpNum];
memset(ops, 0, sizeof(ops));
grpc_op* op = ops;
@@ -59,22 +194,22 @@ static tsi_result make_grpc_call(alts_handshaker_client* client,
GPR_ASSERT(op - ops <= kHandshakerClientOpNum);
op->op = GRPC_OP_RECV_INITIAL_METADATA;
op->data.recv_initial_metadata.recv_initial_metadata =
- &event->initial_metadata;
+ &client->recv_initial_metadata;
op++;
GPR_ASSERT(op - ops <= kHandshakerClientOpNum);
}
op->op = GRPC_OP_SEND_MESSAGE;
- op->data.send_message.send_message = event->send_buffer;
+ op->data.send_message.send_message = client->send_buffer;
op++;
GPR_ASSERT(op - ops <= kHandshakerClientOpNum);
op->op = GRPC_OP_RECV_MESSAGE;
- op->data.recv_message.recv_message = &event->recv_buffer;
+ op->data.recv_message.recv_message = &client->recv_buffer;
op++;
GPR_ASSERT(op - ops <= kHandshakerClientOpNum);
- GPR_ASSERT(grpc_client->grpc_caller != nullptr);
- if (grpc_client->grpc_caller(grpc_client->call, ops,
- static_cast<size_t>(op - ops),
- (void*)event) != GRPC_CALL_OK) {
+ GPR_ASSERT(client->grpc_caller != nullptr);
+ if (client->grpc_caller(client->call, ops, static_cast<size_t>(op - ops),
+ &client->on_handshaker_service_resp_recv) !=
+ GRPC_CALL_OK) {
gpr_log(GPR_ERROR, "Start batch operation failed");
return TSI_INTERNAL_ERROR;
}
@@ -82,7 +217,11 @@ static tsi_result make_grpc_call(alts_handshaker_client* client,
}
/* Create and populate a client_start handshaker request, then serialize it. */
-static grpc_byte_buffer* get_serialized_start_client(alts_tsi_event* event) {
+static grpc_byte_buffer* get_serialized_start_client(
+ alts_handshaker_client* c) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
bool ok = true;
grpc_gcp_handshaker_req* req =
grpc_gcp_handshaker_req_create(CLIENT_START_REQ);
@@ -91,14 +230,14 @@ static grpc_byte_buffer* get_serialized_start_client(alts_tsi_event* event) {
ok &= grpc_gcp_handshaker_req_add_application_protocol(
req, ALTS_APPLICATION_PROTOCOL);
ok &= grpc_gcp_handshaker_req_add_record_protocol(req, ALTS_RECORD_PROTOCOL);
- grpc_gcp_rpc_protocol_versions* versions = &event->options->rpc_versions;
+ grpc_gcp_rpc_protocol_versions* versions = &client->options->rpc_versions;
ok &= grpc_gcp_handshaker_req_set_rpc_versions(
req, versions->max_rpc_version.major, versions->max_rpc_version.minor,
versions->min_rpc_version.major, versions->min_rpc_version.minor);
- char* target_name = grpc_slice_to_c_string(event->target_name);
+ char* target_name = grpc_slice_to_c_string(client->target_name);
ok &= grpc_gcp_handshaker_req_set_target_name(req, target_name);
target_service_account* ptr =
- (reinterpret_cast<grpc_alts_credentials_client_options*>(event->options))
+ (reinterpret_cast<grpc_alts_credentials_client_options*>(client->options))
->target_account_list_head;
while (ptr != nullptr) {
grpc_gcp_handshaker_req_add_target_identity_service_account(req, ptr->data);
@@ -116,19 +255,21 @@ static grpc_byte_buffer* get_serialized_start_client(alts_tsi_event* event) {
return buffer;
}
-static tsi_result handshaker_client_start_client(alts_handshaker_client* client,
- alts_tsi_event* event) {
- if (client == nullptr || event == nullptr) {
- gpr_log(GPR_ERROR, "Invalid arguments to handshaker_client_start_client()");
+static tsi_result handshaker_client_start_client(alts_handshaker_client* c) {
+ if (c == nullptr) {
+ gpr_log(GPR_ERROR, "client is nullptr in handshaker_client_start_client()");
return TSI_INVALID_ARGUMENT;
}
- grpc_byte_buffer* buffer = get_serialized_start_client(event);
+ grpc_byte_buffer* buffer = get_serialized_start_client(c);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
if (buffer == nullptr) {
gpr_log(GPR_ERROR, "get_serialized_start_client() failed");
return TSI_INTERNAL_ERROR;
}
- event->send_buffer = buffer;
- tsi_result result = make_grpc_call(client, event, true /* is_start */);
+ handshaker_client_send_buffer_destroy(client);
+ client->send_buffer = buffer;
+ tsi_result result = make_grpc_call(&client->base, true /* is_start */);
if (result != TSI_OK) {
gpr_log(GPR_ERROR, "make_grpc_call() failed");
}
@@ -137,8 +278,11 @@ static tsi_result handshaker_client_start_client(alts_handshaker_client* client,
/* Create and populate a start_server handshaker request, then serialize it. */
static grpc_byte_buffer* get_serialized_start_server(
- alts_tsi_event* event, grpc_slice* bytes_received) {
+ alts_handshaker_client* c, grpc_slice* bytes_received) {
+ GPR_ASSERT(c != nullptr);
GPR_ASSERT(bytes_received != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
grpc_gcp_handshaker_req* req =
grpc_gcp_handshaker_req_create(SERVER_START_REQ);
bool ok = grpc_gcp_handshaker_req_add_application_protocol(
@@ -148,7 +292,7 @@ static grpc_byte_buffer* get_serialized_start_server(
ok &= grpc_gcp_handshaker_req_set_in_bytes(
req, reinterpret_cast<const char*> GRPC_SLICE_START_PTR(*bytes_received),
GRPC_SLICE_LENGTH(*bytes_received));
- grpc_gcp_rpc_protocol_versions* versions = &event->options->rpc_versions;
+ grpc_gcp_rpc_protocol_versions* versions = &client->options->rpc_versions;
ok &= grpc_gcp_handshaker_req_set_rpc_versions(
req, versions->max_rpc_version.major, versions->max_rpc_version.minor,
versions->min_rpc_version.major, versions->min_rpc_version.minor);
@@ -163,20 +307,22 @@ static grpc_byte_buffer* get_serialized_start_server(
return buffer;
}
-static tsi_result handshaker_client_start_server(alts_handshaker_client* client,
- alts_tsi_event* event,
+static tsi_result handshaker_client_start_server(alts_handshaker_client* c,
grpc_slice* bytes_received) {
- if (client == nullptr || event == nullptr || bytes_received == nullptr) {
+ if (c == nullptr || bytes_received == nullptr) {
gpr_log(GPR_ERROR, "Invalid arguments to handshaker_client_start_server()");
return TSI_INVALID_ARGUMENT;
}
- grpc_byte_buffer* buffer = get_serialized_start_server(event, bytes_received);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ grpc_byte_buffer* buffer = get_serialized_start_server(c, bytes_received);
if (buffer == nullptr) {
gpr_log(GPR_ERROR, "get_serialized_start_server() failed");
return TSI_INTERNAL_ERROR;
}
- event->send_buffer = buffer;
- tsi_result result = make_grpc_call(client, event, true /* is_start */);
+ handshaker_client_send_buffer_destroy(client);
+ client->send_buffer = buffer;
+ tsi_result result = make_grpc_call(&client->base, true /* is_start */);
if (result != TSI_OK) {
gpr_log(GPR_ERROR, "make_grpc_call() failed");
}
@@ -201,40 +347,48 @@ static grpc_byte_buffer* get_serialized_next(grpc_slice* bytes_received) {
return buffer;
}
-static tsi_result handshaker_client_next(alts_handshaker_client* client,
- alts_tsi_event* event,
+static tsi_result handshaker_client_next(alts_handshaker_client* c,
grpc_slice* bytes_received) {
- if (client == nullptr || event == nullptr || bytes_received == nullptr) {
+ if (c == nullptr || bytes_received == nullptr) {
gpr_log(GPR_ERROR, "Invalid arguments to handshaker_client_next()");
return TSI_INVALID_ARGUMENT;
}
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ grpc_slice_unref_internal(client->recv_bytes);
+ client->recv_bytes = grpc_slice_ref(*bytes_received);
grpc_byte_buffer* buffer = get_serialized_next(bytes_received);
if (buffer == nullptr) {
gpr_log(GPR_ERROR, "get_serialized_next() failed");
return TSI_INTERNAL_ERROR;
}
- event->send_buffer = buffer;
- tsi_result result = make_grpc_call(client, event, false /* is_start */);
+ handshaker_client_send_buffer_destroy(client);
+ client->send_buffer = buffer;
+ tsi_result result = make_grpc_call(&client->base, false /* is_start */);
if (result != TSI_OK) {
gpr_log(GPR_ERROR, "make_grpc_call() failed");
}
return result;
}
-static void handshaker_client_shutdown(alts_handshaker_client* client) {
- GPR_ASSERT(client != nullptr);
- alts_grpc_handshaker_client* grpc_client =
- reinterpret_cast<alts_grpc_handshaker_client*>(client);
- GPR_ASSERT(grpc_call_cancel(grpc_client->call, nullptr) == GRPC_CALL_OK);
+static void handshaker_client_shutdown(alts_handshaker_client* c) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ if (client->call != nullptr) {
+ grpc_call_cancel_internal(client->call);
+ }
}
-static void handshaker_client_destruct(alts_handshaker_client* client) {
- if (client == nullptr) {
+static void handshaker_client_destruct(alts_handshaker_client* c) {
+ if (c == nullptr) {
return;
}
- alts_grpc_handshaker_client* grpc_client =
- reinterpret_cast<alts_grpc_handshaker_client*>(client);
- grpc_call_unref(grpc_client->call);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ if (client->call != nullptr) {
+ grpc_call_unref(client->call);
+ }
}
static const alts_handshaker_client_vtable vtable = {
@@ -243,22 +397,45 @@ static const alts_handshaker_client_vtable vtable = {
handshaker_client_destruct};
alts_handshaker_client* alts_grpc_handshaker_client_create(
- grpc_channel* channel, grpc_completion_queue* queue,
- const char* handshaker_service_url) {
- if (channel == nullptr || queue == nullptr ||
- handshaker_service_url == nullptr) {
+ alts_tsi_handshaker* handshaker, grpc_channel* channel,
+ const char* handshaker_service_url, grpc_pollset_set* interested_parties,
+ grpc_alts_credentials_options* options, grpc_slice target_name,
+ grpc_iomgr_cb_func grpc_cb, tsi_handshaker_on_next_done_cb cb,
+ void* user_data, alts_handshaker_client_vtable* vtable_for_testing,
+ bool is_client) {
+ if (channel == nullptr || handshaker_service_url == nullptr) {
gpr_log(GPR_ERROR, "Invalid arguments to alts_handshaker_client_create()");
return nullptr;
}
alts_grpc_handshaker_client* client =
static_cast<alts_grpc_handshaker_client*>(gpr_zalloc(sizeof(*client)));
- client->grpc_caller = grpc_start_batch;
+ client->grpc_caller = grpc_call_start_batch_and_execute;
+ client->handshaker = handshaker;
+ client->cb = cb;
+ client->user_data = user_data;
+ client->send_buffer = nullptr;
+ client->recv_buffer = nullptr;
+ client->options = grpc_alts_credentials_options_copy(options);
+ client->target_name = grpc_slice_copy(target_name);
+ client->recv_bytes = grpc_empty_slice();
+ grpc_metadata_array_init(&client->recv_initial_metadata);
+ client->grpc_cb = grpc_cb;
+ client->is_client = is_client;
+ client->buffer_size = TSI_ALTS_INITIAL_BUFFER_SIZE;
+ client->buffer = static_cast<unsigned char*>(gpr_zalloc(client->buffer_size));
grpc_slice slice = grpc_slice_from_copied_string(handshaker_service_url);
- client->call = grpc_channel_create_call(
- channel, nullptr, GRPC_PROPAGATE_DEFAULTS, queue,
- grpc_slice_from_static_string(ALTS_SERVICE_METHOD), &slice,
- gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
- client->base.vtable = &vtable;
+ client->call =
+ strcmp(handshaker_service_url, ALTS_HANDSHAKER_SERVICE_URL_FOR_TESTING) ==
+ 0
+ ? nullptr
+ : grpc_channel_create_pollset_set_call(
+ channel, nullptr, GRPC_PROPAGATE_DEFAULTS, interested_parties,
+ grpc_slice_from_static_string(ALTS_SERVICE_METHOD), &slice,
+ GRPC_MILLIS_INF_FUTURE, nullptr);
+ client->base.vtable =
+ vtable_for_testing == nullptr ? &vtable : vtable_for_testing;
+ GRPC_CLOSURE_INIT(&client->on_handshaker_service_resp_recv, client->grpc_cb,
+ client, grpc_schedule_on_exec_ctx);
grpc_slice_unref_internal(slice);
return &client->base;
}
@@ -267,21 +444,114 @@ namespace grpc_core {
namespace internal {
void alts_handshaker_client_set_grpc_caller_for_testing(
- alts_handshaker_client* client, alts_grpc_caller caller) {
- GPR_ASSERT(client != nullptr && caller != nullptr);
- alts_grpc_handshaker_client* grpc_client =
- reinterpret_cast<alts_grpc_handshaker_client*>(client);
- grpc_client->grpc_caller = caller;
+ alts_handshaker_client* c, alts_grpc_caller caller) {
+ GPR_ASSERT(c != nullptr && caller != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ client->grpc_caller = caller;
+}
+
+grpc_byte_buffer* alts_handshaker_client_get_send_buffer_for_testing(
+ alts_handshaker_client* c) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ return client->send_buffer;
+}
+
+grpc_byte_buffer** alts_handshaker_client_get_recv_buffer_addr_for_testing(
+ alts_handshaker_client* c) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ return &client->recv_buffer;
+}
+
+grpc_metadata_array* alts_handshaker_client_get_initial_metadata_for_testing(
+ alts_handshaker_client* c) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ return &client->recv_initial_metadata;
+}
+
+void alts_handshaker_client_set_recv_bytes_for_testing(
+ alts_handshaker_client* c, grpc_slice* recv_bytes) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ client->recv_bytes = grpc_slice_ref(*recv_bytes);
+}
+
+void alts_handshaker_client_set_fields_for_testing(
+ alts_handshaker_client* c, alts_tsi_handshaker* handshaker,
+ tsi_handshaker_on_next_done_cb cb, void* user_data,
+ grpc_byte_buffer* recv_buffer, grpc_status_code status) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ client->handshaker = handshaker;
+ client->cb = cb;
+ client->user_data = user_data;
+ client->recv_buffer = recv_buffer;
+ client->status = status;
+}
+
+void alts_handshaker_client_check_fields_for_testing(
+ alts_handshaker_client* c, tsi_handshaker_on_next_done_cb cb,
+ void* user_data, bool has_sent_start_message, grpc_slice* recv_bytes) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ GPR_ASSERT(client->cb == cb);
+ GPR_ASSERT(client->user_data == user_data);
+ if (recv_bytes != nullptr) {
+ GPR_ASSERT(grpc_slice_cmp(client->recv_bytes, *recv_bytes) == 0);
+ }
+ GPR_ASSERT(alts_tsi_handshaker_get_has_sent_start_message_for_testing(
+ client->handshaker) == has_sent_start_message);
+}
+
+void alts_handshaker_client_set_vtable_for_testing(
+ alts_handshaker_client* c, alts_handshaker_client_vtable* vtable) {
+ GPR_ASSERT(c != nullptr);
+ GPR_ASSERT(vtable != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ client->base.vtable = vtable;
+}
+
+alts_tsi_handshaker* alts_handshaker_client_get_handshaker_for_testing(
+ alts_handshaker_client* c) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ return client->handshaker;
+}
+
+void alts_handshaker_client_set_cb_for_testing(
+ alts_handshaker_client* c, tsi_handshaker_on_next_done_cb cb) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ client->cb = cb;
+}
+
+grpc_closure* alts_handshaker_client_get_closure_for_testing(
+ alts_handshaker_client* c) {
+ GPR_ASSERT(c != nullptr);
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ return &client->on_handshaker_service_resp_recv;
}
} // namespace internal
} // namespace grpc_core
-tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client,
- alts_tsi_event* event) {
+tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client) {
if (client != nullptr && client->vtable != nullptr &&
client->vtable->client_start != nullptr) {
- return client->vtable->client_start(client, event);
+ return client->vtable->client_start(client);
}
gpr_log(GPR_ERROR,
"client or client->vtable has not been initialized properly");
@@ -289,11 +559,10 @@ tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client,
}
tsi_result alts_handshaker_client_start_server(alts_handshaker_client* client,
- alts_tsi_event* event,
grpc_slice* bytes_received) {
if (client != nullptr && client->vtable != nullptr &&
client->vtable->server_start != nullptr) {
- return client->vtable->server_start(client, event, bytes_received);
+ return client->vtable->server_start(client, bytes_received);
}
gpr_log(GPR_ERROR,
"client or client->vtable has not been initialized properly");
@@ -301,11 +570,10 @@ tsi_result alts_handshaker_client_start_server(alts_handshaker_client* client,
}
tsi_result alts_handshaker_client_next(alts_handshaker_client* client,
- alts_tsi_event* event,
grpc_slice* bytes_received) {
if (client != nullptr && client->vtable != nullptr &&
client->vtable->next != nullptr) {
- return client->vtable->next(client, event, bytes_received);
+ return client->vtable->next(client, bytes_received);
}
gpr_log(GPR_ERROR,
"client or client->vtable has not been initialized properly");
@@ -319,11 +587,22 @@ void alts_handshaker_client_shutdown(alts_handshaker_client* client) {
}
}
-void alts_handshaker_client_destroy(alts_handshaker_client* client) {
- if (client != nullptr) {
- if (client->vtable != nullptr && client->vtable->destruct != nullptr) {
- client->vtable->destruct(client);
+void alts_handshaker_client_destroy(alts_handshaker_client* c) {
+ if (c != nullptr) {
+ if (c->vtable != nullptr && c->vtable->destruct != nullptr) {
+ c->vtable->destruct(c);
}
+ alts_grpc_handshaker_client* client =
+ reinterpret_cast<alts_grpc_handshaker_client*>(c);
+ grpc_byte_buffer_destroy(client->send_buffer);
+ grpc_byte_buffer_destroy(client->recv_buffer);
+ client->send_buffer = nullptr;
+ client->recv_buffer = nullptr;
+ grpc_metadata_array_destroy(&client->recv_initial_metadata);
+ grpc_slice_unref_internal(client->recv_bytes);
+ grpc_slice_unref_internal(client->target_name);
+ grpc_alts_credentials_options_destroy(client->options);
+ gpr_free(client->buffer);
gpr_free(client);
}
}
diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_client.h b/src/core/tsi/alts/handshaker/alts_handshaker_client.h
index 8dd8fe440d..4b489875f3 100644
--- a/src/core/tsi/alts/handshaker/alts_handshaker_client.h
+++ b/src/core/tsi/alts/handshaker/alts_handshaker_client.h
@@ -21,16 +21,24 @@
#include <grpc/support/port_platform.h>
+#include <grpc/byte_buffer.h>
+#include <grpc/byte_buffer_reader.h>
#include <grpc/grpc.h>
-#include "src/core/tsi/alts/handshaker/alts_tsi_event.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/pollset_set.h"
#define ALTS_SERVICE_METHOD "/grpc.gcp.HandshakerService/DoHandshake"
#define ALTS_APPLICATION_PROTOCOL "grpc"
#define ALTS_RECORD_PROTOCOL "ALTSRP_GCM_AES128_REKEY"
+#define ALTS_HANDSHAKER_SERVICE_URL_FOR_TESTING "lame"
const size_t kAltsAes128GcmRekeyKeyLength = 44;
+typedef struct alts_tsi_handshaker alts_tsi_handshaker;
/**
* A ALTS handshaker client interface. It is used to communicate with
* ALTS handshaker service by scheduling a handshaker request that could be one
@@ -41,63 +49,52 @@ typedef struct alts_handshaker_client alts_handshaker_client;
/* A function that makes the grpc call to the handshaker service. */
typedef grpc_call_error (*alts_grpc_caller)(grpc_call* call, const grpc_op* ops,
- size_t nops, void* tag);
+ size_t nops, grpc_closure* tag);
/* V-table for ALTS handshaker client operations. */
typedef struct alts_handshaker_client_vtable {
- tsi_result (*client_start)(alts_handshaker_client* client,
- alts_tsi_event* event);
+ tsi_result (*client_start)(alts_handshaker_client* client);
tsi_result (*server_start)(alts_handshaker_client* client,
- alts_tsi_event* event, grpc_slice* bytes_received);
- tsi_result (*next)(alts_handshaker_client* client, alts_tsi_event* event,
+ grpc_slice* bytes_received);
+ tsi_result (*next)(alts_handshaker_client* client,
grpc_slice* bytes_received);
void (*shutdown)(alts_handshaker_client* client);
void (*destruct)(alts_handshaker_client* client);
} alts_handshaker_client_vtable;
-struct alts_handshaker_client {
- const alts_handshaker_client_vtable* vtable;
-};
-
/**
* This method schedules a client_start handshaker request to ALTS handshaker
* service.
*
* - client: ALTS handshaker client instance.
- * - event: ALTS TSI event instance.
*
* It returns TSI_OK on success and an error status code on failure.
*/
-tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client,
- alts_tsi_event* event);
+tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client);
/**
* This method schedules a server_start handshaker request to ALTS handshaker
* service.
*
* - client: ALTS handshaker client instance.
- * - event: ALTS TSI event instance.
* - bytes_received: bytes in out_frames returned from the peer's handshaker
* response.
*
* It returns TSI_OK on success and an error status code on failure.
*/
tsi_result alts_handshaker_client_start_server(alts_handshaker_client* client,
- alts_tsi_event* event,
grpc_slice* bytes_received);
/**
* This method schedules a next handshaker request to ALTS handshaker service.
*
* - client: ALTS handshaker client instance.
- * - event: ALTS TSI event instance.
* - bytes_received: bytes in out_frames returned from the peer's handshaker
* response.
*
* It returns TSI_OK on success and an error status code on failure.
*/
tsi_result alts_handshaker_client_next(alts_handshaker_client* client,
- alts_tsi_event* event,
grpc_slice* bytes_received);
/**
@@ -110,38 +107,51 @@ tsi_result alts_handshaker_client_next(alts_handshaker_client* client,
void alts_handshaker_client_shutdown(alts_handshaker_client* client);
/**
- * This method destroys a ALTS handshaker client.
+ * This method destroys an ALTS handshaker client.
*
- * - client: a ALTS handshaker client instance.
+ * - client: an ALTS handshaker client instance.
*/
void alts_handshaker_client_destroy(alts_handshaker_client* client);
/**
- * This method creates a ALTS handshaker client.
+ * This method creates an ALTS handshaker client.
*
+ * - handshaker: ALTS TSI handshaker to which the created handshaker client
+ * belongs to.
* - channel: grpc channel to ALTS handshaker service.
- * - queue: grpc completion queue.
* - handshaker_service_url: address of ALTS handshaker service in the format of
* "host:port".
- *
- * It returns the created ALTS handshaker client on success, and NULL on
- * failure.
+ * - interested_parties: set of pollsets interested in this connection.
+ * - options: ALTS credentials options containing information passed from TSI
+ * caller (e.g., rpc protocol versions)
+ * - target_name: the name of the endpoint that the channel is connecting to,
+ * and will be used for secure naming check
+ * - grpc_cb: gRPC provided callbacks passed from TSI handshaker.
+ * - cb: callback to be executed when tsi_handshaker_next API compltes.
+ * - user_data: argument passed to cb.
+ * - vtable_for_testing: ALTS handshaker client vtable instance used for
+ * testing purpose.
+ * - is_client: a boolean value indicating if the created handshaker client is
+ * used at the client (is_client = true) or server (is_client = false) side. It
+ * returns the created ALTS handshaker client on success, and NULL on failure.
*/
alts_handshaker_client* alts_grpc_handshaker_client_create(
- grpc_channel* channel, grpc_completion_queue* queue,
- const char* handshaker_service_url);
-
-namespace grpc_core {
-namespace internal {
+ alts_tsi_handshaker* handshaker, grpc_channel* channel,
+ const char* handshaker_service_url, grpc_pollset_set* interested_parties,
+ grpc_alts_credentials_options* options, grpc_slice target_name,
+ grpc_iomgr_cb_func grpc_cb, tsi_handshaker_on_next_done_cb cb,
+ void* user_data, alts_handshaker_client_vtable* vtable_for_testing,
+ bool is_client);
/**
- * Unsafe, use for testing only. It allows the caller to change the way that
- * GRPC calls are made to the handshaker service.
+ * This method handles handshaker response returned from ALTS handshaker
+ * service. Note that the only reason the API is exposed is that it is used in
+ * alts_shared_resources.cc.
+ *
+ * - client: an ALTS handshaker client instance.
+ * - is_ok: a boolean value indicating if the handshaker response is ok to read.
*/
-void alts_handshaker_client_set_grpc_caller_for_testing(
- alts_handshaker_client* client, alts_grpc_caller caller);
-
-} // namespace internal
-} // namespace grpc_core
+void alts_handshaker_client_handle_response(alts_handshaker_client* client,
+ bool is_ok);
#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H */
diff --git a/src/core/tsi/alts/handshaker/alts_shared_resource.cc b/src/core/tsi/alts/handshaker/alts_shared_resource.cc
new file mode 100644
index 0000000000..3501257f05
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_shared_resource.cc
@@ -0,0 +1,83 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_shared_resource.h"
+
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+
+static alts_shared_resource_dedicated g_alts_resource_dedicated;
+
+alts_shared_resource_dedicated* grpc_alts_get_shared_resource_dedicated(void) {
+ return &g_alts_resource_dedicated;
+}
+
+static void thread_worker(void* arg) {
+ while (true) {
+ grpc_event event =
+ grpc_completion_queue_next(g_alts_resource_dedicated.cq,
+ gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
+ GPR_ASSERT(event.type != GRPC_QUEUE_TIMEOUT);
+ if (event.type == GRPC_QUEUE_SHUTDOWN) {
+ break;
+ }
+ GPR_ASSERT(event.type == GRPC_OP_COMPLETE);
+ alts_handshaker_client* client =
+ static_cast<alts_handshaker_client*>(event.tag);
+ alts_handshaker_client_handle_response(client, event.success);
+ }
+}
+
+void grpc_alts_shared_resource_dedicated_init() {
+ g_alts_resource_dedicated.cq = nullptr;
+ gpr_mu_init(&g_alts_resource_dedicated.mu);
+}
+
+void grpc_alts_shared_resource_dedicated_start(
+ const char* handshaker_service_url) {
+ gpr_mu_lock(&g_alts_resource_dedicated.mu);
+ if (g_alts_resource_dedicated.cq == nullptr) {
+ g_alts_resource_dedicated.channel =
+ grpc_insecure_channel_create(handshaker_service_url, nullptr, nullptr);
+ g_alts_resource_dedicated.cq =
+ grpc_completion_queue_create_for_next(nullptr);
+ g_alts_resource_dedicated.thread =
+ grpc_core::Thread("alts_tsi_handshaker", &thread_worker, nullptr);
+ g_alts_resource_dedicated.interested_parties = grpc_pollset_set_create();
+ grpc_pollset_set_add_pollset(g_alts_resource_dedicated.interested_parties,
+ grpc_cq_pollset(g_alts_resource_dedicated.cq));
+ g_alts_resource_dedicated.thread.Start();
+ }
+ gpr_mu_unlock(&g_alts_resource_dedicated.mu);
+}
+
+void grpc_alts_shared_resource_dedicated_shutdown() {
+ if (g_alts_resource_dedicated.cq != nullptr) {
+ grpc_pollset_set_del_pollset(g_alts_resource_dedicated.interested_parties,
+ grpc_cq_pollset(g_alts_resource_dedicated.cq));
+ grpc_completion_queue_shutdown(g_alts_resource_dedicated.cq);
+ g_alts_resource_dedicated.thread.Join();
+ grpc_pollset_set_destroy(g_alts_resource_dedicated.interested_parties);
+ grpc_completion_queue_destroy(g_alts_resource_dedicated.cq);
+ grpc_channel_destroy(g_alts_resource_dedicated.channel);
+ }
+ gpr_mu_destroy(&g_alts_resource_dedicated.mu);
+}
diff --git a/src/core/tsi/alts/handshaker/alts_shared_resource.h b/src/core/tsi/alts/handshaker/alts_shared_resource.h
new file mode 100644
index 0000000000..8ae0089a11
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_shared_resource.h
@@ -0,0 +1,73 @@
+/*
+ *
+ * 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 GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_SHARED_RESOURCE_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_SHARED_RESOURCE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/surface/completion_queue.h"
+
+/**
+ * Main struct containing ALTS shared resources used when
+ * employing the dedicated completion queue and thread.
+ */
+typedef struct alts_shared_resource_dedicated {
+ grpc_core::Thread thread;
+ grpc_completion_queue* cq;
+ grpc_pollset_set* interested_parties;
+ grpc_cq_completion storage;
+ gpr_mu mu;
+ grpc_channel* channel;
+} alts_shared_resource_dedicated;
+
+/* This method returns the address of alts_shared_resource_dedicated
+ * object shared by all TSI handshakes.
+ */
+alts_shared_resource_dedicated* grpc_alts_get_shared_resource_dedicated(void);
+
+/**
+ * This method destroys the alts_shared_resource_dedicated object
+ * shared by all TSI handshakes. The applicaiton is responsible for
+ * invoking the API before calling grpc_shutdown().
+ */
+void grpc_alts_shared_resource_dedicated_shutdown();
+
+/**
+ * This method initializes the alts_shared_resource_dedicated object
+ * shared by all TSI handshakes. The application is responsible for
+ * invoking the API after calling grpc_init();
+ */
+void grpc_alts_shared_resource_dedicated_init();
+
+/**
+ * This method populates various fields of the alts_shared_resource_dedicated
+ * object shared by all TSI handshakes and start the dedicated thread.
+ * The API will be invoked by the caller in a lazy manner. That is,
+ * it will get invoked when ALTS TSI handshake occurs for the first time.
+ */
+void grpc_alts_shared_resource_dedicated_start(
+ const char* handshaker_service_url);
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_SHARED_RESOURCE_H \
+ */
diff --git a/src/core/tsi/alts/handshaker/alts_tsi_event.cc b/src/core/tsi/alts/handshaker/alts_tsi_event.cc
deleted file mode 100644
index cb36d5ebd1..0000000000
--- a/src/core/tsi/alts/handshaker/alts_tsi_event.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- *
- * Copyright 2018 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#include <grpc/support/port_platform.h>
-
-#include "src/core/tsi/alts/handshaker/alts_tsi_event.h"
-
-#include <grpc/grpc.h>
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-
-#include "src/core/lib/slice/slice_internal.h"
-
-tsi_result alts_tsi_event_create(alts_tsi_handshaker* handshaker,
- tsi_handshaker_on_next_done_cb cb,
- void* user_data,
- grpc_alts_credentials_options* options,
- grpc_slice target_name,
- alts_tsi_event** event) {
- if (event == nullptr || handshaker == nullptr || cb == nullptr) {
- gpr_log(GPR_ERROR, "Invalid arguments to alts_tsi_event_create()");
- return TSI_INVALID_ARGUMENT;
- }
- alts_tsi_event* e = static_cast<alts_tsi_event*>(gpr_zalloc(sizeof(*e)));
- e->handshaker = handshaker;
- e->cb = cb;
- e->user_data = user_data;
- e->options = grpc_alts_credentials_options_copy(options);
- e->target_name = grpc_slice_copy(target_name);
- grpc_metadata_array_init(&e->initial_metadata);
- grpc_metadata_array_init(&e->trailing_metadata);
- *event = e;
- return TSI_OK;
-}
-
-void alts_tsi_event_dispatch_to_handshaker(alts_tsi_event* event, bool is_ok) {
- if (event == nullptr) {
- gpr_log(
- GPR_ERROR,
- "ALTS TSI event is nullptr in alts_tsi_event_dispatch_to_handshaker()");
- return;
- }
- alts_tsi_handshaker_handle_response(event->handshaker, event->recv_buffer,
- event->status, &event->details, event->cb,
- event->user_data, is_ok);
-}
-
-void alts_tsi_event_destroy(alts_tsi_event* event) {
- if (event == nullptr) {
- return;
- }
- grpc_byte_buffer_destroy(event->send_buffer);
- grpc_byte_buffer_destroy(event->recv_buffer);
- grpc_metadata_array_destroy(&event->initial_metadata);
- grpc_metadata_array_destroy(&event->trailing_metadata);
- grpc_slice_unref_internal(event->details);
- grpc_slice_unref_internal(event->target_name);
- grpc_alts_credentials_options_destroy(event->options);
- gpr_free(event);
-}
diff --git a/src/core/tsi/alts/handshaker/alts_tsi_event.h b/src/core/tsi/alts/handshaker/alts_tsi_event.h
deleted file mode 100644
index 043e75d4a9..0000000000
--- a/src/core/tsi/alts/handshaker/alts_tsi_event.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- *
- * Copyright 2018 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_EVENT_H
-#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_EVENT_H
-
-#include <grpc/support/port_platform.h>
-
-#include <grpc/byte_buffer.h>
-#include <grpc/byte_buffer_reader.h>
-
-#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
-#include "src/core/tsi/transport_security_interface.h"
-
-/**
- * A ALTS TSI event interface. In asynchronous implementation of
- * tsi_handshaker_next(), the function will exit after scheduling a handshaker
- * request to ALTS handshaker service without waiting for response to return.
- * The event is used to link the scheduled handshaker request with the
- * corresponding response so that enough context information can be inferred
- * from it to handle the response. All APIs in the header are thread-compatible.
- */
-
-/**
- * Main struct for ALTS TSI event. It retains ownership on send_buffer and
- * recv_buffer, but not on handshaker.
- */
-typedef struct alts_tsi_event {
- alts_tsi_handshaker* handshaker;
- grpc_byte_buffer* send_buffer;
- grpc_byte_buffer* recv_buffer;
- grpc_status_code status;
- grpc_slice details;
- grpc_metadata_array initial_metadata;
- grpc_metadata_array trailing_metadata;
- tsi_handshaker_on_next_done_cb cb;
- void* user_data;
- grpc_alts_credentials_options* options;
- grpc_slice target_name;
-} alts_tsi_event;
-
-/**
- * This method creates a ALTS TSI event.
- *
- * - handshaker: ALTS TSI handshaker instance associated with the event to be
- * created. The created event does not own the handshaker instance.
- * - cb: callback function to be called when handling data received from ALTS
- * handshaker service.
- * - user_data: argument to callback function.
- * - options: ALTS credentials options.
- * - target_name: name of endpoint used for secure naming check.
- * - event: address of ALTS TSI event instance to be returned from the method.
- *
- * It returns TSI_OK on success and an error status code on failure.
- */
-tsi_result alts_tsi_event_create(alts_tsi_handshaker* handshaker,
- tsi_handshaker_on_next_done_cb cb,
- void* user_data,
- grpc_alts_credentials_options* options,
- grpc_slice target_name,
- alts_tsi_event** event);
-
-/**
- * This method dispatches a ALTS TSI event received from the handshaker service,
- * and a boolean flag indicating if the event is valid to read to ALTS TSI
- * handshaker to process. It is called by TSI thread.
- *
- * - event: ALTS TSI event instance.
- * - is_ok: a boolean value indicating if the event is valid to read.
- */
-void alts_tsi_event_dispatch_to_handshaker(alts_tsi_event* event, bool is_ok);
-
-/**
- * This method destroys the ALTS TSI event.
- */
-void alts_tsi_event_destroy(alts_tsi_event* event);
-
-#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_EVENT_H */
diff --git a/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
index dfdd659b87..1b7e58d3ce 100644
--- a/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
+++ b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
@@ -26,34 +26,34 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
#include <grpc/support/sync.h>
#include <grpc/support/thd_id.h>
#include "src/core/lib/gpr/host_port.h"
#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/tsi/alts/frame_protector/alts_frame_protector.h"
#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+#include "src/core/tsi/alts/handshaker/alts_shared_resource.h"
#include "src/core/tsi/alts/handshaker/alts_tsi_utils.h"
#include "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h"
-#include "src/core/tsi/alts_transport_security.h"
-
-#define TSI_ALTS_INITIAL_BUFFER_SIZE 256
-
-static alts_shared_resource* kSharedResource = alts_get_shared_resource();
/* Main struct for ALTS TSI handshaker. */
-typedef struct alts_tsi_handshaker {
+struct alts_tsi_handshaker {
tsi_handshaker base;
alts_handshaker_client* client;
- grpc_slice recv_bytes;
grpc_slice target_name;
- unsigned char* buffer;
- size_t buffer_size;
bool is_client;
bool has_sent_start_message;
+ bool has_created_handshaker_client;
+ char* handshaker_service_url;
+ grpc_pollset_set* interested_parties;
grpc_alts_credentials_options* options;
-} alts_tsi_handshaker;
+ alts_handshaker_client_vtable* client_vtable_for_testing;
+ grpc_channel* channel;
+};
/* Main struct for ALTS TSI handshaker result. */
typedef struct alts_tsi_handshaker_result {
@@ -193,9 +193,9 @@ static const tsi_handshaker_result_vtable result_vtable = {
handshaker_result_create_frame_protector,
handshaker_result_get_unused_bytes, handshaker_result_destroy};
-static tsi_result create_handshaker_result(grpc_gcp_handshaker_resp* resp,
- bool is_client,
- tsi_handshaker_result** self) {
+tsi_result alts_tsi_handshaker_result_create(grpc_gcp_handshaker_resp* resp,
+ bool is_client,
+ tsi_handshaker_result** self) {
if (self == nullptr || resp == nullptr) {
gpr_log(GPR_ERROR, "Invalid arguments to create_handshaker_result()");
return TSI_INVALID_ARGUMENT;
@@ -234,6 +234,27 @@ static tsi_result create_handshaker_result(grpc_gcp_handshaker_resp* resp,
return TSI_OK;
}
+/* gRPC provided callback used when gRPC thread model is applied. */
+static void on_handshaker_service_resp_recv(void* arg, grpc_error* error) {
+ alts_handshaker_client* client = static_cast<alts_handshaker_client*>(arg);
+ if (client == nullptr) {
+ gpr_log(GPR_ERROR, "ALTS handshaker client is nullptr");
+ return;
+ }
+ alts_handshaker_client_handle_response(client, true);
+}
+
+/* gRPC provided callback used when dedicatd CQ and thread are used.
+ * It serves to safely bring the control back to application. */
+static void on_handshaker_service_resp_recv_dedicated(void* arg,
+ grpc_error* error) {
+ alts_shared_resource_dedicated* resource =
+ grpc_alts_get_shared_resource_dedicated();
+ grpc_cq_end_op(resource->cq, arg, GRPC_ERROR_NONE,
+ [](void* done_arg, grpc_cq_completion* storage) {}, nullptr,
+ &resource->storage);
+}
+
static tsi_result handshaker_next(
tsi_handshaker* self, const unsigned char* received_bytes,
size_t received_bytes_size, const unsigned char** bytes_to_send,
@@ -250,12 +271,36 @@ static tsi_result handshaker_next(
alts_tsi_handshaker* handshaker =
reinterpret_cast<alts_tsi_handshaker*>(self);
tsi_result ok = TSI_OK;
- alts_tsi_event* event = nullptr;
- ok = alts_tsi_event_create(handshaker, cb, user_data, handshaker->options,
- handshaker->target_name, &event);
- if (ok != TSI_OK) {
- gpr_log(GPR_ERROR, "Failed to create ALTS TSI event");
- return ok;
+ if (!handshaker->has_created_handshaker_client) {
+ if (handshaker->channel == nullptr) {
+ grpc_alts_shared_resource_dedicated_start(
+ handshaker->handshaker_service_url);
+ handshaker->interested_parties =
+ grpc_alts_get_shared_resource_dedicated()->interested_parties;
+ GPR_ASSERT(handshaker->interested_parties != nullptr);
+ }
+ grpc_iomgr_cb_func grpc_cb = handshaker->channel == nullptr
+ ? on_handshaker_service_resp_recv_dedicated
+ : on_handshaker_service_resp_recv;
+ grpc_channel* channel =
+ handshaker->channel == nullptr
+ ? grpc_alts_get_shared_resource_dedicated()->channel
+ : handshaker->channel;
+ handshaker->client = alts_grpc_handshaker_client_create(
+ handshaker, channel, handshaker->handshaker_service_url,
+ handshaker->interested_parties, handshaker->options,
+ handshaker->target_name, grpc_cb, cb, user_data,
+ handshaker->client_vtable_for_testing, handshaker->is_client);
+ if (handshaker->client == nullptr) {
+ gpr_log(GPR_ERROR, "Failed to create ALTS handshaker client");
+ return TSI_FAILED_PRECONDITION;
+ }
+ handshaker->has_created_handshaker_client = true;
+ }
+ if (handshaker->channel == nullptr &&
+ handshaker->client_vtable_for_testing == nullptr) {
+ GPR_ASSERT(grpc_cq_begin_op(grpc_alts_get_shared_resource_dedicated()->cq,
+ handshaker->client));
}
grpc_slice slice = (received_bytes == nullptr || received_bytes_size == 0)
? grpc_empty_slice()
@@ -264,16 +309,11 @@ static tsi_result handshaker_next(
received_bytes_size);
if (!handshaker->has_sent_start_message) {
ok = handshaker->is_client
- ? alts_handshaker_client_start_client(handshaker->client, event)
- : alts_handshaker_client_start_server(handshaker->client, event,
- &slice);
+ ? alts_handshaker_client_start_client(handshaker->client)
+ : alts_handshaker_client_start_server(handshaker->client, &slice);
handshaker->has_sent_start_message = true;
} else {
- if (!GRPC_SLICE_IS_EMPTY(handshaker->recv_bytes)) {
- grpc_slice_unref_internal(handshaker->recv_bytes);
- }
- handshaker->recv_bytes = grpc_slice_ref(slice);
- ok = alts_handshaker_client_next(handshaker->client, event, &slice);
+ ok = alts_handshaker_client_next(handshaker->client, &slice);
}
grpc_slice_unref_internal(slice);
if (ok != TSI_OK) {
@@ -283,6 +323,22 @@ static tsi_result handshaker_next(
return TSI_ASYNC;
}
+/*
+ * This API will be invoked by a non-gRPC application, and an ExecCtx needs
+ * to be explicitly created in order to invoke ALTS handshaker client API's
+ * that assumes the caller is inside gRPC core.
+ */
+static tsi_result handshaker_next_dedicated(
+ tsi_handshaker* self, const unsigned char* received_bytes,
+ size_t received_bytes_size, const unsigned char** bytes_to_send,
+ size_t* bytes_to_send_size, tsi_handshaker_result** result,
+ tsi_handshaker_on_next_done_cb cb, void* user_data) {
+ grpc_core::ExecCtx exec_ctx;
+ return handshaker_next(self, received_bytes, received_bytes_size,
+ bytes_to_send, bytes_to_send_size, result, cb,
+ user_data);
+}
+
static void handshaker_shutdown(tsi_handshaker* self) {
GPR_ASSERT(self != nullptr);
if (self->handshake_shutdown) {
@@ -300,10 +356,12 @@ static void handshaker_destroy(tsi_handshaker* self) {
alts_tsi_handshaker* handshaker =
reinterpret_cast<alts_tsi_handshaker*>(self);
alts_handshaker_client_destroy(handshaker->client);
- grpc_slice_unref_internal(handshaker->recv_bytes);
grpc_slice_unref_internal(handshaker->target_name);
grpc_alts_credentials_options_destroy(handshaker->options);
- gpr_free(handshaker->buffer);
+ if (handshaker->channel != nullptr) {
+ grpc_channel_destroy(handshaker->channel);
+ }
+ gpr_free(handshaker->handshaker_service_url);
gpr_free(handshaker);
}
@@ -313,36 +371,19 @@ static const tsi_handshaker_vtable handshaker_vtable = {
nullptr, handshaker_destroy,
handshaker_next, handshaker_shutdown};
-static void thread_worker(void* arg) {
- while (true) {
- grpc_event event = grpc_completion_queue_next(
- kSharedResource->cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
- GPR_ASSERT(event.type != GRPC_QUEUE_TIMEOUT);
- if (event.type == GRPC_QUEUE_SHUTDOWN) {
- /* signal alts_tsi_shutdown() to destroy completion queue. */
- grpc_tsi_alts_signal_for_cq_destroy();
- break;
- }
- /* event.type == GRPC_OP_COMPLETE. */
- alts_tsi_event* alts_event = static_cast<alts_tsi_event*>(event.tag);
- alts_tsi_event_dispatch_to_handshaker(alts_event, event.success);
- alts_tsi_event_destroy(alts_event);
- }
-}
-
-static void init_shared_resources(const char* handshaker_service_url) {
- GPR_ASSERT(handshaker_service_url != nullptr);
- gpr_mu_lock(&kSharedResource->mu);
- if (kSharedResource->channel == nullptr) {
- gpr_cv_init(&kSharedResource->cv);
- kSharedResource->channel =
- grpc_insecure_channel_create(handshaker_service_url, nullptr, nullptr);
- kSharedResource->cq = grpc_completion_queue_create_for_next(nullptr);
- kSharedResource->thread =
- grpc_core::Thread("alts_tsi_handshaker", &thread_worker, nullptr);
- kSharedResource->thread.Start();
- }
- gpr_mu_unlock(&kSharedResource->mu);
+static const tsi_handshaker_vtable handshaker_vtable_dedicated = {
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ handshaker_destroy,
+ handshaker_next_dedicated,
+ handshaker_shutdown};
+
+bool alts_tsi_handshaker_has_shutdown(alts_tsi_handshaker* handshaker) {
+ GPR_ASSERT(handshaker != nullptr);
+ return handshaker->base.handshake_shutdown;
}
tsi_result alts_tsi_handshaker_create(
@@ -354,40 +395,33 @@ tsi_result alts_tsi_handshaker_create(
gpr_log(GPR_ERROR, "Invalid arguments to alts_tsi_handshaker_create()");
return TSI_INVALID_ARGUMENT;
}
- init_shared_resources(handshaker_service_url);
- alts_handshaker_client* client = alts_grpc_handshaker_client_create(
- kSharedResource->channel, kSharedResource->cq, handshaker_service_url);
- if (client == nullptr) {
- gpr_log(GPR_ERROR, "Failed to create ALTS handshaker client");
- return TSI_FAILED_PRECONDITION;
- }
alts_tsi_handshaker* handshaker =
static_cast<alts_tsi_handshaker*>(gpr_zalloc(sizeof(*handshaker)));
- handshaker->client = client;
- handshaker->buffer_size = TSI_ALTS_INITIAL_BUFFER_SIZE;
- handshaker->buffer =
- static_cast<unsigned char*>(gpr_zalloc(handshaker->buffer_size));
+ bool use_dedicated_cq = interested_parties == nullptr;
+ handshaker->client = nullptr;
handshaker->is_client = is_client;
handshaker->has_sent_start_message = false;
handshaker->target_name = target_name == nullptr
? grpc_empty_slice()
: grpc_slice_from_static_string(target_name);
+ handshaker->interested_parties = interested_parties;
+ handshaker->has_created_handshaker_client = false;
+ handshaker->handshaker_service_url = gpr_strdup(handshaker_service_url);
handshaker->options = grpc_alts_credentials_options_copy(options);
- handshaker->base.vtable = &handshaker_vtable;
+ handshaker->base.vtable =
+ use_dedicated_cq ? &handshaker_vtable_dedicated : &handshaker_vtable;
+ handshaker->channel =
+ use_dedicated_cq
+ ? nullptr
+ : grpc_insecure_channel_create(handshaker->handshaker_service_url,
+ nullptr, nullptr);
*self = &handshaker->base;
return TSI_OK;
}
-static bool is_handshake_finished_properly(grpc_gcp_handshaker_resp* resp) {
- GPR_ASSERT(resp != nullptr);
- if (resp->has_result) {
- return true;
- }
- return false;
-}
-
-static void set_unused_bytes(tsi_handshaker_result* self,
- grpc_slice* recv_bytes, size_t bytes_consumed) {
+void alts_tsi_handshaker_result_set_unused_bytes(tsi_handshaker_result* self,
+ grpc_slice* recv_bytes,
+ size_t bytes_consumed) {
GPR_ASSERT(recv_bytes != nullptr && self != nullptr);
if (GRPC_SLICE_LENGTH(*recv_bytes) == bytes_consumed) {
return;
@@ -402,81 +436,6 @@ static void set_unused_bytes(tsi_handshaker_result* self,
result->unused_bytes_size);
}
-void alts_tsi_handshaker_handle_response(alts_tsi_handshaker* handshaker,
- grpc_byte_buffer* recv_buffer,
- grpc_status_code status,
- grpc_slice* details,
- tsi_handshaker_on_next_done_cb cb,
- void* user_data, bool is_ok) {
- /* Invalid input check. */
- if (cb == nullptr) {
- gpr_log(GPR_ERROR,
- "cb is nullptr in alts_tsi_handshaker_handle_response()");
- return;
- }
- if (handshaker == nullptr || recv_buffer == nullptr) {
- gpr_log(GPR_ERROR,
- "Invalid arguments to alts_tsi_handshaker_handle_response()");
- cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
- return;
- }
- if (handshaker->base.handshake_shutdown) {
- gpr_log(GPR_ERROR, "TSI handshake shutdown");
- cb(TSI_HANDSHAKE_SHUTDOWN, user_data, nullptr, 0, nullptr);
- return;
- }
- /* Failed grpc call check. */
- if (!is_ok || status != GRPC_STATUS_OK) {
- gpr_log(GPR_ERROR, "grpc call made to handshaker service failed");
- if (details != nullptr) {
- char* error_details = grpc_slice_to_c_string(*details);
- gpr_log(GPR_ERROR, "error details:%s", error_details);
- gpr_free(error_details);
- }
- cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
- return;
- }
- grpc_gcp_handshaker_resp* resp =
- alts_tsi_utils_deserialize_response(recv_buffer);
- /* Invalid handshaker response check. */
- if (resp == nullptr) {
- gpr_log(GPR_ERROR, "alts_tsi_utils_deserialize_response() failed");
- cb(TSI_DATA_CORRUPTED, user_data, nullptr, 0, nullptr);
- return;
- }
- grpc_slice* slice = static_cast<grpc_slice*>(resp->out_frames.arg);
- unsigned char* bytes_to_send = nullptr;
- size_t bytes_to_send_size = 0;
- if (slice != nullptr) {
- bytes_to_send_size = GRPC_SLICE_LENGTH(*slice);
- while (bytes_to_send_size > handshaker->buffer_size) {
- handshaker->buffer_size *= 2;
- handshaker->buffer = static_cast<unsigned char*>(
- gpr_realloc(handshaker->buffer, handshaker->buffer_size));
- }
- memcpy(handshaker->buffer, GRPC_SLICE_START_PTR(*slice),
- bytes_to_send_size);
- bytes_to_send = handshaker->buffer;
- }
- tsi_handshaker_result* result = nullptr;
- if (is_handshake_finished_properly(resp)) {
- create_handshaker_result(resp, handshaker->is_client, &result);
- set_unused_bytes(result, &handshaker->recv_bytes, resp->bytes_consumed);
- }
- grpc_status_code code = static_cast<grpc_status_code>(resp->status.code);
- if (code != GRPC_STATUS_OK) {
- grpc_slice* details = static_cast<grpc_slice*>(resp->status.details.arg);
- if (details != nullptr) {
- char* error_details = grpc_slice_to_c_string(*details);
- gpr_log(GPR_ERROR, "Error from handshaker service:%s", error_details);
- gpr_free(error_details);
- }
- }
- grpc_gcp_handshaker_resp_destroy(resp);
- cb(alts_tsi_utils_convert_to_tsi_result(code), user_data, bytes_to_send,
- bytes_to_send_size, result);
-}
-
namespace grpc_core {
namespace internal {
@@ -486,29 +445,16 @@ bool alts_tsi_handshaker_get_has_sent_start_message_for_testing(
return handshaker->has_sent_start_message;
}
-bool alts_tsi_handshaker_get_is_client_for_testing(
- alts_tsi_handshaker* handshaker) {
+void alts_tsi_handshaker_set_client_vtable_for_testing(
+ alts_tsi_handshaker* handshaker, alts_handshaker_client_vtable* vtable) {
GPR_ASSERT(handshaker != nullptr);
- return handshaker->is_client;
-}
-
-void alts_tsi_handshaker_set_recv_bytes_for_testing(
- alts_tsi_handshaker* handshaker, grpc_slice* slice) {
- GPR_ASSERT(handshaker != nullptr && slice != nullptr);
- handshaker->recv_bytes = grpc_slice_ref(*slice);
+ handshaker->client_vtable_for_testing = vtable;
}
-grpc_slice alts_tsi_handshaker_get_recv_bytes_for_testing(
+bool alts_tsi_handshaker_get_is_client_for_testing(
alts_tsi_handshaker* handshaker) {
GPR_ASSERT(handshaker != nullptr);
- return handshaker->recv_bytes;
-}
-
-void alts_tsi_handshaker_set_client_for_testing(
- alts_tsi_handshaker* handshaker, alts_handshaker_client* client) {
- GPR_ASSERT(handshaker != nullptr && client != nullptr);
- alts_handshaker_client_destroy(handshaker->client);
- handshaker->client = client;
+ return handshaker->is_client;
}
alts_handshaker_client* alts_tsi_handshaker_get_client_for_testing(
diff --git a/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h
index 48ce69b1da..32f94bc9d3 100644
--- a/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h
+++ b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h
@@ -25,7 +25,8 @@
#include "src/core/lib/iomgr/pollset_set.h"
#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
-#include "src/core/tsi/alts_transport_security.h"
+#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h"
#include "src/core/tsi/transport_security.h"
#include "src/core/tsi/transport_security_interface.h"
@@ -35,10 +36,6 @@
const size_t kTsiAltsNumOfPeerProperties = 3;
-/**
- * Main struct for ALTS TSI handshaker. All APIs in the header are
- * thread-comptabile.
- */
typedef struct alts_tsi_handshaker alts_tsi_handshaker;
/**
@@ -56,7 +53,9 @@ typedef struct alts_tsi_handshaker alts_tsi_handshaker;
* - self: address of ALTS TSI handshaker instance to be returned from the
* method.
*
- * It returns TSI_OK on success and an error status code on failure.
+ * It returns TSI_OK on success and an error status code on failure. Note that
+ * if interested_parties is nullptr, a dedicated TSI thread will be created and
+ * used.
*/
tsi_result alts_tsi_handshaker_create(
const grpc_alts_credentials_options* options, const char* target_name,
@@ -64,23 +63,32 @@ tsi_result alts_tsi_handshaker_create(
grpc_pollset_set* interested_parties, tsi_handshaker** self);
/**
- * This method handles handshaker response returned from ALTS handshaker
- * service.
+ * This method creates an ALTS TSI handshaker result instance.
*
- * - handshaker: ALTS TSI handshaker instance.
- * - recv_buffer: buffer holding data received from the handshaker service.
- * - status: status of the grpc call made to the handshaker service.
- * - details: error details of the grpc call made to the handshaker service.
- * - cb: callback function of ALTS TSI event.
- * - user_data: argument of callback function.
- * - is_ok: a boolean value indicating if the handshaker response is ok to read.
+ * - resp: data received from the handshaker service.
+ * - is_client: a boolean value indicating if the result belongs to a
+ * client or not.
+ * - result: address of ALTS TSI handshaker result instance.
+ */
+tsi_result alts_tsi_handshaker_result_create(grpc_gcp_handshaker_resp* resp,
+ bool is_client,
+ tsi_handshaker_result** result);
+
+/**
+ * This method sets unused bytes of ALTS TSI handshaker result instance.
*
+ * - result: an ALTS TSI handshaker result instance.
+ * - recv_bytes: data received from the handshaker service.
+ * - bytes_consumed: size of data consumed by the handshaker service.
+ */
+void alts_tsi_handshaker_result_set_unused_bytes(tsi_handshaker_result* result,
+ grpc_slice* recv_bytes,
+ size_t bytes_consumed);
+
+/**
+ * This method returns a boolean value indicating if an ALTS TSI handshaker
+ * has been shutdown or not.
*/
-void alts_tsi_handshaker_handle_response(alts_tsi_handshaker* handshaker,
- grpc_byte_buffer* recv_buffer,
- grpc_status_code status,
- grpc_slice* details,
- tsi_handshaker_on_next_done_cb cb,
- void* user_data, bool is_ok);
+bool alts_tsi_handshaker_has_shutdown(alts_tsi_handshaker* handshaker);
#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H */
diff --git a/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h b/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h
index 9612071407..ec2616e95f 100644
--- a/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h
+++ b/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h
@@ -27,27 +27,55 @@ namespace grpc_core {
namespace internal {
/**
- * Unsafe, use for testing only. It allows the caller to change the way the
- * ALTS TSI handshaker schedules handshaker requests.
- */
-void alts_tsi_handshaker_set_client_for_testing(alts_tsi_handshaker* handshaker,
- alts_handshaker_client* client);
+ * Unsafe, use for testing only. */
alts_handshaker_client* alts_tsi_handshaker_get_client_for_testing(
alts_tsi_handshaker* handshaker);
-/* For testing only. */
bool alts_tsi_handshaker_get_has_sent_start_message_for_testing(
alts_tsi_handshaker* handshaker);
+void alts_tsi_handshaker_set_client_vtable_for_testing(
+ alts_tsi_handshaker* handshaker, alts_handshaker_client_vtable* vtable);
+
bool alts_tsi_handshaker_get_is_client_for_testing(
alts_tsi_handshaker* handshaker);
-void alts_tsi_handshaker_set_recv_bytes_for_testing(
- alts_tsi_handshaker* handshaker, grpc_slice* slice);
+void alts_handshaker_client_set_grpc_caller_for_testing(
+ alts_handshaker_client* client, alts_grpc_caller caller);
-grpc_slice alts_tsi_handshaker_get_recv_bytes_for_testing(
- alts_tsi_handshaker* handshaker);
+grpc_byte_buffer* alts_handshaker_client_get_send_buffer_for_testing(
+ alts_handshaker_client* client);
+
+grpc_byte_buffer** alts_handshaker_client_get_recv_buffer_addr_for_testing(
+ alts_handshaker_client* client);
+
+grpc_metadata_array* alts_handshaker_client_get_initial_metadata_for_testing(
+ alts_handshaker_client* client);
+
+void alts_handshaker_client_set_recv_bytes_for_testing(
+ alts_handshaker_client* client, grpc_slice* recv_bytes);
+
+void alts_handshaker_client_check_fields_for_testing(
+ alts_handshaker_client* client, tsi_handshaker_on_next_done_cb cb,
+ void* user_data, bool has_sent_start_message, grpc_slice* recv_bytes);
+
+void alts_handshaker_client_set_fields_for_testing(
+ alts_handshaker_client* client, alts_tsi_handshaker* handshaker,
+ tsi_handshaker_on_next_done_cb cb, void* user_data,
+ grpc_byte_buffer* recv_buffer, grpc_status_code status);
+
+void alts_handshaker_client_set_vtable_for_testing(
+ alts_handshaker_client* client, alts_handshaker_client_vtable* vtable);
+
+alts_tsi_handshaker* alts_handshaker_client_get_handshaker_for_testing(
+ alts_handshaker_client* client);
+
+void alts_handshaker_client_set_cb_for_testing(
+ alts_handshaker_client* client, tsi_handshaker_on_next_done_cb cb);
+
+grpc_closure* alts_handshaker_client_get_closure_for_testing(
+ alts_handshaker_client* client);
} // namespace internal
} // namespace grpc_core
diff --git a/src/core/tsi/alts_transport_security.cc b/src/core/tsi/alts_transport_security.cc
deleted file mode 100644
index dac23bbf7a..0000000000
--- a/src/core/tsi/alts_transport_security.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- *
- * Copyright 2017 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#include <grpc/support/port_platform.h>
-
-#include "src/core/tsi/alts_transport_security.h"
-
-#include <string.h>
-
-static alts_shared_resource g_alts_resource;
-
-alts_shared_resource* alts_get_shared_resource(void) {
- return &g_alts_resource;
-}
-
-static void grpc_tsi_alts_wait_for_cq_drain() {
- gpr_mu_lock(&g_alts_resource.mu);
- while (!g_alts_resource.is_cq_drained) {
- gpr_cv_wait(&g_alts_resource.cv, &g_alts_resource.mu,
- gpr_inf_future(GPR_CLOCK_REALTIME));
- }
- gpr_mu_unlock(&g_alts_resource.mu);
-}
-
-void grpc_tsi_alts_signal_for_cq_destroy() {
- gpr_mu_lock(&g_alts_resource.mu);
- g_alts_resource.is_cq_drained = true;
- gpr_cv_signal(&g_alts_resource.cv);
- gpr_mu_unlock(&g_alts_resource.mu);
-}
-
-void grpc_tsi_alts_init() {
- g_alts_resource.channel = nullptr;
- g_alts_resource.cq = nullptr;
- g_alts_resource.is_cq_drained = false;
- gpr_mu_init(&g_alts_resource.mu);
- gpr_cv_init(&g_alts_resource.cv);
-}
-
-void grpc_tsi_alts_shutdown() {
- if (g_alts_resource.cq != nullptr) {
- grpc_completion_queue_shutdown(g_alts_resource.cq);
- grpc_tsi_alts_wait_for_cq_drain();
- grpc_completion_queue_destroy(g_alts_resource.cq);
- grpc_channel_destroy(g_alts_resource.channel);
- g_alts_resource.thread.Join();
- }
- gpr_cv_destroy(&g_alts_resource.cv);
- gpr_mu_destroy(&g_alts_resource.mu);
-}
diff --git a/src/core/tsi/alts_transport_security.h b/src/core/tsi/alts_transport_security.h
deleted file mode 100644
index d6b8e11137..0000000000
--- a/src/core/tsi/alts_transport_security.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- *
- * Copyright 2017 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#ifndef GRPC_CORE_TSI_ALTS_TRANSPORT_SECURITY_H
-#define GRPC_CORE_TSI_ALTS_TRANSPORT_SECURITY_H
-
-#include <grpc/support/port_platform.h>
-
-#include <grpc/grpc.h>
-#include <grpc/support/sync.h>
-
-#include "src/core/lib/gprpp/thd.h"
-
-typedef struct alts_shared_resource {
- grpc_core::Thread thread;
- grpc_channel* channel;
- grpc_completion_queue* cq;
- gpr_mu mu;
- gpr_cv cv;
- bool is_cq_drained;
-} alts_shared_resource;
-
-/* This method returns the address of alts_shared_resource object shared by all
- * TSI handshakes. */
-alts_shared_resource* alts_get_shared_resource(void);
-
-/* This method signals the thread that invokes grpc_tsi_alts_shutdown() to
- * continue with destroying the cq as a part of shutdown process. */
-
-void grpc_tsi_alts_signal_for_cq_destroy(void);
-
-#endif /* GRPC_CORE_TSI_ALTS_TRANSPORT_SECURITY_H */
diff --git a/src/core/tsi/transport_security.cc b/src/core/tsi/transport_security.cc
index ca861b52de..078a917bba 100644
--- a/src/core/tsi/transport_security.cc
+++ b/src/core/tsi/transport_security.cc
@@ -213,10 +213,10 @@ tsi_result tsi_handshaker_next(
void tsi_handshaker_shutdown(tsi_handshaker* self) {
if (self == nullptr || self->vtable == nullptr) return;
- self->handshake_shutdown = true;
if (self->vtable->shutdown != nullptr) {
self->vtable->shutdown(self);
}
+ self->handshake_shutdown = true;
}
void tsi_handshaker_destroy(tsi_handshaker* self) {
diff --git a/src/cpp/client/channel_cc.cc b/src/cpp/client/channel_cc.cc
index 2cab41b3f5..8e1cea0269 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,
+ size_t interceptor_pos) {
const bool kRegistered = method.channel_tag() && context->authority().empty();
grpc_call* c_call = nullptr;
if (kRegistered) {
@@ -146,18 +147,27 @@ internal::Call Channel::CreateCall(const internal::RpcMethod& method,
}
}
grpc_census_call_set_context(c_call, context->census_context());
+
+ // ClientRpcInfo should be set before call because set_call also checks
+ // whether the call has been cancelled, and if the call was cancelled, we
+ // should notify the interceptors too/
+ auto* info = context->set_client_rpc_info(
+ method.name(), this, interceptor_creators_, interceptor_pos);
context->set_call(c_call, shared_from_this());
- return internal::Call(c_call, this, cq);
+
+ 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 +229,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 +242,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/client/client_context.cc b/src/cpp/client/client_context.cc
index 07a04e4268..50da75f09c 100644
--- a/src/cpp/client/client_context.cc
+++ b/src/cpp/client/client_context.cc
@@ -24,6 +24,7 @@
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
+#include <grpcpp/impl/codegen/interceptor_common.h>
#include <grpcpp/impl/grpc_library.h>
#include <grpcpp/security/credentials.h>
#include <grpcpp/server_context.h>
@@ -86,10 +87,13 @@ void ClientContext::set_call(grpc_call* call,
call_ = call;
channel_ = channel;
if (creds_ && !creds_->ApplyToCall(call_)) {
+ // TODO(yashykt): should interceptors also see this status?
+ SendCancelToInterceptors();
grpc_call_cancel_with_status(call, GRPC_STATUS_CANCELLED,
"Failed to set credentials to rpc.", nullptr);
}
if (call_canceled_) {
+ SendCancelToInterceptors();
grpc_call_cancel(call_, nullptr);
}
}
@@ -110,12 +114,20 @@ void ClientContext::set_compression_algorithm(
void ClientContext::TryCancel() {
std::unique_lock<std::mutex> lock(mu_);
if (call_) {
+ SendCancelToInterceptors();
grpc_call_cancel(call_, nullptr);
} else {
call_canceled_ = true;
}
}
+void ClientContext::SendCancelToInterceptors() {
+ internal::CancelInterceptorBatchMethods cancel_methods;
+ for (size_t i = 0; i < rpc_info_.interceptors_.size(); i++) {
+ rpc_info_.RunInterceptor(&cancel_methods, i);
+ }
+}
+
grpc::string ClientContext::peer() const {
grpc::string peer;
if (call_) {
diff --git a/src/cpp/client/client_interceptor.cc b/src/cpp/client/client_interceptor.cc
new file mode 100644
index 0000000000..3a5cac9830
--- /dev/null
+++ b/src/cpp/client/client_interceptor.cc
@@ -0,0 +1,34 @@
+/*
+ *
+ * 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 <grpcpp/impl/codegen/client_interceptor.h>
+
+namespace grpc {
+
+namespace internal {
+experimental::ClientInterceptorFactoryInterface*
+ g_global_client_interceptor_factory = nullptr;
+}
+
+namespace experimental {
+void RegisterGlobalClientInterceptorFactory(
+ ClientInterceptorFactoryInterface* factory) {
+ internal::g_global_client_interceptor_factory = factory;
+}
+} // namespace experimental
+} // namespace grpc
diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc
index d1cd78e755..7faaa20e78 100644
--- a/src/cpp/client/secure_credentials.cc
+++ b/src/cpp/client/secure_credentials.cc
@@ -228,9 +228,10 @@ int MetadataCredentialsPluginWrapper::GetMetadata(
}
if (w->plugin_->IsBlocking()) {
// Asynchronous return.
- w->thread_pool_->Add(
- std::bind(&MetadataCredentialsPluginWrapper::InvokePlugin, w, context,
- cb, user_data, nullptr, nullptr, nullptr, nullptr));
+ w->thread_pool_->Add([w, context, cb, user_data] {
+ w->MetadataCredentialsPluginWrapper::InvokePlugin(
+ context, cb, user_data, nullptr, nullptr, nullptr, nullptr);
+ });
return 0;
} else {
// Synchronous return.
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/ext/filters/census/context.cc b/src/cpp/ext/filters/census/context.cc
index 4b3250236d..78fc69a805 100644
--- a/src/cpp/ext/filters/census/context.cc
+++ b/src/cpp/ext/filters/census/context.cc
@@ -29,9 +29,15 @@ void GenerateServerContext(absl::string_view tracing, absl::string_view stats,
absl::string_view primary_role,
absl::string_view method, CensusContext* context) {
GrpcTraceContext trace_ctxt;
- TraceContextEncoding::Decode(tracing, &trace_ctxt);
- SpanContext parent_ctx = trace_ctxt.ToSpanContext();
- new (context) CensusContext(method, parent_ctx);
+ if (TraceContextEncoding::Decode(tracing, &trace_ctxt) !=
+ TraceContextEncoding::kEncodeDecodeFailure) {
+ SpanContext parent_ctx = trace_ctxt.ToSpanContext();
+ if (parent_ctx.IsValid()) {
+ new (context) CensusContext(method, parent_ctx);
+ return;
+ }
+ }
+ new (context) CensusContext(method);
}
void GenerateClientContext(absl::string_view method, CensusContext* ctxt,
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/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc
index 536bf022dd..ebb17def32 100644
--- a/src/cpp/server/secure_server_credentials.cc
+++ b/src/cpp/server/secure_server_credentials.cc
@@ -43,9 +43,10 @@ void AuthMetadataProcessorAyncWrapper::Process(
return;
}
if (w->processor_->IsBlocking()) {
- w->thread_pool_->Add(
- std::bind(&AuthMetadataProcessorAyncWrapper::InvokeProcessor, w,
- context, md, num_md, cb, user_data));
+ w->thread_pool_->Add([w, context, md, num_md, cb, user_data] {
+ w->AuthMetadataProcessorAyncWrapper::InvokeProcessor(context, md, num_md,
+ cb, user_data);
+ });
} else {
// invoke directly.
w->InvokeProcessor(context, md, num_md, cb, user_data);
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..7a98bce507 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"
@@ -54,6 +58,9 @@ namespace {
// max-threads set) to the server builder.
#define DEFAULT_MAX_SYNC_SERVER_THREADS INT_MAX
+// How many callback requests of each method should we pre-register at start
+#define DEFAULT_CALLBACK_REQS_PER_METHOD 32
+
class DefaultGlobalCallbacks final : public Server::GlobalCallbacks {
public:
~DefaultGlobalCallbacks() override {}
@@ -127,10 +134,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 +150,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 +179,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();
@@ -192,9 +202,21 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
}
}
+ void PostShutdownCleanup() {
+ if (call_) {
+ grpc_call_unref(call_);
+ call_ = nullptr;
+ }
+ if (cq_) {
+ grpc_completion_queue_destroy(cq_);
+ cq_ = nullptr;
+ }
+ }
+
bool FinalizeResult(void** tag, bool* status) override {
if (!*status) {
grpc_completion_queue_destroy(cq_);
+ cq_ = nullptr;
}
if (call_details_) {
deadline_ = call_details_->deadline;
@@ -204,17 +226,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 +260,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));
+ if (interceptor_methods_.RunInterceptors(
+ [this]() { ContinueRunAfterInterception(); })) {
+ 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 +343,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 +559,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 +569,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
@@ -359,7 +601,17 @@ class Server::SyncRequestThreadManager : public ThreadManager {
void* tag;
bool ok;
while (server_cq_->Next(&tag, &ok)) {
- // Do nothing
+ if (ok) {
+ // If a request was pulled off the queue, it means that the thread
+ // handling the request added it to the completion queue after shutdown
+ // was called - because the thread had already started and checked the
+ // shutdown flag before shutdown was called. In this case, we simply
+ // clean it up here, *after* calling wait on all the worker threads, at
+ // which point we are certain no in-flight requests will add more to the
+ // queue. This fixes an intermittent memory leak on shutdown.
+ SyncRequest* sync_req = static_cast<SyncRequest*>(tag);
+ sync_req->PostShutdownCleanup();
+ }
}
}
@@ -389,8 +641,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 +702,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 +777,31 @@ 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. Register at least some callback requests
+ // TODO(vjpai): Register these dynamically based on need
+ for (int i = 0; i < DEFAULT_CALLBACK_REQS_PER_METHOD; i++) {
+ 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 +852,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 +890,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 +955,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 +985,43 @@ 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_);
+ if (interceptor_methods_.RunInterceptors(
+ [this]() { ContinueFinalizeResultAfterInterception(); })) {
+ // 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 +1029,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 +1063,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 +1076,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 +1090,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 +1124,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..9c01f896e6 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,192 @@ 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);
}
-ServerContext::~ServerContext() {
+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() { Clear(); }
+
+void ServerContext::Clear() {
+ auth_context_.reset();
+ initial_metadata_.clear();
+ trailing_metadata_.clear();
+ client_metadata_.Reset();
if (call_) {
grpc_call_unref(call_);
}
if (completion_op_) {
completion_op_->Unref();
+ completion_tag_.Clear();
}
+ 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_.Set(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_);
@@ -198,6 +298,12 @@ void ServerContext::AddTrailingMetadata(const grpc::string& key,
}
void ServerContext::TryCancel() const {
+ internal::CancelInterceptorBatchMethods cancel_methods;
+ if (rpc_info_) {
+ for (size_t i = 0; i < rpc_info_->interceptors_.size(); i++) {
+ rpc_info_->RunInterceptor(&cancel_methods, i);
+ }
+ }
grpc_call_error err = grpc_call_cancel_with_status(
call_, GRPC_STATUS_CANCELLED, "Cancelled on the server side", nullptr);
if (err != GRPC_CALL_OK) {
@@ -206,12 +312,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/csharp/BUILD-INTEGRATION.md b/src/csharp/BUILD-INTEGRATION.md
new file mode 100644
index 0000000000..3addc2403c
--- /dev/null
+++ b/src/csharp/BUILD-INTEGRATION.md
@@ -0,0 +1,357 @@
+Protocol Buffers/gRPC Integration Into .NET Build
+=================================================
+
+With Grpc.Tools package version 1.17 we made it easier to compile .proto files
+in your project using the `dotnet build` command, Visual Studio, or command-line
+MSBuild. You need to configure the .csproj project according to the way you want
+to integrate Protocol Buffer files into your build. If you are upgrading an
+existing project, read through this list of common scenarios and decide if any
+one of them matches your approach. The protoc command line migration is
+explained near the end of this document; this migration may be the quickest but
+not the long-term solution.
+
+There is also a Reference section at the end of the file.
+
+Reporting issues
+----------------
+
+First thing first, if you found a bug in this new build system, or have a
+scenario that is not easily covered, please open an [issue in the gRPC
+repository](https://github.com/grpc/grpc/issues), and **tag the user @kkm000**
+somewhere in the text (for example, include `/cc @kkm000` at end of the issue
+text) to seize his immediate attention.
+
+Common scenarios
+----------------
+
+### I just want to compile .proto files into my library
+
+This is the approach taken by the examples in the `csharp/examples` directory.
+Protoc output files (for example, `Helloworld.cs` and `HelloworldGrpc.cs`
+compiled from `helloworld.proto`) are placed among *object* and other temporary
+files of your project, and automatically provided as inputs to the C# compiler.
+As with other automatically generated .cs files, they are included in the source
+and symbols NuGet package, if you build one.
+
+Simply reference your .proto files in a `<Protobuf>` item group. The following
+example will add all .proto files in a project and all its subdirectories
+(excluding special directories such as `bin` and `obj`):
+
+```xml
+ <ItemGroup>
+ <Protobuf Include="**/*.proto" />
+ </ItemGroup>
+```
+
+You must add a reference to the NuGet packages Grpc.Tools and Grpc (the latter
+is a meta-package, in turn referencing Grpc.Core and Google.Protobuf packages).
+It is **very important** to mark Grpc.Tools as a development-only dependency, so
+that the *users* of your library do not fetch the tools package:
+
+* "Classic" .csproj with `packages.config` (Visual Studio, Mono): This is
+ handled automatically by NuGet. See the attribute added by Visual Studio to the
+ [packages.config](../../examples/csharp/HelloworldLegacyCsproj/Greeter/packages.config#L6)
+ file in the HelloworldLegacyCsproj/Greeter example.
+
+* "SDK" .csproj (Visual Studio, `dotnet new`): Add an attribute
+ `PrivateAssets="All"` to the Grpc.Tools package reference. See an example in the
+ [Greeter.csproj](../../examples/csharp/Helloworld/Greeter/Greeter.csproj#L10)
+ example project in this repository. If adding a package reference in Visual
+ Studio, edit the project file and add this attribute. [This is a bug in NuGet
+ client](https://github.com/NuGet/Home/issues/4125).
+
+If building a NuGet package from your library with the nuget command line tool
+from a .nuspec file, then the spec file may (and probably should) reference the
+Grpc metapackage, but **do not add a reference to Grpc.Tools** to it. .NET "SDK"
+projects handle this automatically when called from `dotnet pack` by excluding
+any packages with private assets, such as thus marked Grpc.Tools.
+
+#### Per-file options that can be set in Visual Studio
+
+For a "classic" project, you can only add .proto files with all options set to
+default (if you find it necessary to modify these options, then hand-edit the
+.csproj file). Click on the "show all files" button, add files to project, then
+change file type of the .proto files to "Protobuf" in the Properties window
+drop-down. This menu item will appear after you import the Grpc.Tools package:
+
+![Properties in a classic project](doc/integration.md-fig.1-classic.png)
+
+For an "SDK" project, you have more control of some frequently used options.
+**You may need to open and close Visual Studio** for this form to appear in the
+properties window after adding a reference to Grpc.Tools package (we do not know
+whether this is a bug or by design, but it looks like a bug):
+
+![Properties in an SDK project](doc/integration.md-fig.2-sdk.png)
+
+You can also change options of multiple files at once by selecting them in the
+Project Explorer together.
+
+See the Reference section at end of this file for options that can be set
+per-file by modifying the source .csproj directly.
+
+#### My .proto files are in a directory outside the project
+
+Refer to the example files
+[RouteGuide.csproj](../../examples/csharp/RouteGuide/RouteGuide/RouteGuide.csproj#L58-L60)
+and [Greeter.csproj](../../examples/csharp/Helloworld/Greeter/Greeter.csproj#L11)
+in this repository. For the files to show up in Visual Studio properly, add a
+`Link` attribute with just a filename to the `<Protobuf>` item. This will be the
+display name of the file. In the `Include` attribute, specify the complete path
+to file. A relative path is based off the project directory.
+
+Or, if using Visual Studio, add files _as links_ from outside directory. In the
+Add Files dialog, there is a little [down arrow near the Open
+button](https://stackoverflow.com/a/9770061). Click on it, and choose "Add as
+link". If you do not select this option, Visual Studio will copy files to the
+project directory instead.
+
+### I just want to generate proto and gRPC C# sources from my .proto files (no C# compile)
+
+Suppose you want to place generated files right beside each respective source
+.proto file. Create a .csproj library file in the common root of your .proto
+tree, and add a reference to Grpc.Tools package (this works in Windows too, `$`
+below stands for a command prompt in either platform):
+
+```
+/myproject/myprotofiles$ dotnet new classlib
+ . . .
+ Restoring packages for /myproject/myprotofiles/myprotofiles.csproj...
+ . . .
+/myproject/myprotofiles$ rm *.cs <-- remove all *.cs files from template;
+C:\myproject\myprotofiles> del *.cs /y <-- on Windows, use the del command instead.
+/myproject/myprotofiles$ dotnet add package Grpc.Tools
+```
+
+(the latter command also accepts an optional `--version X.Y` switch for a
+specific version of package, should you need one). Next open the generated
+.csproj file in a text editor.
+
+Since you are not building a package, you may not worry about adding
+`PrivateAssets="All"` attribute, but it will not hurt, in case you are
+repurposing the project at some time later. The important part is (1) tell the
+gRPC tools to select the whole directory of files; (2) order placement of each
+output besides its source, and (3) not compile the generated .cs files. Add the
+following stanza under the `<Project>` xml node:
+
+```xml
+ <ItemGroup>
+ <Protobuf Include="**/*.proto" OutputDir="%(RelativePath)" CompileOutputs="false" />
+ </ItemGroup>
+```
+
+The `Include` tells the build system to recursively examine project directory
+and its subdirectories (`**`) include all files matching the wildcard `*.proto`.
+You can instead selectively include your files or selectively exclude files from
+the glob pattern; [MSBuild documentation explains
+that](https://docs.microsoft.com/visualstudio/msbuild/msbuild-items). The
+`OutputDir="%(RelativePath)"` orders the output directory for each .cs file be
+same as the corresponding .proto directory. Finally, `CompileOutputs="false"`
+prevents compiling the generated files into an assembly.
+
+Note that an empty assembly is still generated, but you should ignore it. As
+with any build system, it is used to detect out-of-date dependencies and
+recompile them.
+
+#### I am getting a warning about a missing expected file!
+
+When we are preparing compile, there is no way to know whether a given proto
+file will produce a *Grpc.cs output or not. If the proto file has a `service`
+clause, it will; otherwise, it won't, but the build script cannot know that in
+advance. When we are treating generated .cs files as temporary, this is ok, but
+when generating them for you, creating empty files is probably not. You need to
+tell the compiler which files should be compiled with gRPC services, and which
+only contain protobuffer message definitions.
+
+One option is just ignore the warning. Another is quench it by setting the
+property `Protobuf_NoWarnMissingExpected` to `true`:
+
+```xml
+<PropertyGroup>
+ <Protobuf_NoWarnMissingExpected>true</Protobuf_NoWarnMissingExpected>
+</PropertyGroup>
+```
+
+For a small to medium projects this is sufficient. But because of a missing
+output dependency, the corresponding .proto file will be recompiled on every
+build. If your project is large, or if other large builds depend on generated
+files, and are also needlessly recompiled, you'll want to prevent these rebuilds
+when files have not in fact changed, as follows:
+
+##### Explicitly tell protoc for which files it should use the gRPC plugin
+
+You need to set the `Protobuf` item property `GrpcServices` to `None` for those
+.proto inputs which do not have a `service` declared (or, optionally, those
+which do but you do not want a service/client stub for). The default value for
+the `GrpcServices` is `Both` (both client and server stub are generated). This
+is easy enough to do with glob patterns if your files are laid out in
+directories according to their service use, for example:
+
+```xml
+ <ItemGroup>
+ <Protobuf Include="**/*.proto" OutputDir="%(RelativePath)"
+ CompileOutputs="false" GrpcServices="None" />
+ <Protobuf Update="**/hello/*.proto;**/bye/*.proto" GrpcServices="Both" />
+ </ItemGroup>
+```
+
+In this sample, all .proto files are compiled with `GrpcServices="None"`, except
+for .proto files in subdirectories on any tree level named `hello/` and `bye`,
+which will take `GrpcServices="Both"` Note the use of the `Update` attribute
+instead of `Include`. If you write `Include` by mistake, the files will be added
+to compile *twice*, once with, and once without GrpcServices. Pay attention not
+to do that!
+
+Another example would be the use of globbing if your service .proto files are
+named according to a pattern, for example `*_services.proto`. In this case, The
+`Update` attribute can be written as `Update="**/*_service.proto"`, to set the
+attribute `GrpcServices="Both"` only on these files.
+
+But what if no patterns work, and you cannot sort a large set of .proto file
+into those containing a service and those not? As a last resort,
+
+##### Force creating empty .cs files for missing outputs.
+
+Naturally, this results in a dirtier compiler output tree, but you may clean it
+using other ways (for example, by not copying zero-length .cs files to their
+final destination). Remember, though, that the files are still important to keep
+in their output locations to prevent needless recompilation. You may force
+generating empty files by setting the property `Protobuf_TouchMissingExpected`
+to `true`:
+
+```xml
+ <PropertyGroup>
+ <Protobuf_TouchMissingExpected>true</Protobuf_TouchMissingExpected>
+ </PropertyGroup>
+```
+
+#### But I do not use gRPC at all, I need only protobuffer messages compiled
+
+Set `GrpcServices="None"` on all proto files:
+
+```xml
+ <ItemGroup>
+ <Protobuf Include="**/*.proto" OutputDir="%(RelativeDir)"
+ CompileOutputs="false" GrpcServices="None" />
+ </ItemGroup>
+```
+
+#### That's good so far, but I do not want the `bin` and `obj` directories in my tree
+
+You may create the project in a subdirectory of the root of your files, such as,
+for example, `.build`. In this case, you want to refer to the proto files
+relative to that `.build/` directory as
+
+```xml
+ <ItemGroup>
+ <Protobuf Include="../**/*.proto" ProtoRoot=".."
+ OutputDir="%(RelativeDir)" CompileOutputs="false" />
+ </ItemGroup>
+```
+
+Pay attention to the `ProtoRoot` property. It needs to be set to the directory
+where `import` declarations in the .proto files are looking for files, since the
+project root is no longer the same as the proto root.
+
+Alternatively, you may place the project in a directory *above* your proto root,
+and refer to the files with a subdirectory name:
+
+```xml
+ <ItemGroup>
+ <Protobuf Include="proto_root/**/*.proto" ProtoRoot="proto_root"
+ OutputDir="%(RelativeDir)" CompileOutputs="false" />
+ </ItemGroup>
+```
+
+### Alas, this all is nice, but my scenario is more complex, -OR-
+### I'll investigate that when I have time. I just want to run protoc as I did before.
+
+One option is examine our [.targets and .props files](Grpc.Tools/build/) and see
+if you can create your own build sequence from the provided targets so that it
+fits your needs. Also please open an issue (and tag @kkm000 in it!) with your
+scenario. We'll try to support it if it appears general enough.
+
+But if you just want to run `protoc` using MsBuild `<Exec>` task, as you
+probably did before the version 1.17 of Grpc.Tools, we have a few build
+variables that point to resolved names of tools and common protoc imports.
+You'll have to roll your own dependency checking (or go with a full
+recompilation each time, if that works for you), but at the very least each
+version of the Tools package will point to the correct location of the files,
+and resolve the compiler and plugin executables appropriate for the host system.
+These property variables are:
+
+* `Protobuf_ProtocFullPath` points to the full path and filename of protoc executable, e. g.,
+ "C:\Users\kkm\.nuget\packages\grpc.tools\1.17.0\build\native\bin\windows\protoc.exe".
+
+* `gRPC_PluginFullPath` points to the full path and filename of gRPC plugin, such as
+ "C:\Users\kkm\.nuget\packages\grpc.tools\1.17.0\build\native\bin\windows\grpc_csharp_plugin.exe"
+
+* `Protobuf_StandardImportsPath` points to the standard proto import directory, for example,
+ "C:\Users\kkm\.nuget\packages\grpc.tools\1.17.0\build\native\include". This is
+ the directory where a declaration such as `import "google/protobuf/wrappers.proto";`
+ in a proto file would find its target.
+
+Use MSBuild property expansion syntax `$(VariableName)` in your protoc command
+line to substitute these variables, for instance,
+
+```xml
+ <Target Name="MyProtoCompile">
+ <PropertyGroup>
+ <ProtoCCommand>$(Protobuf_ProtocFullPath) --plugin=protoc-gen-grpc=$(gRPC_PluginFullPath) -I $(Protobuf_StandardImportsPath) ....rest of your command.... </ProtoCCommand>
+ </PropertyGroup>
+ <Message Importance="high" Text="$(ProtoCCommand)" />
+ <Exec Command="$(ProtoCCommand)" />
+ </Target>
+```
+
+Also make sure *not* to include any file names to the `Protobuf` item
+collection, otherwise they will be compiled by default. If, by any chance, you
+used that name for your build scripting, you must rename it.
+
+### What about C++ projects?
+
+This is in the works. Currently, the same variables as above are set to point to
+the protoc binary, C++ gRPC plugin and the standard imports, but nothing else.
+Do not use the `Protobuf` item collection name so that your project remains
+future-proof. We'll use it for C++ projects too.
+
+Reference
+---------
+
+### Protobuf item metadata reference
+
+The following metadata are recognized on the `<Protobuf>` items.
+
+| Name | Default | Value | Synopsis |
+|----------------|-----------|----------------------|----------------------------------|
+| Access | `public` | `public`, `internal` | Generated class access |
+| ProtoCompile | `true` | `true`, `false` | Pass files to protoc? |
+| ProtoRoot | See notes | A directory | Common root for set of files |
+| CompileOutputs | `true` | `true`, `false` | C#-compile generated files? |
+| OutputDir | See notes | A directory | Directory for generated C# files |
+| GrpcOutputDir | See notes | A directory | Directory for generated stubs |
+| GrpcServices | `both` | `none`, `client`, | Generated gRPC stubs |
+| | | `server`, `both` | |
+
+__Notes__
+
+* __ProtoRoot__
+For files _inside_ the project cone, `ProtoRoot` is set by default to the
+project directory. For every file _outside_ of the project directory, the value
+is set to this file's containing directory name, individually per file. If you
+include a subtree of proto files that lies outside of the project directory, you
+need to set this metadatum. There is an example in this file above. The path in
+this variable is relative to the project directory.
+
+* __OutputDir__
+The default value for this metadatum is the value of the property
+`Protobuf_OutputPath`. This property, in turn, unless you set it in your
+project, will be set to the value of the standard MSBuild property
+`IntermediateOutputPath`, which points to the location of compilation object
+outputs, such as "obj/Release/netstandard1.5/". The path in this property is
+considered relative to the project directory.
+
+* __GrpcOutputDir__
+Unless explicitly set, will follow `OutputDir` for any given file.
+
+* __Access__
+Sets generated class access on _both_ generated message and gRPC stub classes.
diff --git a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs
index e7d8939978..5c7d48f786 100644
--- a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs
+++ b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs
@@ -49,7 +49,7 @@ namespace Grpc.Core.Internal.Tests
fakeCall = new FakeNativeCall();
asyncCallServer = new AsyncCallServer<string, string>(
- Marshallers.StringMarshaller.Serializer, Marshallers.StringMarshaller.Deserializer,
+ Marshallers.StringMarshaller.ContextualSerializer, Marshallers.StringMarshaller.ContextualDeserializer,
server);
asyncCallServer.InitializeForTesting(fakeCall);
}
diff --git a/src/csharp/Grpc.Core.Tests/MarshallerTest.cs b/src/csharp/Grpc.Core.Tests/MarshallerTest.cs
index 97f64a0575..ad3e81d61f 100644
--- a/src/csharp/Grpc.Core.Tests/MarshallerTest.cs
+++ b/src/csharp/Grpc.Core.Tests/MarshallerTest.cs
@@ -69,11 +69,8 @@ namespace Grpc.Core.Tests
Assert.AreSame(contextualSerializer, marshaller.ContextualSerializer);
Assert.AreSame(contextualDeserializer, marshaller.ContextualDeserializer);
-
- // test that emulated serializer and deserializer work
- var origMsg = "abc";
- var serialized = marshaller.Serializer(origMsg);
- Assert.AreEqual(origMsg, marshaller.Deserializer(serialized));
+ Assert.Throws(typeof(NotImplementedException), () => marshaller.Serializer("abc"));
+ Assert.Throws(typeof(NotImplementedException), () => marshaller.Deserializer(new byte[] {1, 2, 3}));
}
class FakeSerializationContext : SerializationContext
diff --git a/src/csharp/Grpc.Core/DeserializationContext.cs b/src/csharp/Grpc.Core/DeserializationContext.cs
index 5b6372ef85..d69e0db5bd 100644
--- a/src/csharp/Grpc.Core/DeserializationContext.cs
+++ b/src/csharp/Grpc.Core/DeserializationContext.cs
@@ -16,6 +16,8 @@
#endregion
+using System;
+
namespace Grpc.Core
{
/// <summary>
@@ -41,6 +43,9 @@ namespace Grpc.Core
/// (as there is no practical reason for doing so) and <c>DeserializationContext</c> implementations are free to assume so.
/// </summary>
/// <returns>byte array containing the entire payload.</returns>
- public abstract byte[] PayloadAsNewBuffer();
+ public virtual byte[] PayloadAsNewBuffer()
+ {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index 4cdf0ee6a7..b6d687f71e 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -54,7 +54,7 @@ namespace Grpc.Core.Internal
ClientSideStatus? finishedStatus;
public AsyncCall(CallInvocationDetails<TRequest, TResponse> callDetails)
- : base(callDetails.RequestMarshaller.Serializer, callDetails.ResponseMarshaller.Deserializer)
+ : base(callDetails.RequestMarshaller.ContextualSerializer, callDetails.ResponseMarshaller.ContextualDeserializer)
{
this.details = callDetails.WithOptions(callDetails.Options.Normalize());
this.initialMetadataSent = true; // we always send metadata at the very beginning of the call.
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
index a93dc34620..39c9f7c616 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
@@ -40,8 +40,8 @@ namespace Grpc.Core.Internal
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<AsyncCallBase<TWrite, TRead>>();
protected static readonly Status DeserializeResponseFailureStatus = new Status(StatusCode.Internal, "Failed to deserialize response message.");
- readonly Func<TWrite, byte[]> serializer;
- readonly Func<byte[], TRead> deserializer;
+ readonly Action<TWrite, SerializationContext> serializer;
+ readonly Func<DeserializationContext, TRead> deserializer;
protected readonly object myLock = new object();
@@ -63,7 +63,7 @@ namespace Grpc.Core.Internal
protected bool initialMetadataSent;
protected long streamingWritesCounter; // Number of streaming send operations started so far.
- public AsyncCallBase(Func<TWrite, byte[]> serializer, Func<byte[], TRead> deserializer)
+ public AsyncCallBase(Action<TWrite, SerializationContext> serializer, Func<DeserializationContext, TRead> deserializer)
{
this.serializer = GrpcPreconditions.CheckNotNull(serializer);
this.deserializer = GrpcPreconditions.CheckNotNull(deserializer);
@@ -215,14 +215,26 @@ namespace Grpc.Core.Internal
protected byte[] UnsafeSerialize(TWrite msg)
{
- return serializer(msg);
+ DefaultSerializationContext context = null;
+ try
+ {
+ context = DefaultSerializationContext.GetInitializedThreadLocal();
+ serializer(msg, context);
+ return context.GetPayload();
+ }
+ finally
+ {
+ context?.Reset();
+ }
}
protected Exception TryDeserialize(byte[] payload, out TRead msg)
{
+ DefaultDeserializationContext context = null;
try
{
- msg = deserializer(payload);
+ context = DefaultDeserializationContext.GetInitializedThreadLocal(payload);
+ msg = deserializer(context);
return null;
}
catch (Exception e)
@@ -230,6 +242,11 @@ namespace Grpc.Core.Internal
msg = default(TRead);
return e;
}
+ finally
+ {
+ context?.Reset();
+
+ }
}
/// <summary>
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
index 0ceca4abb8..0bf1fb3b7d 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
@@ -37,7 +37,7 @@ namespace Grpc.Core.Internal
readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
readonly Server server;
- public AsyncCallServer(Func<TResponse, byte[]> serializer, Func<byte[], TRequest> deserializer, Server server) : base(serializer, deserializer)
+ public AsyncCallServer(Action<TResponse, SerializationContext> serializer, Func<DeserializationContext, TRequest> deserializer, Server server) : base(serializer, deserializer)
{
this.server = GrpcPreconditions.CheckNotNull(server);
}
diff --git a/src/csharp/Grpc.Core/Internal/DefaultDeserializationContext.cs b/src/csharp/Grpc.Core/Internal/DefaultDeserializationContext.cs
new file mode 100644
index 0000000000..7ace80e8d5
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/DefaultDeserializationContext.cs
@@ -0,0 +1,66 @@
+#region Copyright notice and license
+
+// 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.
+
+#endregion
+
+using Grpc.Core.Utils;
+using System;
+using System.Threading;
+
+namespace Grpc.Core.Internal
+{
+ internal class DefaultDeserializationContext : DeserializationContext
+ {
+ static readonly ThreadLocal<DefaultDeserializationContext> threadLocalInstance =
+ new ThreadLocal<DefaultDeserializationContext>(() => new DefaultDeserializationContext(), false);
+
+ byte[] payload;
+ bool alreadyCalledPayloadAsNewBuffer;
+
+ public DefaultDeserializationContext()
+ {
+ Reset();
+ }
+
+ public override int PayloadLength => payload.Length;
+
+ public override byte[] PayloadAsNewBuffer()
+ {
+ GrpcPreconditions.CheckState(!alreadyCalledPayloadAsNewBuffer);
+ alreadyCalledPayloadAsNewBuffer = true;
+ return payload;
+ }
+
+ public void Initialize(byte[] payload)
+ {
+ this.payload = GrpcPreconditions.CheckNotNull(payload);
+ this.alreadyCalledPayloadAsNewBuffer = false;
+ }
+
+ public void Reset()
+ {
+ this.payload = null;
+ this.alreadyCalledPayloadAsNewBuffer = true; // mark payload as read
+ }
+
+ public static DefaultDeserializationContext GetInitializedThreadLocal(byte[] payload)
+ {
+ var instance = threadLocalInstance.Value;
+ instance.Initialize(payload);
+ return instance;
+ }
+ }
+}
diff --git a/src/csharp/Grpc.Core/Internal/DefaultSerializationContext.cs b/src/csharp/Grpc.Core/Internal/DefaultSerializationContext.cs
new file mode 100644
index 0000000000..cceb194879
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/DefaultSerializationContext.cs
@@ -0,0 +1,62 @@
+#region Copyright notice and license
+
+// 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.
+
+#endregion
+
+using Grpc.Core.Utils;
+using System.Threading;
+
+namespace Grpc.Core.Internal
+{
+ internal class DefaultSerializationContext : SerializationContext
+ {
+ static readonly ThreadLocal<DefaultSerializationContext> threadLocalInstance =
+ new ThreadLocal<DefaultSerializationContext>(() => new DefaultSerializationContext(), false);
+
+ bool isComplete;
+ byte[] payload;
+
+ public DefaultSerializationContext()
+ {
+ Reset();
+ }
+
+ public override void Complete(byte[] payload)
+ {
+ GrpcPreconditions.CheckState(!isComplete);
+ this.isComplete = true;
+ this.payload = payload;
+ }
+
+ internal byte[] GetPayload()
+ {
+ return this.payload;
+ }
+
+ public void Reset()
+ {
+ this.isComplete = false;
+ this.payload = null;
+ }
+
+ public static DefaultSerializationContext GetInitializedThreadLocal()
+ {
+ var instance = threadLocalInstance.Value;
+ instance.Reset();
+ return instance;
+ }
+ }
+}
diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
index 81522cf8fe..ec732e8c7f 100644
--- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
@@ -52,8 +52,8 @@ namespace Grpc.Core.Internal
public async Task HandleCall(ServerRpcNew newRpc, CompletionQueueSafeHandle cq)
{
var asyncCall = new AsyncCallServer<TRequest, TResponse>(
- method.ResponseMarshaller.Serializer,
- method.RequestMarshaller.Deserializer,
+ method.ResponseMarshaller.ContextualSerializer,
+ method.RequestMarshaller.ContextualDeserializer,
newRpc.Server);
asyncCall.Initialize(newRpc.Call, cq);
@@ -116,8 +116,8 @@ namespace Grpc.Core.Internal
public async Task HandleCall(ServerRpcNew newRpc, CompletionQueueSafeHandle cq)
{
var asyncCall = new AsyncCallServer<TRequest, TResponse>(
- method.ResponseMarshaller.Serializer,
- method.RequestMarshaller.Deserializer,
+ method.ResponseMarshaller.ContextualSerializer,
+ method.RequestMarshaller.ContextualDeserializer,
newRpc.Server);
asyncCall.Initialize(newRpc.Call, cq);
@@ -179,8 +179,8 @@ namespace Grpc.Core.Internal
public async Task HandleCall(ServerRpcNew newRpc, CompletionQueueSafeHandle cq)
{
var asyncCall = new AsyncCallServer<TRequest, TResponse>(
- method.ResponseMarshaller.Serializer,
- method.RequestMarshaller.Deserializer,
+ method.ResponseMarshaller.ContextualSerializer,
+ method.RequestMarshaller.ContextualDeserializer,
newRpc.Server);
asyncCall.Initialize(newRpc.Call, cq);
@@ -242,8 +242,8 @@ namespace Grpc.Core.Internal
public async Task HandleCall(ServerRpcNew newRpc, CompletionQueueSafeHandle cq)
{
var asyncCall = new AsyncCallServer<TRequest, TResponse>(
- method.ResponseMarshaller.Serializer,
- method.RequestMarshaller.Deserializer,
+ method.ResponseMarshaller.ContextualSerializer,
+ method.RequestMarshaller.ContextualDeserializer,
newRpc.Server);
asyncCall.Initialize(newRpc.Call, cq);
diff --git a/src/csharp/Grpc.Core/Marshaller.cs b/src/csharp/Grpc.Core/Marshaller.cs
index 0af9aa586b..34a1849cd7 100644
--- a/src/csharp/Grpc.Core/Marshaller.cs
+++ b/src/csharp/Grpc.Core/Marshaller.cs
@@ -41,6 +41,8 @@ namespace Grpc.Core
{
this.serializer = GrpcPreconditions.CheckNotNull(serializer, nameof(serializer));
this.deserializer = GrpcPreconditions.CheckNotNull(deserializer, nameof(deserializer));
+ // contextual serialization/deserialization is emulated to make the marshaller
+ // usable with the grpc library (required for backward compatibility).
this.contextualSerializer = EmulateContextualSerializer;
this.contextualDeserializer = EmulateContextualDeserializer;
}
@@ -57,10 +59,10 @@ namespace Grpc.Core
{
this.contextualSerializer = GrpcPreconditions.CheckNotNull(serializer, nameof(serializer));
this.contextualDeserializer = GrpcPreconditions.CheckNotNull(deserializer, nameof(deserializer));
- // TODO(jtattermusch): once gRPC C# library switches to using contextual (de)serializer,
- // emulating the simple (de)serializer will become unnecessary.
- this.serializer = EmulateSimpleSerializer;
- this.deserializer = EmulateSimpleDeserializer;
+ // gRPC only uses contextual serializer/deserializer internally, so emulating the legacy
+ // (de)serializer is not necessary.
+ this.serializer = (msg) => { throw new NotImplementedException(); };
+ this.deserializer = (payload) => { throw new NotImplementedException(); };
}
/// <summary>
@@ -85,25 +87,6 @@ namespace Grpc.Core
/// </summary>
public Func<DeserializationContext, T> ContextualDeserializer => this.contextualDeserializer;
- // for backward compatibility, emulate the simple serializer using the contextual one
- private byte[] EmulateSimpleSerializer(T msg)
- {
- // TODO(jtattermusch): avoid the allocation by passing a thread-local instance
- // This code will become unnecessary once gRPC C# library switches to using contextual (de)serializer.
- var context = new EmulatedSerializationContext();
- this.contextualSerializer(msg, context);
- return context.GetPayload();
- }
-
- // for backward compatibility, emulate the simple deserializer using the contextual one
- private T EmulateSimpleDeserializer(byte[] payload)
- {
- // TODO(jtattermusch): avoid the allocation by passing a thread-local instance
- // This code will become unnecessary once gRPC C# library switches to using contextual (de)serializer.
- var context = new EmulatedDeserializationContext(payload);
- return this.contextualDeserializer(context);
- }
-
// for backward compatibility, emulate the contextual serializer using the simple one
private void EmulateContextualSerializer(T message, SerializationContext context)
{
@@ -116,44 +99,6 @@ namespace Grpc.Core
{
return this.deserializer(context.PayloadAsNewBuffer());
}
-
- internal class EmulatedSerializationContext : SerializationContext
- {
- bool isComplete;
- byte[] payload;
-
- public override void Complete(byte[] payload)
- {
- GrpcPreconditions.CheckState(!isComplete);
- this.isComplete = true;
- this.payload = payload;
- }
-
- internal byte[] GetPayload()
- {
- return this.payload;
- }
- }
-
- internal class EmulatedDeserializationContext : DeserializationContext
- {
- readonly byte[] payload;
- bool alreadyCalledPayloadAsNewBuffer;
-
- public EmulatedDeserializationContext(byte[] payload)
- {
- this.payload = GrpcPreconditions.CheckNotNull(payload);
- }
-
- public override int PayloadLength => payload.Length;
-
- public override byte[] PayloadAsNewBuffer()
- {
- GrpcPreconditions.CheckState(!alreadyCalledPayloadAsNewBuffer);
- alreadyCalledPayloadAsNewBuffer = true;
- return payload;
- }
- }
}
/// <summary>
diff --git a/src/csharp/Grpc.Core/SerializationContext.cs b/src/csharp/Grpc.Core/SerializationContext.cs
index cf4d1595da..9aef2adbcd 100644
--- a/src/csharp/Grpc.Core/SerializationContext.cs
+++ b/src/csharp/Grpc.Core/SerializationContext.cs
@@ -16,6 +16,8 @@
#endregion
+using System;
+
namespace Grpc.Core
{
/// <summary>
@@ -29,6 +31,9 @@ namespace Grpc.Core
/// payload which must not be accessed afterwards.
/// </summary>
/// <param name="payload">the serialized form of current message</param>
- public abstract void Complete(byte[] payload);
+ public virtual void Complete(byte[] payload)
+ {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/src/csharp/Grpc.Core/ServerServiceDefinition.cs b/src/csharp/Grpc.Core/ServerServiceDefinition.cs
index 07c6aa1796..b040ab379c 100644
--- a/src/csharp/Grpc.Core/ServerServiceDefinition.cs
+++ b/src/csharp/Grpc.Core/ServerServiceDefinition.cs
@@ -72,7 +72,7 @@ namespace Grpc.Core
}
/// <summary>
- /// Adds a definitions for a single request - single response method.
+ /// Adds a definition for a single request - single response method.
/// </summary>
/// <typeparam name="TRequest">The request message class.</typeparam>
/// <typeparam name="TResponse">The response message class.</typeparam>
@@ -90,7 +90,7 @@ namespace Grpc.Core
}
/// <summary>
- /// Adds a definitions for a client streaming method.
+ /// Adds a definition for a client streaming method.
/// </summary>
/// <typeparam name="TRequest">The request message class.</typeparam>
/// <typeparam name="TResponse">The response message class.</typeparam>
@@ -108,7 +108,7 @@ namespace Grpc.Core
}
/// <summary>
- /// Adds a definitions for a server streaming method.
+ /// Adds a definition for a server streaming method.
/// </summary>
/// <typeparam name="TRequest">The request message class.</typeparam>
/// <typeparam name="TResponse">The response message class.</typeparam>
@@ -126,7 +126,7 @@ namespace Grpc.Core
}
/// <summary>
- /// Adds a definitions for a bidirectional streaming method.
+ /// Adds a definition for a bidirectional streaming method.
/// </summary>
/// <typeparam name="TRequest">The request message class.</typeparam>
/// <typeparam name="TResponse">The response message class.</typeparam>
diff --git a/src/csharp/Grpc.Core/ServiceBinderBase.cs b/src/csharp/Grpc.Core/ServiceBinderBase.cs
new file mode 100644
index 0000000000..d4909f4a26
--- /dev/null
+++ b/src/csharp/Grpc.Core/ServiceBinderBase.cs
@@ -0,0 +1,101 @@
+#region Copyright notice and license
+
+// 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.
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using Grpc.Core.Interceptors;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core
+{
+ /// <summary>
+ /// Allows binding server-side method implementations in alternative serving stacks.
+ /// Instances of this class are usually populated by the <c>BindService</c> method
+ /// that is part of the autogenerated code for a protocol buffers service definition.
+ /// <seealso cref="ServerServiceDefinition"/>
+ /// </summary>
+ public class ServiceBinderBase
+ {
+ /// <summary>
+ /// Adds a definition for a single request - single response method.
+ /// </summary>
+ /// <typeparam name="TRequest">The request message class.</typeparam>
+ /// <typeparam name="TResponse">The response message class.</typeparam>
+ /// <param name="method">The method.</param>
+ /// <param name="handler">The method handler.</param>
+ public virtual void AddMethod<TRequest, TResponse>(
+ Method<TRequest, TResponse> method,
+ UnaryServerMethod<TRequest, TResponse> handler)
+ where TRequest : class
+ where TResponse : class
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Adds a definition for a client streaming method.
+ /// </summary>
+ /// <typeparam name="TRequest">The request message class.</typeparam>
+ /// <typeparam name="TResponse">The response message class.</typeparam>
+ /// <param name="method">The method.</param>
+ /// <param name="handler">The method handler.</param>
+ public virtual void AddMethod<TRequest, TResponse>(
+ Method<TRequest, TResponse> method,
+ ClientStreamingServerMethod<TRequest, TResponse> handler)
+ where TRequest : class
+ where TResponse : class
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Adds a definition for a server streaming method.
+ /// </summary>
+ /// <typeparam name="TRequest">The request message class.</typeparam>
+ /// <typeparam name="TResponse">The response message class.</typeparam>
+ /// <param name="method">The method.</param>
+ /// <param name="handler">The method handler.</param>
+ public virtual void AddMethod<TRequest, TResponse>(
+ Method<TRequest, TResponse> method,
+ ServerStreamingServerMethod<TRequest, TResponse> handler)
+ where TRequest : class
+ where TResponse : class
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Adds a definition for a bidirectional streaming method.
+ /// </summary>
+ /// <typeparam name="TRequest">The request message class.</typeparam>
+ /// <typeparam name="TResponse">The response message class.</typeparam>
+ /// <param name="method">The method.</param>
+ /// <param name="handler">The method handler.</param>
+ public virtual void AddMethod<TRequest, TResponse>(
+ Method<TRequest, TResponse> method,
+ DuplexStreamingServerMethod<TRequest, TResponse> handler)
+ where TRequest : class
+ where TResponse : class
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/csharp/Grpc.Examples/MathGrpc.cs b/src/csharp/Grpc.Examples/MathGrpc.cs
index 9578bb4d81..e5be387e67 100644
--- a/src/csharp/Grpc.Examples/MathGrpc.cs
+++ b/src/csharp/Grpc.Examples/MathGrpc.cs
@@ -287,6 +287,18 @@ namespace Math {
.AddMethod(__Method_Sum, serviceImpl.Sum).Build();
}
+ /// <summary>Register service method implementations with a service binder. Useful when customizing the service binding logic.
+ /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+ /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+ /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+ public static void BindService(grpc::ServiceBinderBase serviceBinder, MathBase serviceImpl)
+ {
+ serviceBinder.AddMethod(__Method_Div, serviceImpl.Div);
+ serviceBinder.AddMethod(__Method_DivMany, serviceImpl.DivMany);
+ serviceBinder.AddMethod(__Method_Fib, serviceImpl.Fib);
+ serviceBinder.AddMethod(__Method_Sum, serviceImpl.Sum);
+ }
+
}
}
#endregion
diff --git a/src/csharp/Grpc.HealthCheck/Health.cs b/src/csharp/Grpc.HealthCheck/Health.cs
index a90f261d28..2c3bb45c3c 100644
--- a/src/csharp/Grpc.HealthCheck/Health.cs
+++ b/src/csharp/Grpc.HealthCheck/Health.cs
@@ -25,15 +25,17 @@ namespace Grpc.Health.V1 {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"ChtncnBjL2hlYWx0aC92MS9oZWFsdGgucHJvdG8SDmdycGMuaGVhbHRoLnYx",
- "IiUKEkhlYWx0aENoZWNrUmVxdWVzdBIPCgdzZXJ2aWNlGAEgASgJIpQBChNI",
+ "IiUKEkhlYWx0aENoZWNrUmVxdWVzdBIPCgdzZXJ2aWNlGAEgASgJIqkBChNI",
"ZWFsdGhDaGVja1Jlc3BvbnNlEkEKBnN0YXR1cxgBIAEoDjIxLmdycGMuaGVh",
- "bHRoLnYxLkhlYWx0aENoZWNrUmVzcG9uc2UuU2VydmluZ1N0YXR1cyI6Cg1T",
+ "bHRoLnYxLkhlYWx0aENoZWNrUmVzcG9uc2UuU2VydmluZ1N0YXR1cyJPCg1T",
"ZXJ2aW5nU3RhdHVzEgsKB1VOS05PV04QABILCgdTRVJWSU5HEAESDwoLTk9U",
- "X1NFUlZJTkcQAjJaCgZIZWFsdGgSUAoFQ2hlY2sSIi5ncnBjLmhlYWx0aC52",
- "MS5IZWFsdGhDaGVja1JlcXVlc3QaIy5ncnBjLmhlYWx0aC52MS5IZWFsdGhD",
- "aGVja1Jlc3BvbnNlQmEKEWlvLmdycGMuaGVhbHRoLnYxQgtIZWFsdGhQcm90",
- "b1ABWixnb29nbGUuZ29sYW5nLm9yZy9ncnBjL2hlYWx0aC9ncnBjX2hlYWx0",
- "aF92MaoCDkdycGMuSGVhbHRoLlYxYgZwcm90bzM="));
+ "X1NFUlZJTkcQAhITCg9TRVJWSUNFX1VOS05PV04QAzKuAQoGSGVhbHRoElAK",
+ "BUNoZWNrEiIuZ3JwYy5oZWFsdGgudjEuSGVhbHRoQ2hlY2tSZXF1ZXN0GiMu",
+ "Z3JwYy5oZWFsdGgudjEuSGVhbHRoQ2hlY2tSZXNwb25zZRJSCgVXYXRjaBIi",
+ "LmdycGMuaGVhbHRoLnYxLkhlYWx0aENoZWNrUmVxdWVzdBojLmdycGMuaGVh",
+ "bHRoLnYxLkhlYWx0aENoZWNrUmVzcG9uc2UwAUJhChFpby5ncnBjLmhlYWx0",
+ "aC52MUILSGVhbHRoUHJvdG9QAVosZ29vZ2xlLmdvbGFuZy5vcmcvZ3JwYy9o",
+ "ZWFsdGgvZ3JwY19oZWFsdGhfdjGqAg5HcnBjLkhlYWx0aC5WMWIGcHJvdG8z"));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
@@ -309,6 +311,10 @@ namespace Grpc.Health.V1 {
[pbr::OriginalName("UNKNOWN")] Unknown = 0,
[pbr::OriginalName("SERVING")] Serving = 1,
[pbr::OriginalName("NOT_SERVING")] NotServing = 2,
+ /// <summary>
+ /// Used only by the Watch method.
+ /// </summary>
+ [pbr::OriginalName("SERVICE_UNKNOWN")] ServiceUnknown = 3,
}
}
diff --git a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
index 5e79c04d2a..51956f2f23 100644
--- a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
+++ b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
@@ -40,6 +40,13 @@ namespace Grpc.Health.V1 {
__Marshaller_grpc_health_v1_HealthCheckRequest,
__Marshaller_grpc_health_v1_HealthCheckResponse);
+ static readonly grpc::Method<global::Grpc.Health.V1.HealthCheckRequest, global::Grpc.Health.V1.HealthCheckResponse> __Method_Watch = new grpc::Method<global::Grpc.Health.V1.HealthCheckRequest, global::Grpc.Health.V1.HealthCheckResponse>(
+ grpc::MethodType.ServerStreaming,
+ __ServiceName,
+ "Watch",
+ __Marshaller_grpc_health_v1_HealthCheckRequest,
+ __Marshaller_grpc_health_v1_HealthCheckResponse);
+
/// <summary>Service descriptor</summary>
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
{
@@ -49,11 +56,44 @@ namespace Grpc.Health.V1 {
/// <summary>Base class for server-side implementations of Health</summary>
public abstract partial class HealthBase
{
+ /// <summary>
+ /// If the requested service is unknown, the call will fail with status
+ /// NOT_FOUND.
+ /// </summary>
+ /// <param name="request">The request received from the client.</param>
+ /// <param name="context">The context of the server-side call handler being invoked.</param>
+ /// <returns>The response to send back to the client (wrapped by a task).</returns>
public virtual global::System.Threading.Tasks.Task<global::Grpc.Health.V1.HealthCheckResponse> Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::ServerCallContext context)
{
throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
}
+ /// <summary>
+ /// Performs a watch for the serving status of the requested service.
+ /// The server will immediately send back a message indicating the current
+ /// serving status. It will then subsequently send a new message whenever
+ /// the service's serving status changes.
+ ///
+ /// If the requested service is unknown when the call is received, the
+ /// server will send a message setting the serving status to
+ /// SERVICE_UNKNOWN but will *not* terminate the call. If at some
+ /// future point, the serving status of the service becomes known, the
+ /// server will send a new message with the service's serving status.
+ ///
+ /// If the call terminates with status UNIMPLEMENTED, then clients
+ /// should assume this method is not supported and should not retry the
+ /// call. If the call terminates with any other status (including OK),
+ /// clients should retry the call with appropriate exponential backoff.
+ /// </summary>
+ /// <param name="request">The request received from the client.</param>
+ /// <param name="responseStream">Used for sending responses back to the client.</param>
+ /// <param name="context">The context of the server-side call handler being invoked.</param>
+ /// <returns>A task indicating completion of the handler.</returns>
+ public virtual global::System.Threading.Tasks.Task Watch(global::Grpc.Health.V1.HealthCheckRequest request, grpc::IServerStreamWriter<global::Grpc.Health.V1.HealthCheckResponse> responseStream, grpc::ServerCallContext context)
+ {
+ throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+ }
+
}
/// <summary>Client for Health</summary>
@@ -79,22 +119,104 @@ namespace Grpc.Health.V1 {
{
}
+ /// <summary>
+ /// If the requested service is unknown, the call will fail with status
+ /// NOT_FOUND.
+ /// </summary>
+ /// <param name="request">The request to send to the server.</param>
+ /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+ /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+ /// <param name="cancellationToken">An optional token for canceling the call.</param>
+ /// <returns>The response received from the server.</returns>
public virtual global::Grpc.Health.V1.HealthCheckResponse Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
{
return Check(request, new grpc::CallOptions(headers, deadline, cancellationToken));
}
+ /// <summary>
+ /// If the requested service is unknown, the call will fail with status
+ /// NOT_FOUND.
+ /// </summary>
+ /// <param name="request">The request to send to the server.</param>
+ /// <param name="options">The options for the call.</param>
+ /// <returns>The response received from the server.</returns>
public virtual global::Grpc.Health.V1.HealthCheckResponse Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::CallOptions options)
{
return CallInvoker.BlockingUnaryCall(__Method_Check, null, options, request);
}
+ /// <summary>
+ /// If the requested service is unknown, the call will fail with status
+ /// NOT_FOUND.
+ /// </summary>
+ /// <param name="request">The request to send to the server.</param>
+ /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+ /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+ /// <param name="cancellationToken">An optional token for canceling the call.</param>
+ /// <returns>The call object.</returns>
public virtual grpc::AsyncUnaryCall<global::Grpc.Health.V1.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
{
return CheckAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
}
+ /// <summary>
+ /// If the requested service is unknown, the call will fail with status
+ /// NOT_FOUND.
+ /// </summary>
+ /// <param name="request">The request to send to the server.</param>
+ /// <param name="options">The options for the call.</param>
+ /// <returns>The call object.</returns>
public virtual grpc::AsyncUnaryCall<global::Grpc.Health.V1.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1.HealthCheckRequest request, grpc::CallOptions options)
{
return CallInvoker.AsyncUnaryCall(__Method_Check, null, options, request);
}
+ /// <summary>
+ /// Performs a watch for the serving status of the requested service.
+ /// The server will immediately send back a message indicating the current
+ /// serving status. It will then subsequently send a new message whenever
+ /// the service's serving status changes.
+ ///
+ /// If the requested service is unknown when the call is received, the
+ /// server will send a message setting the serving status to
+ /// SERVICE_UNKNOWN but will *not* terminate the call. If at some
+ /// future point, the serving status of the service becomes known, the
+ /// server will send a new message with the service's serving status.
+ ///
+ /// If the call terminates with status UNIMPLEMENTED, then clients
+ /// should assume this method is not supported and should not retry the
+ /// call. If the call terminates with any other status (including OK),
+ /// clients should retry the call with appropriate exponential backoff.
+ /// </summary>
+ /// <param name="request">The request to send to the server.</param>
+ /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+ /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+ /// <param name="cancellationToken">An optional token for canceling the call.</param>
+ /// <returns>The call object.</returns>
+ public virtual grpc::AsyncServerStreamingCall<global::Grpc.Health.V1.HealthCheckResponse> Watch(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+ {
+ return Watch(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+ }
+ /// <summary>
+ /// Performs a watch for the serving status of the requested service.
+ /// The server will immediately send back a message indicating the current
+ /// serving status. It will then subsequently send a new message whenever
+ /// the service's serving status changes.
+ ///
+ /// If the requested service is unknown when the call is received, the
+ /// server will send a message setting the serving status to
+ /// SERVICE_UNKNOWN but will *not* terminate the call. If at some
+ /// future point, the serving status of the service becomes known, the
+ /// server will send a new message with the service's serving status.
+ ///
+ /// If the call terminates with status UNIMPLEMENTED, then clients
+ /// should assume this method is not supported and should not retry the
+ /// call. If the call terminates with any other status (including OK),
+ /// clients should retry the call with appropriate exponential backoff.
+ /// </summary>
+ /// <param name="request">The request to send to the server.</param>
+ /// <param name="options">The options for the call.</param>
+ /// <returns>The call object.</returns>
+ public virtual grpc::AsyncServerStreamingCall<global::Grpc.Health.V1.HealthCheckResponse> Watch(global::Grpc.Health.V1.HealthCheckRequest request, grpc::CallOptions options)
+ {
+ return CallInvoker.AsyncServerStreamingCall(__Method_Watch, null, options, request);
+ }
/// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
protected override HealthClient NewInstance(ClientBaseConfiguration configuration)
{
@@ -107,7 +229,18 @@ namespace Grpc.Health.V1 {
public static grpc::ServerServiceDefinition BindService(HealthBase serviceImpl)
{
return grpc::ServerServiceDefinition.CreateBuilder()
- .AddMethod(__Method_Check, serviceImpl.Check).Build();
+ .AddMethod(__Method_Check, serviceImpl.Check)
+ .AddMethod(__Method_Watch, serviceImpl.Watch).Build();
+ }
+
+ /// <summary>Register service method implementations with a service binder. Useful when customizing the service binding logic.
+ /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+ /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+ /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+ public static void BindService(grpc::ServiceBinderBase serviceBinder, HealthBase serviceImpl)
+ {
+ serviceBinder.AddMethod(__Method_Check, serviceImpl.Check);
+ serviceBinder.AddMethod(__Method_Watch, serviceImpl.Watch);
}
}
diff --git a/src/csharp/Grpc.IntegrationTesting/BenchmarkServiceGrpc.cs b/src/csharp/Grpc.IntegrationTesting/BenchmarkServiceGrpc.cs
index b5738593f2..3431b5fa18 100644
--- a/src/csharp/Grpc.IntegrationTesting/BenchmarkServiceGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/BenchmarkServiceGrpc.cs
@@ -324,6 +324,19 @@ namespace Grpc.Testing {
.AddMethod(__Method_StreamingBothWays, serviceImpl.StreamingBothWays).Build();
}
+ /// <summary>Register service method implementations with a service binder. Useful when customizing the service binding logic.
+ /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+ /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+ /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+ public static void BindService(grpc::ServiceBinderBase serviceBinder, BenchmarkServiceBase serviceImpl)
+ {
+ serviceBinder.AddMethod(__Method_UnaryCall, serviceImpl.UnaryCall);
+ serviceBinder.AddMethod(__Method_StreamingCall, serviceImpl.StreamingCall);
+ serviceBinder.AddMethod(__Method_StreamingFromClient, serviceImpl.StreamingFromClient);
+ serviceBinder.AddMethod(__Method_StreamingFromServer, serviceImpl.StreamingFromServer);
+ serviceBinder.AddMethod(__Method_StreamingBothWays, serviceImpl.StreamingBothWays);
+ }
+
}
}
#endregion
diff --git a/src/csharp/Grpc.IntegrationTesting/Control.cs b/src/csharp/Grpc.IntegrationTesting/Control.cs
index 6e00348451..368b86659a 100644
--- a/src/csharp/Grpc.IntegrationTesting/Control.cs
+++ b/src/csharp/Grpc.IntegrationTesting/Control.cs
@@ -34,7 +34,7 @@ namespace Grpc.Testing {
"U2VjdXJpdHlQYXJhbXMSEwoLdXNlX3Rlc3RfY2EYASABKAgSHAoUc2VydmVy",
"X2hvc3Rfb3ZlcnJpZGUYAiABKAkSEQoJY3JlZF90eXBlGAMgASgJIk0KCkNo",
"YW5uZWxBcmcSDAoEbmFtZRgBIAEoCRITCglzdHJfdmFsdWUYAiABKAlIABIT",
- "CglpbnRfdmFsdWUYAyABKAVIAEIHCgV2YWx1ZSLvBAoMQ2xpZW50Q29uZmln",
+ "CglpbnRfdmFsdWUYAyABKAVIAEIHCgV2YWx1ZSKiBQoMQ2xpZW50Q29uZmln",
"EhYKDnNlcnZlcl90YXJnZXRzGAEgAygJEi0KC2NsaWVudF90eXBlGAIgASgO",
"MhguZ3JwYy50ZXN0aW5nLkNsaWVudFR5cGUSNQoPc2VjdXJpdHlfcGFyYW1z",
"GAMgASgLMhwuZ3JwYy50ZXN0aW5nLlNlY3VyaXR5UGFyYW1zEiQKHG91dHN0",
@@ -48,59 +48,60 @@ namespace Grpc.Testing {
"GA4gASgFEhgKEG90aGVyX2NsaWVudF9hcGkYDyABKAkSLgoMY2hhbm5lbF9h",
"cmdzGBAgAygLMhguZ3JwYy50ZXN0aW5nLkNoYW5uZWxBcmcSFgoOdGhyZWFk",
"c19wZXJfY3EYESABKAUSGwoTbWVzc2FnZXNfcGVyX3N0cmVhbRgSIAEoBRIY",
- "ChB1c2VfY29hbGVzY2VfYXBpGBMgASgIIjgKDENsaWVudFN0YXR1cxIoCgVz",
- "dGF0cxgBIAEoCzIZLmdycGMudGVzdGluZy5DbGllbnRTdGF0cyIVCgRNYXJr",
- "Eg0KBXJlc2V0GAEgASgIImgKCkNsaWVudEFyZ3MSKwoFc2V0dXAYASABKAsy",
- "Gi5ncnBjLnRlc3RpbmcuQ2xpZW50Q29uZmlnSAASIgoEbWFyaxgCIAEoCzIS",
- "LmdycGMudGVzdGluZy5NYXJrSABCCQoHYXJndHlwZSL9AgoMU2VydmVyQ29u",
- "ZmlnEi0KC3NlcnZlcl90eXBlGAEgASgOMhguZ3JwYy50ZXN0aW5nLlNlcnZl",
- "clR5cGUSNQoPc2VjdXJpdHlfcGFyYW1zGAIgASgLMhwuZ3JwYy50ZXN0aW5n",
- "LlNlY3VyaXR5UGFyYW1zEgwKBHBvcnQYBCABKAUSHAoUYXN5bmNfc2VydmVy",
- "X3RocmVhZHMYByABKAUSEgoKY29yZV9saW1pdBgIIAEoBRIzCg5wYXlsb2Fk",
- "X2NvbmZpZxgJIAEoCzIbLmdycGMudGVzdGluZy5QYXlsb2FkQ29uZmlnEhEK",
- "CWNvcmVfbGlzdBgKIAMoBRIYChBvdGhlcl9zZXJ2ZXJfYXBpGAsgASgJEhYK",
- "DnRocmVhZHNfcGVyX2NxGAwgASgFEhwKE3Jlc291cmNlX3F1b3RhX3NpemUY",
- "6QcgASgFEi8KDGNoYW5uZWxfYXJncxjqByADKAsyGC5ncnBjLnRlc3Rpbmcu",
- "Q2hhbm5lbEFyZyJoCgpTZXJ2ZXJBcmdzEisKBXNldHVwGAEgASgLMhouZ3Jw",
- "Yy50ZXN0aW5nLlNlcnZlckNvbmZpZ0gAEiIKBG1hcmsYAiABKAsyEi5ncnBj",
- "LnRlc3RpbmcuTWFya0gAQgkKB2FyZ3R5cGUiVQoMU2VydmVyU3RhdHVzEigK",
- "BXN0YXRzGAEgASgLMhkuZ3JwYy50ZXN0aW5nLlNlcnZlclN0YXRzEgwKBHBv",
- "cnQYAiABKAUSDQoFY29yZXMYAyABKAUiDQoLQ29yZVJlcXVlc3QiHQoMQ29y",
- "ZVJlc3BvbnNlEg0KBWNvcmVzGAEgASgFIgYKBFZvaWQi/QEKCFNjZW5hcmlv",
- "EgwKBG5hbWUYASABKAkSMQoNY2xpZW50X2NvbmZpZxgCIAEoCzIaLmdycGMu",
- "dGVzdGluZy5DbGllbnRDb25maWcSEwoLbnVtX2NsaWVudHMYAyABKAUSMQoN",
- "c2VydmVyX2NvbmZpZxgEIAEoCzIaLmdycGMudGVzdGluZy5TZXJ2ZXJDb25m",
- "aWcSEwoLbnVtX3NlcnZlcnMYBSABKAUSFgoOd2FybXVwX3NlY29uZHMYBiAB",
- "KAUSGQoRYmVuY2htYXJrX3NlY29uZHMYByABKAUSIAoYc3Bhd25fbG9jYWxf",
- "d29ya2VyX2NvdW50GAggASgFIjYKCVNjZW5hcmlvcxIpCglzY2VuYXJpb3MY",
- "ASADKAsyFi5ncnBjLnRlc3RpbmcuU2NlbmFyaW8ihAQKFVNjZW5hcmlvUmVz",
- "dWx0U3VtbWFyeRILCgNxcHMYASABKAESGwoTcXBzX3Blcl9zZXJ2ZXJfY29y",
- "ZRgCIAEoARIaChJzZXJ2ZXJfc3lzdGVtX3RpbWUYAyABKAESGAoQc2VydmVy",
- "X3VzZXJfdGltZRgEIAEoARIaChJjbGllbnRfc3lzdGVtX3RpbWUYBSABKAES",
- "GAoQY2xpZW50X3VzZXJfdGltZRgGIAEoARISCgpsYXRlbmN5XzUwGAcgASgB",
- "EhIKCmxhdGVuY3lfOTAYCCABKAESEgoKbGF0ZW5jeV85NRgJIAEoARISCgps",
- "YXRlbmN5Xzk5GAogASgBEhMKC2xhdGVuY3lfOTk5GAsgASgBEhgKEHNlcnZl",
- "cl9jcHVfdXNhZ2UYDCABKAESJgoec3VjY2Vzc2Z1bF9yZXF1ZXN0c19wZXJf",
- "c2Vjb25kGA0gASgBEiIKGmZhaWxlZF9yZXF1ZXN0c19wZXJfc2Vjb25kGA4g",
- "ASgBEiAKGGNsaWVudF9wb2xsc19wZXJfcmVxdWVzdBgPIAEoARIgChhzZXJ2",
- "ZXJfcG9sbHNfcGVyX3JlcXVlc3QYECABKAESIgoac2VydmVyX3F1ZXJpZXNf",
- "cGVyX2NwdV9zZWMYESABKAESIgoaY2xpZW50X3F1ZXJpZXNfcGVyX2NwdV9z",
- "ZWMYEiABKAEigwMKDlNjZW5hcmlvUmVzdWx0EigKCHNjZW5hcmlvGAEgASgL",
- "MhYuZ3JwYy50ZXN0aW5nLlNjZW5hcmlvEi4KCWxhdGVuY2llcxgCIAEoCzIb",
- "LmdycGMudGVzdGluZy5IaXN0b2dyYW1EYXRhEi8KDGNsaWVudF9zdGF0cxgD",
- "IAMoCzIZLmdycGMudGVzdGluZy5DbGllbnRTdGF0cxIvCgxzZXJ2ZXJfc3Rh",
- "dHMYBCADKAsyGS5ncnBjLnRlc3RpbmcuU2VydmVyU3RhdHMSFAoMc2VydmVy",
- "X2NvcmVzGAUgAygFEjQKB3N1bW1hcnkYBiABKAsyIy5ncnBjLnRlc3Rpbmcu",
- "U2NlbmFyaW9SZXN1bHRTdW1tYXJ5EhYKDmNsaWVudF9zdWNjZXNzGAcgAygI",
- "EhYKDnNlcnZlcl9zdWNjZXNzGAggAygIEjkKD3JlcXVlc3RfcmVzdWx0cxgJ",
- "IAMoCzIgLmdycGMudGVzdGluZy5SZXF1ZXN0UmVzdWx0Q291bnQqQQoKQ2xp",
- "ZW50VHlwZRIPCgtTWU5DX0NMSUVOVBAAEhAKDEFTWU5DX0NMSUVOVBABEhAK",
- "DE9USEVSX0NMSUVOVBACKlsKClNlcnZlclR5cGUSDwoLU1lOQ19TRVJWRVIQ",
- "ABIQCgxBU1lOQ19TRVJWRVIQARIYChRBU1lOQ19HRU5FUklDX1NFUlZFUhAC",
- "EhAKDE9USEVSX1NFUlZFUhADKnIKB1JwY1R5cGUSCQoFVU5BUlkQABINCglT",
- "VFJFQU1JTkcQARIZChVTVFJFQU1JTkdfRlJPTV9DTElFTlQQAhIZChVTVFJF",
- "QU1JTkdfRlJPTV9TRVJWRVIQAxIXChNTVFJFQU1JTkdfQk9USF9XQVlTEARi",
- "BnByb3RvMw=="));
+ "ChB1c2VfY29hbGVzY2VfYXBpGBMgASgIEjEKKW1lZGlhbl9sYXRlbmN5X2Nv",
+ "bGxlY3Rpb25faW50ZXJ2YWxfbWlsbGlzGBQgASgFIjgKDENsaWVudFN0YXR1",
+ "cxIoCgVzdGF0cxgBIAEoCzIZLmdycGMudGVzdGluZy5DbGllbnRTdGF0cyIV",
+ "CgRNYXJrEg0KBXJlc2V0GAEgASgIImgKCkNsaWVudEFyZ3MSKwoFc2V0dXAY",
+ "ASABKAsyGi5ncnBjLnRlc3RpbmcuQ2xpZW50Q29uZmlnSAASIgoEbWFyaxgC",
+ "IAEoCzISLmdycGMudGVzdGluZy5NYXJrSABCCQoHYXJndHlwZSL9AgoMU2Vy",
+ "dmVyQ29uZmlnEi0KC3NlcnZlcl90eXBlGAEgASgOMhguZ3JwYy50ZXN0aW5n",
+ "LlNlcnZlclR5cGUSNQoPc2VjdXJpdHlfcGFyYW1zGAIgASgLMhwuZ3JwYy50",
+ "ZXN0aW5nLlNlY3VyaXR5UGFyYW1zEgwKBHBvcnQYBCABKAUSHAoUYXN5bmNf",
+ "c2VydmVyX3RocmVhZHMYByABKAUSEgoKY29yZV9saW1pdBgIIAEoBRIzCg5w",
+ "YXlsb2FkX2NvbmZpZxgJIAEoCzIbLmdycGMudGVzdGluZy5QYXlsb2FkQ29u",
+ "ZmlnEhEKCWNvcmVfbGlzdBgKIAMoBRIYChBvdGhlcl9zZXJ2ZXJfYXBpGAsg",
+ "ASgJEhYKDnRocmVhZHNfcGVyX2NxGAwgASgFEhwKE3Jlc291cmNlX3F1b3Rh",
+ "X3NpemUY6QcgASgFEi8KDGNoYW5uZWxfYXJncxjqByADKAsyGC5ncnBjLnRl",
+ "c3RpbmcuQ2hhbm5lbEFyZyJoCgpTZXJ2ZXJBcmdzEisKBXNldHVwGAEgASgL",
+ "MhouZ3JwYy50ZXN0aW5nLlNlcnZlckNvbmZpZ0gAEiIKBG1hcmsYAiABKAsy",
+ "Ei5ncnBjLnRlc3RpbmcuTWFya0gAQgkKB2FyZ3R5cGUiVQoMU2VydmVyU3Rh",
+ "dHVzEigKBXN0YXRzGAEgASgLMhkuZ3JwYy50ZXN0aW5nLlNlcnZlclN0YXRz",
+ "EgwKBHBvcnQYAiABKAUSDQoFY29yZXMYAyABKAUiDQoLQ29yZVJlcXVlc3Qi",
+ "HQoMQ29yZVJlc3BvbnNlEg0KBWNvcmVzGAEgASgFIgYKBFZvaWQi/QEKCFNj",
+ "ZW5hcmlvEgwKBG5hbWUYASABKAkSMQoNY2xpZW50X2NvbmZpZxgCIAEoCzIa",
+ "LmdycGMudGVzdGluZy5DbGllbnRDb25maWcSEwoLbnVtX2NsaWVudHMYAyAB",
+ "KAUSMQoNc2VydmVyX2NvbmZpZxgEIAEoCzIaLmdycGMudGVzdGluZy5TZXJ2",
+ "ZXJDb25maWcSEwoLbnVtX3NlcnZlcnMYBSABKAUSFgoOd2FybXVwX3NlY29u",
+ "ZHMYBiABKAUSGQoRYmVuY2htYXJrX3NlY29uZHMYByABKAUSIAoYc3Bhd25f",
+ "bG9jYWxfd29ya2VyX2NvdW50GAggASgFIjYKCVNjZW5hcmlvcxIpCglzY2Vu",
+ "YXJpb3MYASADKAsyFi5ncnBjLnRlc3RpbmcuU2NlbmFyaW8ihAQKFVNjZW5h",
+ "cmlvUmVzdWx0U3VtbWFyeRILCgNxcHMYASABKAESGwoTcXBzX3Blcl9zZXJ2",
+ "ZXJfY29yZRgCIAEoARIaChJzZXJ2ZXJfc3lzdGVtX3RpbWUYAyABKAESGAoQ",
+ "c2VydmVyX3VzZXJfdGltZRgEIAEoARIaChJjbGllbnRfc3lzdGVtX3RpbWUY",
+ "BSABKAESGAoQY2xpZW50X3VzZXJfdGltZRgGIAEoARISCgpsYXRlbmN5XzUw",
+ "GAcgASgBEhIKCmxhdGVuY3lfOTAYCCABKAESEgoKbGF0ZW5jeV85NRgJIAEo",
+ "ARISCgpsYXRlbmN5Xzk5GAogASgBEhMKC2xhdGVuY3lfOTk5GAsgASgBEhgK",
+ "EHNlcnZlcl9jcHVfdXNhZ2UYDCABKAESJgoec3VjY2Vzc2Z1bF9yZXF1ZXN0",
+ "c19wZXJfc2Vjb25kGA0gASgBEiIKGmZhaWxlZF9yZXF1ZXN0c19wZXJfc2Vj",
+ "b25kGA4gASgBEiAKGGNsaWVudF9wb2xsc19wZXJfcmVxdWVzdBgPIAEoARIg",
+ "ChhzZXJ2ZXJfcG9sbHNfcGVyX3JlcXVlc3QYECABKAESIgoac2VydmVyX3F1",
+ "ZXJpZXNfcGVyX2NwdV9zZWMYESABKAESIgoaY2xpZW50X3F1ZXJpZXNfcGVy",
+ "X2NwdV9zZWMYEiABKAEigwMKDlNjZW5hcmlvUmVzdWx0EigKCHNjZW5hcmlv",
+ "GAEgASgLMhYuZ3JwYy50ZXN0aW5nLlNjZW5hcmlvEi4KCWxhdGVuY2llcxgC",
+ "IAEoCzIbLmdycGMudGVzdGluZy5IaXN0b2dyYW1EYXRhEi8KDGNsaWVudF9z",
+ "dGF0cxgDIAMoCzIZLmdycGMudGVzdGluZy5DbGllbnRTdGF0cxIvCgxzZXJ2",
+ "ZXJfc3RhdHMYBCADKAsyGS5ncnBjLnRlc3RpbmcuU2VydmVyU3RhdHMSFAoM",
+ "c2VydmVyX2NvcmVzGAUgAygFEjQKB3N1bW1hcnkYBiABKAsyIy5ncnBjLnRl",
+ "c3RpbmcuU2NlbmFyaW9SZXN1bHRTdW1tYXJ5EhYKDmNsaWVudF9zdWNjZXNz",
+ "GAcgAygIEhYKDnNlcnZlcl9zdWNjZXNzGAggAygIEjkKD3JlcXVlc3RfcmVz",
+ "dWx0cxgJIAMoCzIgLmdycGMudGVzdGluZy5SZXF1ZXN0UmVzdWx0Q291bnQq",
+ "VgoKQ2xpZW50VHlwZRIPCgtTWU5DX0NMSUVOVBAAEhAKDEFTWU5DX0NMSUVO",
+ "VBABEhAKDE9USEVSX0NMSUVOVBACEhMKD0NBTExCQUNLX0NMSUVOVBADKlsK",
+ "ClNlcnZlclR5cGUSDwoLU1lOQ19TRVJWRVIQABIQCgxBU1lOQ19TRVJWRVIQ",
+ "ARIYChRBU1lOQ19HRU5FUklDX1NFUlZFUhACEhAKDE9USEVSX1NFUlZFUhAD",
+ "KnIKB1JwY1R5cGUSCQoFVU5BUlkQABINCglTVFJFQU1JTkcQARIZChVTVFJF",
+ "QU1JTkdfRlJPTV9DTElFTlQQAhIZChVTVFJFQU1JTkdfRlJPTV9TRVJWRVIQ",
+ "AxIXChNTVFJFQU1JTkdfQk9USF9XQVlTEARiBnByb3RvMw=="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Grpc.Testing.PayloadsReflection.Descriptor, global::Grpc.Testing.StatsReflection.Descriptor, },
new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Grpc.Testing.ClientType), typeof(global::Grpc.Testing.ServerType), typeof(global::Grpc.Testing.RpcType), }, new pbr::GeneratedClrTypeInfo[] {
@@ -109,7 +110,7 @@ namespace Grpc.Testing {
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.LoadParams), global::Grpc.Testing.LoadParams.Parser, new[]{ "ClosedLoop", "Poisson" }, new[]{ "Load" }, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.SecurityParams), global::Grpc.Testing.SecurityParams.Parser, new[]{ "UseTestCa", "ServerHostOverride", "CredType" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ChannelArg), global::Grpc.Testing.ChannelArg.Parser, new[]{ "Name", "StrValue", "IntValue" }, new[]{ "Value" }, null, null),
- new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ClientConfig), global::Grpc.Testing.ClientConfig.Parser, new[]{ "ServerTargets", "ClientType", "SecurityParams", "OutstandingRpcsPerChannel", "ClientChannels", "AsyncClientThreads", "RpcType", "LoadParams", "PayloadConfig", "HistogramParams", "CoreList", "CoreLimit", "OtherClientApi", "ChannelArgs", "ThreadsPerCq", "MessagesPerStream", "UseCoalesceApi" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ClientConfig), global::Grpc.Testing.ClientConfig.Parser, new[]{ "ServerTargets", "ClientType", "SecurityParams", "OutstandingRpcsPerChannel", "ClientChannels", "AsyncClientThreads", "RpcType", "LoadParams", "PayloadConfig", "HistogramParams", "CoreList", "CoreLimit", "OtherClientApi", "ChannelArgs", "ThreadsPerCq", "MessagesPerStream", "UseCoalesceApi", "MedianLatencyCollectionIntervalMillis" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ClientStatus), global::Grpc.Testing.ClientStatus.Parser, new[]{ "Stats" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.Mark), global::Grpc.Testing.Mark.Parser, new[]{ "Reset" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ClientArgs), global::Grpc.Testing.ClientArgs.Parser, new[]{ "Setup", "Mark" }, new[]{ "Argtype" }, null, null),
@@ -140,6 +141,7 @@ namespace Grpc.Testing {
/// used for some language-specific variants
/// </summary>
[pbr::OriginalName("OTHER_CLIENT")] OtherClient = 2,
+ [pbr::OriginalName("CALLBACK_CLIENT")] CallbackClient = 3,
}
public enum ServerType {
@@ -1054,6 +1056,7 @@ namespace Grpc.Testing {
threadsPerCq_ = other.threadsPerCq_;
messagesPerStream_ = other.messagesPerStream_;
useCoalesceApi_ = other.useCoalesceApi_;
+ medianLatencyCollectionIntervalMillis_ = other.medianLatencyCollectionIntervalMillis_;
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
}
@@ -1278,6 +1281,21 @@ namespace Grpc.Testing {
}
}
+ /// <summary>Field number for the "median_latency_collection_interval_millis" field.</summary>
+ public const int MedianLatencyCollectionIntervalMillisFieldNumber = 20;
+ private int medianLatencyCollectionIntervalMillis_;
+ /// <summary>
+ /// If 0, disabled. Else, specifies the period between gathering latency
+ /// medians in milliseconds.
+ /// </summary>
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public int MedianLatencyCollectionIntervalMillis {
+ get { return medianLatencyCollectionIntervalMillis_; }
+ set {
+ medianLatencyCollectionIntervalMillis_ = value;
+ }
+ }
+
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override bool Equals(object other) {
return Equals(other as ClientConfig);
@@ -1308,6 +1326,7 @@ namespace Grpc.Testing {
if (ThreadsPerCq != other.ThreadsPerCq) return false;
if (MessagesPerStream != other.MessagesPerStream) return false;
if (UseCoalesceApi != other.UseCoalesceApi) return false;
+ if (MedianLatencyCollectionIntervalMillis != other.MedianLatencyCollectionIntervalMillis) return false;
return Equals(_unknownFields, other._unknownFields);
}
@@ -1331,6 +1350,7 @@ namespace Grpc.Testing {
if (ThreadsPerCq != 0) hash ^= ThreadsPerCq.GetHashCode();
if (MessagesPerStream != 0) hash ^= MessagesPerStream.GetHashCode();
if (UseCoalesceApi != false) hash ^= UseCoalesceApi.GetHashCode();
+ if (MedianLatencyCollectionIntervalMillis != 0) hash ^= MedianLatencyCollectionIntervalMillis.GetHashCode();
if (_unknownFields != null) {
hash ^= _unknownFields.GetHashCode();
}
@@ -1403,6 +1423,10 @@ namespace Grpc.Testing {
output.WriteRawTag(152, 1);
output.WriteBool(UseCoalesceApi);
}
+ if (MedianLatencyCollectionIntervalMillis != 0) {
+ output.WriteRawTag(160, 1);
+ output.WriteInt32(MedianLatencyCollectionIntervalMillis);
+ }
if (_unknownFields != null) {
_unknownFields.WriteTo(output);
}
@@ -1456,6 +1480,9 @@ namespace Grpc.Testing {
if (UseCoalesceApi != false) {
size += 2 + 1;
}
+ if (MedianLatencyCollectionIntervalMillis != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(MedianLatencyCollectionIntervalMillis);
+ }
if (_unknownFields != null) {
size += _unknownFields.CalculateSize();
}
@@ -1524,6 +1551,9 @@ namespace Grpc.Testing {
if (other.UseCoalesceApi != false) {
UseCoalesceApi = other.UseCoalesceApi;
}
+ if (other.MedianLatencyCollectionIntervalMillis != 0) {
+ MedianLatencyCollectionIntervalMillis = other.MedianLatencyCollectionIntervalMillis;
+ }
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
@@ -1616,6 +1646,10 @@ namespace Grpc.Testing {
UseCoalesceApi = input.ReadBool();
break;
}
+ case 160: {
+ MedianLatencyCollectionIntervalMillis = input.ReadInt32();
+ break;
+ }
}
}
}
diff --git a/src/csharp/Grpc.IntegrationTesting/EmptyServiceGrpc.cs b/src/csharp/Grpc.IntegrationTesting/EmptyServiceGrpc.cs
index 2d233fbdc0..7e77f8d114 100644
--- a/src/csharp/Grpc.IntegrationTesting/EmptyServiceGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/EmptyServiceGrpc.cs
@@ -80,6 +80,14 @@ namespace Grpc.Testing {
return grpc::ServerServiceDefinition.CreateBuilder().Build();
}
+ /// <summary>Register service method implementations with a service binder. Useful when customizing the service binding logic.
+ /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+ /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+ /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+ public static void BindService(grpc::ServiceBinderBase serviceBinder, EmptyServiceBase serviceImpl)
+ {
+ }
+
}
}
#endregion
diff --git a/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs b/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
index 9f16f41ac1..c66a9a9161 100644
--- a/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
@@ -193,6 +193,16 @@ namespace Grpc.Testing {
.AddMethod(__Method_GetGauge, serviceImpl.GetGauge).Build();
}
+ /// <summary>Register service method implementations with a service binder. Useful when customizing the service binding logic.
+ /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+ /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+ /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+ public static void BindService(grpc::ServiceBinderBase serviceBinder, MetricsServiceBase serviceImpl)
+ {
+ serviceBinder.AddMethod(__Method_GetAllGauges, serviceImpl.GetAllGauges);
+ serviceBinder.AddMethod(__Method_GetGauge, serviceImpl.GetGauge);
+ }
+
}
}
#endregion
diff --git a/src/csharp/Grpc.IntegrationTesting/ReportQpsScenarioServiceGrpc.cs b/src/csharp/Grpc.IntegrationTesting/ReportQpsScenarioServiceGrpc.cs
index 1da0548cb4..954c172272 100644
--- a/src/csharp/Grpc.IntegrationTesting/ReportQpsScenarioServiceGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/ReportQpsScenarioServiceGrpc.cs
@@ -143,6 +143,15 @@ namespace Grpc.Testing {
.AddMethod(__Method_ReportScenario, serviceImpl.ReportScenario).Build();
}
+ /// <summary>Register service method implementations with a service binder. Useful when customizing the service binding logic.
+ /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+ /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+ /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+ public static void BindService(grpc::ServiceBinderBase serviceBinder, ReportQpsScenarioServiceBase serviceImpl)
+ {
+ serviceBinder.AddMethod(__Method_ReportScenario, serviceImpl.ReportScenario);
+ }
+
}
}
#endregion
diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
index 2176916b43..d125fd5627 100644
--- a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
@@ -539,6 +539,22 @@ namespace Grpc.Testing {
.AddMethod(__Method_UnimplementedCall, serviceImpl.UnimplementedCall).Build();
}
+ /// <summary>Register service method implementations with a service binder. Useful when customizing the service binding logic.
+ /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+ /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+ /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+ public static void BindService(grpc::ServiceBinderBase serviceBinder, TestServiceBase serviceImpl)
+ {
+ serviceBinder.AddMethod(__Method_EmptyCall, serviceImpl.EmptyCall);
+ serviceBinder.AddMethod(__Method_UnaryCall, serviceImpl.UnaryCall);
+ serviceBinder.AddMethod(__Method_CacheableUnaryCall, serviceImpl.CacheableUnaryCall);
+ serviceBinder.AddMethod(__Method_StreamingOutputCall, serviceImpl.StreamingOutputCall);
+ serviceBinder.AddMethod(__Method_StreamingInputCall, serviceImpl.StreamingInputCall);
+ serviceBinder.AddMethod(__Method_FullDuplexCall, serviceImpl.FullDuplexCall);
+ serviceBinder.AddMethod(__Method_HalfDuplexCall, serviceImpl.HalfDuplexCall);
+ serviceBinder.AddMethod(__Method_UnimplementedCall, serviceImpl.UnimplementedCall);
+ }
+
}
/// <summary>
/// A simple service NOT implemented at servers so clients can test for
@@ -661,6 +677,15 @@ namespace Grpc.Testing {
.AddMethod(__Method_UnimplementedCall, serviceImpl.UnimplementedCall).Build();
}
+ /// <summary>Register service method implementations with a service binder. Useful when customizing the service binding logic.
+ /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+ /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+ /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+ public static void BindService(grpc::ServiceBinderBase serviceBinder, UnimplementedServiceBase serviceImpl)
+ {
+ serviceBinder.AddMethod(__Method_UnimplementedCall, serviceImpl.UnimplementedCall);
+ }
+
}
/// <summary>
/// A service used to control reconnect server.
@@ -779,6 +804,16 @@ namespace Grpc.Testing {
.AddMethod(__Method_Stop, serviceImpl.Stop).Build();
}
+ /// <summary>Register service method implementations with a service binder. Useful when customizing the service binding logic.
+ /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+ /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+ /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+ public static void BindService(grpc::ServiceBinderBase serviceBinder, ReconnectServiceBase serviceImpl)
+ {
+ serviceBinder.AddMethod(__Method_Start, serviceImpl.Start);
+ serviceBinder.AddMethod(__Method_Stop, serviceImpl.Stop);
+ }
+
}
}
#endregion
diff --git a/src/csharp/Grpc.IntegrationTesting/WorkerServiceGrpc.cs b/src/csharp/Grpc.IntegrationTesting/WorkerServiceGrpc.cs
index b9e8f91231..5b22337d53 100644
--- a/src/csharp/Grpc.IntegrationTesting/WorkerServiceGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/WorkerServiceGrpc.cs
@@ -321,6 +321,18 @@ namespace Grpc.Testing {
.AddMethod(__Method_QuitWorker, serviceImpl.QuitWorker).Build();
}
+ /// <summary>Register service method implementations with a service binder. Useful when customizing the service binding logic.
+ /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+ /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+ /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+ public static void BindService(grpc::ServiceBinderBase serviceBinder, WorkerServiceBase serviceImpl)
+ {
+ serviceBinder.AddMethod(__Method_RunServer, serviceImpl.RunServer);
+ serviceBinder.AddMethod(__Method_RunClient, serviceImpl.RunClient);
+ serviceBinder.AddMethod(__Method_CoreCount, serviceImpl.CoreCount);
+ serviceBinder.AddMethod(__Method_QuitWorker, serviceImpl.QuitWorker);
+ }
+
}
}
#endregion
diff --git a/src/csharp/Grpc.Reflection/ReflectionGrpc.cs b/src/csharp/Grpc.Reflection/ReflectionGrpc.cs
index c00075b7c6..ed55c2f584 100644
--- a/src/csharp/Grpc.Reflection/ReflectionGrpc.cs
+++ b/src/csharp/Grpc.Reflection/ReflectionGrpc.cs
@@ -123,6 +123,15 @@ namespace Grpc.Reflection.V1Alpha {
.AddMethod(__Method_ServerReflectionInfo, serviceImpl.ServerReflectionInfo).Build();
}
+ /// <summary>Register service method implementations with a service binder. Useful when customizing the service binding logic.
+ /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+ /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+ /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+ public static void BindService(grpc::ServiceBinderBase serviceBinder, ServerReflectionBase serviceImpl)
+ {
+ serviceBinder.AddMethod(__Method_ServerReflectionInfo, serviceImpl.ServerReflectionInfo);
+ }
+
}
}
#endregion
diff --git a/src/csharp/README.md b/src/csharp/README.md
index d8aed94988..9a91035d06 100644
--- a/src/csharp/README.md
+++ b/src/csharp/README.md
@@ -87,6 +87,7 @@ $ python tools/run_tests/run_tests.py -l csharp -c dbg
DOCUMENTATION
-------------
+- [.NET Build Integration](BUILD-INTEGRATION.md)
- [API Reference][]
- [Helloworld Example][]
- [RouteGuide Tutorial][]
diff --git a/src/csharp/doc/integration.md-fig.1-classic.png b/src/csharp/doc/integration.md-fig.1-classic.png
new file mode 100644
index 0000000000..80c57261ad
--- /dev/null
+++ b/src/csharp/doc/integration.md-fig.1-classic.png
Binary files differ
diff --git a/src/csharp/doc/integration.md-fig.2-sdk.png b/src/csharp/doc/integration.md-fig.2-sdk.png
new file mode 100644
index 0000000000..6d653e4a58
--- /dev/null
+++ b/src/csharp/doc/integration.md-fig.2-sdk.png
Binary files differ
diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c
index b17f3d9a61..c06bdea7fe 100644
--- a/src/php/ext/grpc/channel.c
+++ b/src/php/ext/grpc/channel.c
@@ -393,6 +393,8 @@ PHP_METHOD(Channel, __construct) {
channel->wrapper->target = strdup(target);
channel->wrapper->args_hashstr = strdup(sha1str);
channel->wrapper->creds_hashstr = NULL;
+ channel->wrapper->creds = creds;
+ channel->wrapper->args = args;
if (creds != NULL && creds->hashstr != NULL) {
php_grpc_int creds_hashstr_len = strlen(creds->hashstr);
char *channel_creds_hashstr = malloc(creds_hashstr_len + 1);
diff --git a/src/php/ext/grpc/channel.h b/src/php/ext/grpc/channel.h
index 27752c9a3f..ce17c4a58a 100644
--- a/src/php/ext/grpc/channel.h
+++ b/src/php/ext/grpc/channel.h
@@ -19,6 +19,7 @@
#ifndef NET_GRPC_PHP_GRPC_CHANNEL_H_
#define NET_GRPC_PHP_GRPC_CHANNEL_H_
+#include "channel_credentials.h"
#include "php_grpc.h"
/* Class entry for the PHP Channel class */
@@ -32,6 +33,8 @@ typedef struct _grpc_channel_wrapper {
char *creds_hashstr;
size_t ref_count;
gpr_mu mu;
+ grpc_channel_args args;
+ wrapped_grpc_channel_credentials *creds;
} grpc_channel_wrapper;
/* Wrapper struct for grpc_channel that can be associated with a PHP object */
diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c
index fabd98975d..cbc7f63be0 100644
--- a/src/php/ext/grpc/php_grpc.c
+++ b/src/php/ext/grpc/php_grpc.c
@@ -26,6 +26,8 @@
#include "call_credentials.h"
#include "server_credentials.h"
#include "completion_queue.h"
+#include <ext/spl/spl_exceptions.h>
+#include <zend_exceptions.h>
ZEND_DECLARE_MODULE_GLOBALS(grpc)
static PHP_GINIT_FUNCTION(grpc);
@@ -86,6 +88,121 @@ ZEND_GET_MODULE(grpc)
}
*/
/* }}} */
+void create_new_channel(
+ wrapped_grpc_channel *channel,
+ char *target,
+ grpc_channel_args args,
+ wrapped_grpc_channel_credentials *creds) {
+ if (creds == NULL) {
+ channel->wrapper->wrapped = grpc_insecure_channel_create(target, &args,
+ NULL);
+ } else {
+ channel->wrapper->wrapped =
+ grpc_secure_channel_create(creds->wrapped, target, &args, NULL);
+ }
+}
+
+void acquire_persistent_locks() {
+ zval *data;
+ PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data)
+ php_grpc_zend_resource *rsrc =
+ (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data)
+ if (rsrc == NULL) {
+ break;
+ }
+ channel_persistent_le_t* le = rsrc->ptr;
+
+ gpr_mu_lock(&le->channel->mu);
+ PHP_GRPC_HASH_FOREACH_END()
+}
+
+void release_persistent_locks() {
+ zval *data;
+ PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data)
+ php_grpc_zend_resource *rsrc =
+ (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data)
+ if (rsrc == NULL) {
+ break;
+ }
+ channel_persistent_le_t* le = rsrc->ptr;
+
+ gpr_mu_unlock(&le->channel->mu);
+ PHP_GRPC_HASH_FOREACH_END()
+}
+
+void destroy_grpc_channels() {
+ zval *data;
+ PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data)
+ php_grpc_zend_resource *rsrc =
+ (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data)
+ if (rsrc == NULL) {
+ break;
+ }
+ channel_persistent_le_t* le = rsrc->ptr;
+
+ wrapped_grpc_channel wrapped_channel;
+ wrapped_channel.wrapper = le->channel;
+ grpc_channel_wrapper *channel = wrapped_channel.wrapper;
+ grpc_channel_destroy(channel->wrapped);
+ PHP_GRPC_HASH_FOREACH_END()
+}
+
+void restart_channels() {
+ zval *data;
+ PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data)
+ php_grpc_zend_resource *rsrc =
+ (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data)
+ if (rsrc == NULL) {
+ break;
+ }
+ channel_persistent_le_t* le = rsrc->ptr;
+
+ wrapped_grpc_channel wrapped_channel;
+ wrapped_channel.wrapper = le->channel;
+ grpc_channel_wrapper *channel = wrapped_channel.wrapper;
+ create_new_channel(&wrapped_channel, channel->target, channel->args,
+ channel->creds);
+ gpr_mu_unlock(&channel->mu);
+ PHP_GRPC_HASH_FOREACH_END()
+}
+
+void prefork() {
+ acquire_persistent_locks();
+}
+
+void postfork_child() {
+ // loop through persistant list and destroy all underlying grpc_channel objs
+ destroy_grpc_channels();
+
+ // clear completion queue
+ grpc_php_shutdown_completion_queue();
+
+ // clean-up grpc_core
+ grpc_shutdown();
+ if (grpc_is_initialized() > 0) {
+ zend_throw_exception(spl_ce_UnexpectedValueException,
+ "Oops, failed to shutdown gRPC Core after fork()",
+ 1 TSRMLS_CC);
+ }
+
+ // restart grpc_core
+ grpc_init();
+ grpc_php_init_completion_queue();
+
+ // re-create grpc_channel and point wrapped to it
+ // unlock wrapped grpc channel mutex
+ restart_channels();
+}
+
+void postfork_parent() {
+ release_persistent_locks();
+}
+
+void register_fork_handlers() {
+ if (getenv("GRPC_ENABLE_FORK_SUPPORT")) {
+ pthread_atfork(&prefork, &postfork_parent, &postfork_child);
+ }
+}
/* {{{ PHP_MINIT_FUNCTION
*/
@@ -265,6 +382,7 @@ PHP_MINFO_FUNCTION(grpc) {
PHP_RINIT_FUNCTION(grpc) {
if (!GRPC_G(initialized)) {
grpc_init();
+ register_fork_handlers();
grpc_php_init_completion_queue(TSRMLS_C);
GRPC_G(initialized) = 1;
}
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/proto/grpc/reflection/v1alpha/BUILD b/src/proto/grpc/reflection/v1alpha/BUILD
index 4605418447..4d919d029e 100644
--- a/src/proto/grpc/reflection/v1alpha/BUILD
+++ b/src/proto/grpc/reflection/v1alpha/BUILD
@@ -22,3 +22,11 @@ grpc_proto_library(
name = "reflection_proto",
srcs = ["reflection.proto"],
)
+
+filegroup(
+ name = "reflection_proto_file",
+ srcs = [
+ "reflection.proto",
+ ],
+)
+
diff --git a/src/proto/grpc/testing/BUILD b/src/proto/grpc/testing/BUILD
index 16721ff2ed..7048911b9a 100644
--- a/src/proto/grpc/testing/BUILD
+++ b/src/proto/grpc/testing/BUILD
@@ -15,6 +15,8 @@
licenses(["notice"]) # Apache v2
load("//bazel:grpc_build_system.bzl", "grpc_proto_library", "grpc_package")
+load("@grpc_python_dependencies//:requirements.bzl", "requirement")
+load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_library")
grpc_package(name = "testing", visibility = "public")
@@ -58,12 +60,30 @@ grpc_proto_library(
has_services = False,
)
+py_proto_library(
+ name = "py_empty_proto",
+ protos = ["empty.proto",],
+ with_grpc = True,
+ deps = [
+ requirement('protobuf'),
+ ],
+)
+
grpc_proto_library(
name = "messages_proto",
srcs = ["messages.proto"],
has_services = False,
)
+py_proto_library(
+ name = "py_messages_proto",
+ protos = ["messages.proto",],
+ with_grpc = True,
+ deps = [
+ requirement('protobuf'),
+ ],
+)
+
grpc_proto_library(
name = "metrics_proto",
srcs = ["metrics.proto"],
@@ -116,3 +136,17 @@ grpc_proto_library(
"messages_proto",
],
)
+
+py_proto_library(
+ name = "py_test_proto",
+ protos = ["test.proto",],
+ with_grpc = True,
+ deps = [
+ requirement('protobuf'),
+ ],
+ proto_deps = [
+ ":py_empty_proto",
+ ":py_messages_proto",
+ ]
+)
+
diff --git a/src/proto/grpc/testing/control.proto b/src/proto/grpc/testing/control.proto
index 4cfdc2cafb..564d8234b7 100644
--- a/src/proto/grpc/testing/control.proto
+++ b/src/proto/grpc/testing/control.proto
@@ -33,6 +33,7 @@ enum ServerType {
ASYNC_SERVER = 1;
ASYNC_GENERIC_SERVER = 2;
OTHER_SERVER = 3; // used for some language-specific variants
+ CALLBACK_SERVER = 4;
}
enum RpcType {
diff --git a/src/proto/grpc/testing/proto2/BUILD.bazel b/src/proto/grpc/testing/proto2/BUILD.bazel
new file mode 100644
index 0000000000..c4c4f004ef
--- /dev/null
+++ b/src/proto/grpc/testing/proto2/BUILD.bazel
@@ -0,0 +1,30 @@
+load("@grpc_python_dependencies//:requirements.bzl", "requirement")
+load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_library")
+
+package(default_visibility = ["//visibility:public"])
+
+py_proto_library(
+ name = "empty2_proto",
+ protos = [
+ "empty2.proto",
+ ],
+ with_grpc = True,
+ deps = [
+ requirement('protobuf'),
+ ],
+)
+
+py_proto_library(
+ name = "empty2_extensions_proto",
+ protos = [
+ "empty2_extensions.proto",
+ ],
+ proto_deps = [
+ ":empty2_proto",
+ ],
+ with_grpc = True,
+ deps = [
+ requirement('protobuf'),
+ ],
+)
+
diff --git a/src/python/.gitignore b/src/python/.gitignore
index 7b520579a0..41813129bd 100644
--- a/src/python/.gitignore
+++ b/src/python/.gitignore
@@ -1,3 +1,4 @@
gens/
*_pb2.py
*_pb2_grpc.py
+*.egg-info/
diff --git a/src/python/grpcio/_parallel_compile_patch.py b/src/python/grpcio/_parallel_compile_patch.py
new file mode 100644
index 0000000000..4d03ef49ba
--- /dev/null
+++ b/src/python/grpcio/_parallel_compile_patch.py
@@ -0,0 +1,63 @@
+# 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.
+"""Patches the compile() to allow enable parallel compilation of C/C++.
+
+build_ext has lots of C/C++ files and normally them one by one.
+Enabling parallel build helps a lot.
+"""
+
+import distutils.ccompiler
+import os
+
+try:
+ BUILD_EXT_COMPILER_JOBS = int(
+ os.environ.get('GRPC_PYTHON_BUILD_EXT_COMPILER_JOBS', '1'))
+except ValueError:
+ BUILD_EXT_COMPILER_JOBS = 1
+
+
+# monkey-patch for parallel compilation
+def _parallel_compile(self,
+ sources,
+ output_dir=None,
+ macros=None,
+ include_dirs=None,
+ debug=0,
+ extra_preargs=None,
+ extra_postargs=None,
+ depends=None):
+ # setup the same way as distutils.ccompiler.CCompiler
+ # https://github.com/python/cpython/blob/31368a4f0e531c19affe2a1becd25fc316bc7501/Lib/distutils/ccompiler.py#L564
+ macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
+ output_dir, macros, include_dirs, sources, depends, extra_postargs)
+ cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
+
+ def _compile_single_file(obj):
+ try:
+ src, ext = build[obj]
+ except KeyError:
+ return
+ self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
+
+ # run compilation of individual files in parallel
+ import multiprocessing.pool
+ multiprocessing.pool.ThreadPool(BUILD_EXT_COMPILER_JOBS).map(
+ _compile_single_file, objects)
+ return objects
+
+
+def monkeypatch_compile_maybe():
+ """Monkeypatching is dumb, but the build speed gain is worth it."""
+ if BUILD_EXT_COMPILER_JOBS > 1:
+ distutils.ccompiler.CCompiler.compile = _parallel_compile
diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py
index 0a3097111f..b805f4277b 100644
--- a/src/python/grpcio/commands.py
+++ b/src/python/grpcio/commands.py
@@ -39,36 +39,6 @@ PROTO_STEM = os.path.join(GRPC_STEM, 'src', 'proto')
PROTO_GEN_STEM = os.path.join(GRPC_STEM, 'src', 'python', 'gens')
CYTHON_STEM = os.path.join(PYTHON_STEM, 'grpc', '_cython')
-CONF_PY_ADDENDUM = """
-extensions.append('sphinx.ext.napoleon')
-napoleon_google_docstring = True
-napoleon_numpy_docstring = True
-napoleon_include_special_with_doc = True
-
-html_theme = 'sphinx_rtd_theme'
-copyright = "2016, The gRPC Authors"
-"""
-
-API_GLOSSARY = """
-
-Glossary
-================
-
-.. glossary::
-
- metadatum
- A key-value pair included in the HTTP header. It is a
- 2-tuple where the first entry is the key and the
- second is the value, i.e. (key, value). The metadata key is an ASCII str,
- and must be a valid HTTP header name. The metadata value can be
- either a valid HTTP ASCII str, or bytes. If bytes are provided,
- the key must end with '-bin', i.e.
- ``('binary-metadata-bin', b'\\x00\\xFF')``
-
- metadata
- A sequence of metadatum.
-"""
-
class CommandError(Exception):
"""Simple exception class for GRPC custom commands."""
@@ -124,25 +94,14 @@ class SphinxDocumentation(setuptools.Command):
def run(self):
# We import here to ensure that setup.py has had a chance to install the
# relevant package eggs first.
- import sphinx
- import sphinx.apidoc
- metadata = self.distribution.metadata
- src_dir = os.path.join(PYTHON_STEM, 'grpc')
- sys.path.append(src_dir)
- sphinx.apidoc.main([
- '', '--force', '--full', '-H', metadata.name, '-A', metadata.author,
- '-V', metadata.version, '-R', metadata.version, '-o',
- os.path.join('doc', 'src'), src_dir
- ])
- conf_filepath = os.path.join('doc', 'src', 'conf.py')
- with open(conf_filepath, 'a') as conf_file:
- conf_file.write(CONF_PY_ADDENDUM)
- glossary_filepath = os.path.join('doc', 'src', 'grpc.rst')
- with open(glossary_filepath, 'a') as glossary_filepath:
- glossary_filepath.write(API_GLOSSARY)
- sphinx.main(
- ['', os.path.join('doc', 'src'),
- os.path.join('doc', 'build')])
+ import sphinx.cmd.build
+ source_dir = os.path.join(GRPC_STEM, 'doc', 'python', 'sphinx')
+ target_dir = os.path.join(GRPC_STEM, 'doc', 'build')
+ exit_code = sphinx.cmd.build.build_main(
+ ['-b', 'html', '-W', '--keep-going', source_dir, target_dir])
+ if exit_code is not 0:
+ raise CommandError(
+ "Documentation generation has warnings or errors")
class BuildProjectMetadata(setuptools.Command):
@@ -253,6 +212,12 @@ class BuildExt(build_ext.build_ext):
LINK_OPTIONS = {}
def build_extensions(self):
+ # This special conditioning is here due to difference of compiler
+ # behavior in gcc and clang. The clang doesn't take --stdc++11
+ # flags but gcc does. Since the setuptools of Python only support
+ # all C or all C++ compilation, the mix of C and C++ will crash.
+ # *By default*, the macOS use clang and Linux use gcc, that's why
+ # the special condition here is checking platform.
if "darwin" in sys.platform:
config = os.environ.get('CONFIG', 'opt')
target_path = os.path.abspath(
@@ -274,8 +239,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/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py
index 863696d236..88c5b6d5be 100644
--- a/src/python/grpcio/grpc/__init__.py
+++ b/src/python/grpcio/grpc/__init__.py
@@ -15,12 +15,14 @@
import abc
import enum
+import logging
import sys
-
import six
from grpc._cython import cygrpc as _cygrpc
+logging.getLogger(__name__).addHandler(logging.NullHandler())
+
############################## Future Interface ###############################
@@ -48,11 +50,13 @@ class Future(six.with_metaclass(abc.ABCMeta)):
Returns:
bool:
Returns True if the computation was canceled.
+
Returns False under all other circumstances, for example:
+
1. computation has begun and could not be canceled.
2. computation has finished
3. computation is scheduled for execution and it is impossible
- to determine its state without blocking.
+ to determine its state without blocking.
"""
raise NotImplementedError()
@@ -66,7 +70,9 @@ class Future(six.with_metaclass(abc.ABCMeta)):
bool:
Returns True if the computation was cancelled before its result became
available.
- False under all other circumstances, for example:
+
+ Returns False under all other circumstances, for example:
+
1. computation was not cancelled.
2. computation's result is available.
"""
@@ -79,9 +85,9 @@ class Future(six.with_metaclass(abc.ABCMeta)):
This method does not block.
Returns:
- bool:
Returns True if the computation is scheduled for execution or
currently executing.
+
Returns False if the computation already executed or was cancelled.
"""
raise NotImplementedError()
@@ -210,7 +216,33 @@ class ChannelConnectivity(enum.Enum):
@enum.unique
class StatusCode(enum.Enum):
- """Mirrors grpc_status_code in the gRPC Core."""
+ """Mirrors grpc_status_code in the gRPC Core.
+
+ Attributes:
+ OK: Not an error; returned on success
+ CANCELLED: The operation was cancelled (typically by the caller).
+ UNKNOWN: Unknown error.
+ INVALID_ARGUMENT: Client specified an invalid argument.
+ DEADLINE_EXCEEDED: Deadline expired before operation could complete.
+ NOT_FOUND: Some requested entity (e.g., file or directory) was not found.
+ ALREADY_EXISTS: Some entity that we attempted to create (e.g., file or directory)
+ already exists.
+ PERMISSION_DENIED: The caller does not have permission to execute the specified
+ operation.
+ UNAUTHENTICATED: The request does not have valid authentication credentials for the
+ operation.
+ RESOURCE_EXHAUSTED: Some resource has been exhausted, perhaps a per-user quota, or
+ perhaps the entire file system is out of space.
+ FAILED_PRECONDITION: Operation was rejected because the system is not in a state
+ required for the operation's execution.
+ ABORTED: The operation was aborted, typically due to a concurrency issue
+ like sequencer check failures, transaction aborts, etc.
+ UNIMPLEMENTED: Operation is not implemented or not supported/enabled in this service.
+ INTERNAL: Internal errors. Means some invariants expected by underlying
+ system has been broken.
+ UNAVAILABLE: The service is currently unavailable.
+ DATA_LOSS: Unrecoverable data loss or corruption.
+ """
OK = (_cygrpc.StatusCode.ok, 'ok')
CANCELLED = (_cygrpc.StatusCode.cancelled, 'cancelled')
UNKNOWN = (_cygrpc.StatusCode.unknown, 'unknown')
@@ -357,6 +389,8 @@ class ClientCallDetails(six.with_metaclass(abc.ABCMeta)):
metadata: Optional :term:`metadata` to be transmitted to
the service-side of the RPC.
credentials: An optional CallCredentials for the RPC.
+ wait_for_ready: This is an EXPERIMENTAL argument. An optional flag t
+ enable wait for ready mechanism.
"""
@@ -450,8 +484,7 @@ class StreamUnaryClientInterceptor(six.with_metaclass(abc.ABCMeta)):
actual RPC on the underlying Channel. It is the interceptor's
responsibility to call it if it decides to move the RPC forward.
The interceptor can use
- `response_future = continuation(client_call_details,
- request_iterator)`
+ `response_future = continuation(client_call_details, request_iterator)`
to continue with the RPC. `continuation` returns an object that is
both a Call for the RPC and a Future. In the event of RPC completion,
the return Call-Future's result value will be the response message
@@ -462,11 +495,11 @@ class StreamUnaryClientInterceptor(six.with_metaclass(abc.ABCMeta)):
request_iterator: An iterator that yields request values for the RPC.
Returns:
- An object that is both a Call for the RPC and a Future.
- In the event of RPC completion, the return Call-Future's
- result value will be the response message of the RPC.
- Should the event terminate with non-OK status, the returned
- Call-Future's exception value will be an RpcError.
+ An object that is both a Call for the RPC and a Future.
+ In the event of RPC completion, the return Call-Future's
+ result value will be the response message of the RPC.
+ Should the event terminate with non-OK status, the returned
+ Call-Future's exception value will be an RpcError.
"""
raise NotImplementedError()
@@ -482,13 +515,13 @@ class StreamStreamClientInterceptor(six.with_metaclass(abc.ABCMeta)):
request_iterator):
"""Intercepts a stream-stream invocation.
+ Args:
continuation: A function that proceeds with the invocation by
executing the next interceptor in chain or invoking the
actual RPC on the underlying Channel. It is the interceptor's
responsibility to call it if it decides to move the RPC forward.
The interceptor can use
- `response_iterator = continuation(client_call_details,
- request_iterator)`
+ `response_iterator = continuation(client_call_details, request_iterator)`
to continue with the RPC. `continuation` returns an object that is
both a Call for the RPC and an iterator for response values.
Drawing response values from the returned Call-iterator may
@@ -499,10 +532,10 @@ class StreamStreamClientInterceptor(six.with_metaclass(abc.ABCMeta)):
request_iterator: An iterator that yields request values for the RPC.
Returns:
- An object that is both a Call for the RPC and an iterator of
- response values. Drawing response values from the returned
- Call-iterator may raise RpcError indicating termination of
- the RPC with non-OK status.
+ An object that is both a Call for the RPC and an iterator of
+ response values. Drawing response values from the returned
+ Call-iterator may raise RpcError indicating termination of
+ the RPC with non-OK status.
"""
raise NotImplementedError()
@@ -609,7 +642,12 @@ class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
"""Affords invoking a unary-unary RPC from client-side."""
@abc.abstractmethod
- def __call__(self, request, timeout=None, metadata=None, credentials=None):
+ def __call__(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
"""Synchronously invokes the underlying RPC.
Args:
@@ -619,6 +657,8 @@ class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
metadata: Optional :term:`metadata` to be transmitted to the
service-side of the RPC.
credentials: An optional CallCredentials for the RPC.
+ wait_for_ready: This is an EXPERIMENTAL argument. An optional
+ flag to enable wait for ready mechanism
Returns:
The response value for the RPC.
@@ -631,7 +671,12 @@ class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
raise NotImplementedError()
@abc.abstractmethod
- def with_call(self, request, timeout=None, metadata=None, credentials=None):
+ def with_call(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
"""Synchronously invokes the underlying RPC.
Args:
@@ -641,6 +686,8 @@ class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
metadata: Optional :term:`metadata` to be transmitted to the
service-side of the RPC.
credentials: An optional CallCredentials for the RPC.
+ wait_for_ready: This is an EXPERIMENTAL argument. An optional
+ flag to enable wait for ready mechanism
Returns:
The response value for the RPC and a Call value for the RPC.
@@ -653,7 +700,12 @@ class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
raise NotImplementedError()
@abc.abstractmethod
- def future(self, request, timeout=None, metadata=None, credentials=None):
+ def future(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
"""Asynchronously invokes the underlying RPC.
Args:
@@ -663,6 +715,8 @@ class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
metadata: Optional :term:`metadata` to be transmitted to the
service-side of the RPC.
credentials: An optional CallCredentials for the RPC.
+ wait_for_ready: This is an EXPERIMENTAL argument. An optional
+ flag to enable wait for ready mechanism
Returns:
An object that is both a Call for the RPC and a Future.
@@ -678,7 +732,12 @@ class UnaryStreamMultiCallable(six.with_metaclass(abc.ABCMeta)):
"""Affords invoking a unary-stream RPC from client-side."""
@abc.abstractmethod
- def __call__(self, request, timeout=None, metadata=None, credentials=None):
+ def __call__(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
"""Invokes the underlying RPC.
Args:
@@ -688,6 +747,8 @@ class UnaryStreamMultiCallable(six.with_metaclass(abc.ABCMeta)):
metadata: An optional :term:`metadata` to be transmitted to the
service-side of the RPC.
credentials: An optional CallCredentials for the RPC.
+ wait_for_ready: This is an EXPERIMENTAL argument. An optional
+ flag to enable wait for ready mechanism
Returns:
An object that is both a Call for the RPC and an iterator of
@@ -706,7 +767,8 @@ class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
+ credentials=None,
+ wait_for_ready=None):
"""Synchronously invokes the underlying RPC.
Args:
@@ -717,6 +779,8 @@ class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
metadata: Optional :term:`metadata` to be transmitted to the
service-side of the RPC.
credentials: An optional CallCredentials for the RPC.
+ wait_for_ready: This is an EXPERIMENTAL argument. An optional
+ flag to enable wait for ready mechanism
Returns:
The response value for the RPC.
@@ -733,7 +797,8 @@ class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
+ credentials=None,
+ wait_for_ready=None):
"""Synchronously invokes the underlying RPC on the client.
Args:
@@ -744,6 +809,8 @@ class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
metadata: Optional :term:`metadata` to be transmitted to the
service-side of the RPC.
credentials: An optional CallCredentials for the RPC.
+ wait_for_ready: This is an EXPERIMENTAL argument. An optional
+ flag to enable wait for ready mechanism
Returns:
The response value for the RPC and a Call object for the RPC.
@@ -760,7 +827,8 @@ class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
+ credentials=None,
+ wait_for_ready=None):
"""Asynchronously invokes the underlying RPC on the client.
Args:
@@ -770,6 +838,8 @@ class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
metadata: Optional :term:`metadata` to be transmitted to the
service-side of the RPC.
credentials: An optional CallCredentials for the RPC.
+ wait_for_ready: This is an EXPERIMENTAL argument. An optional
+ flag to enable wait for ready mechanism
Returns:
An object that is both a Call for the RPC and a Future.
@@ -789,7 +859,8 @@ class StreamStreamMultiCallable(six.with_metaclass(abc.ABCMeta)):
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
+ credentials=None,
+ wait_for_ready=None):
"""Invokes the underlying RPC on the client.
Args:
@@ -799,6 +870,8 @@ class StreamStreamMultiCallable(six.with_metaclass(abc.ABCMeta)):
metadata: Optional :term:`metadata` to be transmitted to the
service-side of the RPC.
credentials: An optional CallCredentials for the RPC.
+ wait_for_ready: This is an EXPERIMENTAL argument. An optional
+ flag to enable wait for ready mechanism
Returns:
An object that is both a Call for the RPC and an iterator of
@@ -972,8 +1045,7 @@ class ServicerContext(six.with_metaclass(abc.ABCMeta, RpcContext)):
"""Gets one or more peer identity(s).
Equivalent to
- servicer_context.auth_context().get(
- servicer_context.peer_identity_key())
+ servicer_context.auth_context().get(servicer_context.peer_identity_key())
Returns:
An iterable of the identities, or None if the call is not
diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py
index eeeb4ddb33..ab154d8512 100644
--- a/src/python/grpcio/grpc/_channel.py
+++ b/src/python/grpcio/grpc/_channel.py
@@ -24,7 +24,6 @@ from grpc import _grpcio_metadata
from grpc._cython import cygrpc
from grpc.framework.foundation import callable_util
-logging.basicConfig()
_LOGGER = logging.getLogger(__name__)
_USER_AGENT = 'grpc-python/{}'.format(_grpcio_metadata.__version__)
@@ -467,10 +466,11 @@ def _end_unary_response_blocking(state, call, with_call, deadline):
raise _Rendezvous(state, None, None, deadline)
-def _stream_unary_invocation_operationses(metadata):
+def _stream_unary_invocation_operationses(metadata, initial_metadata_flags):
return (
(
- cygrpc.SendInitialMetadataOperation(metadata, _EMPTY_FLAGS),
+ cygrpc.SendInitialMetadataOperation(metadata,
+ initial_metadata_flags),
cygrpc.ReceiveMessageOperation(_EMPTY_FLAGS),
cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),
),
@@ -478,15 +478,19 @@ def _stream_unary_invocation_operationses(metadata):
)
-def _stream_unary_invocation_operationses_and_tags(metadata):
+def _stream_unary_invocation_operationses_and_tags(metadata,
+ initial_metadata_flags):
return tuple((
operations,
None,
- ) for operations in _stream_unary_invocation_operationses(metadata))
+ )
+ for operations in _stream_unary_invocation_operationses(
+ metadata, initial_metadata_flags))
class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
+ # pylint: disable=too-many-arguments
def __init__(self, channel, managed_call, method, request_serializer,
response_deserializer):
self._channel = channel
@@ -495,15 +499,18 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
self._request_serializer = request_serializer
self._response_deserializer = response_deserializer
- def _prepare(self, request, timeout, metadata):
+ def _prepare(self, request, timeout, metadata, wait_for_ready):
deadline, serialized_request, rendezvous = _start_unary_request(
request, timeout, self._request_serializer)
+ initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
+ wait_for_ready)
if serialized_request is None:
return None, None, None, rendezvous
else:
state = _RPCState(_UNARY_UNARY_INITIAL_DUE, None, None, None, None)
operations = (
- cygrpc.SendInitialMetadataOperation(metadata, _EMPTY_FLAGS),
+ cygrpc.SendInitialMetadataOperation(metadata,
+ initial_metadata_flags),
cygrpc.SendMessageOperation(serialized_request, _EMPTY_FLAGS),
cygrpc.SendCloseFromClientOperation(_EMPTY_FLAGS),
cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),
@@ -512,9 +519,10 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
)
return state, operations, deadline, None
- def _blocking(self, request, timeout, metadata, credentials):
+ def _blocking(self, request, timeout, metadata, credentials,
+ wait_for_ready):
state, operations, deadline, rendezvous = self._prepare(
- request, timeout, metadata)
+ request, timeout, metadata, wait_for_ready)
if state is None:
raise rendezvous
else:
@@ -528,17 +536,34 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
_handle_event(event, state, self._response_deserializer)
return state, call,
- def __call__(self, request, timeout=None, metadata=None, credentials=None):
- state, call, = self._blocking(request, timeout, metadata, credentials)
+ def __call__(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
+ state, call, = self._blocking(request, timeout, metadata, credentials,
+ wait_for_ready)
return _end_unary_response_blocking(state, call, False, None)
- def with_call(self, request, timeout=None, metadata=None, credentials=None):
- state, call, = self._blocking(request, timeout, metadata, credentials)
+ def with_call(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
+ state, call, = self._blocking(request, timeout, metadata, credentials,
+ wait_for_ready)
return _end_unary_response_blocking(state, call, True, None)
- def future(self, request, timeout=None, metadata=None, credentials=None):
+ def future(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
state, operations, deadline, rendezvous = self._prepare(
- request, timeout, metadata)
+ request, timeout, metadata, wait_for_ready)
if state is None:
raise rendezvous
else:
@@ -553,6 +578,7 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
+ # pylint: disable=too-many-arguments
def __init__(self, channel, managed_call, method, request_serializer,
response_deserializer):
self._channel = channel
@@ -561,16 +587,24 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
self._request_serializer = request_serializer
self._response_deserializer = response_deserializer
- def __call__(self, request, timeout=None, metadata=None, credentials=None):
+ def __call__(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
deadline, serialized_request, rendezvous = _start_unary_request(
request, timeout, self._request_serializer)
+ initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
+ wait_for_ready)
if serialized_request is None:
raise rendezvous
else:
state = _RPCState(_UNARY_STREAM_INITIAL_DUE, None, None, None, None)
operationses = (
(
- cygrpc.SendInitialMetadataOperation(metadata, _EMPTY_FLAGS),
+ cygrpc.SendInitialMetadataOperation(metadata,
+ initial_metadata_flags),
cygrpc.SendMessageOperation(serialized_request,
_EMPTY_FLAGS),
cygrpc.SendCloseFromClientOperation(_EMPTY_FLAGS),
@@ -589,6 +623,7 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
+ # pylint: disable=too-many-arguments
def __init__(self, channel, managed_call, method, request_serializer,
response_deserializer):
self._channel = channel
@@ -597,13 +632,17 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
self._request_serializer = request_serializer
self._response_deserializer = response_deserializer
- def _blocking(self, request_iterator, timeout, metadata, credentials):
+ def _blocking(self, request_iterator, timeout, metadata, credentials,
+ wait_for_ready):
deadline = _deadline(timeout)
state = _RPCState(_STREAM_UNARY_INITIAL_DUE, None, None, None, None)
+ initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
+ wait_for_ready)
call = self._channel.segregated_call(
0, self._method, None, deadline, metadata, None
if credentials is None else credentials._credentials,
- _stream_unary_invocation_operationses_and_tags(metadata))
+ _stream_unary_invocation_operationses_and_tags(
+ metadata, initial_metadata_flags))
_consume_request_iterator(request_iterator, state, call,
self._request_serializer, None)
while True:
@@ -619,32 +658,38 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
+ credentials=None,
+ wait_for_ready=None):
state, call, = self._blocking(request_iterator, timeout, metadata,
- credentials)
+ credentials, wait_for_ready)
return _end_unary_response_blocking(state, call, False, None)
def with_call(self,
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
+ credentials=None,
+ wait_for_ready=None):
state, call, = self._blocking(request_iterator, timeout, metadata,
- credentials)
+ credentials, wait_for_ready)
return _end_unary_response_blocking(state, call, True, None)
def future(self,
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
+ credentials=None,
+ wait_for_ready=None):
deadline = _deadline(timeout)
state = _RPCState(_STREAM_UNARY_INITIAL_DUE, None, None, None, None)
event_handler = _event_handler(state, self._response_deserializer)
+ initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
+ wait_for_ready)
call = self._managed_call(
0, self._method, None, deadline, metadata, None
if credentials is None else credentials._credentials,
- _stream_unary_invocation_operationses(metadata), event_handler)
+ _stream_unary_invocation_operationses(
+ metadata, initial_metadata_flags), event_handler)
_consume_request_iterator(request_iterator, state, call,
self._request_serializer, event_handler)
return _Rendezvous(state, call, self._response_deserializer, deadline)
@@ -652,6 +697,7 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
+ # pylint: disable=too-many-arguments
def __init__(self, channel, managed_call, method, request_serializer,
response_deserializer):
self._channel = channel
@@ -664,12 +710,16 @@ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
+ credentials=None,
+ wait_for_ready=None):
deadline = _deadline(timeout)
state = _RPCState(_STREAM_STREAM_INITIAL_DUE, None, None, None, None)
+ initial_metadata_flags = _InitialMetadataFlags().with_wait_for_ready(
+ wait_for_ready)
operationses = (
(
- cygrpc.SendInitialMetadataOperation(metadata, _EMPTY_FLAGS),
+ cygrpc.SendInitialMetadataOperation(metadata,
+ initial_metadata_flags),
cygrpc.ReceiveStatusOnClientOperation(_EMPTY_FLAGS),
),
(cygrpc.ReceiveInitialMetadataOperation(_EMPTY_FLAGS),),
@@ -684,6 +734,24 @@ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
return _Rendezvous(state, call, self._response_deserializer, deadline)
+class _InitialMetadataFlags(int):
+ """Stores immutable initial metadata flags"""
+
+ def __new__(cls, value=_EMPTY_FLAGS):
+ value &= cygrpc.InitialMetadataFlags.used_mask
+ return super(_InitialMetadataFlags, cls).__new__(cls, value)
+
+ def with_wait_for_ready(self, wait_for_ready):
+ if wait_for_ready is not None:
+ if wait_for_ready:
+ self = self.__class__(self | cygrpc.InitialMetadataFlags.wait_for_ready | \
+ cygrpc.InitialMetadataFlags.wait_for_ready_explicitly_set)
+ elif not wait_for_ready:
+ self = self.__class__(self & ~cygrpc.InitialMetadataFlags.wait_for_ready | \
+ cygrpc.InitialMetadataFlags.wait_for_ready_explicitly_set)
+ return self
+
+
class _ChannelCallState(object):
def __init__(self, channel):
@@ -980,8 +1048,9 @@ class Channel(grpc.Channel):
# for as long as they are in use and to close them after using them,
# then deletion of this grpc._channel.Channel instance can be made to
# effect closure of the underlying cygrpc.Channel instance.
- cygrpc.fork_unregister_channel(self)
+ if cygrpc is not None: # Globals may have already been collected.
+ cygrpc.fork_unregister_channel(self)
# This prevent the failed-at-initializing object removal from failing.
# Though the __init__ failed, the removal will still trigger __del__.
- if hasattr(self, "_connectivity_state"):
+ if _moot is not None and hasattr(self, "_connectivity_state"):
_moot(self._connectivity_state)
diff --git a/src/python/grpcio/grpc/_common.py b/src/python/grpcio/grpc/_common.py
index 3805c7e82a..f69127e38e 100644
--- a/src/python/grpcio/grpc/_common.py
+++ b/src/python/grpcio/grpc/_common.py
@@ -20,7 +20,6 @@ import six
import grpc
from grpc._cython import cygrpc
-logging.basicConfig()
_LOGGER = logging.getLogger(__name__)
CYGRPC_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY = {
@@ -66,18 +65,13 @@ def encode(s):
if isinstance(s, bytes):
return s
else:
- return s.encode('ascii')
+ return s.encode('utf8')
def decode(b):
- if isinstance(b, str):
- return b
- else:
- try:
- return b.decode('utf8')
- except UnicodeDecodeError:
- _LOGGER.exception('Invalid encoding on %s', b)
- return b.decode('latin1')
+ if isinstance(b, bytes):
+ return b.decode('utf-8', 'replace')
+ return b
def _transform(message, transformer, exception_message):
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
index 4781219319..23428f0b0c 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
@@ -140,6 +140,10 @@ cdef extern from "grpc/grpc.h":
const int GRPC_WRITE_NO_COMPRESS
const int GRPC_WRITE_USED_MASK
+ const int GRPC_INITIAL_METADATA_WAIT_FOR_READY
+ const int GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET
+ const int GRPC_INITIAL_METADATA_USED_MASK
+
const int GRPC_MAX_COMPLETION_QUEUE_PLUCKERS
ctypedef struct grpc_completion_queue:
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc_string.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc_string.pyx.pxi
index 334e561baa..00a1b23a67 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc_string.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc_string.pyx.pxi
@@ -14,7 +14,6 @@
import logging
-logging.basicConfig()
_LOGGER = logging.getLogger(__name__)
# This function will ascii encode unicode string inputs if neccesary.
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/metadata.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/metadata.pyx.pxi
index c39fef08fa..53f0c7f0bb 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/metadata.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/metadata.pyx.pxi
@@ -15,6 +15,12 @@
import collections
+class InitialMetadataFlags:
+ used_mask = GRPC_INITIAL_METADATA_USED_MASK
+ wait_for_ready = GRPC_INITIAL_METADATA_WAIT_FOR_READY
+ wait_for_ready_explicitly_set = GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET
+
+
_Metadatum = collections.namedtuple('_Metadatum', ('key', 'value',))
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi
index 7decae95bb..e17ca6d335 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi
@@ -14,14 +14,16 @@
from libc.string cimport memcpy
-import pkg_resources
+import pkgutil
cdef grpc_ssl_roots_override_result ssl_roots_override_callback(
char **pem_root_certs) nogil:
with gil:
- temporary_pem_root_certs = pkg_resources.resource_string(
- __name__.rstrip('.cygrpc'), '_credentials/roots.pem')
+ pkg = __name__
+ if pkg.endswith('.cygrpc'):
+ pkg = pkg[:-len('.cygrpc')]
+ temporary_pem_root_certs = pkgutil.get_data(pkg, '_credentials/roots.pem')
pem_root_certs[0] = <char *>gpr_malloc(len(temporary_pem_root_certs) + 1)
memcpy(
pem_root_certs[0], <char *>temporary_pem_root_certs,
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
index 5779437b92..ce701724fd 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
@@ -18,7 +18,6 @@ import logging
import time
import grpc
-logging.basicConfig()
_LOGGER = logging.getLogger(__name__)
cdef class Server:
diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pyx b/src/python/grpcio/grpc/_cython/cygrpc.pyx
index 026f7ba2e3..ae5c07bfc8 100644
--- a/src/python/grpcio/grpc/_cython/cygrpc.pyx
+++ b/src/python/grpcio/grpc/_cython/cygrpc.pyx
@@ -15,7 +15,6 @@
cimport cpython
-import pkg_resources
import os.path
import sys
diff --git a/src/python/grpcio/grpc/_interceptor.py b/src/python/grpcio/grpc/_interceptor.py
index 1d2d374ad1..4345114026 100644
--- a/src/python/grpcio/grpc/_interceptor.py
+++ b/src/python/grpcio/grpc/_interceptor.py
@@ -46,7 +46,7 @@ def service_pipeline(interceptors):
class _ClientCallDetails(
collections.namedtuple(
'_ClientCallDetails',
- ('method', 'timeout', 'metadata', 'credentials')),
+ ('method', 'timeout', 'metadata', 'credentials', 'wait_for_ready')),
grpc.ClientCallDetails):
pass
@@ -72,7 +72,12 @@ def _unwrap_client_call_details(call_details, default_details):
except AttributeError:
credentials = default_details.credentials
- return method, timeout, metadata, credentials
+ try:
+ wait_for_ready = call_details.wait_for_ready
+ except AttributeError:
+ wait_for_ready = default_details.wait_for_ready
+
+ return method, timeout, metadata, credentials, wait_for_ready
class _FailureOutcome(grpc.RpcError, grpc.Future, grpc.Call):
@@ -193,28 +198,39 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
self._method = method
self._interceptor = interceptor
- def __call__(self, request, timeout=None, metadata=None, credentials=None):
+ def __call__(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
response, ignored_call = self._with_call(
request,
timeout=timeout,
metadata=metadata,
- credentials=credentials)
+ credentials=credentials,
+ wait_for_ready=wait_for_ready)
return response
- def _with_call(self, request, timeout=None, metadata=None,
- credentials=None):
- client_call_details = _ClientCallDetails(self._method, timeout,
- metadata, credentials)
+ def _with_call(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
+ client_call_details = _ClientCallDetails(
+ self._method, timeout, metadata, credentials, wait_for_ready)
def continuation(new_details, request):
- new_method, new_timeout, new_metadata, new_credentials = (
+ new_method, new_timeout, new_metadata, new_credentials, new_wait_for_ready = (
_unwrap_client_call_details(new_details, client_call_details))
try:
response, call = self._thunk(new_method).with_call(
request,
timeout=new_timeout,
metadata=new_metadata,
- credentials=new_credentials)
+ credentials=new_credentials,
+ wait_for_ready=new_wait_for_ready)
return _UnaryOutcome(response, call)
except grpc.RpcError:
raise
@@ -225,25 +241,37 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
continuation, client_call_details, request)
return call.result(), call
- def with_call(self, request, timeout=None, metadata=None, credentials=None):
+ def with_call(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
return self._with_call(
request,
timeout=timeout,
metadata=metadata,
- credentials=credentials)
+ credentials=credentials,
+ wait_for_ready=wait_for_ready)
- def future(self, request, timeout=None, metadata=None, credentials=None):
- client_call_details = _ClientCallDetails(self._method, timeout,
- metadata, credentials)
+ def future(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
+ client_call_details = _ClientCallDetails(
+ self._method, timeout, metadata, credentials, wait_for_ready)
def continuation(new_details, request):
- new_method, new_timeout, new_metadata, new_credentials = (
+ new_method, new_timeout, new_metadata, new_credentials, new_wait_for_ready = (
_unwrap_client_call_details(new_details, client_call_details))
return self._thunk(new_method).future(
request,
timeout=new_timeout,
metadata=new_metadata,
- credentials=new_credentials)
+ credentials=new_credentials,
+ wait_for_ready=new_wait_for_ready)
try:
return self._interceptor.intercept_unary_unary(
@@ -259,18 +287,24 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
self._method = method
self._interceptor = interceptor
- def __call__(self, request, timeout=None, metadata=None, credentials=None):
- client_call_details = _ClientCallDetails(self._method, timeout,
- metadata, credentials)
+ def __call__(self,
+ request,
+ timeout=None,
+ metadata=None,
+ credentials=None,
+ wait_for_ready=None):
+ client_call_details = _ClientCallDetails(
+ self._method, timeout, metadata, credentials, wait_for_ready)
def continuation(new_details, request):
- new_method, new_timeout, new_metadata, new_credentials = (
+ new_method, new_timeout, new_metadata, new_credentials, new_wait_for_ready = (
_unwrap_client_call_details(new_details, client_call_details))
return self._thunk(new_method)(
request,
timeout=new_timeout,
metadata=new_metadata,
- credentials=new_credentials)
+ credentials=new_credentials,
+ wait_for_ready=new_wait_for_ready)
try:
return self._interceptor.intercept_unary_stream(
@@ -290,31 +324,35 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
+ credentials=None,
+ wait_for_ready=None):
response, ignored_call = self._with_call(
request_iterator,
timeout=timeout,
metadata=metadata,
- credentials=credentials)
+ credentials=credentials,
+ wait_for_ready=wait_for_ready)
return response
def _with_call(self,
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
- client_call_details = _ClientCallDetails(self._method, timeout,
- metadata, credentials)
+ credentials=None,
+ wait_for_ready=None):
+ client_call_details = _ClientCallDetails(
+ self._method, timeout, metadata, credentials, wait_for_ready)
def continuation(new_details, request_iterator):
- new_method, new_timeout, new_metadata, new_credentials = (
+ new_method, new_timeout, new_metadata, new_credentials, new_wait_for_ready = (
_unwrap_client_call_details(new_details, client_call_details))
try:
response, call = self._thunk(new_method).with_call(
request_iterator,
timeout=new_timeout,
metadata=new_metadata,
- credentials=new_credentials)
+ credentials=new_credentials,
+ wait_for_ready=new_wait_for_ready)
return _UnaryOutcome(response, call)
except grpc.RpcError:
raise
@@ -329,29 +367,33 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
+ credentials=None,
+ wait_for_ready=None):
return self._with_call(
request_iterator,
timeout=timeout,
metadata=metadata,
- credentials=credentials)
+ credentials=credentials,
+ wait_for_ready=wait_for_ready)
def future(self,
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
- client_call_details = _ClientCallDetails(self._method, timeout,
- metadata, credentials)
+ credentials=None,
+ wait_for_ready=None):
+ client_call_details = _ClientCallDetails(
+ self._method, timeout, metadata, credentials, wait_for_ready)
def continuation(new_details, request_iterator):
- new_method, new_timeout, new_metadata, new_credentials = (
+ new_method, new_timeout, new_metadata, new_credentials, new_wait_for_ready = (
_unwrap_client_call_details(new_details, client_call_details))
return self._thunk(new_method).future(
request_iterator,
timeout=new_timeout,
metadata=new_metadata,
- credentials=new_credentials)
+ credentials=new_credentials,
+ wait_for_ready=new_wait_for_ready)
try:
return self._interceptor.intercept_stream_unary(
@@ -371,18 +413,20 @@ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
request_iterator,
timeout=None,
metadata=None,
- credentials=None):
- client_call_details = _ClientCallDetails(self._method, timeout,
- metadata, credentials)
+ credentials=None,
+ wait_for_ready=None):
+ client_call_details = _ClientCallDetails(
+ self._method, timeout, metadata, credentials, wait_for_ready)
def continuation(new_details, request_iterator):
- new_method, new_timeout, new_metadata, new_credentials = (
+ new_method, new_timeout, new_metadata, new_credentials, new_wait_for_ready = (
_unwrap_client_call_details(new_details, client_call_details))
return self._thunk(new_method)(
request_iterator,
timeout=new_timeout,
metadata=new_metadata,
- credentials=new_credentials)
+ credentials=new_credentials,
+ wait_for_ready=new_wait_for_ready)
try:
return self._interceptor.intercept_stream_stream(
diff --git a/src/python/grpcio/grpc/_plugin_wrapping.py b/src/python/grpcio/grpc/_plugin_wrapping.py
index 88ab4d8371..916ee080b6 100644
--- a/src/python/grpcio/grpc/_plugin_wrapping.py
+++ b/src/python/grpcio/grpc/_plugin_wrapping.py
@@ -20,7 +20,6 @@ import grpc
from grpc import _common
from grpc._cython import cygrpc
-logging.basicConfig()
_LOGGER = logging.getLogger(__name__)
diff --git a/src/python/grpcio/grpc/_server.py b/src/python/grpcio/grpc/_server.py
index daa000a6e1..7276a7fd90 100644
--- a/src/python/grpcio/grpc/_server.py
+++ b/src/python/grpcio/grpc/_server.py
@@ -27,7 +27,6 @@ from grpc import _interceptor
from grpc._cython import cygrpc
from grpc.framework.foundation import callable_util
-logging.basicConfig()
_LOGGER = logging.getLogger(__name__)
_SHUTDOWN_TAG = 'shutdown'
diff --git a/src/python/grpcio/grpc/framework/foundation/callable_util.py b/src/python/grpcio/grpc/framework/foundation/callable_util.py
index fb8d5f7c1e..24daf3406f 100644
--- a/src/python/grpcio/grpc/framework/foundation/callable_util.py
+++ b/src/python/grpcio/grpc/framework/foundation/callable_util.py
@@ -21,7 +21,6 @@ import logging
import six
-logging.basicConfig()
_LOGGER = logging.getLogger(__name__)
diff --git a/src/python/grpcio/grpc/framework/foundation/logging_pool.py b/src/python/grpcio/grpc/framework/foundation/logging_pool.py
index 7702d1785f..216e3990db 100644
--- a/src/python/grpcio/grpc/framework/foundation/logging_pool.py
+++ b/src/python/grpcio/grpc/framework/foundation/logging_pool.py
@@ -17,7 +17,6 @@ import logging
from concurrent import futures
-logging.basicConfig()
_LOGGER = logging.getLogger(__name__)
diff --git a/src/python/grpcio/grpc/framework/foundation/stream_util.py b/src/python/grpcio/grpc/framework/foundation/stream_util.py
index 9184f95873..1faaf29bd7 100644
--- a/src/python/grpcio/grpc/framework/foundation/stream_util.py
+++ b/src/python/grpcio/grpc/framework/foundation/stream_util.py
@@ -19,7 +19,6 @@ import threading
from grpc.framework.foundation import stream
_NO_VALUE = object()
-logging.basicConfig()
_LOGGER = logging.getLogger(__name__)
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index cb48b9f62c..a82d6e1ec7 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -208,6 +208,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/transport/timeout_encoding.cc',
'src/core/lib/transport/transport.cc',
'src/core/lib/transport/transport_op_string.cc',
+ 'src/core/lib/uri/uri_parser.cc',
'src/core/lib/debug/trace.cc',
'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc',
'src/core/ext/transport/chttp2/transport/bin_decoder.cc',
@@ -280,7 +281,7 @@ CORE_SOURCE_FILES = [
'src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc',
'src/core/tsi/alts/frame_protector/frame_handler.cc',
'src/core/tsi/alts/handshaker/alts_handshaker_client.cc',
- 'src/core/tsi/alts/handshaker/alts_tsi_event.cc',
+ 'src/core/tsi/alts/handshaker/alts_shared_resource.cc',
'src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc',
'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc',
'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc',
@@ -322,19 +323,17 @@ CORE_SOURCE_FILES = [
'src/core/ext/filters/client_channel/lb_policy.cc',
'src/core/ext/filters/client_channel/lb_policy_factory.cc',
'src/core/ext/filters/client_channel/lb_policy_registry.cc',
- 'src/core/ext/filters/client_channel/method_params.cc',
'src/core/ext/filters/client_channel/parse_address.cc',
'src/core/ext/filters/client_channel/proxy_mapper.cc',
'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
'src/core/ext/filters/client_channel/resolver.cc',
'src/core/ext/filters/client_channel/resolver_registry.cc',
+ 'src/core/ext/filters/client_channel/resolver_result_parsing.cc',
'src/core/ext/filters/client_channel/retry_throttle.cc',
'src/core/ext/filters/client_channel/subchannel.cc',
'src/core/ext/filters/client_channel/subchannel_index.cc',
- 'src/core/ext/filters/client_channel/uri_parser.cc',
'src/core/ext/filters/deadline/deadline_filter.cc',
'src/core/ext/filters/client_channel/health/health.pb.c',
- 'src/core/tsi/alts_transport_security.cc',
'src/core/tsi/fake_transport_security.cc',
'src/core/tsi/local_transport_security.cc',
'src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc',
diff --git a/src/python/grpcio_reflection/grpc_reflection/v1alpha/BUILD.bazel b/src/python/grpcio_reflection/grpc_reflection/v1alpha/BUILD.bazel
new file mode 100644
index 0000000000..3a2ba26371
--- /dev/null
+++ b/src/python/grpcio_reflection/grpc_reflection/v1alpha/BUILD.bazel
@@ -0,0 +1,34 @@
+load("@grpc_python_dependencies//:requirements.bzl", "requirement")
+load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_library")
+
+package(default_visibility = ["//visibility:public"])
+
+genrule(
+ name = "mv_reflection_proto",
+ srcs = [
+ "//src/proto/grpc/reflection/v1alpha:reflection_proto_file",
+ ],
+ outs = ["reflection.proto",],
+ cmd = "cp $< $@",
+)
+
+py_proto_library(
+ name = "py_reflection_proto",
+ protos = [":mv_reflection_proto",],
+ with_grpc = True,
+ deps = [
+ requirement('protobuf'),
+ ],
+)
+
+py_library(
+ name = "grpc_reflection",
+ srcs = ["reflection.py",],
+ deps = [
+ ":py_reflection_proto",
+ "//src/python/grpcio/grpc:grpcio",
+ requirement('protobuf'),
+ ],
+ imports=["../../",],
+)
+
diff --git a/src/python/grpcio_tests/commands.py b/src/python/grpcio_tests/commands.py
index 6931d93ef0..d163f6fb68 100644
--- a/src/python/grpcio_tests/commands.py
+++ b/src/python/grpcio_tests/commands.py
@@ -130,7 +130,9 @@ class TestGevent(setuptools.Command):
# Beta API is unsupported for gevent
'protoc_plugin.beta_python_plugin_test',
'unit.beta._beta_features_test',
- )
+ # TODO(https://github.com/grpc/grpc/issues/15411) unpin gevent version
+ # This test will stuck while running higher version of gevent
+ 'unit._auth_context_test.AuthContextTest.testSessionResumption')
description = 'run tests with gevent. Assumes grpc/gevent are installed'
user_options = []
diff --git a/src/python/grpcio_tests/tests/_sanity/_sanity_test.py b/src/python/grpcio_tests/tests/_sanity/_sanity_test.py
index b4079850ff..7da6e7b34c 100644
--- a/src/python/grpcio_tests/tests/_sanity/_sanity_test.py
+++ b/src/python/grpcio_tests/tests/_sanity/_sanity_test.py
@@ -13,9 +13,9 @@
# limitations under the License.
import json
+import pkgutil
import unittest
-import pkg_resources
import six
import tests
@@ -35,7 +35,7 @@ class SanityTest(unittest.TestCase):
loader.suite)
})
- tests_json_string = pkg_resources.resource_string('tests', 'tests.json')
+ tests_json_string = pkgutil.get_data('tests', 'tests.json')
tests_json = json.loads(tests_json_string.decode()
if six.PY3 else tests_json_string)
diff --git a/src/python/grpcio_tests/tests/interop/BUILD.bazel b/src/python/grpcio_tests/tests/interop/BUILD.bazel
new file mode 100644
index 0000000000..aebdbf67eb
--- /dev/null
+++ b/src/python/grpcio_tests/tests/interop/BUILD.bazel
@@ -0,0 +1,101 @@
+load("@grpc_python_dependencies//:requirements.bzl", "requirement")
+
+package(default_visibility = ["//visibility:public"])
+
+py_library(
+ name = "_intraop_test_case",
+ srcs = ["_intraop_test_case.py"],
+ deps = [
+ ":methods",
+ ],
+ imports=["../../",],
+)
+
+py_library(
+ name = "client",
+ srcs = ["client.py"],
+ deps = [
+ "//src/python/grpcio/grpc:grpcio",
+ ":methods",
+ ":resources",
+ "//src/proto/grpc/testing:py_test_proto",
+ requirement('google-auth'),
+ ],
+ imports=["../../",],
+)
+
+py_library(
+ name = "methods",
+ srcs = ["methods.py"],
+ deps = [
+ "//src/python/grpcio/grpc:grpcio",
+ "//src/proto/grpc/testing:py_empty_proto",
+ "//src/proto/grpc/testing:py_messages_proto",
+ "//src/proto/grpc/testing:py_test_proto",
+ requirement('google-auth'),
+ requirement('requests'),
+ requirement('enum34'),
+ requirement('urllib3'),
+ requirement('chardet'),
+ requirement('certifi'),
+ requirement('idna'),
+ ],
+ imports=["../../",],
+)
+
+py_library(
+ name = "resources",
+ srcs = ["resources.py"],
+ data = [
+ "//src/python/grpcio_tests/tests/interop/credentials",
+ ],
+)
+
+py_library(
+ name = "server",
+ srcs = ["server.py"],
+ deps = [
+ "//src/python/grpcio/grpc:grpcio",
+ ":methods",
+ ":resources",
+ "//src/python/grpcio_tests/tests/unit:test_common",
+ "//src/proto/grpc/testing:py_test_proto",
+ ],
+ imports=["../../",],
+)
+
+py_test(
+ name="_insecure_intraop_test",
+ size="small",
+ srcs=["_insecure_intraop_test.py",],
+ main="_insecure_intraop_test.py",
+ deps=[
+ "//src/python/grpcio/grpc:grpcio",
+ ":_intraop_test_case",
+ ":methods",
+ ":server",
+ "//src/python/grpcio_tests/tests/unit:test_common",
+ "//src/proto/grpc/testing:py_test_proto",
+ ],
+ imports=["../../",],
+ data=[
+ "//src/python/grpcio_tests/tests/unit/credentials",
+ ],
+)
+
+py_test(
+ name="_secure_intraop_test",
+ size="small",
+ srcs=["_secure_intraop_test.py",],
+ main="_secure_intraop_test.py",
+ deps=[
+ "//src/python/grpcio/grpc:grpcio",
+ ":_intraop_test_case",
+ ":methods",
+ ":server",
+ "//src/python/grpcio_tests/tests/unit:test_common",
+ "//src/proto/grpc/testing:py_test_proto",
+ ],
+ imports=["../../",],
+)
+
diff --git a/src/python/grpcio_tests/tests/interop/credentials/BUILD.bazel b/src/python/grpcio_tests/tests/interop/credentials/BUILD.bazel
new file mode 100644
index 0000000000..bc2b997292
--- /dev/null
+++ b/src/python/grpcio_tests/tests/interop/credentials/BUILD.bazel
@@ -0,0 +1,9 @@
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+ name="credentials",
+ srcs=glob([
+ "**",
+ ]),
+)
+
diff --git a/src/python/grpcio_tests/tests/interop/methods.py b/src/python/grpcio_tests/tests/interop/methods.py
index cda15a68a3..721dedf0b7 100644
--- a/src/python/grpcio_tests/tests/interop/methods.py
+++ b/src/python/grpcio_tests/tests/interop/methods.py
@@ -457,6 +457,22 @@ def _per_rpc_creds(stub, args):
response.username))
+def _special_status_message(stub, args):
+ details = b'\t\ntest with whitespace\r\nand Unicode BMP \xe2\x98\xba and non-BMP \xf0\x9f\x98\x88\t\n'.decode(
+ 'utf-8')
+ code = 2
+ status = grpc.StatusCode.UNKNOWN # code = 2
+
+ # Test with a UnaryCall
+ request = messages_pb2.SimpleRequest(
+ response_type=messages_pb2.COMPRESSABLE,
+ response_size=1,
+ payload=messages_pb2.Payload(body=b'\x00'),
+ response_status=messages_pb2.EchoStatus(code=code, message=details))
+ response_future = stub.UnaryCall.future(request)
+ _validate_status_code_and_details(response_future, status, details)
+
+
@enum.unique
class TestCase(enum.Enum):
EMPTY_UNARY = 'empty_unary'
@@ -476,6 +492,7 @@ class TestCase(enum.Enum):
JWT_TOKEN_CREDS = 'jwt_token_creds'
PER_RPC_CREDS = 'per_rpc_creds'
TIMEOUT_ON_SLEEPING_SERVER = 'timeout_on_sleeping_server'
+ SPECIAL_STATUS_MESSAGE = 'special_status_message'
def test_interoperability(self, stub, args):
if self is TestCase.EMPTY_UNARY:
@@ -512,6 +529,8 @@ class TestCase(enum.Enum):
_jwt_token_creds(stub, args)
elif self is TestCase.PER_RPC_CREDS:
_per_rpc_creds(stub, args)
+ elif self is TestCase.SPECIAL_STATUS_MESSAGE:
+ _special_status_message(stub, args)
else:
raise NotImplementedError(
'Test case "%s" not implemented!' % self.name)
diff --git a/src/python/grpcio_tests/tests/interop/resources.py b/src/python/grpcio_tests/tests/interop/resources.py
index 2f76cf5db6..a55919a60a 100644
--- a/src/python/grpcio_tests/tests/interop/resources.py
+++ b/src/python/grpcio_tests/tests/interop/resources.py
@@ -14,27 +14,24 @@
"""Constants and functions for data used in interoperability testing."""
import argparse
+import pkgutil
import os
-import pkg_resources
-
_ROOT_CERTIFICATES_RESOURCE_PATH = 'credentials/ca.pem'
_PRIVATE_KEY_RESOURCE_PATH = 'credentials/server1.key'
_CERTIFICATE_CHAIN_RESOURCE_PATH = 'credentials/server1.pem'
def test_root_certificates():
- return pkg_resources.resource_string(__name__,
- _ROOT_CERTIFICATES_RESOURCE_PATH)
+ return pkgutil.get_data(__name__, _ROOT_CERTIFICATES_RESOURCE_PATH)
def private_key():
- return pkg_resources.resource_string(__name__, _PRIVATE_KEY_RESOURCE_PATH)
+ return pkgutil.get_data(__name__, _PRIVATE_KEY_RESOURCE_PATH)
def certificate_chain():
- return pkg_resources.resource_string(__name__,
- _CERTIFICATE_CHAIN_RESOURCE_PATH)
+ return pkgutil.get_data(__name__, _CERTIFICATE_CHAIN_RESOURCE_PATH)
def parse_bool(value):
diff --git a/src/python/grpcio_tests/tests/interop/server.py b/src/python/grpcio_tests/tests/interop/server.py
index 768cdaf468..72f88a1c98 100644
--- a/src/python/grpcio_tests/tests/interop/server.py
+++ b/src/python/grpcio_tests/tests/interop/server.py
@@ -25,8 +25,8 @@ from tests.interop import methods
from tests.interop import resources
from tests.unit import test_common
-logging.basicConfig()
_ONE_DAY_IN_SECONDS = 60 * 60 * 24
+logging.basicConfig()
_LOGGER = logging.getLogger(__name__)
diff --git a/src/python/grpcio_tests/tests/reflection/BUILD.bazel b/src/python/grpcio_tests/tests/reflection/BUILD.bazel
new file mode 100644
index 0000000000..c0efb0b7ce
--- /dev/null
+++ b/src/python/grpcio_tests/tests/reflection/BUILD.bazel
@@ -0,0 +1,21 @@
+load("@grpc_python_dependencies//:requirements.bzl", "requirement")
+
+package(default_visibility = ["//visibility:public"])
+
+py_test(
+ name="_reflection_servicer_test",
+ size="small",
+ timeout="moderate",
+ srcs=["_reflection_servicer_test.py",],
+ main="_reflection_servicer_test.py",
+ deps=[
+ "//src/python/grpcio/grpc:grpcio",
+ "//src/python/grpcio_reflection/grpc_reflection/v1alpha:grpc_reflection",
+ "//src/python/grpcio_tests/tests/unit:test_common",
+ "//src/proto/grpc/testing:py_empty_proto",
+ "//src/proto/grpc/testing/proto2:empty2_extensions_proto",
+ requirement('protobuf'),
+ ],
+ imports=["../../",],
+)
+
diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json
index 76d5d22d57..a3006d9afc 100644
--- a/src/python/grpcio_tests/tests/tests.json
+++ b/src/python/grpcio_tests/tests/tests.json
@@ -42,11 +42,14 @@
"unit._cython.cygrpc_test.SecureServerSecureClient",
"unit._cython.cygrpc_test.TypeSmokeTest",
"unit._empty_message_test.EmptyMessageTest",
+ "unit._error_message_encoding_test.ErrorMessageEncodingTest",
"unit._exit_test.ExitTest",
"unit._interceptor_test.InterceptorTest",
"unit._invalid_metadata_test.InvalidMetadataTest",
"unit._invocation_defects_test.InvocationDefectsTest",
+ "unit._logging_test.LoggingTest",
"unit._metadata_code_details_test.MetadataCodeDetailsTest",
+ "unit._metadata_flags_test.MetadataFlagsTest",
"unit._metadata_test.MetadataTest",
"unit._reconnect_test.ReconnectTest",
"unit._resource_exhausted_test.ResourceExhaustedTest",
diff --git a/src/python/grpcio_tests/tests/unit/BUILD.bazel b/src/python/grpcio_tests/tests/unit/BUILD.bazel
index dcd6d9fbb2..de33b81e32 100644
--- a/src/python/grpcio_tests/tests/unit/BUILD.bazel
+++ b/src/python/grpcio_tests/tests/unit/BUILD.bazel
@@ -17,6 +17,7 @@ GRPCIO_TESTS_UNIT = [
"_interceptor_test.py",
"_invalid_metadata_test.py",
"_invocation_defects_test.py",
+ "_logging_test.py",
"_metadata_code_details_test.py",
"_metadata_test.py",
# TODO: Issue 16336
diff --git a/src/python/grpcio_tests/tests/unit/_api_test.py b/src/python/grpcio_tests/tests/unit/_api_test.py
index f6245be77d..38072861a4 100644
--- a/src/python/grpcio_tests/tests/unit/_api_test.py
+++ b/src/python/grpcio_tests/tests/unit/_api_test.py
@@ -14,6 +14,7 @@
"""Test of gRPC Python's application-layer API."""
import unittest
+import logging
import six
@@ -102,4 +103,5 @@ class ChannelTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_auth_context_test.py b/src/python/grpcio_tests/tests/unit/_auth_context_test.py
index d174051070..b1b5bbdcab 100644
--- a/src/python/grpcio_tests/tests/unit/_auth_context_test.py
+++ b/src/python/grpcio_tests/tests/unit/_auth_context_test.py
@@ -15,6 +15,7 @@
import pickle
import unittest
+import logging
import grpc
from grpc import _channel
@@ -187,4 +188,5 @@ class AuthContextTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_auth_test.py b/src/python/grpcio_tests/tests/unit/_auth_test.py
index e2cb938936..d9df2add4f 100644
--- a/src/python/grpcio_tests/tests/unit/_auth_test.py
+++ b/src/python/grpcio_tests/tests/unit/_auth_test.py
@@ -16,6 +16,7 @@
import collections
import threading
import unittest
+import logging
from grpc import _auth
@@ -77,4 +78,5 @@ class AccessTokenAuthMetadataPluginTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_channel_args_test.py b/src/python/grpcio_tests/tests/unit/_channel_args_test.py
index dd1d2969a2..228c0e4c16 100644
--- a/src/python/grpcio_tests/tests/unit/_channel_args_test.py
+++ b/src/python/grpcio_tests/tests/unit/_channel_args_test.py
@@ -15,6 +15,7 @@
from concurrent import futures
import unittest
+import logging
import grpc
@@ -62,4 +63,5 @@ class ChannelArgsTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_channel_close_test.py b/src/python/grpcio_tests/tests/unit/_channel_close_test.py
index af3a9ee1ee..82fa165710 100644
--- a/src/python/grpcio_tests/tests/unit/_channel_close_test.py
+++ b/src/python/grpcio_tests/tests/unit/_channel_close_test.py
@@ -13,6 +13,7 @@
# limitations under the License.
"""Tests server and client side compression."""
+import logging
import threading
import time
import unittest
@@ -182,4 +183,5 @@ class ChannelCloseTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py b/src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py
index f9eb0011dc..727fb7d65f 100644
--- a/src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py
+++ b/src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py
@@ -13,6 +13,7 @@
# limitations under the License.
"""Tests of grpc._channel.Channel connectivity."""
+import logging
import threading
import time
import unittest
@@ -142,4 +143,5 @@ class ChannelConnectivityTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
index 30b486079c..345460ef40 100644
--- a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
+++ b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
@@ -15,6 +15,7 @@
import threading
import unittest
+import logging
import grpc
from tests.unit.framework.common import test_constants
@@ -85,4 +86,5 @@ class ChannelReadyFutureTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_compression_test.py b/src/python/grpcio_tests/tests/unit/_compression_test.py
index 0b11f03adf..876d8e827e 100644
--- a/src/python/grpcio_tests/tests/unit/_compression_test.py
+++ b/src/python/grpcio_tests/tests/unit/_compression_test.py
@@ -15,6 +15,7 @@
import unittest
+import logging
import grpc
from grpc import _grpcio_metadata
@@ -117,4 +118,5 @@ class CompressionTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_credentials_test.py b/src/python/grpcio_tests/tests/unit/_credentials_test.py
index f487fe66a2..be7378ecbc 100644
--- a/src/python/grpcio_tests/tests/unit/_credentials_test.py
+++ b/src/python/grpcio_tests/tests/unit/_credentials_test.py
@@ -14,6 +14,7 @@
"""Tests of credentials."""
import unittest
+import logging
import grpc
@@ -54,4 +55,5 @@ class CredentialsTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_empty_message_test.py b/src/python/grpcio_tests/tests/unit/_empty_message_test.py
index c55ef61c13..3e8393b53c 100644
--- a/src/python/grpcio_tests/tests/unit/_empty_message_test.py
+++ b/src/python/grpcio_tests/tests/unit/_empty_message_test.py
@@ -13,6 +13,7 @@
# limitations under the License.
import unittest
+import logging
import grpc
@@ -118,4 +119,5 @@ class EmptyMessageTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_error_message_encoding_test.py b/src/python/grpcio_tests/tests/unit/_error_message_encoding_test.py
new file mode 100644
index 0000000000..6c551df3ec
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_error_message_encoding_test.py
@@ -0,0 +1,86 @@
+# 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.
+"""Tests 'utf-8' encoded error message."""
+
+import unittest
+import weakref
+
+import grpc
+
+from tests.unit import test_common
+from tests.unit.framework.common import test_constants
+
+_UNICODE_ERROR_MESSAGES = [
+ b'\xe2\x80\x9d'.decode('utf-8'),
+ b'abc\x80\xd0\xaf'.decode('latin-1'),
+ b'\xc3\xa9'.decode('utf-8'),
+]
+
+_REQUEST = b'\x00\x00\x00'
+_RESPONSE = b'\x00\x00\x00'
+
+_UNARY_UNARY = '/test/UnaryUnary'
+
+
+class _MethodHandler(grpc.RpcMethodHandler):
+
+ def __init__(self, request_streaming=None, response_streaming=None):
+ self.request_streaming = request_streaming
+ self.response_streaming = response_streaming
+ self.request_deserializer = None
+ self.response_serializer = None
+ self.unary_stream = None
+ self.stream_unary = None
+ self.stream_stream = None
+
+ def unary_unary(self, request, servicer_context):
+ servicer_context.set_code(grpc.StatusCode.UNKNOWN)
+ servicer_context.set_details(request.decode('utf-8'))
+ return _RESPONSE
+
+
+class _GenericHandler(grpc.GenericRpcHandler):
+
+ def __init__(self, test):
+ self._test = test
+
+ def service(self, handler_call_details):
+ return _MethodHandler()
+
+
+class ErrorMessageEncodingTest(unittest.TestCase):
+
+ def setUp(self):
+ self._server = test_common.test_server()
+ self._server.add_generic_rpc_handlers((_GenericHandler(
+ weakref.proxy(self)),))
+ port = self._server.add_insecure_port('[::]:0')
+ self._server.start()
+ self._channel = grpc.insecure_channel('localhost:%d' % port)
+
+ def tearDown(self):
+ self._server.stop(0)
+
+ def testMessageEncoding(self):
+ for message in _UNICODE_ERROR_MESSAGES:
+ multi_callable = self._channel.unary_unary(_UNARY_UNARY)
+ with self.assertRaises(grpc.RpcError) as cm:
+ multi_callable(message.encode('utf-8'))
+
+ self.assertEqual(cm.exception.code(), grpc.StatusCode.UNKNOWN)
+ self.assertEqual(cm.exception.details(), message)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_exit_scenarios.py b/src/python/grpcio_tests/tests/unit/_exit_scenarios.py
index 0a0239a63d..f489db12cb 100644
--- a/src/python/grpcio_tests/tests/unit/_exit_scenarios.py
+++ b/src/python/grpcio_tests/tests/unit/_exit_scenarios.py
@@ -16,6 +16,7 @@
import argparse
import threading
import time
+import logging
import grpc
@@ -161,6 +162,7 @@ def infinite_request_iterator():
if __name__ == '__main__':
+ logging.basicConfig()
parser = argparse.ArgumentParser()
parser.add_argument('scenario', type=str)
parser.add_argument(
diff --git a/src/python/grpcio_tests/tests/unit/_exit_test.py b/src/python/grpcio_tests/tests/unit/_exit_test.py
index f40f3ae07c..5226537579 100644
--- a/src/python/grpcio_tests/tests/unit/_exit_test.py
+++ b/src/python/grpcio_tests/tests/unit/_exit_test.py
@@ -26,6 +26,7 @@ import sys
import threading
import time
import unittest
+import logging
from tests.unit import _exit_scenarios
@@ -187,4 +188,5 @@ class ExitTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_interceptor_test.py b/src/python/grpcio_tests/tests/unit/_interceptor_test.py
index 3d547b71cd..99db0ac58b 100644
--- a/src/python/grpcio_tests/tests/unit/_interceptor_test.py
+++ b/src/python/grpcio_tests/tests/unit/_interceptor_test.py
@@ -17,6 +17,7 @@ import collections
import itertools
import threading
import unittest
+import logging
from concurrent import futures
import grpc
@@ -598,4 +599,5 @@ class InterceptorTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py b/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py
index f153089a24..9771764635 100644
--- a/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py
+++ b/src/python/grpcio_tests/tests/unit/_invalid_metadata_test.py
@@ -14,6 +14,7 @@
"""Test of RPCs made against gRPC Python's application-layer API."""
import unittest
+import logging
import grpc
@@ -131,4 +132,5 @@ class InvalidMetadataTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py b/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py
index 93a5fdf9ff..00949e2236 100644
--- a/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py
+++ b/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py
@@ -15,6 +15,7 @@
import itertools
import threading
import unittest
+import logging
import grpc
@@ -271,4 +272,5 @@ class InvocationDefectsTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_logging_test.py b/src/python/grpcio_tests/tests/unit/_logging_test.py
new file mode 100644
index 0000000000..631b9de9db
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_logging_test.py
@@ -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.
+"""Test of gRPC Python's interaction with the python logging module"""
+
+import unittest
+import six
+from six.moves import reload_module
+import logging
+import grpc
+import functools
+import sys
+
+
+def patch_stderr(f):
+
+ @functools.wraps(f)
+ def _impl(*args, **kwargs):
+ old_stderr = sys.stderr
+ sys.stderr = six.StringIO()
+ try:
+ f(*args, **kwargs)
+ finally:
+ sys.stderr = old_stderr
+
+ return _impl
+
+
+def isolated_logging(f):
+
+ @functools.wraps(f)
+ def _impl(*args, **kwargs):
+ reload_module(logging)
+ reload_module(grpc)
+ try:
+ f(*args, **kwargs)
+ finally:
+ reload_module(logging)
+
+ return _impl
+
+
+class LoggingTest(unittest.TestCase):
+
+ @isolated_logging
+ def test_logger_not_occupied(self):
+ self.assertEqual(0, len(logging.getLogger().handlers))
+
+ @patch_stderr
+ @isolated_logging
+ def test_handler_found(self):
+ self.assertEqual(0, len(sys.stderr.getvalue()))
+
+ @isolated_logging
+ def test_can_configure_logger(self):
+ intended_stream = six.StringIO()
+ logging.basicConfig(stream=intended_stream)
+ self.assertEqual(1, len(logging.getLogger().handlers))
+ self.assertIs(logging.getLogger().handlers[0].stream, intended_stream)
+
+ @isolated_logging
+ def test_grpc_logger(self):
+ self.assertIn("grpc", logging.Logger.manager.loggerDict)
+ root_logger = logging.getLogger("grpc")
+ self.assertEqual(1, len(root_logger.handlers))
+ self.assertIsInstance(root_logger.handlers[0], logging.NullHandler)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py b/src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py
index ca10bd4dab..0dafab827a 100644
--- a/src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py
+++ b/src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py
@@ -15,6 +15,7 @@
import threading
import unittest
+import logging
import grpc
@@ -656,4 +657,5 @@ class MetadataCodeDetailsTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_metadata_flags_test.py b/src/python/grpcio_tests/tests/unit/_metadata_flags_test.py
new file mode 100644
index 0000000000..2d352e99d4
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_metadata_flags_test.py
@@ -0,0 +1,251 @@
+# 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.
+"""Tests metadata flags feature by testing wait-for-ready semantics"""
+
+import time
+import weakref
+import unittest
+import threading
+import socket
+from six.moves import queue
+
+import grpc
+
+from tests.unit import test_common
+from tests.unit.framework.common import test_constants
+
+_UNARY_UNARY = '/test/UnaryUnary'
+_UNARY_STREAM = '/test/UnaryStream'
+_STREAM_UNARY = '/test/StreamUnary'
+_STREAM_STREAM = '/test/StreamStream'
+
+_REQUEST = b'\x00\x00\x00'
+_RESPONSE = b'\x00\x00\x00'
+
+
+def handle_unary_unary(test, request, servicer_context):
+ return _RESPONSE
+
+
+def handle_unary_stream(test, request, servicer_context):
+ for _ in range(test_constants.STREAM_LENGTH):
+ yield _RESPONSE
+
+
+def handle_stream_unary(test, request_iterator, servicer_context):
+ for _ in request_iterator:
+ pass
+ return _RESPONSE
+
+
+def handle_stream_stream(test, request_iterator, servicer_context):
+ for _ in request_iterator:
+ yield _RESPONSE
+
+
+class _MethodHandler(grpc.RpcMethodHandler):
+
+ def __init__(self, test, request_streaming, response_streaming):
+ self.request_streaming = request_streaming
+ self.response_streaming = response_streaming
+ self.request_deserializer = None
+ self.response_serializer = None
+ self.unary_unary = None
+ self.unary_stream = None
+ self.stream_unary = None
+ self.stream_stream = None
+ if self.request_streaming and self.response_streaming:
+ self.stream_stream = lambda req, ctx: handle_stream_stream(test, req, ctx)
+ elif self.request_streaming:
+ self.stream_unary = lambda req, ctx: handle_stream_unary(test, req, ctx)
+ elif self.response_streaming:
+ self.unary_stream = lambda req, ctx: handle_unary_stream(test, req, ctx)
+ else:
+ self.unary_unary = lambda req, ctx: handle_unary_unary(test, req, ctx)
+
+
+class _GenericHandler(grpc.GenericRpcHandler):
+
+ def __init__(self, test):
+ self._test = test
+
+ def service(self, handler_call_details):
+ if handler_call_details.method == _UNARY_UNARY:
+ return _MethodHandler(self._test, False, False)
+ elif handler_call_details.method == _UNARY_STREAM:
+ return _MethodHandler(self._test, False, True)
+ elif handler_call_details.method == _STREAM_UNARY:
+ return _MethodHandler(self._test, True, False)
+ elif handler_call_details.method == _STREAM_STREAM:
+ return _MethodHandler(self._test, True, True)
+ else:
+ return None
+
+
+def get_free_loopback_tcp_port():
+ tcp = socket.socket(socket.AF_INET6)
+ tcp.bind(('', 0))
+ address_tuple = tcp.getsockname()
+ return tcp, "[::1]:%s" % (address_tuple[1])
+
+
+def create_dummy_channel():
+ """Creating dummy channels is a workaround for retries"""
+ _, addr = get_free_loopback_tcp_port()
+ return grpc.insecure_channel(addr)
+
+
+def perform_unary_unary_call(channel, wait_for_ready=None):
+ channel.unary_unary(_UNARY_UNARY).__call__(
+ _REQUEST,
+ timeout=test_constants.LONG_TIMEOUT,
+ wait_for_ready=wait_for_ready)
+
+
+def perform_unary_unary_with_call(channel, wait_for_ready=None):
+ channel.unary_unary(_UNARY_UNARY).with_call(
+ _REQUEST,
+ timeout=test_constants.LONG_TIMEOUT,
+ wait_for_ready=wait_for_ready)
+
+
+def perform_unary_unary_future(channel, wait_for_ready=None):
+ channel.unary_unary(_UNARY_UNARY).future(
+ _REQUEST,
+ timeout=test_constants.LONG_TIMEOUT,
+ wait_for_ready=wait_for_ready).result(
+ timeout=test_constants.LONG_TIMEOUT)
+
+
+def perform_unary_stream_call(channel, wait_for_ready=None):
+ response_iterator = channel.unary_stream(_UNARY_STREAM).__call__(
+ _REQUEST,
+ timeout=test_constants.LONG_TIMEOUT,
+ wait_for_ready=wait_for_ready)
+ for _ in response_iterator:
+ pass
+
+
+def perform_stream_unary_call(channel, wait_for_ready=None):
+ channel.stream_unary(_STREAM_UNARY).__call__(
+ iter([_REQUEST] * test_constants.STREAM_LENGTH),
+ timeout=test_constants.LONG_TIMEOUT,
+ wait_for_ready=wait_for_ready)
+
+
+def perform_stream_unary_with_call(channel, wait_for_ready=None):
+ channel.stream_unary(_STREAM_UNARY).with_call(
+ iter([_REQUEST] * test_constants.STREAM_LENGTH),
+ timeout=test_constants.LONG_TIMEOUT,
+ wait_for_ready=wait_for_ready)
+
+
+def perform_stream_unary_future(channel, wait_for_ready=None):
+ channel.stream_unary(_STREAM_UNARY).future(
+ iter([_REQUEST] * test_constants.STREAM_LENGTH),
+ timeout=test_constants.LONG_TIMEOUT,
+ wait_for_ready=wait_for_ready).result(
+ timeout=test_constants.LONG_TIMEOUT)
+
+
+def perform_stream_stream_call(channel, wait_for_ready=None):
+ response_iterator = channel.stream_stream(_STREAM_STREAM).__call__(
+ iter([_REQUEST] * test_constants.STREAM_LENGTH),
+ timeout=test_constants.LONG_TIMEOUT,
+ wait_for_ready=wait_for_ready)
+ for _ in response_iterator:
+ pass
+
+
+_ALL_CALL_CASES = [
+ perform_unary_unary_call, perform_unary_unary_with_call,
+ perform_unary_unary_future, perform_unary_stream_call,
+ perform_stream_unary_call, perform_stream_unary_with_call,
+ perform_stream_unary_future, perform_stream_stream_call
+]
+
+
+class MetadataFlagsTest(unittest.TestCase):
+
+ def check_connection_does_failfast(self, fn, channel, wait_for_ready=None):
+ try:
+ fn(channel, wait_for_ready)
+ self.fail("The Call should fail")
+ except BaseException as e: # pylint: disable=broad-except
+ self.assertIn('StatusCode.UNAVAILABLE', str(e))
+
+ def test_call_wait_for_ready_default(self):
+ for perform_call in _ALL_CALL_CASES:
+ self.check_connection_does_failfast(perform_call,
+ create_dummy_channel())
+
+ def test_call_wait_for_ready_disabled(self):
+ for perform_call in _ALL_CALL_CASES:
+ self.check_connection_does_failfast(
+ perform_call, create_dummy_channel(), wait_for_ready=False)
+
+ def test_call_wait_for_ready_enabled(self):
+ # To test the wait mechanism, Python thread is required to make
+ # client set up first without handling them case by case.
+ # Also, Python thread don't pass the unhandled exceptions to
+ # main thread. So, it need another method to store the
+ # exceptions and raise them again in main thread.
+ unhandled_exceptions = queue.Queue()
+ tcp, addr = get_free_loopback_tcp_port()
+ wg = test_common.WaitGroup(len(_ALL_CALL_CASES))
+
+ def wait_for_transient_failure(channel_connectivity):
+ if channel_connectivity == grpc.ChannelConnectivity.TRANSIENT_FAILURE:
+ wg.done()
+
+ def test_call(perform_call):
+ try:
+ channel = grpc.insecure_channel(addr)
+ channel.subscribe(wait_for_transient_failure)
+ perform_call(channel, wait_for_ready=True)
+ except BaseException as e: # pylint: disable=broad-except
+ # If the call failed, the thread would be destroyed. The channel
+ # object can be collected before calling the callback, which
+ # will result in a deadlock.
+ wg.done()
+ unhandled_exceptions.put(e, True)
+
+ test_threads = []
+ for perform_call in _ALL_CALL_CASES:
+ test_thread = threading.Thread(
+ target=test_call, args=(perform_call,))
+ test_thread.exception = None
+ test_thread.start()
+ test_threads.append(test_thread)
+
+ # Start the server after the connections are waiting
+ wg.wait()
+ tcp.close()
+ server = test_common.test_server()
+ server.add_generic_rpc_handlers((_GenericHandler(weakref.proxy(self)),))
+ server.add_insecure_port(addr)
+ server.start()
+
+ for test_thread in test_threads:
+ test_thread.join()
+
+ # Stop the server to make test end properly
+ server.stop(0)
+
+ if not unhandled_exceptions.empty():
+ raise unhandled_exceptions.get(True)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_metadata_test.py b/src/python/grpcio_tests/tests/unit/_metadata_test.py
index 5908421011..777ab683e3 100644
--- a/src/python/grpcio_tests/tests/unit/_metadata_test.py
+++ b/src/python/grpcio_tests/tests/unit/_metadata_test.py
@@ -15,6 +15,7 @@
import unittest
import weakref
+import logging
import grpc
from grpc import _channel
@@ -237,4 +238,5 @@ class MetadataTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_reconnect_test.py b/src/python/grpcio_tests/tests/unit/_reconnect_test.py
index a708d8d862..f6d4fcbd0a 100644
--- a/src/python/grpcio_tests/tests/unit/_reconnect_test.py
+++ b/src/python/grpcio_tests/tests/unit/_reconnect_test.py
@@ -15,6 +15,7 @@
import socket
import time
+import logging
import unittest
import grpc
@@ -100,4 +101,5 @@ class ReconnectTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_resource_exhausted_test.py b/src/python/grpcio_tests/tests/unit/_resource_exhausted_test.py
index df4b129018..4fead8fcd5 100644
--- a/src/python/grpcio_tests/tests/unit/_resource_exhausted_test.py
+++ b/src/python/grpcio_tests/tests/unit/_resource_exhausted_test.py
@@ -15,6 +15,7 @@
import threading
import unittest
+import logging
import grpc
from grpc import _channel
@@ -253,4 +254,5 @@ class ResourceExhaustedTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_rpc_test.py b/src/python/grpcio_tests/tests/unit/_rpc_test.py
index 34e7831a98..a768d6c7c1 100644
--- a/src/python/grpcio_tests/tests/unit/_rpc_test.py
+++ b/src/python/grpcio_tests/tests/unit/_rpc_test.py
@@ -16,6 +16,7 @@
import itertools
import threading
import unittest
+import logging
from concurrent import futures
import grpc
@@ -846,4 +847,5 @@ class RPCTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_server_ssl_cert_config_test.py b/src/python/grpcio_tests/tests/unit/_server_ssl_cert_config_test.py
index 0d78034b7b..e733a59a5b 100644
--- a/src/python/grpcio_tests/tests/unit/_server_ssl_cert_config_test.py
+++ b/src/python/grpcio_tests/tests/unit/_server_ssl_cert_config_test.py
@@ -35,6 +35,7 @@ import os
import six
import threading
import unittest
+import logging
from concurrent import futures
@@ -518,4 +519,5 @@ class ServerSSLCertReloadTestCertConfigReuse(_ServerSSLCertReloadTest):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_server_test.py b/src/python/grpcio_tests/tests/unit/_server_test.py
index acf4a17921..2c8205f365 100644
--- a/src/python/grpcio_tests/tests/unit/_server_test.py
+++ b/src/python/grpcio_tests/tests/unit/_server_test.py
@@ -14,6 +14,7 @@
from concurrent import futures
import unittest
+import logging
import grpc
@@ -49,4 +50,5 @@ class ServerTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_session_cache_test.py b/src/python/grpcio_tests/tests/unit/_session_cache_test.py
index b4e4670fa7..9223a6da03 100644
--- a/src/python/grpcio_tests/tests/unit/_session_cache_test.py
+++ b/src/python/grpcio_tests/tests/unit/_session_cache_test.py
@@ -15,6 +15,7 @@
import pickle
import unittest
+import logging
import grpc
from grpc import _channel
@@ -142,4 +143,5 @@ class SSLSessionCacheTest(unittest.TestCase):
if __name__ == '__main__':
+ logging.basicConfig()
unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/resources.py b/src/python/grpcio_tests/tests/unit/resources.py
index 51a8979f58..6efd870fc8 100644
--- a/src/python/grpcio_tests/tests/unit/resources.py
+++ b/src/python/grpcio_tests/tests/unit/resources.py
@@ -14,8 +14,7 @@
"""Constants and functions for data used in testing."""
import os
-
-import pkg_resources
+import pkgutil
_ROOT_CERTIFICATES_RESOURCE_PATH = 'credentials/ca.pem'
_PRIVATE_KEY_RESOURCE_PATH = 'credentials/server1.key'
@@ -23,94 +22,92 @@ _CERTIFICATE_CHAIN_RESOURCE_PATH = 'credentials/server1.pem'
def test_root_certificates():
- return pkg_resources.resource_string(__name__,
- _ROOT_CERTIFICATES_RESOURCE_PATH)
+ return pkgutil.get_data(__name__, _ROOT_CERTIFICATES_RESOURCE_PATH)
def private_key():
- return pkg_resources.resource_string(__name__, _PRIVATE_KEY_RESOURCE_PATH)
+ return pkgutil.get_data(__name__, _PRIVATE_KEY_RESOURCE_PATH)
def certificate_chain():
- return pkg_resources.resource_string(__name__,
- _CERTIFICATE_CHAIN_RESOURCE_PATH)
+ return pkgutil.get_data(__name__, _CERTIFICATE_CHAIN_RESOURCE_PATH)
def cert_hier_1_root_ca_cert():
- return pkg_resources.resource_string(
+ return pkgutil.get_data(
__name__, 'credentials/certificate_hierarchy_1/certs/ca.cert.pem')
def cert_hier_1_intermediate_ca_cert():
- return pkg_resources.resource_string(
+ return pkgutil.get_data(
__name__,
'credentials/certificate_hierarchy_1/intermediate/certs/intermediate.cert.pem'
)
def cert_hier_1_client_1_key():
- return pkg_resources.resource_string(
+ return pkgutil.get_data(
__name__,
'credentials/certificate_hierarchy_1/intermediate/private/client.key.pem'
)
def cert_hier_1_client_1_cert():
- return pkg_resources.resource_string(
+ return pkgutil.get_data(
__name__,
'credentials/certificate_hierarchy_1/intermediate/certs/client.cert.pem'
)
def cert_hier_1_server_1_key():
- return pkg_resources.resource_string(
+ return pkgutil.get_data(
__name__,
'credentials/certificate_hierarchy_1/intermediate/private/localhost-1.key.pem'
)
def cert_hier_1_server_1_cert():
- return pkg_resources.resource_string(
+ return pkgutil.get_data(
__name__,
'credentials/certificate_hierarchy_1/intermediate/certs/localhost-1.cert.pem'
)
def cert_hier_2_root_ca_cert():
- return pkg_resources.resource_string(
+ return pkgutil.get_data(
__name__, 'credentials/certificate_hierarchy_2/certs/ca.cert.pem')
def cert_hier_2_intermediate_ca_cert():
- return pkg_resources.resource_string(
+ return pkgutil.get_data(
__name__,
'credentials/certificate_hierarchy_2/intermediate/certs/intermediate.cert.pem'
)
def cert_hier_2_client_1_key():
- return pkg_resources.resource_string(
+ return pkgutil.get_data(
__name__,
'credentials/certificate_hierarchy_2/intermediate/private/client.key.pem'
)
def cert_hier_2_client_1_cert():
- return pkg_resources.resource_string(
+ return pkgutil.get_data(
__name__,
'credentials/certificate_hierarchy_2/intermediate/certs/client.cert.pem'
)
def cert_hier_2_server_1_key():
- return pkg_resources.resource_string(
+ return pkgutil.get_data(
__name__,
'credentials/certificate_hierarchy_2/intermediate/private/localhost-1.key.pem'
)
def cert_hier_2_server_1_cert():
- return pkg_resources.resource_string(
+ return pkgutil.get_data(
__name__,
'credentials/certificate_hierarchy_2/intermediate/certs/localhost-1.cert.pem'
)
diff --git a/src/python/grpcio_tests/tests/unit/test_common.py b/src/python/grpcio_tests/tests/unit/test_common.py
index 61717ae135..bc3b24862d 100644
--- a/src/python/grpcio_tests/tests/unit/test_common.py
+++ b/src/python/grpcio_tests/tests/unit/test_common.py
@@ -14,6 +14,7 @@
"""Common code used throughout tests of gRPC."""
import collections
+import threading
from concurrent import futures
import grpc
@@ -107,3 +108,28 @@ def test_server(max_workers=10):
return grpc.server(
futures.ThreadPoolExecutor(max_workers=max_workers),
options=(('grpc.so_reuseport', 0),))
+
+
+class WaitGroup(object):
+
+ def __init__(self, n=0):
+ self.count = n
+ self.cv = threading.Condition()
+
+ def add(self, n):
+ self.cv.acquire()
+ self.count += n
+ self.cv.release()
+
+ def done(self):
+ self.cv.acquire()
+ self.count -= 1
+ if self.count == 0:
+ self.cv.notify_all()
+ self.cv.release()
+
+ def wait(self):
+ self.cv.acquire()
+ while self.count > 0:
+ self.cv.wait()
+ self.cv.release()
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/config.m4.template b/templates/config.m4.template
index 19f9904911..18378cfc34 100644
--- a/templates/config.m4.template
+++ b/templates/config.m4.template
@@ -45,7 +45,8 @@
% endfor
, $ext_shared, , -fvisibility=hidden ${"\\"}
-DOPENSSL_NO_ASM -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN ${"\\"}
- -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0)
+ -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0 ${"\\"}
+ -DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1)
PHP_ADD_BUILD_DIR($ext_builddir/src/php/ext/grpc)
<%
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/client_channel/uri_fuzzer_test.cc b/test/core/client_channel/uri_fuzzer_test.cc
index ee38453166..a88e2ac5cf 100644
--- a/test/core/client_channel/uri_fuzzer_test.cc
+++ b/test/core/client_channel/uri_fuzzer_test.cc
@@ -23,8 +23,8 @@
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
-#include "src/core/ext/filters/client_channel/uri_parser.h"
#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/uri/uri_parser.h"
bool squelch = true;
bool leak_check = true;
diff --git a/test/core/client_channel/uri_parser_test.cc b/test/core/client_channel/uri_parser_test.cc
index 254bfddfb3..ec4f755dda 100644
--- a/test/core/client_channel/uri_parser_test.cc
+++ b/test/core/client_channel/uri_parser_test.cc
@@ -16,7 +16,7 @@
*
*/
-#include "src/core/ext/filters/client_channel/uri_parser.h"
+#include "src/core/lib/uri/uri_parser.h"
#include <string.h>
diff --git a/test/core/debug/BUILD b/test/core/debug/BUILD
new file mode 100644
index 0000000000..1592472532
--- /dev/null
+++ b/test/core/debug/BUILD
@@ -0,0 +1,34 @@
+# 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.
+
+load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_cc_binary", "grpc_package")
+
+grpc_package(name = "test/core/debug")
+
+licenses(["notice"]) # Apache v2
+
+grpc_cc_test(
+ name = "stats_test",
+ srcs = ["stats_test.cc"],
+ external_deps = [
+ "gtest",
+ ],
+ language = "C++",
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ ],
+)
diff --git a/test/core/end2end/tests/channelz.cc b/test/core/end2end/tests/channelz.cc
index 7c61b7910b..922783aa0d 100644
--- a/test/core/end2end/tests/channelz.cc
+++ b/test/core/end2end/tests/channelz.cc
@@ -29,6 +29,7 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
+#include "src/core/lib/channel/channelz_registry.h"
#include "src/core/lib/gpr/string.h"
#include "test/core/end2end/cq_verifier.h"
@@ -238,7 +239,6 @@ static void test_channelz(grpc_end2end_test_config config) {
json = channelz_channel->RenderJsonString();
GPR_ASSERT(json != nullptr);
- gpr_log(GPR_INFO, "%s", json);
GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"2\""));
GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"1\""));
GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"1\""));
@@ -250,7 +250,6 @@ static void test_channelz(grpc_end2end_test_config config) {
json = channelz_server->RenderJsonString();
GPR_ASSERT(json != nullptr);
- gpr_log(GPR_INFO, "%s", json);
GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"2\""));
GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"1\""));
GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"1\""));
@@ -292,7 +291,6 @@ static void test_channelz_with_channel_trace(grpc_end2end_test_config config) {
char* json = channelz_channel->RenderJsonString();
GPR_ASSERT(json != nullptr);
- gpr_log(GPR_INFO, "%s", json);
GPR_ASSERT(nullptr != strstr(json, "\"trace\""));
GPR_ASSERT(nullptr != strstr(json, "\"description\":\"Channel created\""));
GPR_ASSERT(nullptr != strstr(json, "\"severity\":\"CT_INFO\""));
@@ -300,7 +298,6 @@ static void test_channelz_with_channel_trace(grpc_end2end_test_config config) {
json = channelz_server->RenderJsonString();
GPR_ASSERT(json != nullptr);
- gpr_log(GPR_INFO, "%s", json);
GPR_ASSERT(nullptr != strstr(json, "\"trace\""));
GPR_ASSERT(nullptr != strstr(json, "\"description\":\"Server created\""));
GPR_ASSERT(nullptr != strstr(json, "\"severity\":\"CT_INFO\""));
diff --git a/test/core/end2end/tests/streaming_error_response.cc b/test/core/end2end/tests/streaming_error_response.cc
index 4c357e077e..713975a33f 100644
--- a/test/core/end2end/tests/streaming_error_response.cc
+++ b/test/core/end2end/tests/streaming_error_response.cc
@@ -89,7 +89,8 @@ static void end_test(grpc_end2end_test_fixture* f) {
}
/* Client sends a request with payload, server reads then returns status. */
-static void test(grpc_end2end_test_config config, bool request_status_early) {
+static void test(grpc_end2end_test_config config, bool request_status_early,
+ bool recv_message_separately) {
grpc_call* c;
grpc_call* s;
grpc_slice response_payload1_slice = grpc_slice_from_copied_string("hello");
@@ -116,6 +117,7 @@ static void test(grpc_end2end_test_config config, bool request_status_early) {
int was_cancelled = 2;
gpr_timespec deadline = five_seconds_from_now();
+ GPR_ASSERT(!recv_message_separately || request_status_early);
c = grpc_channel_create_call(f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
grpc_slice_from_static_string("/foo"), nullptr,
deadline, nullptr);
@@ -136,9 +138,11 @@ static void test(grpc_end2end_test_config config, bool request_status_early) {
op->op = GRPC_OP_RECV_INITIAL_METADATA;
op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
op++;
- op->op = GRPC_OP_RECV_MESSAGE;
- op->data.recv_message.recv_message = &response_payload1_recv;
- op++;
+ if (!recv_message_separately) {
+ op->op = GRPC_OP_RECV_MESSAGE;
+ op->data.recv_message.recv_message = &response_payload1_recv;
+ op++;
+ }
if (request_status_early) {
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
@@ -168,10 +172,24 @@ static void test(grpc_end2end_test_config config, bool request_status_early) {
nullptr);
GPR_ASSERT(GRPC_CALL_OK == error);
+ if (recv_message_separately) {
+ memset(ops, 0, sizeof(ops));
+ op = ops;
+ op->op = GRPC_OP_RECV_MESSAGE;
+ op->data.recv_message.recv_message = &response_payload1_recv;
+ op++;
+ error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops), tag(4),
+ nullptr);
+ GPR_ASSERT(GRPC_CALL_OK == error);
+ }
+
CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
if (!request_status_early) {
CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
}
+ if (recv_message_separately) {
+ CQ_EXPECT_COMPLETION(cqv, tag(4), 1);
+ }
cq_verify(cqv);
memset(ops, 0, sizeof(ops));
@@ -265,8 +283,9 @@ static void test(grpc_end2end_test_config config, bool request_status_early) {
}
void streaming_error_response(grpc_end2end_test_config config) {
- test(config, false);
- test(config, true);
+ test(config, false, false);
+ test(config, true, false);
+ test(config, true, true);
}
void streaming_error_response_pre_init(void) {}
diff --git a/test/core/gprpp/ref_counted_test.cc b/test/core/gprpp/ref_counted_test.cc
index f85a2e4675..62a3ea4d53 100644
--- a/test/core/gprpp/ref_counted_test.cc
+++ b/test/core/gprpp/ref_counted_test.cc
@@ -29,7 +29,10 @@ namespace {
class Foo : public RefCounted<Foo> {
public:
- Foo() {}
+ Foo() {
+ static_assert(std::has_virtual_destructor<Foo>::value,
+ "PolymorphicRefCount doesn't have a virtual dtor");
+ }
};
TEST(RefCounted, Basic) {
@@ -45,6 +48,28 @@ TEST(RefCounted, ExtraRef) {
foo->Unref();
}
+class FooNonPolymorphic
+ : public RefCounted<FooNonPolymorphic, NonPolymorphicRefCount> {
+ public:
+ FooNonPolymorphic() {
+ static_assert(!std::has_virtual_destructor<FooNonPolymorphic>::value,
+ "NonPolymorphicRefCount has a virtual dtor");
+ }
+};
+
+TEST(RefCountedNonPolymorphic, Basic) {
+ FooNonPolymorphic* foo = New<FooNonPolymorphic>();
+ foo->Unref();
+}
+
+TEST(RefCountedNonPolymorphic, ExtraRef) {
+ FooNonPolymorphic* foo = New<FooNonPolymorphic>();
+ RefCountedPtr<FooNonPolymorphic> foop = foo->Ref();
+ foop.release();
+ foo->Unref();
+ foo->Unref();
+}
+
// Note: We use DebugOnlyTraceFlag instead of TraceFlag to ensure that
// things build properly in both debug and non-debug cases.
DebugOnlyTraceFlag foo_tracer(true, "foo");
@@ -66,6 +91,26 @@ TEST(RefCountedWithTracing, Basic) {
foo->Unref(DEBUG_LOCATION, "original_ref");
}
+class FooNonPolymorphicWithTracing
+ : public RefCountedWithTracing<FooNonPolymorphicWithTracing,
+ NonPolymorphicRefCount> {
+ public:
+ FooNonPolymorphicWithTracing() : RefCountedWithTracing(&foo_tracer) {}
+};
+
+TEST(RefCountedNonPolymorphicWithTracing, Basic) {
+ FooNonPolymorphicWithTracing* foo = New<FooNonPolymorphicWithTracing>();
+ RefCountedPtr<FooNonPolymorphicWithTracing> foop =
+ foo->Ref(DEBUG_LOCATION, "extra_ref");
+ foop.release();
+ foo->Unref(DEBUG_LOCATION, "extra_ref");
+ // Can use the no-argument methods, too.
+ foop = foo->Ref();
+ foop.release();
+ foo->Unref();
+ foo->Unref(DEBUG_LOCATION, "original_ref");
+}
+
} // namespace
} // namespace testing
} // namespace grpc_core
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/tsi/alts/handshaker/alts_handshaker_client_test.cc b/test/core/tsi/alts/handshaker/alts_handshaker_client_test.cc
index c8d88aa72c..9a4541bb7d 100644
--- a/test/core/tsi/alts/handshaker/alts_handshaker_client_test.cc
+++ b/test/core/tsi/alts/handshaker/alts_handshaker_client_test.cc
@@ -19,14 +19,14 @@
#include <grpc/grpc.h>
#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
-#include "src/core/tsi/alts/handshaker/alts_tsi_event.h"
+#include "src/core/tsi/alts/handshaker/alts_shared_resource.h"
#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h"
#include "src/core/tsi/transport_security.h"
#include "src/core/tsi/transport_security_interface.h"
#include "test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h"
#define ALTS_HANDSHAKER_CLIENT_TEST_OUT_FRAME "Hello Google"
-#define ALTS_HANDSHAKER_CLIENT_TEST_HANDSHAKER_SERVICE_URL "lame"
#define ALTS_HANDSHAKER_CLIENT_TEST_TARGET_NAME "bigtable.google.api.com"
#define ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT1 "A@google.com"
#define ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT2 "B@google.com"
@@ -37,37 +37,22 @@ const size_t kMaxRpcVersionMinor = 2;
const size_t kMinRpcVersionMajor = 2;
const size_t kMinRpcVersionMinor = 1;
+using grpc_core::internal::alts_handshaker_client_get_closure_for_testing;
+using grpc_core::internal::
+ alts_handshaker_client_get_initial_metadata_for_testing;
+using grpc_core::internal::
+ alts_handshaker_client_get_recv_buffer_addr_for_testing;
+using grpc_core::internal::alts_handshaker_client_get_send_buffer_for_testing;
using grpc_core::internal::alts_handshaker_client_set_grpc_caller_for_testing;
typedef struct alts_handshaker_client_test_config {
grpc_channel* channel;
grpc_completion_queue* cq;
alts_handshaker_client* client;
+ alts_handshaker_client* server;
grpc_slice out_frame;
} alts_handshaker_client_test_config;
-static alts_tsi_event* alts_tsi_event_create_for_testing(bool is_client) {
- alts_tsi_event* e = static_cast<alts_tsi_event*>(gpr_zalloc(sizeof(*e)));
- grpc_metadata_array_init(&e->initial_metadata);
- grpc_metadata_array_init(&e->trailing_metadata);
- e->options = is_client ? grpc_alts_credentials_client_options_create()
- : grpc_alts_credentials_server_options_create();
- if (is_client) {
- grpc_alts_credentials_client_options_add_target_service_account(
- e->options, ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT1);
- grpc_alts_credentials_client_options_add_target_service_account(
- e->options, ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT2);
- }
- grpc_gcp_rpc_protocol_versions* versions = &e->options->rpc_versions;
- GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_max(
- versions, kMaxRpcVersionMajor, kMaxRpcVersionMinor));
- GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_min(
- versions, kMinRpcVersionMajor, kMinRpcVersionMinor));
- e->target_name =
- grpc_slice_from_static_string(ALTS_HANDSHAKER_CLIENT_TEST_TARGET_NAME);
- return e;
-}
-
static void validate_rpc_protocol_versions(
grpc_gcp_rpc_protocol_versions* versions) {
GPR_ASSERT(versions != nullptr);
@@ -101,11 +86,11 @@ static void validate_target_identities(
/**
* Validate if grpc operation data is correctly populated with the fields of
- * ALTS TSI event.
+ * ALTS handshaker client.
*/
-static bool validate_op(alts_tsi_event* event, const grpc_op* op, size_t nops,
- bool is_start) {
- GPR_ASSERT(event != nullptr && op != nullptr && nops != 0);
+static bool validate_op(alts_handshaker_client* c, const grpc_op* op,
+ size_t nops, bool is_start) {
+ GPR_ASSERT(c != nullptr && op != nullptr && nops != 0);
bool ok = true;
grpc_op* start_op = const_cast<grpc_op*>(op);
if (is_start) {
@@ -113,23 +98,22 @@ static bool validate_op(alts_tsi_event* event, const grpc_op* op, size_t nops,
ok &= (op->data.send_initial_metadata.count == 0);
op++;
GPR_ASSERT((size_t)(op - start_op) <= kHandshakerClientOpNum);
-
ok &= (op->op == GRPC_OP_RECV_INITIAL_METADATA);
ok &= (op->data.recv_initial_metadata.recv_initial_metadata ==
- &event->initial_metadata);
+ alts_handshaker_client_get_initial_metadata_for_testing(c));
op++;
GPR_ASSERT((size_t)(op - start_op) <= kHandshakerClientOpNum);
}
ok &= (op->op == GRPC_OP_SEND_MESSAGE);
- ok &= (op->data.send_message.send_message == event->send_buffer);
+ ok &= (op->data.send_message.send_message ==
+ alts_handshaker_client_get_send_buffer_for_testing(c));
op++;
GPR_ASSERT((size_t)(op - start_op) <= kHandshakerClientOpNum);
-
ok &= (op->op == GRPC_OP_RECV_MESSAGE);
- ok &= (op->data.recv_message.recv_message == &event->recv_buffer);
+ ok &= (op->data.recv_message.recv_message ==
+ alts_handshaker_client_get_recv_buffer_addr_for_testing(c));
op++;
GPR_ASSERT((size_t)(op - start_op) <= kHandshakerClientOpNum);
-
return ok;
}
@@ -152,7 +136,7 @@ static grpc_gcp_handshaker_req* deserialize_handshaker_req(
*/
static grpc_call_error check_must_not_be_called(grpc_call* call,
const grpc_op* ops, size_t nops,
- void* tag) {
+ grpc_closure* tag) {
GPR_ASSERT(0);
}
@@ -164,10 +148,14 @@ static grpc_call_error check_must_not_be_called(grpc_call* call,
*/
static grpc_call_error check_client_start_success(grpc_call* call,
const grpc_op* op,
- size_t nops, void* tag) {
- alts_tsi_event* event = static_cast<alts_tsi_event*>(tag);
- grpc_gcp_handshaker_req* req =
- deserialize_handshaker_req(CLIENT_START_REQ, event->send_buffer);
+ size_t nops,
+ grpc_closure* closure) {
+ alts_handshaker_client* client =
+ static_cast<alts_handshaker_client*>(closure->cb_arg);
+ GPR_ASSERT(alts_handshaker_client_get_closure_for_testing(client) == closure);
+ grpc_gcp_handshaker_req* req = deserialize_handshaker_req(
+ CLIENT_START_REQ,
+ alts_handshaker_client_get_send_buffer_for_testing(client));
GPR_ASSERT(req->client_start.handshake_security_protocol ==
grpc_gcp_HandshakeProtocol_ALTS);
const void* data = (static_cast<repeated_field*>(
@@ -194,7 +182,7 @@ static grpc_call_error check_client_start_success(grpc_call* call,
GRPC_SLICE_LENGTH(*target_name)) == 0);
GPR_ASSERT(GRPC_SLICE_LENGTH(*target_name) ==
strlen(ALTS_HANDSHAKER_CLIENT_TEST_TARGET_NAME));
- GPR_ASSERT(validate_op(event, op, nops, true /* is_start */));
+ GPR_ASSERT(validate_op(client, op, nops, true /* is_start */));
grpc_gcp_handshaker_req_destroy(req);
return GRPC_CALL_OK;
}
@@ -207,10 +195,14 @@ static grpc_call_error check_client_start_success(grpc_call* call,
*/
static grpc_call_error check_server_start_success(grpc_call* call,
const grpc_op* op,
- size_t nops, void* tag) {
- alts_tsi_event* event = static_cast<alts_tsi_event*>(tag);
- grpc_gcp_handshaker_req* req =
- deserialize_handshaker_req(SERVER_START_REQ, event->send_buffer);
+ size_t nops,
+ grpc_closure* closure) {
+ alts_handshaker_client* client =
+ static_cast<alts_handshaker_client*>(closure->cb_arg);
+ GPR_ASSERT(alts_handshaker_client_get_closure_for_testing(client) == closure);
+ grpc_gcp_handshaker_req* req = deserialize_handshaker_req(
+ SERVER_START_REQ,
+ alts_handshaker_client_get_send_buffer_for_testing(client));
const void* data = (static_cast<repeated_field*>(
req->server_start.application_protocols.arg))
->data;
@@ -231,7 +223,7 @@ static grpc_call_error check_server_start_success(grpc_call* call,
ALTS_RECORD_PROTOCOL,
GRPC_SLICE_LENGTH(*record_protocol)) == 0);
validate_rpc_protocol_versions(&req->server_start.rpc_versions);
- GPR_ASSERT(validate_op(event, op, nops, true /* is_start */));
+ GPR_ASSERT(validate_op(client, op, nops, true /* is_start */));
grpc_gcp_handshaker_req_destroy(req);
return GRPC_CALL_OK;
}
@@ -242,16 +234,18 @@ static grpc_call_error check_server_start_success(grpc_call* call,
* and op is correctly populated.
*/
static grpc_call_error check_next_success(grpc_call* call, const grpc_op* op,
- size_t nops, void* tag) {
- alts_tsi_event* event = static_cast<alts_tsi_event*>(tag);
- grpc_gcp_handshaker_req* req =
- deserialize_handshaker_req(NEXT_REQ, event->send_buffer);
+ size_t nops, grpc_closure* closure) {
+ alts_handshaker_client* client =
+ static_cast<alts_handshaker_client*>(closure->cb_arg);
+ GPR_ASSERT(alts_handshaker_client_get_closure_for_testing(client) == closure);
+ grpc_gcp_handshaker_req* req = deserialize_handshaker_req(
+ NEXT_REQ, alts_handshaker_client_get_send_buffer_for_testing(client));
grpc_slice* in_bytes = static_cast<grpc_slice*>(req->next.in_bytes.arg);
GPR_ASSERT(in_bytes != nullptr);
GPR_ASSERT(memcmp(GRPC_SLICE_START_PTR(*in_bytes),
ALTS_HANDSHAKER_CLIENT_TEST_OUT_FRAME,
GRPC_SLICE_LENGTH(*in_bytes)) == 0);
- GPR_ASSERT(validate_op(event, op, nops, false /* is_start */));
+ GPR_ASSERT(validate_op(client, op, nops, false /* is_start */));
grpc_gcp_handshaker_req_destroy(req);
return GRPC_CALL_OK;
}
@@ -262,21 +256,54 @@ static grpc_call_error check_next_success(grpc_call* call, const grpc_op* op,
*/
static grpc_call_error check_grpc_call_failure(grpc_call* call,
const grpc_op* op, size_t nops,
- void* tag) {
+ grpc_closure* tag) {
return GRPC_CALL_ERROR;
}
+static grpc_alts_credentials_options* create_credentials_options(
+ bool is_client) {
+ grpc_alts_credentials_options* options =
+ is_client ? grpc_alts_credentials_client_options_create()
+ : grpc_alts_credentials_server_options_create();
+ if (is_client) {
+ grpc_alts_credentials_client_options_add_target_service_account(
+ options, ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT1);
+ grpc_alts_credentials_client_options_add_target_service_account(
+ options, ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT2);
+ }
+ grpc_gcp_rpc_protocol_versions* versions = &options->rpc_versions;
+ GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_max(
+ versions, kMaxRpcVersionMajor, kMaxRpcVersionMinor));
+ GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_min(
+ versions, kMinRpcVersionMajor, kMinRpcVersionMinor));
+ return options;
+}
+
static alts_handshaker_client_test_config* create_config() {
alts_handshaker_client_test_config* config =
static_cast<alts_handshaker_client_test_config*>(
gpr_zalloc(sizeof(*config)));
config->channel = grpc_insecure_channel_create(
- ALTS_HANDSHAKER_CLIENT_TEST_HANDSHAKER_SERVICE_URL, nullptr, nullptr);
+ ALTS_HANDSHAKER_SERVICE_URL_FOR_TESTING, nullptr, nullptr);
config->cq = grpc_completion_queue_create_for_next(nullptr);
+ grpc_alts_credentials_options* client_options =
+ create_credentials_options(true /* is_client */);
+ grpc_alts_credentials_options* server_options =
+ create_credentials_options(false /* is_client */);
+ config->server = alts_grpc_handshaker_client_create(
+ nullptr, config->channel, ALTS_HANDSHAKER_SERVICE_URL_FOR_TESTING,
+ nullptr, server_options,
+ grpc_slice_from_static_string(ALTS_HANDSHAKER_CLIENT_TEST_TARGET_NAME),
+ nullptr, nullptr, nullptr, nullptr, false);
config->client = alts_grpc_handshaker_client_create(
- config->channel, config->cq,
- ALTS_HANDSHAKER_CLIENT_TEST_HANDSHAKER_SERVICE_URL);
+ nullptr, config->channel, ALTS_HANDSHAKER_SERVICE_URL_FOR_TESTING,
+ nullptr, client_options,
+ grpc_slice_from_static_string(ALTS_HANDSHAKER_CLIENT_TEST_TARGET_NAME),
+ nullptr, nullptr, nullptr, nullptr, true);
GPR_ASSERT(config->client != nullptr);
+ GPR_ASSERT(config->server != nullptr);
+ grpc_alts_credentials_options_destroy(client_options);
+ grpc_alts_credentials_options_destroy(server_options);
config->out_frame =
grpc_slice_from_static_string(ALTS_HANDSHAKER_CLIENT_TEST_OUT_FRAME);
return config;
@@ -289,6 +316,7 @@ static void destroy_config(alts_handshaker_client_test_config* config) {
grpc_completion_queue_destroy(config->cq);
grpc_channel_destroy(config->channel);
alts_handshaker_client_destroy(config->client);
+ alts_handshaker_client_destroy(config->server);
grpc_slice_unref(config->out_frame);
gpr_free(config);
}
@@ -296,73 +324,50 @@ static void destroy_config(alts_handshaker_client_test_config* config) {
static void schedule_request_invalid_arg_test() {
/* Initialization. */
alts_handshaker_client_test_config* config = create_config();
- alts_tsi_event* event = nullptr;
-
/* Tests. */
alts_handshaker_client_set_grpc_caller_for_testing(config->client,
check_must_not_be_called);
- event = alts_tsi_event_create_for_testing(true /* is_client */);
/* Check client_start. */
- GPR_ASSERT(alts_handshaker_client_start_client(nullptr, event) ==
- TSI_INVALID_ARGUMENT);
- GPR_ASSERT(alts_handshaker_client_start_client(config->client, nullptr) ==
+ GPR_ASSERT(alts_handshaker_client_start_client(nullptr) ==
TSI_INVALID_ARGUMENT);
-
/* Check server_start. */
- GPR_ASSERT(alts_handshaker_client_start_server(
- config->client, event, nullptr) == TSI_INVALID_ARGUMENT);
- GPR_ASSERT(alts_handshaker_client_start_server(config->client, nullptr,
- &config->out_frame) ==
+ GPR_ASSERT(alts_handshaker_client_start_server(config->server, nullptr) ==
TSI_INVALID_ARGUMENT);
- GPR_ASSERT(alts_handshaker_client_start_server(
- nullptr, event, &config->out_frame) == TSI_INVALID_ARGUMENT);
-
- /* Check next. */
- GPR_ASSERT(alts_handshaker_client_next(config->client, event, nullptr) ==
+ GPR_ASSERT(alts_handshaker_client_start_server(nullptr, &config->out_frame) ==
TSI_INVALID_ARGUMENT);
- GPR_ASSERT(alts_handshaker_client_next(config->client, nullptr,
- &config->out_frame) ==
+ /* Check next. */
+ GPR_ASSERT(alts_handshaker_client_next(config->client, nullptr) ==
TSI_INVALID_ARGUMENT);
- GPR_ASSERT(alts_handshaker_client_next(nullptr, event, &config->out_frame) ==
+ GPR_ASSERT(alts_handshaker_client_next(nullptr, &config->out_frame) ==
TSI_INVALID_ARGUMENT);
-
/* Check shutdown. */
alts_handshaker_client_shutdown(nullptr);
-
/* Cleanup. */
- alts_tsi_event_destroy(event);
destroy_config(config);
}
static void schedule_request_success_test() {
/* Initialization. */
alts_handshaker_client_test_config* config = create_config();
- alts_tsi_event* event = nullptr;
-
/* Check client_start success. */
alts_handshaker_client_set_grpc_caller_for_testing(
config->client, check_client_start_success);
- event = alts_tsi_event_create_for_testing(true /* is_client. */);
- GPR_ASSERT(alts_handshaker_client_start_client(config->client, event) ==
- TSI_OK);
- alts_tsi_event_destroy(event);
-
+ GPR_ASSERT(alts_handshaker_client_start_client(config->client) == TSI_OK);
/* Check server_start success. */
alts_handshaker_client_set_grpc_caller_for_testing(
- config->client, check_server_start_success);
- event = alts_tsi_event_create_for_testing(false /* is_client. */);
- GPR_ASSERT(alts_handshaker_client_start_server(config->client, event,
+ config->server, check_server_start_success);
+ GPR_ASSERT(alts_handshaker_client_start_server(config->server,
&config->out_frame) == TSI_OK);
- alts_tsi_event_destroy(event);
-
- /* Check next success. */
+ /* Check client next success. */
alts_handshaker_client_set_grpc_caller_for_testing(config->client,
check_next_success);
- event = alts_tsi_event_create_for_testing(true /* is_client. */);
- GPR_ASSERT(alts_handshaker_client_next(config->client, event,
- &config->out_frame) == TSI_OK);
- alts_tsi_event_destroy(event);
-
+ GPR_ASSERT(alts_handshaker_client_next(config->client, &config->out_frame) ==
+ TSI_OK);
+ /* Check server next success. */
+ alts_handshaker_client_set_grpc_caller_for_testing(config->server,
+ check_next_success);
+ GPR_ASSERT(alts_handshaker_client_next(config->server, &config->out_frame) ==
+ TSI_OK);
/* Cleanup. */
destroy_config(config);
}
@@ -370,30 +375,22 @@ static void schedule_request_success_test() {
static void schedule_request_grpc_call_failure_test() {
/* Initialization. */
alts_handshaker_client_test_config* config = create_config();
- alts_tsi_event* event = nullptr;
-
/* Check client_start failure. */
alts_handshaker_client_set_grpc_caller_for_testing(config->client,
check_grpc_call_failure);
- event = alts_tsi_event_create_for_testing(true /* is_client. */);
- GPR_ASSERT(alts_handshaker_client_start_client(config->client, event) ==
+ GPR_ASSERT(alts_handshaker_client_start_client(config->client) ==
TSI_INTERNAL_ERROR);
- alts_tsi_event_destroy(event);
-
/* Check server_start failure. */
- event = alts_tsi_event_create_for_testing(false /* is_client. */);
- GPR_ASSERT(alts_handshaker_client_start_server(config->client, event,
- &config->out_frame) ==
+ alts_handshaker_client_set_grpc_caller_for_testing(config->server,
+ check_grpc_call_failure);
+ GPR_ASSERT(alts_handshaker_client_start_server(
+ config->server, &config->out_frame) == TSI_INTERNAL_ERROR);
+ /* Check client next failure. */
+ GPR_ASSERT(alts_handshaker_client_next(config->client, &config->out_frame) ==
+ TSI_INTERNAL_ERROR);
+ /* Check server next failure. */
+ GPR_ASSERT(alts_handshaker_client_next(config->server, &config->out_frame) ==
TSI_INTERNAL_ERROR);
- alts_tsi_event_destroy(event);
-
- /* Check next failure. */
- event = alts_tsi_event_create_for_testing(true /* is_cleint. */);
- GPR_ASSERT(
- alts_handshaker_client_next(config->client, event, &config->out_frame) ==
- TSI_INTERNAL_ERROR);
- alts_tsi_event_destroy(event);
-
/* Cleanup. */
destroy_config(config);
}
@@ -401,13 +398,13 @@ static void schedule_request_grpc_call_failure_test() {
int main(int argc, char** argv) {
/* Initialization. */
grpc_init();
-
+ grpc_alts_shared_resource_dedicated_init();
/* Tests. */
schedule_request_invalid_arg_test();
schedule_request_success_test();
schedule_request_grpc_call_failure_test();
-
/* Cleanup. */
+ grpc_alts_shared_resource_dedicated_shutdown();
grpc_shutdown();
return 0;
}
diff --git a/test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc b/test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc
index e9eb7e175f..316ff13816 100644
--- a/test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc
+++ b/test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc
@@ -24,7 +24,7 @@
#include "src/core/lib/gprpp/thd.h"
#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
-#include "src/core/tsi/alts/handshaker/alts_tsi_event.h"
+#include "src/core/tsi/alts/handshaker/alts_shared_resource.h"
#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h"
#include "test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h"
@@ -43,12 +43,18 @@
#define ALTS_TSI_HANDSHAKER_TEST_MIN_RPC_VERSION_MAJOR 2
#define ALTS_TSI_HANDSHAKER_TEST_MIN_RPC_VERSION_MINOR 1
+using grpc_core::internal::alts_handshaker_client_check_fields_for_testing;
+using grpc_core::internal::alts_handshaker_client_get_handshaker_for_testing;
using grpc_core::internal::
- alts_tsi_handshaker_get_has_sent_start_message_for_testing;
+ alts_handshaker_client_get_recv_buffer_addr_for_testing;
+using grpc_core::internal::alts_handshaker_client_set_cb_for_testing;
+using grpc_core::internal::alts_handshaker_client_set_fields_for_testing;
+using grpc_core::internal::alts_handshaker_client_set_recv_bytes_for_testing;
+using grpc_core::internal::alts_handshaker_client_set_vtable_for_testing;
+using grpc_core::internal::alts_tsi_handshaker_get_client_for_testing;
using grpc_core::internal::alts_tsi_handshaker_get_is_client_for_testing;
-using grpc_core::internal::alts_tsi_handshaker_get_recv_bytes_for_testing;
-using grpc_core::internal::alts_tsi_handshaker_set_client_for_testing;
-using grpc_core::internal::alts_tsi_handshaker_set_recv_bytes_for_testing;
+using grpc_core::internal::alts_tsi_handshaker_set_client_vtable_for_testing;
+static bool should_handshaker_client_api_succeed = true;
/* ALTS mock notification. */
typedef struct notification {
@@ -57,12 +63,6 @@ typedef struct notification {
bool notified;
} notification;
-/* ALTS mock handshaker client. */
-typedef struct alts_mock_handshaker_client {
- alts_handshaker_client base;
- bool used_for_success_test;
-} alts_mock_handshaker_client;
-
/* Type of ALTS handshaker response. */
typedef enum {
INVALID,
@@ -73,10 +73,7 @@ typedef enum {
SERVER_NEXT,
} alts_handshaker_response_type;
-static alts_tsi_event* client_start_event;
-static alts_tsi_event* client_next_event;
-static alts_tsi_event* server_start_event;
-static alts_tsi_event* server_next_event;
+static alts_handshaker_client* cb_event = nullptr;
static notification caller_to_tsi_notification;
static notification tsi_to_caller_notification;
@@ -311,89 +308,69 @@ static void on_server_next_success_cb(tsi_result status, void* user_data,
signal(&tsi_to_caller_notification);
}
-static tsi_result mock_client_start(alts_handshaker_client* self,
- alts_tsi_event* event) {
- alts_mock_handshaker_client* client =
- reinterpret_cast<alts_mock_handshaker_client*>(self);
- if (!client->used_for_success_test) {
- alts_tsi_event_destroy(event);
+static tsi_result mock_client_start(alts_handshaker_client* client) {
+ if (!should_handshaker_client_api_succeed) {
return TSI_INTERNAL_ERROR;
}
- GPR_ASSERT(event->cb == on_client_start_success_cb);
- GPR_ASSERT(event->user_data == nullptr);
- GPR_ASSERT(!alts_tsi_handshaker_get_has_sent_start_message_for_testing(
- event->handshaker));
+ alts_handshaker_client_check_fields_for_testing(
+ client, on_client_start_success_cb, nullptr, false, nullptr);
/* Populate handshaker response for client_start request. */
- event->recv_buffer = generate_handshaker_response(CLIENT_START);
- client_start_event = event;
+ grpc_byte_buffer** recv_buffer_ptr =
+ alts_handshaker_client_get_recv_buffer_addr_for_testing(client);
+ *recv_buffer_ptr = generate_handshaker_response(CLIENT_START);
+ cb_event = client;
signal(&caller_to_tsi_notification);
return TSI_OK;
}
static void mock_shutdown(alts_handshaker_client* self) {}
-static tsi_result mock_server_start(alts_handshaker_client* self,
- alts_tsi_event* event,
+static tsi_result mock_server_start(alts_handshaker_client* client,
grpc_slice* bytes_received) {
- alts_mock_handshaker_client* client =
- reinterpret_cast<alts_mock_handshaker_client*>(self);
- if (!client->used_for_success_test) {
- alts_tsi_event_destroy(event);
+ if (!should_handshaker_client_api_succeed) {
return TSI_INTERNAL_ERROR;
}
- GPR_ASSERT(event->cb == on_server_start_success_cb);
- GPR_ASSERT(event->user_data == nullptr);
+ alts_handshaker_client_check_fields_for_testing(
+ client, on_server_start_success_cb, nullptr, false, nullptr);
grpc_slice slice = grpc_empty_slice();
GPR_ASSERT(grpc_slice_cmp(*bytes_received, slice) == 0);
- GPR_ASSERT(!alts_tsi_handshaker_get_has_sent_start_message_for_testing(
- event->handshaker));
/* Populate handshaker response for server_start request. */
- event->recv_buffer = generate_handshaker_response(SERVER_START);
- server_start_event = event;
+ grpc_byte_buffer** recv_buffer_ptr =
+ alts_handshaker_client_get_recv_buffer_addr_for_testing(client);
+ *recv_buffer_ptr = generate_handshaker_response(SERVER_START);
+ cb_event = client;
grpc_slice_unref(slice);
signal(&caller_to_tsi_notification);
return TSI_OK;
}
-static tsi_result mock_next(alts_handshaker_client* self, alts_tsi_event* event,
+static tsi_result mock_next(alts_handshaker_client* client,
grpc_slice* bytes_received) {
- alts_mock_handshaker_client* client =
- reinterpret_cast<alts_mock_handshaker_client*>(self);
- if (!client->used_for_success_test) {
- alts_tsi_event_destroy(event);
+ if (!should_handshaker_client_api_succeed) {
return TSI_INTERNAL_ERROR;
}
- bool is_client =
- alts_tsi_handshaker_get_is_client_for_testing(event->handshaker);
- if (is_client) {
- GPR_ASSERT(event->cb == on_client_next_success_cb);
- } else {
- GPR_ASSERT(event->cb == on_server_next_success_cb);
- }
- GPR_ASSERT(event->user_data == nullptr);
+ alts_tsi_handshaker* handshaker =
+ alts_handshaker_client_get_handshaker_for_testing(client);
+ bool is_client = alts_tsi_handshaker_get_is_client_for_testing(handshaker);
+ tsi_handshaker_on_next_done_cb cb =
+ is_client ? on_client_next_success_cb : on_server_next_success_cb;
+ alts_handshaker_client_set_cb_for_testing(client, cb);
+ alts_handshaker_client_set_recv_bytes_for_testing(client, bytes_received);
+ alts_handshaker_client_check_fields_for_testing(client, cb, nullptr, true,
+ bytes_received);
GPR_ASSERT(bytes_received != nullptr);
GPR_ASSERT(memcmp(GRPC_SLICE_START_PTR(*bytes_received),
ALTS_TSI_HANDSHAKER_TEST_RECV_BYTES,
GRPC_SLICE_LENGTH(*bytes_received)) == 0);
- GPR_ASSERT(grpc_slice_cmp(alts_tsi_handshaker_get_recv_bytes_for_testing(
- event->handshaker),
- *bytes_received) == 0);
- GPR_ASSERT(alts_tsi_handshaker_get_has_sent_start_message_for_testing(
- event->handshaker));
/* Populate handshaker response for next request. */
grpc_slice out_frame =
grpc_slice_from_static_string(ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME);
- if (is_client) {
- event->recv_buffer = generate_handshaker_response(CLIENT_NEXT);
- } else {
- event->recv_buffer = generate_handshaker_response(SERVER_NEXT);
- }
- alts_tsi_handshaker_set_recv_bytes_for_testing(event->handshaker, &out_frame);
- if (is_client) {
- client_next_event = event;
- } else {
- server_next_event = event;
- }
+ grpc_byte_buffer** recv_buffer_ptr =
+ alts_handshaker_client_get_recv_buffer_addr_for_testing(client);
+ *recv_buffer_ptr = is_client ? generate_handshaker_response(CLIENT_NEXT)
+ : generate_handshaker_response(SERVER_NEXT);
+ alts_handshaker_client_set_recv_bytes_for_testing(client, &out_frame);
+ cb_event = client;
signal(&caller_to_tsi_notification);
grpc_slice_unref(out_frame);
return TSI_OK;
@@ -401,38 +378,27 @@ static tsi_result mock_next(alts_handshaker_client* self, alts_tsi_event* event,
static void mock_destruct(alts_handshaker_client* client) {}
-static const alts_handshaker_client_vtable vtable = {
- mock_client_start, mock_server_start, mock_next, mock_shutdown,
- mock_destruct};
-
-static alts_handshaker_client* alts_mock_handshaker_client_create(
- bool used_for_success_test) {
- alts_mock_handshaker_client* client =
- static_cast<alts_mock_handshaker_client*>(gpr_zalloc(sizeof(*client)));
- client->base.vtable = &vtable;
- client->used_for_success_test = used_for_success_test;
- return &client->base;
-}
+static alts_handshaker_client_vtable vtable = {mock_client_start,
+ mock_server_start, mock_next,
+ mock_shutdown, mock_destruct};
-static tsi_handshaker* create_test_handshaker(bool used_for_success_test,
- bool is_client) {
+static tsi_handshaker* create_test_handshaker(bool is_client) {
tsi_handshaker* handshaker = nullptr;
- alts_handshaker_client* client =
- alts_mock_handshaker_client_create(used_for_success_test);
grpc_alts_credentials_options* options =
grpc_alts_credentials_client_options_create();
- alts_tsi_handshaker_create(options, "target_name", "lame", is_client, nullptr,
- &handshaker);
+ alts_tsi_handshaker_create(options, "target_name",
+ ALTS_HANDSHAKER_SERVICE_URL_FOR_TESTING, is_client,
+ nullptr, &handshaker);
alts_tsi_handshaker* alts_handshaker =
reinterpret_cast<alts_tsi_handshaker*>(handshaker);
- alts_tsi_handshaker_set_client_for_testing(alts_handshaker, client);
+ alts_tsi_handshaker_set_client_vtable_for_testing(alts_handshaker, &vtable);
grpc_alts_credentials_options_destroy(options);
return handshaker;
}
static void check_handshaker_next_invalid_input() {
/* Initialization. */
- tsi_handshaker* handshaker = create_test_handshaker(true, true);
+ tsi_handshaker* handshaker = create_test_handshaker(true);
/* Check nullptr handshaker. */
GPR_ASSERT(tsi_handshaker_next(nullptr, nullptr, 0, nullptr, nullptr, nullptr,
check_must_not_be_called,
@@ -447,8 +413,7 @@ static void check_handshaker_next_invalid_input() {
static void check_handshaker_shutdown_invalid_input() {
/* Initialization. */
- tsi_handshaker* handshaker = create_test_handshaker(
- false /* used_for_success_test */, true /* is_client */);
+ tsi_handshaker* handshaker = create_test_handshaker(true /* is_client */);
/* Check nullptr handshaker. */
tsi_handshaker_shutdown(nullptr);
/* Cleanup. */
@@ -460,10 +425,10 @@ static void check_handshaker_next_success() {
* Create handshakers for which internal mock client is going to do
* correctness check.
*/
- tsi_handshaker* client_handshaker = create_test_handshaker(
- true /* used_for_success_test */, true /* is_client */);
- tsi_handshaker* server_handshaker = create_test_handshaker(
- true /* used_for_success_test */, false /* is_client */);
+ tsi_handshaker* client_handshaker =
+ create_test_handshaker(true /* is_client */);
+ tsi_handshaker* server_handshaker =
+ create_test_handshaker(false /* is_client */);
/* Client start. */
GPR_ASSERT(tsi_handshaker_next(client_handshaker, nullptr, 0, nullptr,
nullptr, nullptr, on_client_start_success_cb,
@@ -494,9 +459,7 @@ static void check_handshaker_next_success() {
}
static void check_handshaker_next_with_shutdown() {
- /* Initialization. */
- tsi_handshaker* handshaker = create_test_handshaker(
- true /* used_for_success_test */, true /* is_client*/);
+ tsi_handshaker* handshaker = create_test_handshaker(true /* is_client*/);
/* next(success) -- shutdown(success) -- next (fail) */
GPR_ASSERT(tsi_handshaker_next(handshaker, nullptr, 0, nullptr, nullptr,
nullptr, on_client_start_success_cb,
@@ -514,20 +477,18 @@ static void check_handshaker_next_with_shutdown() {
}
static void check_handle_response_with_shutdown(void* unused) {
- /* Client start. */
wait(&caller_to_tsi_notification);
- alts_tsi_event_dispatch_to_handshaker(client_start_event, true /* is_ok */);
- alts_tsi_event_destroy(client_start_event);
+ alts_handshaker_client_handle_response(cb_event, true /* is_ok */);
}
static void check_handshaker_next_failure() {
/**
* Create handshakers for which internal mock client is always going to fail.
*/
- tsi_handshaker* client_handshaker = create_test_handshaker(
- false /* used_for_success_test */, true /* is_client */);
- tsi_handshaker* server_handshaker = create_test_handshaker(
- false /* used_for_success_test */, false /* is_client */);
+ tsi_handshaker* client_handshaker =
+ create_test_handshaker(true /* is_client */);
+ tsi_handshaker* server_handshaker =
+ create_test_handshaker(false /* is_client */);
/* Client start. */
GPR_ASSERT(tsi_handshaker_next(client_handshaker, nullptr, 0, nullptr,
nullptr, nullptr, check_must_not_be_called,
@@ -578,34 +539,46 @@ static void on_failed_grpc_call_cb(tsi_result status, void* user_data,
}
static void check_handle_response_invalid_input() {
+ /* Initialization. */
+ notification_init(&caller_to_tsi_notification);
+ notification_init(&tsi_to_caller_notification);
/**
* Create a handshaker at the client side, for which internal mock client is
* always going to fail.
*/
- tsi_handshaker* handshaker = create_test_handshaker(
- false /* used_for_success_test */, true /* is_client */);
+ tsi_handshaker* handshaker = create_test_handshaker(true /* is_client */);
+ tsi_handshaker_next(handshaker, nullptr, 0, nullptr, nullptr, nullptr,
+ on_client_start_success_cb, nullptr);
alts_tsi_handshaker* alts_handshaker =
reinterpret_cast<alts_tsi_handshaker*>(handshaker);
- grpc_byte_buffer recv_buffer;
+ grpc_slice slice = grpc_empty_slice();
+ grpc_byte_buffer* recv_buffer = grpc_raw_byte_buffer_create(&slice, 1);
+ alts_handshaker_client* client =
+ alts_tsi_handshaker_get_client_for_testing(alts_handshaker);
/* Check nullptr handshaker. */
- alts_tsi_handshaker_handle_response(nullptr, &recv_buffer, GRPC_STATUS_OK,
- nullptr, on_invalid_input_cb, nullptr,
- true);
+ alts_handshaker_client_set_fields_for_testing(client, nullptr,
+ on_invalid_input_cb, nullptr,
+ recv_buffer, GRPC_STATUS_OK);
+ alts_handshaker_client_handle_response(client, true);
/* Check nullptr recv_bytes. */
- alts_tsi_handshaker_handle_response(alts_handshaker, nullptr, GRPC_STATUS_OK,
- nullptr, on_invalid_input_cb, nullptr,
- true);
+ alts_handshaker_client_set_fields_for_testing(client, alts_handshaker,
+ on_invalid_input_cb, nullptr,
+ nullptr, GRPC_STATUS_OK);
+ alts_handshaker_client_handle_response(client, true);
/* Check failed grpc call made to handshaker service. */
- alts_tsi_handshaker_handle_response(alts_handshaker, &recv_buffer,
- GRPC_STATUS_UNKNOWN, nullptr,
- on_failed_grpc_call_cb, nullptr, true);
-
- alts_tsi_handshaker_handle_response(alts_handshaker, &recv_buffer,
- GRPC_STATUS_OK, nullptr,
- on_failed_grpc_call_cb, nullptr, false);
-
+ alts_handshaker_client_set_fields_for_testing(
+ client, alts_handshaker, on_failed_grpc_call_cb, nullptr, recv_buffer,
+ GRPC_STATUS_UNKNOWN);
+ alts_handshaker_client_handle_response(client, true);
+ alts_handshaker_client_set_fields_for_testing(client, alts_handshaker,
+ on_failed_grpc_call_cb, nullptr,
+ recv_buffer, GRPC_STATUS_OK);
+ alts_handshaker_client_handle_response(client, false);
/* Cleanup. */
+ grpc_slice_unref(slice);
tsi_handshaker_destroy(handshaker);
+ notification_destroy(&caller_to_tsi_notification);
+ notification_destroy(&tsi_to_caller_notification);
}
static void on_invalid_resp_cb(tsi_result status, void* user_data,
@@ -620,41 +593,45 @@ static void on_invalid_resp_cb(tsi_result status, void* user_data,
}
static void check_handle_response_invalid_resp() {
+ /* Initialization. */
+ notification_init(&caller_to_tsi_notification);
+ notification_init(&tsi_to_caller_notification);
/**
* Create a handshaker at the client side, for which internal mock client is
* always going to fail.
*/
- tsi_handshaker* handshaker = create_test_handshaker(
- false /* used_for_success_test */, true /* is_client */);
+ tsi_handshaker* handshaker = create_test_handshaker(true /* is_client */);
+ tsi_handshaker_next(handshaker, nullptr, 0, nullptr, nullptr, nullptr,
+ on_client_start_success_cb, nullptr);
alts_tsi_handshaker* alts_handshaker =
reinterpret_cast<alts_tsi_handshaker*>(handshaker);
+ alts_handshaker_client* client =
+ alts_tsi_handshaker_get_client_for_testing(alts_handshaker);
/* Tests. */
grpc_byte_buffer* recv_buffer = generate_handshaker_response(INVALID);
- alts_tsi_handshaker_handle_response(alts_handshaker, recv_buffer,
- GRPC_STATUS_OK, nullptr,
- on_invalid_resp_cb, nullptr, true);
+ alts_handshaker_client_set_fields_for_testing(client, alts_handshaker,
+ on_invalid_resp_cb, nullptr,
+ recv_buffer, GRPC_STATUS_OK);
+ alts_handshaker_client_handle_response(client, true);
/* Cleanup. */
- grpc_byte_buffer_destroy(recv_buffer);
tsi_handshaker_destroy(handshaker);
+ notification_destroy(&caller_to_tsi_notification);
+ notification_destroy(&tsi_to_caller_notification);
}
static void check_handle_response_success(void* unused) {
/* Client start. */
wait(&caller_to_tsi_notification);
- alts_tsi_event_dispatch_to_handshaker(client_start_event, true /* is_ok */);
- alts_tsi_event_destroy(client_start_event);
+ alts_handshaker_client_handle_response(cb_event, true /* is_ok */);
/* Client next. */
wait(&caller_to_tsi_notification);
- alts_tsi_event_dispatch_to_handshaker(client_next_event, true /* is_ok */);
- alts_tsi_event_destroy(client_next_event);
+ alts_handshaker_client_handle_response(cb_event, true /* is_ok */);
/* Server start. */
wait(&caller_to_tsi_notification);
- alts_tsi_event_dispatch_to_handshaker(server_start_event, true /* is_ok */);
- alts_tsi_event_destroy(server_start_event);
+ alts_handshaker_client_handle_response(cb_event, true /* is_ok */);
/* Server next. */
wait(&caller_to_tsi_notification);
- alts_tsi_event_dispatch_to_handshaker(server_next_event, true /* is_ok */);
- alts_tsi_event_destroy(server_next_event);
+ alts_handshaker_client_handle_response(cb_event, true /* is_ok */);
}
static void on_failed_resp_cb(tsi_result status, void* user_data,
@@ -669,22 +646,30 @@ static void on_failed_resp_cb(tsi_result status, void* user_data,
}
static void check_handle_response_failure() {
+ /* Initialization. */
+ notification_init(&caller_to_tsi_notification);
+ notification_init(&tsi_to_caller_notification);
/**
* Create a handshaker at the client side, for which internal mock client is
* always going to fail.
*/
- tsi_handshaker* handshaker = create_test_handshaker(
- false /* used_for_success_test */, true /* is_client */);
+ tsi_handshaker* handshaker = create_test_handshaker(true /* is_client */);
+ tsi_handshaker_next(handshaker, nullptr, 0, nullptr, nullptr, nullptr,
+ on_client_start_success_cb, nullptr);
alts_tsi_handshaker* alts_handshaker =
reinterpret_cast<alts_tsi_handshaker*>(handshaker);
+ alts_handshaker_client* client =
+ alts_tsi_handshaker_get_client_for_testing(alts_handshaker);
/* Tests. */
grpc_byte_buffer* recv_buffer = generate_handshaker_response(FAILED);
- alts_tsi_handshaker_handle_response(alts_handshaker, recv_buffer,
- GRPC_STATUS_OK, nullptr,
- on_failed_resp_cb, nullptr, true);
- grpc_byte_buffer_destroy(recv_buffer);
+ alts_handshaker_client_set_fields_for_testing(client, alts_handshaker,
+ on_failed_resp_cb, nullptr,
+ recv_buffer, GRPC_STATUS_OK);
+ alts_handshaker_client_handle_response(client, true /* is_ok*/);
/* Cleanup. */
tsi_handshaker_destroy(handshaker);
+ notification_destroy(&caller_to_tsi_notification);
+ notification_destroy(&tsi_to_caller_notification);
}
static void on_shutdown_resp_cb(tsi_result status, void* user_data,
@@ -699,26 +684,38 @@ static void on_shutdown_resp_cb(tsi_result status, void* user_data,
}
static void check_handle_response_after_shutdown() {
- tsi_handshaker* handshaker = create_test_handshaker(
- true /* used_for_success_test */, true /* is_client */);
+ /* Initialization. */
+ notification_init(&caller_to_tsi_notification);
+ notification_init(&tsi_to_caller_notification);
+ tsi_handshaker* handshaker = create_test_handshaker(true /* is_client */);
+ tsi_handshaker_next(handshaker, nullptr, 0, nullptr, nullptr, nullptr,
+ on_client_start_success_cb, nullptr);
alts_tsi_handshaker* alts_handshaker =
reinterpret_cast<alts_tsi_handshaker*>(handshaker);
+ alts_handshaker_client* client =
+ alts_tsi_handshaker_get_client_for_testing(alts_handshaker);
+ grpc_byte_buffer** recv_buffer_ptr =
+ alts_handshaker_client_get_recv_buffer_addr_for_testing(client);
+ grpc_byte_buffer_destroy(*recv_buffer_ptr);
+
/* Tests. */
tsi_handshaker_shutdown(handshaker);
grpc_byte_buffer* recv_buffer = generate_handshaker_response(CLIENT_START);
- alts_tsi_handshaker_handle_response(alts_handshaker, recv_buffer,
- GRPC_STATUS_OK, nullptr,
- on_shutdown_resp_cb, nullptr, true);
- grpc_byte_buffer_destroy(recv_buffer);
+ alts_handshaker_client_set_fields_for_testing(client, alts_handshaker,
+ on_shutdown_resp_cb, nullptr,
+ recv_buffer, GRPC_STATUS_OK);
+ alts_handshaker_client_handle_response(client, true);
/* Cleanup. */
tsi_handshaker_destroy(handshaker);
+ notification_destroy(&caller_to_tsi_notification);
+ notification_destroy(&tsi_to_caller_notification);
}
void check_handshaker_next_fails_after_shutdown() {
/* Initialization. */
notification_init(&caller_to_tsi_notification);
notification_init(&tsi_to_caller_notification);
- client_start_event = nullptr;
+ cb_event = nullptr;
/* Tests. */
grpc_core::Thread thd("alts_tsi_handshaker_test",
&check_handle_response_with_shutdown, nullptr);
@@ -734,10 +731,6 @@ void check_handshaker_success() {
/* Initialization. */
notification_init(&caller_to_tsi_notification);
notification_init(&tsi_to_caller_notification);
- client_start_event = nullptr;
- client_next_event = nullptr;
- server_start_event = nullptr;
- server_next_event = nullptr;
/* Tests. */
grpc_core::Thread thd("alts_tsi_handshaker_test",
&check_handle_response_success, nullptr);
@@ -752,17 +745,21 @@ void check_handshaker_success() {
int main(int argc, char** argv) {
/* Initialization. */
grpc_init();
+ grpc_alts_shared_resource_dedicated_init();
/* Tests. */
+ should_handshaker_client_api_succeed = true;
check_handshaker_success();
check_handshaker_next_invalid_input();
- check_handshaker_shutdown_invalid_input();
check_handshaker_next_fails_after_shutdown();
+ check_handle_response_after_shutdown();
+ should_handshaker_client_api_succeed = false;
+ check_handshaker_shutdown_invalid_input();
check_handshaker_next_failure();
check_handle_response_invalid_input();
check_handle_response_invalid_resp();
check_handle_response_failure();
- check_handle_response_after_shutdown();
/* Cleanup. */
+ grpc_alts_shared_resource_dedicated_shutdown();
grpc_shutdown();
return 0;
}
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..4e3d841db0 100644
--- a/test/cpp/end2end/BUILD
+++ b/test/cpp/end2end/BUILD
@@ -35,6 +35,20 @@ grpc_cc_library(
],
)
+grpc_cc_library(
+ name = "interceptors_util",
+ testonly = True,
+ srcs = ["interceptors_util.cc"],
+ 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 +131,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,
@@ -125,6 +159,7 @@ grpc_cc_library(
"gtest",
],
deps = [
+ ":interceptors_util",
":test_service_impl",
"//:gpr",
"//:grpc",
@@ -470,6 +505,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..f04ffe4f2d 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);
@@ -626,6 +651,28 @@ TEST_F(ChannelzServerTest, GetServerSocketsTest) {
EXPECT_EQ(get_server_sockets_response.socket_ref_size(), 1);
}
+TEST_F(ChannelzServerTest, GetServerListenSocketsTest) {
+ ResetStubs();
+ ConfigureProxy(1);
+ GetServersRequest get_server_request;
+ GetServersResponse get_server_response;
+ get_server_request.set_start_server_id(0);
+ ClientContext get_server_context;
+ Status s = channelz_stub_->GetServers(&get_server_context, get_server_request,
+ &get_server_response);
+ EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message();
+ EXPECT_EQ(get_server_response.server_size(), 1);
+ EXPECT_EQ(get_server_response.server(0).listen_socket_size(), 1);
+ GetSocketRequest get_socket_request;
+ GetSocketResponse get_socket_response;
+ get_socket_request.set_socket_id(
+ get_server_response.server(0).listen_socket(0).socket_id());
+ ClientContext get_socket_context;
+ s = channelz_stub_->GetSocket(&get_socket_context, get_socket_request,
+ &get_socket_response);
+ EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message();
+}
+
} // namespace testing
} // namespace grpc
diff --git a/test/cpp/end2end/client_callback_end2end_test.cc b/test/cpp/end2end/client_callback_end2end_test.cc
index 62a85641c7..a35991396a 100644
--- a/test/cpp/end2end/client_callback_end2end_test.cc
+++ b/test/cpp/end2end/client_callback_end2end_test.cc
@@ -34,6 +34,7 @@
#include "test/core/util/test_config.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>
@@ -41,13 +42,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;
@@ -75,11 +101,13 @@ class ClientCallbackEnd2endTest : public ::testing::Test {
test_string += "Hello world. ";
request.set_message(test_string);
-
+ grpc::string val;
if (with_binary_metadata) {
+ request.mutable_param()->set_echo_metadata(true);
char bytes[8] = {'\0', '\1', '\2', '\3',
'\4', '\5', '\6', static_cast<char>(i)};
- cli_ctx.AddMetadata("custom-bin", grpc::string(bytes, 8));
+ val = grpc::string(bytes, 8);
+ cli_ctx.AddMetadata("custom-bin", val);
}
cli_ctx.set_compression_algorithm(GRPC_COMPRESS_GZIP);
@@ -89,10 +117,18 @@ class ClientCallbackEnd2endTest : public ::testing::Test {
bool done = false;
stub_->experimental_async()->Echo(
&cli_ctx, &request, &response,
- [&request, &response, &done, &mu, &cv](Status s) {
+ [&cli_ctx, &request, &response, &done, &mu, &cv, val,
+ with_binary_metadata](Status s) {
GPR_ASSERT(s.ok());
EXPECT_EQ(request.message(), response.message());
+ if (with_binary_metadata) {
+ EXPECT_EQ(
+ 1u, cli_ctx.GetServerTrailingMetadata().count("custom-bin"));
+ EXPECT_EQ(val, ToString(cli_ctx.GetServerTrailingMetadata()
+ .find("custom-bin")
+ ->second));
+ }
std::lock_guard<std::mutex> l(mu);
done = true;
cv.notify_one();
@@ -151,37 +187,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 +230,7 @@ TEST_F(ClientCallbackEnd2endTest, MultipleRpcsWithVariedBinaryMetadataValue) {
}
}
-TEST_F(ClientCallbackEnd2endTest, MultipleRpcs) {
+TEST_P(ClientCallbackEnd2endTest, MultipleRpcs) {
ResetStub();
std::vector<std::thread> threads;
threads.reserve(10);
@@ -205,7 +242,7 @@ TEST_F(ClientCallbackEnd2endTest, MultipleRpcs) {
}
}
-TEST_F(ClientCallbackEnd2endTest, CancelRpcBeforeStart) {
+TEST_P(ClientCallbackEnd2endTest, CancelRpcBeforeStart) {
ResetStub();
EchoRequest request;
EchoResponse response;
@@ -230,6 +267,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..0b34ec93ae
--- /dev/null
+++ b/test/cpp/end2end/client_interceptors_end2end_test.cc
@@ -0,0 +1,667 @@
+/*
+ *
+ * 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 {
+
+/* 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);
+ }
+};
+
+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_;
+};
+
+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);
+}
+
+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_;
+};
+
+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);
+}
+
+class ClientGlobalInterceptorEnd2endTest : public ::testing::Test {
+ protected:
+ ClientGlobalInterceptorEnd2endTest() {
+ 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();
+ }
+
+ ~ClientGlobalInterceptorEnd2endTest() { server_->Shutdown(); }
+
+ std::string server_address_;
+ TestServiceImpl service_;
+ std::unique_ptr<Server> server_;
+};
+
+TEST_F(ClientGlobalInterceptorEnd2endTest, DummyGlobalInterceptor) {
+ // We should ideally be registering a global interceptor only once per
+ // process, but for the purposes of testing, it should be fine to modify the
+ // registered global interceptor when there are no ongoing gRPC operations
+ DummyInterceptorFactory global_factory;
+ experimental::RegisterGlobalClientInterceptorFactory(&global_factory);
+ 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
+ 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 with the global interceptor
+ EXPECT_EQ(DummyInterceptor::GetNumTimesRun(), 21);
+ // Reset the global interceptor. This is again 'safe' because there are no
+ // other ongoing gRPC operations
+ experimental::RegisterGlobalClientInterceptorFactory(nullptr);
+}
+
+TEST_F(ClientGlobalInterceptorEnd2endTest, LoggingGlobalInterceptor) {
+ // We should ideally be registering a global interceptor only once per
+ // process, but for the purposes of testing, it should be fine to modify the
+ // registered global interceptor when there are no ongoing gRPC operations
+ LoggingInterceptorFactory global_factory;
+ experimental::RegisterGlobalClientInterceptorFactory(&global_factory);
+ 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
+ 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);
+ // Reset the global interceptor. This is again 'safe' because there are no
+ // other ongoing gRPC operations
+ experimental::RegisterGlobalClientInterceptorFactory(nullptr);
+}
+
+TEST_F(ClientGlobalInterceptorEnd2endTest, HijackingGlobalInterceptor) {
+ // We should ideally be registering a global interceptor only once per
+ // process, but for the purposes of testing, it should be fine to modify the
+ // registered global interceptor when there are no ongoing gRPC operations
+ HijackingInterceptorFactory global_factory;
+ experimental::RegisterGlobalClientInterceptorFactory(&global_factory);
+ 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
+ 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);
+ // Reset the global interceptor. This is again 'safe' because there are no
+ // other ongoing gRPC operations
+ experimental::RegisterGlobalClientInterceptorFactory(nullptr);
+}
+
+} // 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..312065a2df 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"
@@ -146,7 +149,7 @@ class ClientLbEnd2endTest : public ::testing::Test {
void StartServers(size_t num_servers,
std::vector<int> ports = std::vector<int>()) {
- CreateServers(num_servers, ports);
+ CreateServers(num_servers, std::move(ports));
for (size_t i = 0; i < num_servers; ++i) {
StartServer(i);
}
@@ -188,6 +191,11 @@ class ClientLbEnd2endTest : public ::testing::Test {
grpc_channel_args_destroy(fake_results);
}
+ void SetFailureOnReresolution() {
+ grpc_core::ExecCtx exec_ctx;
+ response_generator_->SetFailureOnReresolution();
+ }
+
std::vector<int> GetServersPorts() {
std::vector<int> ports;
for (const auto& server : servers_) ports.push_back(server->port_);
@@ -207,19 +215,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 +239,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 +287,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 +319,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 +531,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.
@@ -720,6 +733,23 @@ TEST_F(ClientLbEnd2endTest, PickFirstCheckStateBeforeStartWatch) {
EXPECT_EQ("pick_first", channel_2->GetLoadBalancingPolicyName());
}
+TEST_F(ClientLbEnd2endTest, PickFirstIdleOnDisconnect) {
+ // Start server, send RPC, and make sure channel is READY.
+ const int kNumServers = 1;
+ StartServers(kNumServers);
+ auto channel = BuildChannel(""); // pick_first is the default.
+ auto stub = BuildStub(channel);
+ SetNextResolution(GetServersPorts());
+ CheckRpcSendOk(stub, DEBUG_LOCATION);
+ EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_READY);
+ // Stop server. Channel should go into state IDLE.
+ SetFailureOnReresolution();
+ servers_[0]->Shutdown();
+ EXPECT_TRUE(WaitForChannelNotReady(channel.get()));
+ EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_IDLE);
+ servers_.clear();
+}
+
TEST_F(ClientLbEnd2endTest, RoundRobin) {
// Start servers and send one RPC per server.
const int kNumServers = 3;
@@ -835,7 +865,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 +874,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 +984,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/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index fc07681535..03291e1785 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -40,6 +40,7 @@
#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/string_ref_helper.h"
#include "test/cpp/util/test_credentials_provider.h"
@@ -179,7 +180,7 @@ class Proxy : public ::grpc::testing::EchoTestService::Service {
}
private:
- std::unique_ptr< ::grpc::testing::EchoTestService::Stub> stub_;
+ std::unique_ptr<::grpc::testing::EchoTestService::Stub> stub_;
};
class TestServiceImplDupPkg
@@ -194,9 +195,14 @@ class TestServiceImplDupPkg
class TestScenario {
public:
- TestScenario(bool proxy, bool inproc_stub, const grpc::string& creds_type)
- : use_proxy(proxy), inproc(inproc_stub), credentials_type(creds_type) {}
+ TestScenario(bool interceptors, bool proxy, bool inproc_stub,
+ const grpc::string& creds_type)
+ : use_interceptors(interceptors),
+ use_proxy(proxy),
+ inproc(inproc_stub),
+ credentials_type(creds_type) {}
void Log() const;
+ bool use_interceptors;
bool use_proxy;
bool inproc;
const grpc::string credentials_type;
@@ -204,8 +210,9 @@ class TestScenario {
static std::ostream& operator<<(std::ostream& out,
const TestScenario& scenario) {
- return out << "TestScenario{use_proxy="
- << (scenario.use_proxy ? "true" : "false")
+ return out << "TestScenario{use_interceptors="
+ << (scenario.use_interceptors ? "true" : "false")
+ << ", use_proxy=" << (scenario.use_proxy ? "true" : "false")
<< ", inproc=" << (scenario.inproc ? "true" : "false")
<< ", credentials='" << scenario.credentials_type << "'}";
}
@@ -260,6 +267,18 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> {
if (GetParam().credentials_type != kInsecureCredentialsType) {
server_creds->SetAuthMetadataProcessor(processor);
}
+ if (GetParam().use_interceptors) {
+ std::vector<
+ std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+ creators;
+ // Add 20 dummy server interceptors
+ creators.reserve(20);
+ for (auto i = 0; i < 20; i++) {
+ creators.push_back(std::unique_ptr<DummyInterceptorFactory>(
+ new DummyInterceptorFactory()));
+ }
+ builder.experimental().SetInterceptorCreators(std::move(creators));
+ }
builder.AddListeningPort(server_address_.str(), server_creds);
builder.RegisterService(&service_);
builder.RegisterService("foo.test.youtube.com", &special_service_);
@@ -292,10 +311,21 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> {
args.SetString(GRPC_ARG_SECONDARY_USER_AGENT_STRING, "end2end_test");
if (!GetParam().inproc) {
- channel_ =
- CreateCustomChannel(server_address_.str(), channel_creds, args);
+ if (!GetParam().use_interceptors) {
+ channel_ =
+ CreateCustomChannel(server_address_.str(), channel_creds, args);
+ } else {
+ channel_ = CreateCustomChannelWithInterceptors(
+ server_address_.str(), channel_creds, args,
+ CreateDummyClientInterceptors());
+ }
} else {
- channel_ = server_->InProcessChannel(args);
+ if (!GetParam().use_interceptors) {
+ channel_ = server_->InProcessChannel(args);
+ } else {
+ channel_ = server_->experimental().InProcessChannelWithInterceptors(
+ args, CreateDummyClientInterceptors());
+ }
}
}
@@ -320,6 +350,7 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> {
}
stub_ = grpc::testing::EchoTestService::NewStub(channel_);
+ DummyInterceptor::Reset();
}
bool is_server_started_;
@@ -376,6 +407,7 @@ class End2endServerTryCancelTest : public End2endTest {
// NOTE: Do not call this function with server_try_cancel == DO_NOT_CANCEL.
void TestRequestStreamServerCancel(
ServerTryCancelRequestPhase server_try_cancel, int num_msgs_to_send) {
+ RestartServer(std::shared_ptr<AuthMetadataProcessor>());
ResetStub();
EchoRequest request;
EchoResponse response;
@@ -432,6 +464,10 @@ class End2endServerTryCancelTest : public End2endTest {
EXPECT_FALSE(s.ok());
EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
+ // Make sure that the server interceptors were notified
+ if (GetParam().use_interceptors) {
+ EXPECT_EQ(20, DummyInterceptor::GetNumTimesCancel());
+ }
}
// Helper for testing server-streaming RPCs which are cancelled on the server.
@@ -449,6 +485,7 @@ class End2endServerTryCancelTest : public End2endTest {
// NOTE: Do not call this function with server_try_cancel == DO_NOT_CANCEL.
void TestResponseStreamServerCancel(
ServerTryCancelRequestPhase server_try_cancel) {
+ RestartServer(std::shared_ptr<AuthMetadataProcessor>());
ResetStub();
EchoRequest request;
EchoResponse response;
@@ -508,7 +545,10 @@ class End2endServerTryCancelTest : public End2endTest {
}
EXPECT_FALSE(s.ok());
- EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
+ // Make sure that the server interceptors were notified
+ if (GetParam().use_interceptors) {
+ EXPECT_EQ(20, DummyInterceptor::GetNumTimesCancel());
+ }
}
// Helper for testing bidirectional-streaming RPCs which are cancelled on the
@@ -526,6 +566,7 @@ class End2endServerTryCancelTest : public End2endTest {
// NOTE: Do not call this function with server_try_cancel == DO_NOT_CANCEL.
void TestBidiStreamServerCancel(ServerTryCancelRequestPhase server_try_cancel,
int num_messages) {
+ RestartServer(std::shared_ptr<AuthMetadataProcessor>());
ResetStub();
EchoRequest request;
EchoResponse response;
@@ -592,6 +633,10 @@ class End2endServerTryCancelTest : public End2endTest {
EXPECT_FALSE(s.ok());
EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
+ // Make sure that the server interceptors were notified
+ if (GetParam().use_interceptors) {
+ EXPECT_EQ(20, DummyInterceptor::GetNumTimesCancel());
+ }
}
};
@@ -989,6 +1034,9 @@ TEST_P(End2endTest, CancelRpcBeforeStart) {
Status s = stub_->Echo(&context, request, &response);
EXPECT_EQ("", response.message());
EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
+ if (GetParam().use_interceptors) {
+ EXPECT_EQ(20, DummyInterceptor::GetNumTimesCancel());
+ }
}
// Client cancels request stream after sending two messages
@@ -1009,6 +1057,9 @@ TEST_P(End2endTest, ClientCancelsRequestStream) {
EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
EXPECT_EQ(response.message(), "");
+ if (GetParam().use_interceptors) {
+ EXPECT_EQ(20, DummyInterceptor::GetNumTimesCancel());
+ }
}
// Client cancels server stream after sending some messages
@@ -1041,6 +1092,9 @@ TEST_P(End2endTest, ClientCancelsResponseStream) {
// The final status could be either of CANCELLED or OK depending on
// who won the race.
EXPECT_GE(grpc::StatusCode::CANCELLED, s.error_code());
+ if (GetParam().use_interceptors) {
+ EXPECT_EQ(20, DummyInterceptor::GetNumTimesCancel());
+ }
}
// Client cancels bidi stream after sending some messages
@@ -1074,6 +1128,9 @@ TEST_P(End2endTest, ClientCancelsBidi) {
Status s = stream->Finish();
EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
+ if (GetParam().use_interceptors) {
+ EXPECT_EQ(20, DummyInterceptor::GetNumTimesCancel());
+ }
}
TEST_P(End2endTest, RpcMaxMessageSize) {
@@ -1802,13 +1859,16 @@ std::vector<TestScenario> CreateTestScenarios(bool use_proxy,
}
GPR_ASSERT(!credentials_types.empty());
for (const auto& cred : credentials_types) {
- scenarios.emplace_back(false, false, cred);
+ scenarios.emplace_back(false, false, false, cred);
+ scenarios.emplace_back(true, false, false, cred);
if (use_proxy) {
- scenarios.emplace_back(true, false, cred);
+ scenarios.emplace_back(false, true, false, cred);
+ scenarios.emplace_back(true, true, false, cred);
}
}
if (test_inproc && insec_ok()) {
- scenarios.emplace_back(false, true, kInsecureCredentialsType);
+ scenarios.emplace_back(false, false, true, kInsecureCredentialsType);
+ scenarios.emplace_back(true, false, true, kInsecureCredentialsType);
}
return scenarios;
}
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.cc b/test/cpp/end2end/interceptors_util.cc
new file mode 100644
index 0000000000..602d1695a3
--- /dev/null
+++ b/test/cpp/end2end/interceptors_util.cc
@@ -0,0 +1,151 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "test/cpp/end2end/interceptors_util.h"
+
+namespace grpc {
+namespace testing {
+
+std::atomic<int> DummyInterceptor::num_times_run_;
+std::atomic<int> DummyInterceptor::num_times_run_reverse_;
+std::atomic<int> DummyInterceptor::num_times_cancel_;
+
+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;
+}
+
+std::unique_ptr<std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>
+CreateDummyClientInterceptors() {
+ 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()));
+ }
+ return creators;
+}
+
+} // namespace testing
+} // namespace grpc
diff --git a/test/cpp/end2end/interceptors_util.h b/test/cpp/end2end/interceptors_util.h
new file mode 100644
index 0000000000..b4c4791fca
--- /dev/null
+++ b/test/cpp/end2end/interceptors_util.h
@@ -0,0 +1,279 @@
+/*
+ *
+ * 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 <condition_variable>
+
+#include <grpcpp/channel.h>
+
+#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 {
+/* This interceptor does nothing. Just keeps a global count on the number of
+ * times it was invoked. */
+class DummyInterceptor : public experimental::Interceptor {
+ public:
+ DummyInterceptor() {}
+
+ 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_++;
+ } else if (methods->QueryInterceptionHookPoint(
+ experimental::InterceptionHookPoints::PRE_SEND_CANCEL)) {
+ num_times_cancel_++;
+ }
+ methods->Proceed();
+ }
+
+ static void Reset() {
+ num_times_run_.store(0);
+ num_times_run_reverse_.store(0);
+ num_times_cancel_.store(0);
+ }
+
+ static int GetNumTimesRun() {
+ EXPECT_EQ(num_times_run_.load(), num_times_run_reverse_.load());
+ return num_times_run_.load();
+ }
+
+ static int GetNumTimesCancel() { return num_times_cancel_.load(); }
+
+ private:
+ static std::atomic<int> num_times_run_;
+ static std::atomic<int> num_times_run_reverse_;
+ static std::atomic<int> num_times_cancel_;
+};
+
+class DummyInterceptorFactory
+ : public experimental::ClientInterceptorFactoryInterface,
+ public experimental::ServerInterceptorFactoryInterface {
+ public:
+ virtual experimental::Interceptor* CreateClientInterceptor(
+ experimental::ClientRpcInfo* info) override {
+ return new DummyInterceptor();
+ }
+
+ virtual experimental::Interceptor* CreateServerInterceptor(
+ experimental::ServerRpcInfo* info) override {
+ return new DummyInterceptor();
+ }
+};
+
+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);
+
+void MakeClientStreamingCall(const std::shared_ptr<Channel>& channel);
+
+void MakeServerStreamingCall(const std::shared_ptr<Channel>& channel);
+
+void MakeBidiStreamingCall(const std::shared_ptr<Channel>& channel);
+
+void MakeCallbackCall(const std::shared_ptr<Channel>& channel);
+
+bool CheckMetadata(const std::multimap<grpc::string_ref, grpc::string_ref>& map,
+ const string& key, const string& value);
+
+std::unique_ptr<std::vector<
+ std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>
+CreateDummyClientInterceptors();
+
+inline void* tag(int i) { return (void*)static_cast<intptr_t>(i); }
+inline 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..295d63516b
--- /dev/null
+++ b/test/cpp/end2end/server_interceptors_end2end_test.cc
@@ -0,0 +1,583 @@
+/*
+ *
+ * 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 {
+
+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;
+ creators.reserve(20);
+ 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;
+ creators.reserve(20);
+ 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;
+ creators.reserve(20);
+ 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/interop/interop_client.cc b/test/cpp/interop/interop_client.cc
index a99cf8122f..4ff153f980 100644
--- a/test/cpp/interop/interop_client.cc
+++ b/test/cpp/interop/interop_client.cc
@@ -76,7 +76,7 @@ void UnaryCompressionChecks(const InteropClientContextInspector& inspector,
InteropClient::ServiceStub::ServiceStub(
ChannelCreationFunc channel_creation_func, bool new_stub_every_call)
- : channel_creation_func_(channel_creation_func),
+ : channel_creation_func_(std::move(channel_creation_func)),
channel_(channel_creation_func_()),
new_stub_every_call_(new_stub_every_call) {
// If new_stub_every_call is false, then this is our chance to initialize
@@ -112,7 +112,7 @@ void InteropClient::ServiceStub::ResetChannel() {
InteropClient::InteropClient(ChannelCreationFunc channel_creation_func,
bool new_stub_every_test_case,
bool do_not_abort_on_transient_failures)
- : serviceStub_(channel_creation_func, new_stub_every_test_case),
+ : serviceStub_(std::move(channel_creation_func), new_stub_every_test_case),
do_not_abort_on_transient_failures_(do_not_abort_on_transient_failures) {}
bool InteropClient::AssertStatusOk(const Status& s,
diff --git a/test/cpp/interop/stress_interop_client.cc b/test/cpp/interop/stress_interop_client.cc
index 7dc1956f78..5bbe913365 100644
--- a/test/cpp/interop/stress_interop_client.cc
+++ b/test/cpp/interop/stress_interop_client.cc
@@ -73,7 +73,7 @@ StressTestInteropClient::StressTestInteropClient(
long sleep_duration_ms, bool do_not_abort_on_transient_failures)
: test_id_(test_id),
server_address_(server_address),
- channel_creation_func_(channel_creation_func),
+ channel_creation_func_(std::move(channel_creation_func)),
interop_client_(new InteropClient(channel_creation_func_, false,
do_not_abort_on_transient_failures)),
test_selector_(test_selector),
diff --git a/test/cpp/microbenchmarks/bm_call_create.cc b/test/cpp/microbenchmarks/bm_call_create.cc
index 1f7831096c..446dc93edb 100644
--- a/test/cpp/microbenchmarks/bm_call_create.cc
+++ b/test/cpp/microbenchmarks/bm_call_create.cc
@@ -468,7 +468,7 @@ class NoOp {
class SendEmptyMetadata {
public:
- SendEmptyMetadata() {
+ SendEmptyMetadata() : op_payload_(nullptr) {
memset(&op_, 0, sizeof(op_));
op_.on_complete = GRPC_CLOSURE_INIT(&closure_, DoNothing, nullptr,
grpc_schedule_on_exec_ctx);
diff --git a/test/cpp/microbenchmarks/bm_chttp2_hpack.cc b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc
index ba4b57ad22..85d233dd26 100644
--- a/test/cpp/microbenchmarks/bm_chttp2_hpack.cc
+++ b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc
@@ -778,8 +778,7 @@ static void free_timeout(void* p) { gpr_free(p); }
// Benchmark the current on_initial_header implementation
static void OnInitialHeader(void* user_data, grpc_mdelem md) {
// Setup for benchmark. This will bloat the absolute values of this benchmark
- grpc_chttp2_incoming_metadata_buffer buffer;
- grpc_chttp2_incoming_metadata_buffer_init(&buffer, (gpr_arena*)user_data);
+ grpc_chttp2_incoming_metadata_buffer buffer((gpr_arena*)user_data);
bool seen_error = false;
// Below here is the code we actually care about benchmarking
@@ -822,7 +821,6 @@ static void OnInitialHeader(void* user_data, grpc_mdelem md) {
GPR_ASSERT(0);
}
}
- grpc_chttp2_incoming_metadata_buffer_destroy(&buffer);
}
// Benchmark timeout handling
diff --git a/test/cpp/microbenchmarks/bm_chttp2_transport.cc b/test/cpp/microbenchmarks/bm_chttp2_transport.cc
index 189923a841..f7ae16e61d 100644
--- a/test/cpp/microbenchmarks/bm_chttp2_transport.cc
+++ b/test/cpp/microbenchmarks/bm_chttp2_transport.cc
@@ -262,7 +262,7 @@ static void BM_StreamCreateDestroy(benchmark::State& state) {
Fixture f(grpc::ChannelArguments(), true);
Stream s(&f);
grpc_transport_stream_op_batch op;
- grpc_transport_stream_op_batch_payload op_payload;
+ grpc_transport_stream_op_batch_payload op_payload(nullptr);
memset(&op, 0, sizeof(op));
op.cancel_stream = true;
op.payload = &op_payload;
@@ -308,8 +308,7 @@ static void BM_StreamCreateSendInitialMetadataDestroy(benchmark::State& state) {
Fixture f(grpc::ChannelArguments(), true);
Stream s(&f);
grpc_transport_stream_op_batch op;
- grpc_transport_stream_op_batch_payload op_payload;
- memset(&op_payload, 0, sizeof(op_payload));
+ grpc_transport_stream_op_batch_payload op_payload(nullptr);
std::unique_ptr<Closure> start;
std::unique_ptr<Closure> done;
@@ -360,8 +359,7 @@ static void BM_TransportEmptyOp(benchmark::State& state) {
Stream s(&f);
s.Init(state);
grpc_transport_stream_op_batch op;
- grpc_transport_stream_op_batch_payload op_payload;
- memset(&op_payload, 0, sizeof(op_payload));
+ grpc_transport_stream_op_batch_payload op_payload(nullptr);
auto reset_op = [&]() {
memset(&op, 0, sizeof(op));
op.payload = &op_payload;
@@ -393,8 +391,7 @@ static void BM_TransportStreamSend(benchmark::State& state) {
auto s = std::unique_ptr<Stream>(new Stream(&f));
s->Init(state);
grpc_transport_stream_op_batch op;
- grpc_transport_stream_op_batch_payload op_payload;
- memset(&op_payload, 0, sizeof(op_payload));
+ grpc_transport_stream_op_batch_payload op_payload(nullptr);
auto reset_op = [&]() {
memset(&op, 0, sizeof(op));
op.payload = &op_payload;
@@ -526,8 +523,7 @@ static void BM_TransportStreamRecv(benchmark::State& state) {
Fixture f(grpc::ChannelArguments(), true);
Stream s(&f);
s.Init(state);
- grpc_transport_stream_op_batch_payload op_payload;
- memset(&op_payload, 0, sizeof(op_payload));
+ grpc_transport_stream_op_batch_payload op_payload(nullptr);
grpc_transport_stream_op_batch op;
grpc_core::OrphanablePtr<grpc_core::ByteStream> recv_stream;
grpc_slice incoming_data = CreateIncomingDataSlice(state.range(0), 16384);
diff --git a/test/cpp/qps/BUILD b/test/cpp/qps/BUILD
index 2ef7441371..26f43284a6 100644
--- a/test/cpp/qps/BUILD
+++ b/test/cpp/qps/BUILD
@@ -36,6 +36,7 @@ grpc_cc_library(
"qps_server_builder.cc",
"qps_worker.cc",
"server_async.cc",
+ "server_callback.cc",
"server_sync.cc",
],
hdrs = [
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
index 4ed34e0405..668d941916 100644
--- a/test/cpp/qps/client.h
+++ b/test/cpp/qps/client.h
@@ -94,9 +94,11 @@ class ClientRequestCreator<ByteBuffer> {
public:
ClientRequestCreator(ByteBuffer* req, const PayloadConfig& payload_config) {
if (payload_config.has_bytebuf_params()) {
- std::unique_ptr<char[]> buf(
- new char[payload_config.bytebuf_params().req_size()]);
- Slice slice(buf.get(), payload_config.bytebuf_params().req_size());
+ size_t req_sz =
+ static_cast<size_t>(payload_config.bytebuf_params().req_size());
+ std::unique_ptr<char[]> buf(new char[req_sz]);
+ memset(buf.get(), 0, req_sz);
+ Slice slice(buf.get(), req_sz);
*req = ByteBuffer(&slice, 1);
} else {
GPR_ASSERT(false); // not appropriate for this specialization
diff --git a/test/cpp/qps/qps_worker.cc b/test/cpp/qps/qps_worker.cc
index d97d95d8f3..23fe72316a 100644
--- a/test/cpp/qps/qps_worker.cc
+++ b/test/cpp/qps/qps_worker.cc
@@ -79,6 +79,8 @@ static std::unique_ptr<Server> CreateServer(const ServerConfig& config) {
return CreateAsyncServer(config);
case ServerType::ASYNC_GENERIC_SERVER:
return CreateAsyncGenericServer(config);
+ case ServerType::CALLBACK_SERVER:
+ return CreateCallbackServer(config);
default:
abort();
}
diff --git a/test/cpp/qps/server.h b/test/cpp/qps/server.h
index 6b1ef93617..89b0e3af4b 100644
--- a/test/cpp/qps/server.h
+++ b/test/cpp/qps/server.h
@@ -152,6 +152,7 @@ class Server {
std::unique_ptr<Server> CreateSynchronousServer(const ServerConfig& config);
std::unique_ptr<Server> CreateAsyncServer(const ServerConfig& config);
std::unique_ptr<Server> CreateAsyncGenericServer(const ServerConfig& config);
+std::unique_ptr<Server> CreateCallbackServer(const ServerConfig& config);
} // namespace testing
} // namespace grpc
diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc
index 5cd975cf74..a5f8347c26 100644
--- a/test/cpp/qps/server_async.cc
+++ b/test/cpp/qps/server_async.cc
@@ -562,6 +562,7 @@ static Status ProcessGenericRPC(const PayloadConfig& payload_config,
request->Clear();
int resp_size = payload_config.bytebuf_params().resp_size();
std::unique_ptr<char[]> buf(new char[resp_size]);
+ memset(buf.get(), 0, static_cast<size_t>(resp_size));
Slice slice(buf.get(), resp_size);
*response = ByteBuffer(&slice, 1);
return Status::OK;
diff --git a/test/cpp/qps/server_callback.cc b/test/cpp/qps/server_callback.cc
new file mode 100644
index 0000000000..8bedd44739
--- /dev/null
+++ b/test/cpp/qps/server_callback.cc
@@ -0,0 +1,96 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpcpp/security/server_credentials.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_context.h>
+
+#include "src/core/lib/gpr/host_port.h"
+#include "src/proto/grpc/testing/benchmark_service.grpc.pb.h"
+#include "test/cpp/qps/qps_server_builder.h"
+#include "test/cpp/qps/server.h"
+#include "test/cpp/qps/usage_timer.h"
+
+namespace grpc {
+namespace testing {
+
+class BenchmarkCallbackServiceImpl final
+ : public BenchmarkService::ExperimentalCallbackService {
+ public:
+ void UnaryCall(
+ ServerContext* context, const SimpleRequest* request,
+ SimpleResponse* response,
+ experimental::ServerCallbackRpcController* controller) override {
+ auto s = SetResponse(request, response);
+ controller->Finish(s);
+ }
+
+ private:
+ static Status SetResponse(const SimpleRequest* request,
+ SimpleResponse* response) {
+ if (request->response_size() > 0) {
+ if (!Server::SetPayload(request->response_type(),
+ request->response_size(),
+ response->mutable_payload())) {
+ return Status(grpc::StatusCode::INTERNAL, "Error creating payload.");
+ }
+ }
+ return Status::OK;
+ }
+};
+
+class CallbackServer final : public grpc::testing::Server {
+ public:
+ explicit CallbackServer(const ServerConfig& config) : Server(config) {
+ std::unique_ptr<ServerBuilder> builder = CreateQpsServerBuilder();
+
+ auto port_num = port();
+ // Negative port number means inproc server, so no listen port needed
+ if (port_num >= 0) {
+ char* server_address = nullptr;
+ gpr_join_host_port(&server_address, "::", port_num);
+ builder->AddListeningPort(server_address,
+ Server::CreateServerCredentials(config));
+ gpr_free(server_address);
+ }
+
+ ApplyConfigToBuilder(config, builder.get());
+
+ builder->RegisterService(&service_);
+
+ impl_ = builder->BuildAndStart();
+ }
+
+ std::shared_ptr<Channel> InProcessChannel(
+ const ChannelArguments& args) override {
+ return impl_->InProcessChannel(args);
+ }
+
+ private:
+ BenchmarkCallbackServiceImpl service_;
+ std::unique_ptr<grpc::Server> impl_;
+};
+
+std::unique_ptr<grpc::testing::Server> CreateCallbackServer(
+ const ServerConfig& config) {
+ return std::unique_ptr<Server>(new CallbackServer(config));
+}
+
+} // namespace testing
+} // namespace grpc
diff --git a/test/cpp/util/BUILD b/test/cpp/util/BUILD
index c8d4333ef0..57eaf3baf2 100644
--- a/test/cpp/util/BUILD
+++ b/test/cpp/util/BUILD
@@ -185,6 +185,7 @@ grpc_cc_test(
external_deps = [
"gtest",
],
+ tags = ["nomsan"], # death tests seem to be incompatible with msan
deps = [
":grpc_cli_libs",
":test_util",
diff --git a/test/cpp/util/byte_buffer_proto_helper.h b/test/cpp/util/byte_buffer_proto_helper.h
index eb923eccb5..3d01fb2468 100644
--- a/test/cpp/util/byte_buffer_proto_helper.h
+++ b/test/cpp/util/byte_buffer_proto_helper.h
@@ -27,12 +27,13 @@
namespace grpc {
namespace testing {
-bool ParseFromByteBuffer(ByteBuffer* buffer, grpc::protobuf::Message* message);
+bool ParseFromByteBuffer(ByteBuffer* buffer,
+ ::grpc::protobuf::Message* message);
std::unique_ptr<ByteBuffer> SerializeToByteBuffer(
- grpc::protobuf::Message* message);
+ ::grpc::protobuf::Message* message);
-bool SerializeToByteBufferInPlace(grpc::protobuf::Message* message,
+bool SerializeToByteBufferInPlace(::grpc::protobuf::Message* message,
ByteBuffer* buffer);
} // namespace testing
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/third_party/data-plane-api b/third_party/data-plane-api
new file mode 160000
+Subproject 911001cdca003337bdb93fab32740cde61bafee
diff --git a/third_party/googleapis b/third_party/googleapis
new file mode 160000
+Subproject 80ed4d0bbf65d57cc267dfc63bd2584557f11f9
diff --git a/third_party/protoc-gen-validate b/third_party/protoc-gen-validate
new file mode 160000
+Subproject e143189bf6f37b3957fb31743df6a1bcf4a8c68
diff --git a/tools/bazel.rc b/tools/bazel.rc
index 39f8071535..59e597b472 100644
--- a/tools/bazel.rc
+++ b/tools/bazel.rc
@@ -1,51 +1,59 @@
+# bazelrc file
+# bazel >= 0.18 looks for %workspace%/.bazelrc (which redirects here)
+# Older bazel versions look for %workspace%/tools/bazel.rc (this file)
+# See https://github.com/bazelbuild/bazel/issues/6319
+
build --client_env=CC=clang
-build --copt -DGRPC_BAZEL_BUILD
+build --copt=-DGRPC_BAZEL_BUILD
+
+build:opt --compilation_mode=opt
+build:opt --copt=-Wframe-larger-than=16384
-build:opt --copt -Wframe-larger-than=16384
+build:dbg --compilation_mode=dbg
build:asan --strip=never
-build:asan --copt -fsanitize-coverage=edge
-build:asan --copt -fsanitize=address
-build:asan --copt -O0
-build:asan --copt -fno-omit-frame-pointer
-build:asan --copt -DGPR_NO_DIRECT_SYSCALLS
-build:asan --linkopt -fsanitize=address
+build:asan --copt=-fsanitize=address
+build:asan --copt=-O0
+build:asan --copt=-fno-omit-frame-pointer
+build:asan --copt=-DGPR_NO_DIRECT_SYSCALLS
+build:asan --copt=-DADDRESS_SANITIZER # used by absl
+build:asan --linkopt=-fsanitize=address
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 --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=-DGPR_NO_DIRECT_SYSCALLS
+build:msan --copt=-DMEMORY_SANITIZER # used by absl
+build:msan --linkopt=-fsanitize=memory
build:msan --action_env=MSAN_OPTIONS=poison_in_dtor=1
build:tsan --strip=never
-build:tsan --copt -fsanitize=thread
-build:tsan --copt -fno-omit-frame-pointer
-build:tsan --copt -DGPR_NO_DIRECT_SYSCALLS
-build:tsan --copt -DGRPC_TSAN
-build:tsan --linkopt -fsanitize=thread
+build:tsan --copt=-fsanitize=thread
+build:tsan --copt=-fno-omit-frame-pointer
+build:tsan --copt=-DGPR_NO_DIRECT_SYSCALLS
+build:tsan --copt=-DGRPC_TSAN
+# TODO(jtattermusch): ideally we would set --copt=-DTHREAD_SANITIZER (used by absl)
+# but it seems to do more harm than good and triggers #17175
+build:tsan --linkopt=-fsanitize=thread
build:tsan --action_env=TSAN_OPTIONS=suppressions=test/core/util/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1
build:ubsan --strip=never
-build:ubsan --copt -fsanitize-coverage=edge
-build:ubsan --copt -fsanitize=undefined
-build:ubsan --copt -fno-omit-frame-pointer
-build:ubsan --copt -DGRPC_UBSAN
-build:ubsan --copt -DNDEBUG
-build:ubsan --copt -fno-sanitize=function,vptr
-build:ubsan --linkopt -fsanitize=undefined
+build:ubsan --copt=-fsanitize=undefined
+build:ubsan --copt=-fno-omit-frame-pointer
+build:ubsan --copt=-DGRPC_UBSAN
+build:ubsan --copt=-DNDEBUG
+build:ubsan --copt=-DUNDEFINED_BEHAVIOR_SANITIZER # used by absl
+build:ubsan --copt=-fno-sanitize=function,vptr
+build:ubsan --linkopt=-fsanitize=undefined
build:ubsan --action_env=UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1:suppressions=test/core/util/ubsan_suppressions.txt
build:basicprof --strip=never
-build:basicprof --copt -DNDEBUG
-build:basicprof --copt -O2
-build:basicprof --copt -DGRPC_BASIC_PROFILER
-build:basicprof --copt -DGRPC_TIMERS_RDTSC
+build:basicprof --copt=-DNDEBUG
+build:basicprof --copt=-O2
+build:basicprof --copt=-DGRPC_BASIC_PROFILER
+build:basicprof --copt=-DGRPC_TIMERS_RDTSC
diff --git a/tools/distrib/python/docgen.py b/tools/distrib/python/docgen.py
index 732d948bbc..de47c00618 100755
--- a/tools/distrib/python/docgen.py
+++ b/tools/distrib/python/docgen.py
@@ -32,12 +32,10 @@ parser.add_argument(
help='GRPC/GPR libraries build configuration',
default='opt')
parser.add_argument('--submit', action='store_true')
-parser.add_argument('--gh-user', type=str, help='GitHub user to push as.')
parser.add_argument(
- '--gh-repo-owner',
+ '--repo-owner',
type=str,
- help=('Owner of the GitHub repository to be pushed; '
- 'defaults to --gh-user.'))
+ help=('Owner of the GitHub repository to be pushed'))
parser.add_argument('--doc-branch', type=str)
args = parser.parse_args()
@@ -70,7 +68,7 @@ subprocess_arguments_list = [
'env': environment
},
{
- 'args': [VIRTUALENV_PIP_PATH, 'install', '--upgrade', 'pip==10.0.1'],
+ 'args': [VIRTUALENV_PIP_PATH, 'install', '--upgrade', 'pip==18.1'],
'env': environment
},
{
@@ -78,7 +76,7 @@ subprocess_arguments_list = [
'env': environment
},
{
- 'args': [VIRTUALENV_PYTHON_PATH, SETUP_PATH, 'build'],
+ 'args': [VIRTUALENV_PIP_PATH, 'install', 'Sphinx~=1.8.1'],
'env': environment
},
{
@@ -91,12 +89,12 @@ for subprocess_arguments in subprocess_arguments_list:
print('Running command: {}'.format(subprocess_arguments['args']))
subprocess.check_call(**subprocess_arguments)
-if args.submit:
- assert args.gh_user
+if not args.submit:
+ print('Please check generated Python doc inside doc/build')
+elif args.submit:
+ assert args.repo_owner
assert args.doc_branch
- github_user = args.gh_user
- github_repository_owner = (args.gh_repo_owner
- if args.gh_repo_owner else args.gh_user)
+ github_repository_owner = args.repo_owner
# Create a temporary directory out of tree, checkout gh-pages from the
# specified repository, edit it, and push it. It's up to the user to then go
# onto GitHub and make a PR against grpc/grpc:gh-pages.
@@ -109,16 +107,19 @@ if args.submit:
print('Cloning your repository...')
subprocess.check_call(
[
- 'git', 'clone', 'https://{}@github.com/{}/grpc'.format(
- github_user, github_repository_owner)
+ 'git',
+ 'clone',
+ '--branch',
+ 'gh-pages',
+ 'https://github.com/grpc/grpc',
],
cwd=repo_parent_dir)
+ subprocess.check_call(['git', 'checkout', '-b', doc_branch], cwd=repo_dir)
subprocess.check_call(
- ['git', 'remote', 'add', 'upstream', 'https://github.com/grpc/grpc'],
- cwd=repo_dir)
- subprocess.check_call(['git', 'fetch', 'upstream'], cwd=repo_dir)
- subprocess.check_call(
- ['git', 'checkout', 'upstream/gh-pages', '-b', doc_branch],
+ [
+ 'git', 'remote', 'add', 'ssh-origin',
+ 'git@github.com:%s/grpc.git' % (github_repository_owner)
+ ],
cwd=repo_dir)
print('Updating documentation...')
shutil.rmtree(python_doc_dir, ignore_errors=True)
@@ -130,7 +131,7 @@ if args.submit:
['git', 'commit', '-m', 'Auto-update Python documentation'],
cwd=repo_dir)
subprocess.check_call(
- ['git', 'push', '--set-upstream', 'origin', doc_branch],
+ ['git', 'push', '--set-upstream', 'ssh-origin', doc_branch],
cwd=repo_dir)
except subprocess.CalledProcessError:
print('Failed to push documentation. Examine this directory and push '
diff --git a/tools/distrib/python/grpcio_tools/MANIFEST.in b/tools/distrib/python/grpcio_tools/MANIFEST.in
index 11ce367747..4943751879 100644
--- a/tools/distrib/python/grpcio_tools/MANIFEST.in
+++ b/tools/distrib/python/grpcio_tools/MANIFEST.in
@@ -1,3 +1,4 @@
+include _parallel_compile_patch.py
include grpc_version.py
include protoc_deps.py
include protoc_lib_deps.py
diff --git a/tools/distrib/python/grpcio_tools/_parallel_compile_patch.py b/tools/distrib/python/grpcio_tools/_parallel_compile_patch.py
new file mode 100644
index 0000000000..4d03ef49ba
--- /dev/null
+++ b/tools/distrib/python/grpcio_tools/_parallel_compile_patch.py
@@ -0,0 +1,63 @@
+# 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.
+"""Patches the compile() to allow enable parallel compilation of C/C++.
+
+build_ext has lots of C/C++ files and normally them one by one.
+Enabling parallel build helps a lot.
+"""
+
+import distutils.ccompiler
+import os
+
+try:
+ BUILD_EXT_COMPILER_JOBS = int(
+ os.environ.get('GRPC_PYTHON_BUILD_EXT_COMPILER_JOBS', '1'))
+except ValueError:
+ BUILD_EXT_COMPILER_JOBS = 1
+
+
+# monkey-patch for parallel compilation
+def _parallel_compile(self,
+ sources,
+ output_dir=None,
+ macros=None,
+ include_dirs=None,
+ debug=0,
+ extra_preargs=None,
+ extra_postargs=None,
+ depends=None):
+ # setup the same way as distutils.ccompiler.CCompiler
+ # https://github.com/python/cpython/blob/31368a4f0e531c19affe2a1becd25fc316bc7501/Lib/distutils/ccompiler.py#L564
+ macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
+ output_dir, macros, include_dirs, sources, depends, extra_postargs)
+ cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
+
+ def _compile_single_file(obj):
+ try:
+ src, ext = build[obj]
+ except KeyError:
+ return
+ self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
+
+ # run compilation of individual files in parallel
+ import multiprocessing.pool
+ multiprocessing.pool.ThreadPool(BUILD_EXT_COMPILER_JOBS).map(
+ _compile_single_file, objects)
+ return objects
+
+
+def monkeypatch_compile_maybe():
+ """Monkeypatching is dumb, but the build speed gain is worth it."""
+ if BUILD_EXT_COMPILER_JOBS > 1:
+ distutils.ccompiler.CCompiler.compile = _parallel_compile
diff --git a/tools/distrib/python/grpcio_tools/setup.py b/tools/distrib/python/grpcio_tools/setup.py
index c13dfe9ade..64c468cbf7 100644
--- a/tools/distrib/python/grpcio_tools/setup.py
+++ b/tools/distrib/python/grpcio_tools/setup.py
@@ -34,9 +34,12 @@ from setuptools.command import build_ext
os.chdir(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.abspath('.'))
+import _parallel_compile_patch
import protoc_lib_deps
import grpc_version
+_parallel_compile_patch.monkeypatch_compile_maybe()
+
CLASSIFIERS = [
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python',
diff --git a/tools/dockerfile/interoptest/grpc_interop_cxx/build_interop.sh b/tools/dockerfile/interoptest/grpc_interop_cxx/build_interop.sh
index fd549fb9e5..8b8c6be8f1 100755
--- a/tools/dockerfile/interoptest/grpc_interop_cxx/build_interop.sh
+++ b/tools/dockerfile/interoptest/grpc_interop_cxx/build_interop.sh
@@ -28,10 +28,10 @@ cp -r /var/local/jenkins/service_account $HOME || true
cd /var/local/git/grpc
-make install-certs
+make install-certs -j4
# build C++ interop client & server
-make interop_client interop_server -j2
+make interop_client interop_server -j4
# build C++ http2 client
-make http2_client
+make http2_client -j4
diff --git a/tools/dockerfile/interoptest/grpc_interop_php/build_interop.sh b/tools/dockerfile/interoptest/grpc_interop_php/build_interop.sh
index 999976d15d..fc5c22083a 100755
--- a/tools/dockerfile/interoptest/grpc_interop_php/build_interop.sh
+++ b/tools/dockerfile/interoptest/grpc_interop_php/build_interop.sh
@@ -28,12 +28,13 @@ cp -r /var/local/jenkins/service_account $HOME || true
cd /var/local/git/grpc
-# gRPC core and protobuf need to be installed
-make install
+# Install gRPC C core and build codegen plugins
+make -j4 install_c plugins
-(cd src/php/ext/grpc && phpize && ./configure && make)
+(cd src/php/ext/grpc && phpize && ./configure && make -j4)
-(cd third_party/protobuf && make install)
+# Install protobuf (need access to protoc)
+(cd third_party/protobuf && make -j4 install)
(cd src/php && php -d extension=ext/grpc/modules/grpc.so /usr/local/bin/composer install)
diff --git a/tools/dockerfile/interoptest/grpc_interop_php7/build_interop.sh b/tools/dockerfile/interoptest/grpc_interop_php7/build_interop.sh
index efa97530c8..248a8f680b 100755
--- a/tools/dockerfile/interoptest/grpc_interop_php7/build_interop.sh
+++ b/tools/dockerfile/interoptest/grpc_interop_php7/build_interop.sh
@@ -28,12 +28,13 @@ cp -r /var/local/jenkins/service_account $HOME || true
cd /var/local/git/grpc
-# gRPC core and protobuf need to be installed
-make install
+# Install gRPC C core and build codegen plugins
+make -j4 install_c plugins
-(cd src/php/ext/grpc && phpize && ./configure && make)
+(cd src/php/ext/grpc && phpize && ./configure && make -j4)
-(cd third_party/protobuf && make install)
+# Install protobuf (need access to protoc)
+(cd third_party/protobuf && make -j4 install)
(cd src/php && php -d extension=ext/grpc/modules/grpc.so /usr/local/bin/composer install)
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..a96683883c 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 \
@@ -1176,9 +1183,11 @@ src/core/lib/transport/status_metadata.h \
src/core/lib/transport/timeout_encoding.h \
src/core/lib/transport/transport.h \
src/core/lib/transport/transport_impl.h \
+src/core/lib/uri/uri_parser.h \
src/cpp/README.md \
src/cpp/client/channel_cc.cc \
src/cpp/client/client_context.cc \
+src/cpp/client/client_interceptor.cc \
src/cpp/client/create_channel.cc \
src/cpp/client/create_channel_internal.cc \
src/cpp/client/create_channel_internal.h \
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 437253c01b..7d105b32ce 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -926,8 +926,6 @@ src/core/ext/filters/client_channel/lb_policy_factory.cc \
src/core/ext/filters/client_channel/lb_policy_factory.h \
src/core/ext/filters/client_channel/lb_policy_registry.cc \
src/core/ext/filters/client_channel/lb_policy_registry.h \
-src/core/ext/filters/client_channel/method_params.cc \
-src/core/ext/filters/client_channel/method_params.h \
src/core/ext/filters/client_channel/parse_address.cc \
src/core/ext/filters/client_channel/parse_address.h \
src/core/ext/filters/client_channel/proxy_mapper.cc \
@@ -956,14 +954,14 @@ src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \
src/core/ext/filters/client_channel/resolver_factory.h \
src/core/ext/filters/client_channel/resolver_registry.cc \
src/core/ext/filters/client_channel/resolver_registry.h \
+src/core/ext/filters/client_channel/resolver_result_parsing.cc \
+src/core/ext/filters/client_channel/resolver_result_parsing.h \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/retry_throttle.h \
src/core/ext/filters/client_channel/subchannel.cc \
src/core/ext/filters/client_channel/subchannel.h \
src/core/ext/filters/client_channel/subchannel_index.cc \
src/core/ext/filters/client_channel/subchannel_index.h \
-src/core/ext/filters/client_channel/uri_parser.cc \
-src/core/ext/filters/client_channel/uri_parser.h \
src/core/ext/filters/deadline/deadline_filter.cc \
src/core/ext/filters/deadline/deadline_filter.h \
src/core/ext/filters/http/client/http_client_filter.cc \
@@ -1478,6 +1476,8 @@ src/core/lib/transport/transport.cc \
src/core/lib/transport/transport.h \
src/core/lib/transport/transport_impl.h \
src/core/lib/transport/transport_op_string.cc \
+src/core/lib/uri/uri_parser.cc \
+src/core/lib/uri/uri_parser.h \
src/core/plugin_registry/grpc_plugin_registry.cc \
src/core/tsi/README.md \
src/core/tsi/alts/crypt/aes_gcm.cc \
@@ -1501,8 +1501,8 @@ src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc \
src/core/tsi/alts/handshaker/alts_handshaker_service_api.h \
src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc \
src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h \
-src/core/tsi/alts/handshaker/alts_tsi_event.cc \
-src/core/tsi/alts/handshaker/alts_tsi_event.h \
+src/core/tsi/alts/handshaker/alts_shared_resource.cc \
+src/core/tsi/alts/handshaker/alts_shared_resource.h \
src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc \
src/core/tsi/alts/handshaker/alts_tsi_handshaker.h \
src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h \
@@ -1527,8 +1527,6 @@ src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc \
src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h \
src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc \
src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h \
-src/core/tsi/alts_transport_security.cc \
-src/core/tsi/alts_transport_security.h \
src/core/tsi/fake_transport_security.cc \
src/core/tsi/fake_transport_security.h \
src/core/tsi/grpc_shadow_boringssl.h \
diff --git a/tools/gcp/utils/big_query_utils.py b/tools/gcp/utils/big_query_utils.py
index 3e811ca2bf..6e9cda376b 100755
--- a/tools/gcp/utils/big_query_utils.py
+++ b/tools/gcp/utils/big_query_utils.py
@@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import print_function
+
import argparse
import json
import uuid
@@ -50,11 +52,11 @@ def create_dataset(biq_query, project_id, dataset_id):
dataset_req.execute(num_retries=NUM_RETRIES)
except HttpError as http_error:
if http_error.resp.status == 409:
- print 'Warning: The dataset %s already exists' % dataset_id
+ print('Warning: The dataset %s already exists' % dataset_id)
else:
# Note: For more debugging info, print "http_error.content"
- print 'Error in creating dataset: %s. Err: %s' % (dataset_id,
- http_error)
+ print('Error in creating dataset: %s. Err: %s' % (dataset_id,
+ http_error))
is_success = False
return is_success
@@ -122,13 +124,13 @@ def create_table2(big_query,
table_req = big_query.tables().insert(
projectId=project_id, datasetId=dataset_id, body=body)
res = table_req.execute(num_retries=NUM_RETRIES)
- print 'Successfully created %s "%s"' % (res['kind'], res['id'])
+ print('Successfully created %s "%s"' % (res['kind'], res['id']))
except HttpError as http_error:
if http_error.resp.status == 409:
- print 'Warning: Table %s already exists' % table_id
+ print('Warning: Table %s already exists' % table_id)
else:
- print 'Error in creating table: %s. Err: %s' % (table_id,
- http_error)
+ print('Error in creating table: %s. Err: %s' % (table_id,
+ http_error))
is_success = False
return is_success
@@ -154,9 +156,9 @@ def patch_table(big_query, project_id, dataset_id, table_id, fields_schema):
tableId=table_id,
body=body)
res = table_req.execute(num_retries=NUM_RETRIES)
- print 'Successfully patched %s "%s"' % (res['kind'], res['id'])
+ print('Successfully patched %s "%s"' % (res['kind'], res['id']))
except HttpError as http_error:
- print 'Error in creating table: %s. Err: %s' % (table_id, http_error)
+ print('Error in creating table: %s. Err: %s' % (table_id, http_error))
is_success = False
return is_success
@@ -172,10 +174,10 @@ def insert_rows(big_query, project_id, dataset_id, table_id, rows_list):
body=body)
res = insert_req.execute(num_retries=NUM_RETRIES)
if res.get('insertErrors', None):
- print 'Error inserting rows! Response: %s' % res
+ print('Error inserting rows! Response: %s' % res)
is_success = False
except HttpError as http_error:
- print 'Error inserting rows to the table %s' % table_id
+ print('Error inserting rows to the table %s' % table_id)
is_success = False
return is_success
@@ -189,8 +191,8 @@ def sync_query_job(big_query, project_id, query, timeout=5000):
projectId=project_id,
body=query_data).execute(num_retries=NUM_RETRIES)
except HttpError as http_error:
- print 'Query execute job failed with error: %s' % http_error
- print http_error.content
+ print('Query execute job failed with error: %s' % http_error)
+ print(http_error.content)
return query_job
diff --git a/tools/internal_ci/README.md b/tools/internal_ci/README.md
index e80e342dcd..af582c471e 100644
--- a/tools/internal_ci/README.md
+++ b/tools/internal_ci/README.md
@@ -1,6 +1,7 @@
-# Internal continuous integration
+# Kokoro CI job configurations and testing scripts
-gRPC's externally facing testing is managed by Jenkins CI (see `tools/jenkins`
-directory). Nevertheless, some of the tests are better suited for being run
-on internal infrastructure and using an internal CI system. Configuration for
-such tests is under this directory.
+gRPC uses a continous integration tool called "Kokoro" (a.k.a "internal CI")
+for running majority of its open source tests.
+This directory contains the external part of kokoro test job configurations
+(the actual job definitions live in an internal repository) and the shell
+scripts that act as entry points to exectute the actual tests.
diff --git a/tools/internal_ci/helper_scripts/prepare_build_macos_rc b/tools/internal_ci/helper_scripts/prepare_build_macos_rc
index 3212e80854..24a3545ded 100644
--- a/tools/internal_ci/helper_scripts/prepare_build_macos_rc
+++ b/tools/internal_ci/helper_scripts/prepare_build_macos_rc
@@ -15,14 +15,8 @@
# Source this rc script to prepare the environment for macos builds
-sudo launchctl limit maxfiles unlimited unlimited
-
-# show current maxfiles
+# show original open file limit values
launchctl limit maxfiles
-
-ulimit -n 10000
-
-# show current limits
ulimit -a
# synchronize the clock
@@ -57,25 +51,25 @@ source $HOME/.rvm/scripts/rvm
set -e # rvm commands are very verbose
time rvm install 2.5.0
rvm use 2.5.0 --default
-gem install bundler --no-ri --no-doc
-gem install cocoapods --version 1.3.1 --no-ri --no-doc
-gem install rake-compiler --no-ri --no-doc
+time gem install bundler --no-ri --no-doc
+time gem install cocoapods --version 1.3.1 --no-ri --no-doc
+time gem install rake-compiler --no-ri --no-doc
rvm osx-ssl-certs status all
rvm osx-ssl-certs update all
set -ex
# cocoapods
export LANG=en_US.UTF-8
-pod repo update # needed by python
+time pod repo update # needed by python
# python
-pip install virtualenv --user python
-pip install -U Mako six tox setuptools twisted pyyaml --user python
+time pip install virtualenv --user python
+time pip install -U Mako six tox setuptools twisted pyyaml --user python
export PYTHONPATH=/Library/Python/3.4/site-packages
# Install Python 3.7
-curl -O https://www.python.org/ftp/python/3.7.0/python-3.7.0-macosx10.9.pkg
-sudo installer -pkg ./python-3.7.0-macosx10.9.pkg -target /
+time curl -O https://www.python.org/ftp/python/3.7.0/python-3.7.0-macosx10.9.pkg
+time sudo installer -pkg ./python-3.7.0-macosx10.9.pkg -target /
# set xcode version for Obj-C tests
sudo xcode-select -switch /Applications/Xcode_9.2.app/Contents/Developer/
@@ -88,11 +82,12 @@ export DOTNET_CLI_TELEMETRY_OPTOUT=true
# TODO(jtattermusch): better debugging of clock skew, remove once not needed
date
-git submodule update --init
+time 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 dfef004a60..87ec60c7c0 100755
--- a/tools/internal_ci/linux/grpc_asan_on_foundry.sh
+++ b/tools/internal_ci/linux/grpc_asan_on_foundry.sh
@@ -13,8 +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="--copt=-gmlt --strip=never --copt=-fsanitize=address --linkopt=-fsanitize=address --test_timeout=3600 --cache_test_results=no"
-EXCLUDE_TESTS="--test_tag_filters=-qps_json_driver,-json_run_localhost"
-github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}" "${EXCLUDE_TESTS}"
+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..fdd5b2e4cd 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"
+EXTRA_FLAGS="--config=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..30b2b17a67 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"
+EXTRA_FLAGS="--config=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_python_bazel_test_in_docker.sh b/tools/internal_ci/linux/grpc_python_bazel_test_in_docker.sh
index 4f98d0a93a..156d65955a 100755
--- a/tools/internal_ci/linux/grpc_python_bazel_test_in_docker.sh
+++ b/tools/internal_ci/linux/grpc_python_bazel_test_in_docker.sh
@@ -24,4 +24,4 @@ git clone /var/local/jenkins/grpc /var/local/git/grpc
&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
${name}')
cd /var/local/git/grpc/test
-bazel test --spawn_strategy=standalone --genrule_strategy=standalone //src/python/...
+bazel test --spawn_strategy=standalone --genrule_strategy=standalone --test_output=errors //src/python/...
diff --git a/tools/internal_ci/linux/grpc_tsan_on_foundry.sh b/tools/internal_ci/linux/grpc_tsan_on_foundry.sh
index 366b5cbe34..fcf3095d48 100644
--- a/tools/internal_ci/linux/grpc_tsan_on_foundry.sh
+++ b/tools/internal_ci/linux/grpc_tsan_on_foundry.sh
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+set -ex
+
export UPLOAD_TEST_RESULTS=true
-EXTRA_FLAGS="--copt=-gmlt --strip=never --copt=-fsanitize=thread --linkopt=-fsanitize=thread --test_timeout=3600 --action_env=TSAN_OPTIONS=suppressions=test/core/util/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1 --cache_test_results=no"
-EXCLUDE_TESTS="--test_tag_filters=-qps_json_driver,-json_run_localhost"
-github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}" "${EXCLUDE_TESTS}"
+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 e0ae9103c4..f45be4d1c9 100644
--- a/tools/internal_ci/linux/grpc_ubsan_on_foundry.sh
+++ b/tools/internal_ci/linux/grpc_ubsan_on_foundry.sh
@@ -15,64 +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 \
- --copt=-fsanitize=undefined \
- --linkopt=-fsanitize=undefined \
- --crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/experimental/ubuntu16_04_clang/1.0/bazel_0.15.0/ubsan: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 \
- --cache_test_results=no \
- --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=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 39c991f291..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,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-EXTRA_FLAGS="--copt=-gmlt --strip=never --copt=-fsanitize=address --linkopt=-fsanitize=address --test_timeout=3600"
-EXCLUDE_TESTS="--test_tag_filters=-qps_json_driver,-json_run_localhost"
-github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}" "${EXCLUDE_TESTS}"
+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..f1e6588517 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.
-EXTRA_FLAGS="-c dbg --test_timeout=300,450,1200,3600"
+set -ex
+
+EXTRA_FLAGS="--config=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..77744de49f 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.
-EXTRA_FLAGS="-c opt --test_timeout=300,450,1200,3600"
+set -ex
+
+EXTRA_FLAGS="--config=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_msan_on_foundry.sh b/tools/internal_ci/linux/pull_request/grpc_msan_on_foundry.sh
new file mode 100644
index 0000000000..c85a837a44
--- /dev/null
+++ b/tools/internal_ci/linux/pull_request/grpc_msan_on_foundry.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+# 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.
+
+set -ex
+
+github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh --config=msan
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 3dee115300..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,6 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-EXTRA_FLAGS="--copt=-gmlt --strip=never --copt=-fsanitize=thread --linkopt=-fsanitize=thread --test_timeout=3600 --action_env=TSAN_OPTIONS=suppressions=test/core/util/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1"
-EXCLUDE_TESTS="--test_tag_filters=-qps_json_driver,-json_run_localhost"
-github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}" "${EXCLUDE_TESTS}"
+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 8547fa4d93..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
@@ -15,59 +15,4 @@
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 \
- --copt=-fsanitize=undefined \
- --linkopt=-fsanitize=undefined \
- --crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/experimental/ubuntu16_04_clang/1.0/bazel_0.15.0/ubsan: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"
-
-if [ "$FAILED" != "" ]
-then
- exit 1
-fi
+github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh --config=ubsan
diff --git a/tools/internal_ci/macos/grpc_basictests_dbg.cfg b/tools/internal_ci/macos/grpc_basictests_dbg.cfg
index e6f9c7ec87..1c7ed26d64 100644
--- a/tools/internal_ci/macos/grpc_basictests_dbg.cfg
+++ b/tools/internal_ci/macos/grpc_basictests_dbg.cfg
@@ -27,5 +27,5 @@ action {
env_vars {
key: "RUN_TESTS_FLAGS"
- value: "-f basictests macos dbg --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
+ value: "-f basictests macos dbg --exclude objc --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
}
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/grpc_basictests_opt.cfg b/tools/internal_ci/macos/grpc_basictests_opt.cfg
index f2a83fe95a..f46ebaa786 100644
--- a/tools/internal_ci/macos/grpc_basictests_opt.cfg
+++ b/tools/internal_ci/macos/grpc_basictests_opt.cfg
@@ -27,5 +27,5 @@ action {
env_vars {
key: "RUN_TESTS_FLAGS"
- value: "-f basictests macos opt --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
+ value: "-f basictests macos opt --exclude objc --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
}
diff --git a/tools/internal_ci/macos/pull_request/grpc_basictests_dbg.cfg b/tools/internal_ci/macos/pull_request/grpc_basictests_dbg.cfg
index c759397b78..c91c033916 100644
--- a/tools/internal_ci/macos/pull_request/grpc_basictests_dbg.cfg
+++ b/tools/internal_ci/macos/pull_request/grpc_basictests_dbg.cfg
@@ -27,5 +27,5 @@ action {
env_vars {
key: "RUN_TESTS_FLAGS"
- value: "-f basictests macos dbg --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
+ value: "-f basictests macos dbg --exclude objc --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
}
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/internal_ci/macos/pull_request/grpc_basictests_opt.cfg b/tools/internal_ci/macos/pull_request/grpc_basictests_opt.cfg
index 4d68341405..532234fa84 100644
--- a/tools/internal_ci/macos/pull_request/grpc_basictests_opt.cfg
+++ b/tools/internal_ci/macos/pull_request/grpc_basictests_opt.cfg
@@ -27,5 +27,5 @@ action {
env_vars {
key: "RUN_TESTS_FLAGS"
- value: "-f basictests macos opt --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
+ value: "-f basictests macos opt --exclude objc --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 019033e52e..19739e9ee1 100644
--- a/tools/remote_build/README.md
+++ b/tools/remote_build/README.md
@@ -17,10 +17,17 @@ and tests run by Kokoro CI.
## Running remote build manually from dev workstation
-Run from repository root:
+Run from repository root (opt, dbg):
```
# manual run of bazel tests remotely on Foundry
-bazel --bazelrc=tools/remote_build/manual.bazelrc test -c opt //test/...
+bazel --bazelrc=tools/remote_build/manual.bazelrc test --config=opt //test/...
```
-TODO: add instructions for running sanitizer builds
+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 040e6bd620..b4fdc70637 100644
--- a/tools/remote_build/manual.bazelrc
+++ b/tools/remote_build/manual.bazelrc
@@ -30,13 +30,16 @@ build --auth_enabled=true
# Set flags for uploading to BES in order to view results in the Bazel Build
# Results UI.
-#build --bes_backend="buildeventservice.googleapis.com"
-#build --bes_timeout=60s
-#build --bes_results_url="https://source.cloud.google.com/results/invocations/"
-#build --project_id=grpc-testing
+build --bes_backend="buildeventservice.googleapis.com"
+build --bes_timeout=60s
+build --bes_results_url="https://source.cloud.google.com/results/invocations/"
+build --project_id=grpc-testing
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 ae382aeabe..75a42a317e 100644
--- a/tools/remote_build/rbe_common.bazelrc
+++ b/tools/remote_build/rbe_common.bazelrc
@@ -42,3 +42,44 @@ build --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
build --define GRPC_PORT_ISOLATED_RUNTIME=1
# without verbose gRPC logs the test outputs are not very useful
test --test_env=GRPC_VERBOSITY=debug
+
+# address sanitizer: most settings are already in %workspace%/.bazelrc
+# we only need a few additional ones that are Foundry specific
+build:asan --copt=-gmlt
+# TODO(jtattermusch): use more reasonable test timeout
+build:asan --test_timeout=3600
+build:asan --test_tag_filters=-qps_json_driver
+
+# 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
+# TODO(jtattermusch): revisit the disabled tests
+build:msan --test_tag_filters=-nomsan,-json_run_localhost
+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
+build:tsan --copt=-gmlt
+# TODO(jtattermusch): use more reasonable test timeout
+build:tsan --test_timeout=3600
+build:tsan --test_tag_filters=-qps_json_driver
+
+# 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
+# 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.
+build:ubsan --action_env=UBSAN_SYMBOLIZER_PATH=/usr/local/bin/llvm-symbolizer
diff --git a/tools/run_tests/artifacts/artifact_targets.py b/tools/run_tests/artifacts/artifact_targets.py
index d18ea2aca1..aa1d0c3bf2 100644
--- a/tools/run_tests/artifacts/artifact_targets.py
+++ b/tools/run_tests/artifacts/artifact_targets.py
@@ -124,6 +124,8 @@ class PythonArtifact:
# https://github.com/resin-io-projects/armv7hf-debian-qemu/issues/9
# A QEMU bug causes submodule update to hang, so we copy directly
environ['RELATIVE_COPY_PATH'] = '.'
+ # Parallel builds are counterproductive in emulated environment
+ environ['GRPC_PYTHON_BUILD_EXT_COMPILER_JOBS'] = '1'
extra_args = ' --entrypoint=/usr/bin/qemu-arm-static '
return create_docker_jobspec(
self.name,
diff --git a/tools/run_tests/artifacts/build_artifact_python.bat b/tools/run_tests/artifacts/build_artifact_python.bat
index d277668c94..795e80dc40 100644
--- a/tools/run_tests/artifacts/build_artifact_python.bat
+++ b/tools/run_tests/artifacts/build_artifact_python.bat
@@ -22,6 +22,10 @@ pip install -rrequirements.txt
set GRPC_PYTHON_BUILD_WITH_CYTHON=1
+@rem Allow build_ext to build C/C++ files in parallel
+@rem by enabling a monkeypatch. It speeds up the build a lot.
+set GRPC_PYTHON_BUILD_EXT_COMPILER_JOBS=2
+
mkdir -p %ARTIFACTS_OUT%
set ARTIFACT_DIR=%cd%\%ARTIFACTS_OUT%
diff --git a/tools/run_tests/artifacts/build_artifact_python.sh b/tools/run_tests/artifacts/build_artifact_python.sh
index 2878005bb2..65f2d1c765 100755
--- a/tools/run_tests/artifacts/build_artifact_python.sh
+++ b/tools/run_tests/artifacts/build_artifact_python.sh
@@ -22,6 +22,11 @@ export PYTHON=${PYTHON:-python}
export PIP=${PIP:-pip}
export AUDITWHEEL=${AUDITWHEEL:-auditwheel}
+# Allow build_ext to build C/C++ files in parallel
+# by enabling a monkeypatch. It speeds up the build a lot.
+# Use externally provided GRPC_PYTHON_BUILD_EXT_COMPILER_JOBS value if set.
+export GRPC_PYTHON_BUILD_EXT_COMPILER_JOBS=${GRPC_PYTHON_BUILD_EXT_COMPILER_JOBS:-2}
+
mkdir -p "${ARTIFACTS_OUT}"
ARTIFACT_DIR="$PWD/${ARTIFACTS_OUT}"
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index 3a442864e8..100eb45ac5 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -3394,6 +3394,29 @@
"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.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++",
@@ -3578,12 +3601,16 @@
"grpc++_test_util",
"grpc_test_util"
],
- "headers": [],
+ "headers": [
+ "test/cpp/end2end/interceptors_util.h"
+ ],
"is_filegroup": false,
"language": "c++",
"name": "end2end_test",
"src": [
- "test/cpp/end2end/end2end_test.cc"
+ "test/cpp/end2end/end2end_test.cc",
+ "test/cpp/end2end/interceptors_util.cc",
+ "test/cpp/end2end/interceptors_util.h"
],
"third_party": false,
"type": "target"
@@ -4702,6 +4729,29 @@
"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.cc",
+ "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",
@@ -7836,6 +7886,7 @@
"test/cpp/qps/report.h",
"test/cpp/qps/server.h",
"test/cpp/qps/server_async.cc",
+ "test/cpp/qps/server_callback.cc",
"test/cpp/qps/server_sync.cc",
"test/cpp/qps/stats.h",
"test/cpp/qps/usage_timer.cc",
@@ -9092,7 +9143,7 @@
"src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h",
"src/core/tsi/alts/frame_protector/frame_handler.h",
"src/core/tsi/alts/handshaker/alts_handshaker_client.h",
- "src/core/tsi/alts/handshaker/alts_tsi_event.h",
+ "src/core/tsi/alts/handshaker/alts_shared_resource.h",
"src/core/tsi/alts/handshaker/alts_tsi_handshaker.h",
"src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h",
"src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h",
@@ -9123,8 +9174,8 @@
"src/core/tsi/alts/frame_protector/frame_handler.h",
"src/core/tsi/alts/handshaker/alts_handshaker_client.cc",
"src/core/tsi/alts/handshaker/alts_handshaker_client.h",
- "src/core/tsi/alts/handshaker/alts_tsi_event.cc",
- "src/core/tsi/alts/handshaker/alts_tsi_event.h",
+ "src/core/tsi/alts/handshaker/alts_shared_resource.cc",
+ "src/core/tsi/alts/handshaker/alts_shared_resource.h",
"src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc",
"src/core/tsi/alts/handshaker/alts_tsi_handshaker.h",
"src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h",
@@ -9604,7 +9655,8 @@
"src/core/lib/transport/status_metadata.cc",
"src/core/lib/transport/timeout_encoding.cc",
"src/core/lib/transport/transport.cc",
- "src/core/lib/transport/transport_op_string.cc"
+ "src/core/lib/transport/transport_op_string.cc",
+ "src/core/lib/uri/uri_parser.cc"
],
"third_party": false,
"type": "filegroup"
@@ -9761,7 +9813,8 @@
"src/core/lib/transport/status_metadata.h",
"src/core/lib/transport/timeout_encoding.h",
"src/core/lib/transport/transport.h",
- "src/core/lib/transport/transport_impl.h"
+ "src/core/lib/transport/transport_impl.h",
+ "src/core/lib/uri/uri_parser.h"
],
"is_filegroup": true,
"language": "c",
@@ -9912,7 +9965,8 @@
"src/core/lib/transport/status_metadata.h",
"src/core/lib/transport/timeout_encoding.h",
"src/core/lib/transport/transport.h",
- "src/core/lib/transport/transport_impl.h"
+ "src/core/lib/transport/transport_impl.h",
+ "src/core/lib/uri/uri_parser.h"
],
"third_party": false,
"type": "filegroup"
@@ -9981,17 +10035,16 @@
"src/core/ext/filters/client_channel/lb_policy.h",
"src/core/ext/filters/client_channel/lb_policy_factory.h",
"src/core/ext/filters/client_channel/lb_policy_registry.h",
- "src/core/ext/filters/client_channel/method_params.h",
"src/core/ext/filters/client_channel/parse_address.h",
"src/core/ext/filters/client_channel/proxy_mapper.h",
"src/core/ext/filters/client_channel/proxy_mapper_registry.h",
"src/core/ext/filters/client_channel/resolver.h",
"src/core/ext/filters/client_channel/resolver_factory.h",
"src/core/ext/filters/client_channel/resolver_registry.h",
+ "src/core/ext/filters/client_channel/resolver_result_parsing.h",
"src/core/ext/filters/client_channel/retry_throttle.h",
"src/core/ext/filters/client_channel/subchannel.h",
- "src/core/ext/filters/client_channel/subchannel_index.h",
- "src/core/ext/filters/client_channel/uri_parser.h"
+ "src/core/ext/filters/client_channel/subchannel_index.h"
],
"is_filegroup": true,
"language": "c",
@@ -10021,8 +10074,6 @@
"src/core/ext/filters/client_channel/lb_policy_factory.h",
"src/core/ext/filters/client_channel/lb_policy_registry.cc",
"src/core/ext/filters/client_channel/lb_policy_registry.h",
- "src/core/ext/filters/client_channel/method_params.cc",
- "src/core/ext/filters/client_channel/method_params.h",
"src/core/ext/filters/client_channel/parse_address.cc",
"src/core/ext/filters/client_channel/parse_address.h",
"src/core/ext/filters/client_channel/proxy_mapper.cc",
@@ -10034,14 +10085,14 @@
"src/core/ext/filters/client_channel/resolver_factory.h",
"src/core/ext/filters/client_channel/resolver_registry.cc",
"src/core/ext/filters/client_channel/resolver_registry.h",
+ "src/core/ext/filters/client_channel/resolver_result_parsing.cc",
+ "src/core/ext/filters/client_channel/resolver_result_parsing.h",
"src/core/ext/filters/client_channel/retry_throttle.cc",
"src/core/ext/filters/client_channel/retry_throttle.h",
"src/core/ext/filters/client_channel/subchannel.cc",
"src/core/ext/filters/client_channel/subchannel.h",
"src/core/ext/filters/client_channel/subchannel_index.cc",
- "src/core/ext/filters/client_channel/subchannel_index.h",
- "src/core/ext/filters/client_channel/uri_parser.cc",
- "src/core/ext/filters/client_channel/uri_parser.h"
+ "src/core/ext/filters/client_channel/subchannel_index.h"
],
"third_party": false,
"type": "filegroup"
@@ -11060,7 +11111,6 @@
"tsi_interface"
],
"headers": [
- "src/core/tsi/alts_transport_security.h",
"src/core/tsi/fake_transport_security.h",
"src/core/tsi/local_transport_security.h",
"src/core/tsi/ssl/session_cache/ssl_session.h",
@@ -11073,8 +11123,6 @@
"language": "c",
"name": "tsi",
"src": [
- "src/core/tsi/alts_transport_security.cc",
- "src/core/tsi/alts_transport_security.h",
"src/core/tsi/fake_transport_security.cc",
"src/core/tsi/fake_transport_security.h",
"src/core/tsi/local_transport_security.cc",
@@ -11154,6 +11202,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 +11216,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 +11278,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 +11292,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 +11451,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 +11556,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",
@@ -11504,6 +11566,7 @@
"include/grpcpp/support/time.h",
"src/cpp/client/channel_cc.cc",
"src/cpp/client/client_context.cc",
+ "src/cpp/client/client_interceptor.cc",
"src/cpp/client/create_channel.cc",
"src/cpp/client/create_channel_internal.cc",
"src/cpp/client/create_channel_internal.h",
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 145d2526e8..340343caf0 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -3990,7 +3990,7 @@
"posix",
"windows"
],
- "cpu_cost": 1.0,
+ "cpu_cost": 10.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
@@ -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/helper_scripts/build_python.sh b/tools/run_tests/helper_scripts/build_python.sh
index 6990244e51..8394f07e51 100755
--- a/tools/run_tests/helper_scripts/build_python.sh
+++ b/tools/run_tests/helper_scripts/build_python.sh
@@ -56,6 +56,12 @@ function is_linux() {
fi
}
+function inside_venv() {
+ if [[ -n "${VIRTUAL_ENV}" ]]; then
+ echo true
+ fi
+}
+
# Associated virtual environment name for the given python command.
function venv() {
$1 -c "import sys; print('py{}{}'.format(*sys.version_info[:2]))"
@@ -80,6 +86,8 @@ function toolchain() {
fi
}
+# TODO(jtattermusch): this adds dependency on grealpath on mac
+# (brew install coreutils) for little reason.
# Command to invoke the linux command `realpath` or equivalent.
function script_realpath() {
# Find `realpath`
@@ -112,6 +120,10 @@ export CFLAGS="-I$ROOT/include -std=gnu99 -fno-wrapv $CFLAGS"
export GRPC_PYTHON_BUILD_WITH_CYTHON=1
export LANG=en_US.UTF-8
+# Allow build_ext to build C/C++ files in parallel
+# by enabling a monkeypatch. It speeds up the build a lot.
+export GRPC_PYTHON_BUILD_EXT_COMPILER_JOBS=4
+
# If ccache is available on Linux, use it.
if [ "$(is_linux)" ]; then
# We're not on Darwin (Mac OS X)
@@ -128,10 +140,14 @@ fi
# Perform build operations #
############################
-# Instantiate the virtualenv from the Python version passed in.
-$PYTHON -m pip install --user virtualenv
-$PYTHON -m virtualenv "$VENV"
-VENV_PYTHON=$(script_realpath "$VENV/$VENV_RELATIVE_PYTHON")
+if [[ "$(inside_venv)" ]]; then
+ VENV_PYTHON="$PYTHON"
+else
+ # Instantiate the virtualenv from the Python version passed in.
+ $PYTHON -m pip install --user virtualenv
+ $PYTHON -m virtualenv "$VENV"
+ VENV_PYTHON=$(script_realpath "$VENV/$VENV_RELATIVE_PYTHON")
+fi
# See https://github.com/grpc/grpc/issues/14815 for more context. We cannot rely
# on pip to upgrade itself because if pip is too old, it may not have the required
@@ -149,10 +165,13 @@ pip_install_dir() {
}
case "$VENV" in
- *gevent*)
+ *py35_gevent*)
# TODO(https://github.com/grpc/grpc/issues/15411) unpin this
$VENV_PYTHON -m pip install gevent==1.3.b1
;;
+ *gevent*)
+ $VENV_PYTHON -m pip install -U gevent
+ ;;
esac
$VENV_PYTHON -m pip install --upgrade pip==10.0.1
diff --git a/tools/run_tests/python_utils/jobset.py b/tools/run_tests/python_utils/jobset.py
index b732e1e03e..578712c393 100755
--- a/tools/run_tests/python_utils/jobset.py
+++ b/tools/run_tests/python_utils/jobset.py
@@ -13,8 +13,6 @@
# limitations under the License.
"""Run a group of subprocesses and then finish."""
-from __future__ import print_function
-
import logging
import multiprocessing
import os
@@ -118,7 +116,7 @@ def eintr_be_gone(fn):
while True:
try:
return fn()
- except IOError, e:
+ except IOError as e:
if e.errno != errno.EINTR:
raise
@@ -144,7 +142,7 @@ def message(tag, msg, explanatory_text=None, do_newline=False):
if do_newline or explanatory_text is not None else ''))
sys.stdout.flush()
return
- except IOError, e:
+ except IOError as e:
if e.errno != errno.EINTR:
raise
diff --git a/tools/run_tests/python_utils/port_server.py b/tools/run_tests/python_utils/port_server.py
index 83e09c09d0..243eef6827 100755
--- a/tools/run_tests/python_utils/port_server.py
+++ b/tools/run_tests/python_utils/port_server.py
@@ -14,15 +14,17 @@
# limitations under the License.
"""Manage TCP ports for unit tests; started by run_tests.py"""
+from __future__ import print_function
+
import argparse
-from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
+from six.moves.BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
+from six.moves.socketserver import ThreadingMixIn
import hashlib
import os
import socket
import sys
import time
import random
-from SocketServer import ThreadingMixIn
import threading
import platform
@@ -32,7 +34,7 @@ import platform
_MY_VERSION = 20
if len(sys.argv) == 2 and sys.argv[1] == 'dump_version':
- print _MY_VERSION
+ print(_MY_VERSION)
sys.exit(0)
argp = argparse.ArgumentParser(description='Server for httpcli_test')
@@ -47,7 +49,7 @@ if args.logfile is not None:
sys.stderr = open(args.logfile, 'w')
sys.stdout = sys.stderr
-print 'port server running on port %d' % args.port
+print('port server running on port %d' % args.port)
pool = []
in_use = {}
@@ -74,7 +76,7 @@ def can_connect(port):
try:
s.connect(('localhost', port))
return True
- except socket.error, e:
+ except socket.error as e:
return False
finally:
s.close()
@@ -86,7 +88,7 @@ def can_bind(port, proto):
try:
s.bind(('localhost', port))
return True
- except socket.error, e:
+ except socket.error as e:
return False
finally:
s.close()
@@ -95,7 +97,7 @@ def can_bind(port, proto):
def refill_pool(max_timeout, req):
"""Scan for ports not marked for being in use"""
chk = [
- port for port in list(range(1025, 32766))
+ port for port in range(1025, 32766)
if port not in cronet_restricted_ports
]
random.shuffle(chk)
diff --git a/tools/run_tests/python_utils/report_utils.py b/tools/run_tests/python_utils/report_utils.py
index b2a256ce29..8d8dedb929 100644
--- a/tools/run_tests/python_utils/report_utils.py
+++ b/tools/run_tests/python_utils/report_utils.py
@@ -13,8 +13,6 @@
# limitations under the License.
"""Generate XML and HTML test reports."""
-from __future__ import print_function
-
try:
from mako.runtime import Context
from mako.template import Template
diff --git a/tools/run_tests/python_utils/start_port_server.py b/tools/run_tests/python_utils/start_port_server.py
index 37995acbdf..0a32bf4418 100644
--- a/tools/run_tests/python_utils/start_port_server.py
+++ b/tools/run_tests/python_utils/start_port_server.py
@@ -12,8 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import urllib
-import jobset
+from __future__ import print_function
+
+from . import jobset
+
+import six.moves.urllib.request as request
import logging
import os
import socket
@@ -33,8 +36,8 @@ def start_port_server():
# otherwise, leave it up
try:
version = int(
- urllib.urlopen('http://localhost:%d/version_number' %
- _PORT_SERVER_PORT).read())
+ request.urlopen('http://localhost:%d/version_number' %
+ _PORT_SERVER_PORT).read())
logging.info('detected port server running version %d', version)
running = True
except Exception as e:
@@ -51,7 +54,7 @@ def start_port_server():
running = (version >= current_version)
if not running:
logging.info('port_server version mismatch: killing the old one')
- urllib.urlopen(
+ request.urlopen(
'http://localhost:%d/quitquitquit' % _PORT_SERVER_PORT).read()
time.sleep(1)
if not running:
@@ -92,7 +95,7 @@ def start_port_server():
# try one final time: maybe another build managed to start one
time.sleep(1)
try:
- urllib.urlopen(
+ request.urlopen(
'http://localhost:%d/get' % _PORT_SERVER_PORT).read()
logging.info(
'last ditch attempt to contact port server succeeded')
@@ -101,11 +104,11 @@ def start_port_server():
logging.exception(
'final attempt to contact port server failed')
port_log = open(logfile, 'r').read()
- print port_log
+ print(port_log)
sys.exit(1)
try:
port_server_url = 'http://localhost:%d/get' % _PORT_SERVER_PORT
- urllib.urlopen(port_server_url).read()
+ request.urlopen(port_server_url).read()
logging.info('port server is up and ready')
break
except socket.timeout:
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/python_utils/watch_dirs.py b/tools/run_tests/python_utils/watch_dirs.py
index d2ad303a07..f2f1c006df 100755
--- a/tools/run_tests/python_utils/watch_dirs.py
+++ b/tools/run_tests/python_utils/watch_dirs.py
@@ -15,13 +15,14 @@
import os
import time
+from six import string_types
class DirWatcher(object):
"""Helper to watch a (set) of directories for modifications."""
def __init__(self, paths):
- if isinstance(paths, basestring):
+ if isinstance(paths, string_types):
paths = [paths]
self._done = False
self.paths = list(paths)
diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py
index 5722a88182..ff6682c3cf 100755
--- a/tools/run_tests/run_interop_tests.py
+++ b/tools/run_tests/run_interop_tests.py
@@ -63,17 +63,17 @@ _SKIP_ADVANCED = [
'unimplemented_service'
]
+_SKIP_SPECIAL_STATUS_MESSAGE = ['special_status_message']
+
_TEST_TIMEOUT = 3 * 60
# disable this test on core-based languages,
# see https://github.com/grpc/grpc/issues/9779
_SKIP_DATA_FRAME_PADDING = ['data_frame_padding']
-# report suffix is important for reports to get picked up by internal CI
-_INTERNAL_CL_XML_REPORT = 'sponge_log.xml'
-
-# report suffix is important for reports to get picked up by internal CI
-_XML_REPORT = 'report.xml'
+# report suffix "sponge_log.xml" is important for reports to get picked up by internal CI
+_DOCKER_BUILD_XML_REPORT = 'interop_docker_build/sponge_log.xml'
+_TESTS_XML_REPORT = 'interop_test/sponge_log.xml'
class CXXLanguage:
@@ -100,7 +100,7 @@ class CXXLanguage:
return {}
def unimplemented_test_cases(self):
- return _SKIP_DATA_FRAME_PADDING
+ return _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
def unimplemented_test_cases_server(self):
return []
@@ -129,7 +129,7 @@ class CSharpLanguage:
return {}
def unimplemented_test_cases(self):
- return _SKIP_SERVER_COMPRESSION + _SKIP_DATA_FRAME_PADDING
+ return _SKIP_SERVER_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION
@@ -158,7 +158,7 @@ class CSharpCoreCLRLanguage:
return {}
def unimplemented_test_cases(self):
- return _SKIP_SERVER_COMPRESSION + _SKIP_DATA_FRAME_PADDING
+ return _SKIP_SERVER_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION
@@ -188,10 +188,10 @@ class DartLanguage:
return {}
def unimplemented_test_cases(self):
- return _SKIP_COMPRESSION
+ return _SKIP_COMPRESSION + _SKIP_SPECIAL_STATUS_MESSAGE
def unimplemented_test_cases_server(self):
- return _SKIP_COMPRESSION
+ return _SKIP_COMPRESSION + _SKIP_SPECIAL_STATUS_MESSAGE
def __str__(self):
return 'dart'
@@ -248,7 +248,7 @@ class JavaOkHttpClient:
return {}
def unimplemented_test_cases(self):
- return _SKIP_DATA_FRAME_PADDING
+ return _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
def __str__(self):
return 'javaokhttp'
@@ -309,7 +309,7 @@ class Http2Server:
return {}
def unimplemented_test_cases(self):
- return _TEST_CASES + _SKIP_DATA_FRAME_PADDING
+ return _TEST_CASES + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
def unimplemented_test_cases_server(self):
return _TEST_CASES
@@ -339,7 +339,7 @@ class Http2Client:
return {}
def unimplemented_test_cases(self):
- return _TEST_CASES
+ return _TEST_CASES + _SKIP_SPECIAL_STATUS_MESSAGE
def unimplemented_test_cases_server(self):
return _TEST_CASES
@@ -431,7 +431,7 @@ class PHPLanguage:
return {}
def unimplemented_test_cases(self):
- return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING
+ return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
def unimplemented_test_cases_server(self):
return []
@@ -456,7 +456,7 @@ class PHP7Language:
return {}
def unimplemented_test_cases(self):
- return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING
+ return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
def unimplemented_test_cases_server(self):
return []
@@ -491,7 +491,7 @@ class ObjcLanguage:
# cmdline argument. Here we return all but one test cases as unimplemented,
# and depend upon ObjC test's behavior that it runs all cases even when
# we tell it to run just one.
- return _TEST_CASES[1:] + _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING
+ return _TEST_CASES[1:] + _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION
@@ -526,7 +526,7 @@ class RubyLanguage:
return {}
def unimplemented_test_cases(self):
- return _SKIP_SERVER_COMPRESSION + _SKIP_DATA_FRAME_PADDING
+ return _SKIP_SERVER_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION
@@ -610,7 +610,7 @@ _TEST_CASES = [
'custom_metadata', 'status_code_and_message', 'unimplemented_method',
'client_compressed_unary', 'server_compressed_unary',
'client_compressed_streaming', 'server_compressed_streaming',
- 'unimplemented_service'
+ 'unimplemented_service', 'special_status_message'
]
_AUTH_TEST_CASES = [
@@ -1161,8 +1161,9 @@ argp.add_argument(
default=False,
action='store_const',
const=True,
- help=('Put reports into subdirectories to improve '
- 'presentation of results by Internal CI.'))
+ help=(
+ '(Deprecated, has no effect) Put reports into subdirectories to improve '
+ 'presentation of results by Internal CI.'))
argp.add_argument(
'--bq_result_table',
default='',
@@ -1251,8 +1252,12 @@ if args.use_docker:
if args.verbose:
print('Jobs to run: \n%s\n' % '\n'.join(str(j) for j in build_jobs))
- num_failures, _ = jobset.run(
+ num_failures, build_resultset = jobset.run(
build_jobs, newline_on_success=True, maxjobs=args.jobs)
+
+ report_utils.render_junit_xml_report(build_resultset,
+ _DOCKER_BUILD_XML_REPORT)
+
if num_failures == 0:
jobset.message(
'SUCCESS',
@@ -1315,7 +1320,7 @@ try:
for language in languages:
for test_case in _TEST_CASES:
if not test_case in language.unimplemented_test_cases():
- if not test_case in _SKIP_ADVANCED + _SKIP_COMPRESSION:
+ if not test_case in _SKIP_ADVANCED + _SKIP_COMPRESSION + _SKIP_SPECIAL_STATUS_MESSAGE:
tls_test_job = cloud_to_prod_jobspec(
language,
test_case,
@@ -1517,10 +1522,7 @@ try:
write_cmdlog_maybe(server_manual_cmd_log, 'interop_server_cmds.sh')
write_cmdlog_maybe(client_manual_cmd_log, 'interop_client_cmds.sh')
- xml_report_name = _XML_REPORT
- if args.internal_ci:
- xml_report_name = _INTERNAL_CL_XML_REPORT
- report_utils.render_junit_xml_report(resultset, xml_report_name)
+ report_utils.render_junit_xml_report(resultset, _TESTS_XML_REPORT)
for name, job in resultset.items():
if "http2" in name:
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 44151f49fb..a1f2aaab2f 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -765,8 +765,10 @@ class PythonLanguage(object):
return 'stretch_' + self.args.compiler[len('python'):]
elif self.args.compiler == 'python_alpine':
return 'alpine'
- else:
+ elif self.args.compiler == 'python3.4':
return 'jessie'
+ else:
+ return 'stretch_3.7'
def _get_pythons(self, args):
if args.arch == 'x86':
@@ -844,7 +846,7 @@ class PythonLanguage(object):
else:
return (
python27_config,
- python34_config,
+ python37_config,
)
elif args.compiler == 'python2.7':
return (python27_config,)
@@ -1512,7 +1514,7 @@ if args.travis:
_FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}
if 'all' in args.language:
- lang_list = _LANGUAGES.keys()
+ lang_list = list(_LANGUAGES.keys())
else:
lang_list = args.language
# We don't support code coverage on some languages
@@ -1719,9 +1721,9 @@ def _has_epollexclusive():
try:
subprocess.check_call(binary)
return True
- except subprocess.CalledProcessError, e:
+ except subprocess.CalledProcessError as e:
return False
- except OSError, e:
+ except OSError as e:
# For languages other than C and Windows the binary won't exist
return False
diff --git a/tools/run_tests/run_tests_matrix.py b/tools/run_tests/run_tests_matrix.py
index 00fc68ad17..d93add00cd 100755
--- a/tools/run_tests/run_tests_matrix.py
+++ b/tools/run_tests/run_tests_matrix.py
@@ -574,11 +574,8 @@ if __name__ == "__main__":
print('Will run these tests:')
for job in jobs:
- if args.dry_run:
- print(' %s: "%s"' % (job.shortname, ' '.join(job.cmdline)))
- else:
- print(' %s' % job.shortname)
- print
+ print(' %s: "%s"' % (job.shortname, ' '.join(job.cmdline)))
+ print('')
if args.dry_run:
print('--dry_run was used, exiting')
diff --git a/tools/run_tests/sanity/check_submodules.sh b/tools/run_tests/sanity/check_submodules.sh
index 2c7c140716..fa2628f18e 100755
--- a/tools/run_tests/sanity/check_submodules.sh
+++ b/tools/run_tests/sanity/check_submodules.sh
@@ -30,13 +30,16 @@ 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)
+ 911001cdca003337bdb93fab32740cde61bafee3 third_party/data-plane-api (heads/master)
30dbc81fb5ffdc98ea9b14b1918bfe4e8779b26e third_party/gflags (v2.2.0-5-g30dbc81)
+ 80ed4d0bbf65d57cc267dfc63bd2584557f11f9b third_party/googleapis (common-protos-1_3_1-915-g80ed4d0bb)
ec44c6c1675c25b9827aacd08c02433cccde7780 third_party/googletest (release-1.8.0)
6599cac0965be8e5a835ab7a5684bbef033d5ad0 third_party/libcxx (heads/release_60)
9245d481eb3e890f708ff2d7dadf2a10c04748ba third_party/libcxxabi (heads/release_60)
48cb18e5c419ddd23d9badcfe4e9df7bde1979b2 third_party/protobuf (v3.6.0.1-37-g48cb18e5)
+ e143189bf6f37b3957fb31743df6a1bcf4a8c685 third_party/protoc-gen-validate (v0.0.10)
cacf7f1d4e3d44d871b605da3b647f07d718623f third_party/zlib (v1.2.11)
EOF
diff --git a/tools/run_tests/sanity/core_banned_functions.py b/tools/run_tests/sanity/core_banned_functions.py
index 8afd826453..549ae14f5a 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'],
@@ -38,6 +39,7 @@ BANNED_EXCEPT = {
'grpc_wsa_error(': ['src/core/lib/iomgr/error.cc'],
'grpc_log_if_error(': ['src/core/lib/iomgr/error.cc'],
'grpc_slice_malloc(': ['src/core/lib/slice/slice.cc'],
+ 'grpc_call_cancel_internal(': ['src/core/lib/surface/call.cc'],
'grpc_closure_create(': ['src/core/lib/iomgr/closure.cc'],
'grpc_closure_init(': ['src/core/lib/iomgr/closure.cc'],
'grpc_closure_sched(': ['src/core/lib/iomgr/closure.cc'],