Merge branch 'master' into aio-status

pull/22688/head^2
Lidi Zheng 5 years ago committed by GitHub
commit 916f6db913
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      BUILD
  2. 5
      BUILD.gn
  3. 6
      CMakeLists.txt
  4. 6
      Makefile
  5. 10
      build_autogenerated.yaml
  6. 3
      config.m4
  7. 3
      config.w32
  8. 4
      gRPC-C++.podspec
  9. 7
      gRPC-Core.podspec
  10. 5
      grpc.gemspec
  11. 6
      grpc.gyp
  12. 10
      include/grpc/impl/codegen/grpc_types.h
  13. 5
      package.xml
  14. 833
      src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc
  15. 140
      src/core/ext/filters/client_channel/xds/xds_api.cc
  16. 17
      src/core/ext/filters/client_channel/xds/xds_api.h
  17. 118
      src/core/ext/filters/client_channel/xds/xds_client.cc
  18. 4
      src/core/ext/filters/client_channel/xds/xds_client.h
  19. 35
      src/core/ext/filters/http/http_filters_plugin.cc
  20. 358
      src/core/ext/filters/http/message_compress/message_decompress_filter.cc
  21. 29
      src/core/ext/filters/http/message_compress/message_decompress_filter.h
  22. 5
      src/core/lib/iomgr/cfstream_handle.cc
  23. 356
      src/core/lib/iomgr/ev_apple.cc
  24. 43
      src/core/lib/iomgr/ev_apple.h
  25. 104
      src/core/lib/iomgr/iomgr_posix_cfstream.cc
  26. 20
      src/core/lib/iomgr/pollset_set_custom.cc
  27. 1
      src/core/lib/iomgr/port.h
  28. 49
      src/core/lib/surface/byte_buffer_reader.cc
  29. 2
      src/core/lib/surface/server.cc
  30. 9
      src/core/lib/transport/byte_stream.h
  31. 4
      src/core/plugin_registry/grpc_plugin_registry.cc
  32. 4
      src/core/plugin_registry/grpc_unsecure_plugin_registry.cc
  33. 9
      src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTests.xcscheme
  34. 9
      src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/MacTests.xcscheme
  35. 9
      src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/PerfTests.xcscheme
  36. 9
      src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/TvTests.xcscheme
  37. 9
      src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/UnitTests.xcscheme
  38. 28
      src/proto/grpc/testing/echo.proto
  39. 1
      src/proto/grpc/testing/xds/lds_rds_for_test.proto
  40. 3
      src/python/grpcio/grpc_core_dependencies.py
  41. 2
      src/python/grpcio_reflection/grpc_reflection/v1alpha/BUILD.bazel
  42. 57
      src/python/grpcio_reflection/grpc_reflection/v1alpha/_async.py
  43. 110
      src/python/grpcio_reflection/grpc_reflection/v1alpha/_base.py
  44. 137
      src/python/grpcio_reflection/grpc_reflection/v1alpha/reflection.py
  45. 30
      src/python/grpcio_tests/tests_aio/reflection/BUILD.bazel
  46. 13
      src/python/grpcio_tests/tests_aio/reflection/__init__.py
  47. 193
      src/python/grpcio_tests/tests_aio/reflection/reflection_servicer_test.py
  48. 1
      src/python/grpcio_tests/tests_aio/tests.json
  49. 30
      test/core/channel/minimal_stack_is_minimal_test.cc
  50. 42
      test/core/end2end/cq_verifier.cc
  51. 117
      test/core/end2end/tests/compressed_payload.cc
  52. 47
      test/core/end2end/tests/workaround_cronet_compression.cc
  53. 73
      test/core/surface/byte_buffer_reader_test.cc
  54. 334
      test/cpp/end2end/test_service_impl.cc
  55. 344
      test/cpp/end2end/test_service_impl.h
  56. 597
      test/cpp/end2end/xds_end2end_test.cc
  57. 8
      test/cpp/interop/client_helper.h
  58. 9
      test/cpp/interop/interop_client.cc
  59. 5
      test/cpp/interop/interop_server.cc
  60. 8
      test/cpp/interop/server_helper.cc
  61. 2
      test/cpp/interop/server_helper.h
  62. 2
      test/cpp/ios/Podfile
  63. 43
      test/cpp/util/grpc_tool_test.cc
  64. 5
      tools/doxygen/Doxyfile.c++.internal
  65. 5
      tools/doxygen/Doxyfile.core.internal
  66. 2
      tools/internal_ci/macos/grpc_basictests_python.cfg
  67. 2
      tools/internal_ci/macos/pull_request/grpc_basictests_python.cfg

19
BUILD

@ -322,6 +322,7 @@ grpc_cc_library(
"grpc_lb_policy_eds",
"grpc_lb_policy_grpclb",
"grpc_lb_policy_lrs",
"grpc_lb_policy_xds_routing",
"grpc_resolver_xds",
],
)
@ -341,6 +342,7 @@ grpc_cc_library(
"grpc_lb_policy_eds_secure",
"grpc_lb_policy_grpclb_secure",
"grpc_lb_policy_lrs_secure",
"grpc_lb_policy_xds_routing",
"grpc_resolver_xds_secure",
"grpc_secure",
"grpc_transport_chttp2_client_secure",
@ -724,7 +726,6 @@ grpc_cc_library(
"src/core/lib/iomgr/endpoint_pair_windows.cc",
"src/core/lib/iomgr/error.cc",
"src/core/lib/iomgr/error_cfstream.cc",
"src/core/lib/iomgr/ev_apple.cc",
"src/core/lib/iomgr/ev_epoll1_linux.cc",
"src/core/lib/iomgr/ev_epollex_linux.cc",
"src/core/lib/iomgr/ev_poll_posix.cc",
@ -886,7 +887,6 @@ grpc_cc_library(
"src/core/lib/iomgr/error.h",
"src/core/lib/iomgr/error_cfstream.h",
"src/core/lib/iomgr/error_internal.h",
"src/core/lib/iomgr/ev_apple.h",
"src/core/lib/iomgr/ev_epoll1_linux.h",
"src/core/lib/iomgr/ev_epollex_linux.h",
"src/core/lib/iomgr/ev_poll_posix.h",
@ -991,6 +991,7 @@ grpc_cc_library(
],
language = "c++",
public_hdrs = GRPC_PUBLIC_HDRS,
use_cfstream = True,
deps = [
"eventmanager_libuv",
"gpr_base",
@ -1194,11 +1195,13 @@ grpc_cc_library(
"src/core/ext/filters/http/client/http_client_filter.cc",
"src/core/ext/filters/http/http_filters_plugin.cc",
"src/core/ext/filters/http/message_compress/message_compress_filter.cc",
"src/core/ext/filters/http/message_compress/message_decompress_filter.cc",
"src/core/ext/filters/http/server/http_server_filter.cc",
],
hdrs = [
"src/core/ext/filters/http/client/http_client_filter.h",
"src/core/ext/filters/http/message_compress/message_compress_filter.h",
"src/core/ext/filters/http/message_compress/message_decompress_filter.h",
"src/core/ext/filters/http/server/http_server_filter.h",
],
language = "c++",
@ -1453,6 +1456,18 @@ grpc_cc_library(
],
)
grpc_cc_library(
name = "grpc_lb_policy_xds_routing",
srcs = [
"src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc",
],
language = "c++",
deps = [
"grpc_base",
"grpc_client_channel",
],
)
grpc_cc_library(
name = "grpc_lb_address_filtering",
srcs = [

@ -248,6 +248,7 @@ config("grpc_config") {
"src/core/ext/filters/client_channel/lb_policy/xds/eds.cc",
"src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc",
"src/core/ext/filters/client_channel/lb_policy/xds/xds.h",
"src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.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",
@ -318,6 +319,8 @@ config("grpc_config") {
"src/core/ext/filters/http/http_filters_plugin.cc",
"src/core/ext/filters/http/message_compress/message_compress_filter.cc",
"src/core/ext/filters/http/message_compress/message_compress_filter.h",
"src/core/ext/filters/http/message_compress/message_decompress_filter.cc",
"src/core/ext/filters/http/message_compress/message_decompress_filter.h",
"src/core/ext/filters/http/server/http_server_filter.cc",
"src/core/ext/filters/http/server/http_server_filter.h",
"src/core/ext/filters/max_age/max_age_filter.cc",
@ -604,8 +607,6 @@ config("grpc_config") {
"src/core/lib/iomgr/error_cfstream.cc",
"src/core/lib/iomgr/error_cfstream.h",
"src/core/lib/iomgr/error_internal.h",
"src/core/lib/iomgr/ev_apple.cc",
"src/core/lib/iomgr/ev_apple.h",
"src/core/lib/iomgr/ev_epoll1_linux.cc",
"src/core/lib/iomgr/ev_epoll1_linux.h",
"src/core/lib/iomgr/ev_epollex_linux.cc",

@ -1330,6 +1330,7 @@ add_library(grpc
src/core/ext/filters/client_channel/lb_policy/xds/cds.cc
src/core/ext/filters/client_channel/lb_policy/xds/eds.cc
src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc
src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc
src/core/ext/filters/client_channel/lb_policy_registry.cc
src/core/ext/filters/client_channel/local_subchannel_pool.cc
src/core/ext/filters/client_channel/parse_address.cc
@ -1369,6 +1370,7 @@ add_library(grpc
src/core/ext/filters/http/client_authority_filter.cc
src/core/ext/filters/http/http_filters_plugin.cc
src/core/ext/filters/http/message_compress/message_compress_filter.cc
src/core/ext/filters/http/message_compress/message_decompress_filter.cc
src/core/ext/filters/http/server/http_server_filter.cc
src/core/ext/filters/max_age/max_age_filter.cc
src/core/ext/filters/message_size/message_size_filter.cc
@ -1512,7 +1514,6 @@ add_library(grpc
src/core/lib/iomgr/endpoint_pair_windows.cc
src/core/lib/iomgr/error.cc
src/core/lib/iomgr/error_cfstream.cc
src/core/lib/iomgr/ev_apple.cc
src/core/lib/iomgr/ev_epoll1_linux.cc
src/core/lib/iomgr/ev_epollex_linux.cc
src/core/lib/iomgr/ev_poll_posix.cc
@ -1990,6 +1991,7 @@ add_library(grpc_unsecure
src/core/ext/filters/client_channel/lb_policy/xds/cds.cc
src/core/ext/filters/client_channel/lb_policy/xds/eds.cc
src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc
src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc
src/core/ext/filters/client_channel/lb_policy_registry.cc
src/core/ext/filters/client_channel/local_subchannel_pool.cc
src/core/ext/filters/client_channel/parse_address.cc
@ -2029,6 +2031,7 @@ add_library(grpc_unsecure
src/core/ext/filters/http/client_authority_filter.cc
src/core/ext/filters/http/http_filters_plugin.cc
src/core/ext/filters/http/message_compress/message_compress_filter.cc
src/core/ext/filters/http/message_compress/message_decompress_filter.cc
src/core/ext/filters/http/server/http_server_filter.cc
src/core/ext/filters/max_age/max_age_filter.cc
src/core/ext/filters/message_size/message_size_filter.cc
@ -2166,7 +2169,6 @@ add_library(grpc_unsecure
src/core/lib/iomgr/endpoint_pair_windows.cc
src/core/lib/iomgr/error.cc
src/core/lib/iomgr/error_cfstream.cc
src/core/lib/iomgr/ev_apple.cc
src/core/lib/iomgr/ev_epoll1_linux.cc
src/core/lib/iomgr/ev_epollex_linux.cc
src/core/lib/iomgr/ev_poll_posix.cc

@ -3655,6 +3655,7 @@ LIBGRPC_SRC = \
src/core/ext/filters/client_channel/lb_policy/xds/cds.cc \
src/core/ext/filters/client_channel/lb_policy/xds/eds.cc \
src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc \
src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc \
src/core/ext/filters/client_channel/lb_policy_registry.cc \
src/core/ext/filters/client_channel/local_subchannel_pool.cc \
src/core/ext/filters/client_channel/parse_address.cc \
@ -3694,6 +3695,7 @@ LIBGRPC_SRC = \
src/core/ext/filters/http/client_authority_filter.cc \
src/core/ext/filters/http/http_filters_plugin.cc \
src/core/ext/filters/http/message_compress/message_compress_filter.cc \
src/core/ext/filters/http/message_compress/message_decompress_filter.cc \
src/core/ext/filters/http/server/http_server_filter.cc \
src/core/ext/filters/max_age/max_age_filter.cc \
src/core/ext/filters/message_size/message_size_filter.cc \
@ -3837,7 +3839,6 @@ LIBGRPC_SRC = \
src/core/lib/iomgr/endpoint_pair_windows.cc \
src/core/lib/iomgr/error.cc \
src/core/lib/iomgr/error_cfstream.cc \
src/core/lib/iomgr/ev_apple.cc \
src/core/lib/iomgr/ev_epoll1_linux.cc \
src/core/lib/iomgr/ev_epollex_linux.cc \
src/core/lib/iomgr/ev_poll_posix.cc \
@ -4289,6 +4290,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/ext/filters/client_channel/lb_policy/xds/cds.cc \
src/core/ext/filters/client_channel/lb_policy/xds/eds.cc \
src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc \
src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc \
src/core/ext/filters/client_channel/lb_policy_registry.cc \
src/core/ext/filters/client_channel/local_subchannel_pool.cc \
src/core/ext/filters/client_channel/parse_address.cc \
@ -4328,6 +4330,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/ext/filters/http/client_authority_filter.cc \
src/core/ext/filters/http/http_filters_plugin.cc \
src/core/ext/filters/http/message_compress/message_compress_filter.cc \
src/core/ext/filters/http/message_compress/message_decompress_filter.cc \
src/core/ext/filters/http/server/http_server_filter.cc \
src/core/ext/filters/max_age/max_age_filter.cc \
src/core/ext/filters/message_size/message_size_filter.cc \
@ -4465,7 +4468,6 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/iomgr/endpoint_pair_windows.cc \
src/core/lib/iomgr/error.cc \
src/core/lib/iomgr/error_cfstream.cc \
src/core/lib/iomgr/ev_apple.cc \
src/core/lib/iomgr/ev_epoll1_linux.cc \
src/core/lib/iomgr/ev_epollex_linux.cc \
src/core/lib/iomgr/ev_poll_posix.cc \

@ -423,6 +423,7 @@ libs:
- src/core/ext/filters/http/client/http_client_filter.h
- src/core/ext/filters/http/client_authority_filter.h
- src/core/ext/filters/http/message_compress/message_compress_filter.h
- src/core/ext/filters/http/message_compress/message_decompress_filter.h
- src/core/ext/filters/http/server/http_server_filter.h
- src/core/ext/filters/max_age/max_age_filter.h
- src/core/ext/filters/message_size/message_size_filter.h
@ -566,7 +567,6 @@ libs:
- src/core/lib/iomgr/error.h
- src/core/lib/iomgr/error_cfstream.h
- src/core/lib/iomgr/error_internal.h
- src/core/lib/iomgr/ev_apple.h
- src/core/lib/iomgr/ev_epoll1_linux.h
- src/core/lib/iomgr/ev_epollex_linux.h
- src/core/lib/iomgr/ev_poll_posix.h
@ -757,6 +757,7 @@ libs:
- src/core/ext/filters/client_channel/lb_policy/xds/cds.cc
- src/core/ext/filters/client_channel/lb_policy/xds/eds.cc
- src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc
- src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc
- src/core/ext/filters/client_channel/lb_policy_registry.cc
- src/core/ext/filters/client_channel/local_subchannel_pool.cc
- src/core/ext/filters/client_channel/parse_address.cc
@ -796,6 +797,7 @@ libs:
- src/core/ext/filters/http/client_authority_filter.cc
- src/core/ext/filters/http/http_filters_plugin.cc
- src/core/ext/filters/http/message_compress/message_compress_filter.cc
- src/core/ext/filters/http/message_compress/message_decompress_filter.cc
- src/core/ext/filters/http/server/http_server_filter.cc
- src/core/ext/filters/max_age/max_age_filter.cc
- src/core/ext/filters/message_size/message_size_filter.cc
@ -939,7 +941,6 @@ libs:
- src/core/lib/iomgr/endpoint_pair_windows.cc
- src/core/lib/iomgr/error.cc
- src/core/lib/iomgr/error_cfstream.cc
- src/core/lib/iomgr/ev_apple.cc
- src/core/lib/iomgr/ev_epoll1_linux.cc
- src/core/lib/iomgr/ev_epollex_linux.cc
- src/core/lib/iomgr/ev_poll_posix.cc
@ -1327,6 +1328,7 @@ libs:
- src/core/ext/filters/http/client/http_client_filter.h
- src/core/ext/filters/http/client_authority_filter.h
- src/core/ext/filters/http/message_compress/message_compress_filter.h
- src/core/ext/filters/http/message_compress/message_decompress_filter.h
- src/core/ext/filters/http/server/http_server_filter.h
- src/core/ext/filters/max_age/max_age_filter.h
- src/core/ext/filters/message_size/message_size_filter.h
@ -1467,7 +1469,6 @@ libs:
- src/core/lib/iomgr/error.h
- src/core/lib/iomgr/error_cfstream.h
- src/core/lib/iomgr/error_internal.h
- src/core/lib/iomgr/ev_apple.h
- src/core/lib/iomgr/ev_epoll1_linux.h
- src/core/lib/iomgr/ev_epollex_linux.h
- src/core/lib/iomgr/ev_poll_posix.h
@ -1596,6 +1597,7 @@ libs:
- src/core/ext/filters/client_channel/lb_policy/xds/cds.cc
- src/core/ext/filters/client_channel/lb_policy/xds/eds.cc
- src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc
- src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc
- src/core/ext/filters/client_channel/lb_policy_registry.cc
- src/core/ext/filters/client_channel/local_subchannel_pool.cc
- src/core/ext/filters/client_channel/parse_address.cc
@ -1635,6 +1637,7 @@ libs:
- src/core/ext/filters/http/client_authority_filter.cc
- src/core/ext/filters/http/http_filters_plugin.cc
- src/core/ext/filters/http/message_compress/message_compress_filter.cc
- src/core/ext/filters/http/message_compress/message_decompress_filter.cc
- src/core/ext/filters/http/server/http_server_filter.cc
- src/core/ext/filters/max_age/max_age_filter.cc
- src/core/ext/filters/message_size/message_size_filter.cc
@ -1772,7 +1775,6 @@ libs:
- src/core/lib/iomgr/endpoint_pair_windows.cc
- src/core/lib/iomgr/error.cc
- src/core/lib/iomgr/error_cfstream.cc
- src/core/lib/iomgr/ev_apple.cc
- src/core/lib/iomgr/ev_epoll1_linux.cc
- src/core/lib/iomgr/ev_epollex_linux.cc
- src/core/lib/iomgr/ev_poll_posix.cc

@ -65,6 +65,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/ext/filters/client_channel/lb_policy/xds/cds.cc \
src/core/ext/filters/client_channel/lb_policy/xds/eds.cc \
src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc \
src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc \
src/core/ext/filters/client_channel/lb_policy_registry.cc \
src/core/ext/filters/client_channel/local_subchannel_pool.cc \
src/core/ext/filters/client_channel/parse_address.cc \
@ -104,6 +105,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/ext/filters/http/client_authority_filter.cc \
src/core/ext/filters/http/http_filters_plugin.cc \
src/core/ext/filters/http/message_compress/message_compress_filter.cc \
src/core/ext/filters/http/message_compress/message_decompress_filter.cc \
src/core/ext/filters/http/server/http_server_filter.cc \
src/core/ext/filters/max_age/max_age_filter.cc \
src/core/ext/filters/message_size/message_size_filter.cc \
@ -286,7 +288,6 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/iomgr/endpoint_pair_windows.cc \
src/core/lib/iomgr/error.cc \
src/core/lib/iomgr/error_cfstream.cc \
src/core/lib/iomgr/ev_apple.cc \
src/core/lib/iomgr/ev_epoll1_linux.cc \
src/core/lib/iomgr/ev_epollex_linux.cc \
src/core/lib/iomgr/ev_poll_posix.cc \

@ -34,6 +34,7 @@ if (PHP_GRPC != "no") {
"src\\core\\ext\\filters\\client_channel\\lb_policy\\xds\\cds.cc " +
"src\\core\\ext\\filters\\client_channel\\lb_policy\\xds\\eds.cc " +
"src\\core\\ext\\filters\\client_channel\\lb_policy\\xds\\lrs.cc " +
"src\\core\\ext\\filters\\client_channel\\lb_policy\\xds\\xds_routing.cc " +
"src\\core\\ext\\filters\\client_channel\\lb_policy_registry.cc " +
"src\\core\\ext\\filters\\client_channel\\local_subchannel_pool.cc " +
"src\\core\\ext\\filters\\client_channel\\parse_address.cc " +
@ -73,6 +74,7 @@ if (PHP_GRPC != "no") {
"src\\core\\ext\\filters\\http\\client_authority_filter.cc " +
"src\\core\\ext\\filters\\http\\http_filters_plugin.cc " +
"src\\core\\ext\\filters\\http\\message_compress\\message_compress_filter.cc " +
"src\\core\\ext\\filters\\http\\message_compress\\message_decompress_filter.cc " +
"src\\core\\ext\\filters\\http\\server\\http_server_filter.cc " +
"src\\core\\ext\\filters\\max_age\\max_age_filter.cc " +
"src\\core\\ext\\filters\\message_size\\message_size_filter.cc " +
@ -255,7 +257,6 @@ if (PHP_GRPC != "no") {
"src\\core\\lib\\iomgr\\endpoint_pair_windows.cc " +
"src\\core\\lib\\iomgr\\error.cc " +
"src\\core\\lib\\iomgr\\error_cfstream.cc " +
"src\\core\\lib\\iomgr\\ev_apple.cc " +
"src\\core\\lib\\iomgr\\ev_epoll1_linux.cc " +
"src\\core\\lib\\iomgr\\ev_epollex_linux.cc " +
"src\\core\\lib\\iomgr\\ev_poll_posix.cc " +

@ -274,6 +274,7 @@ Pod::Spec.new do |s|
'src/core/ext/filters/http/client/http_client_filter.h',
'src/core/ext/filters/http/client_authority_filter.h',
'src/core/ext/filters/http/message_compress/message_compress_filter.h',
'src/core/ext/filters/http/message_compress/message_decompress_filter.h',
'src/core/ext/filters/http/server/http_server_filter.h',
'src/core/ext/filters/max_age/max_age_filter.h',
'src/core/ext/filters/message_size/message_size_filter.h',
@ -445,7 +446,6 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/error.h',
'src/core/lib/iomgr/error_cfstream.h',
'src/core/lib/iomgr/error_internal.h',
'src/core/lib/iomgr/ev_apple.h',
'src/core/lib/iomgr/ev_epoll1_linux.h',
'src/core/lib/iomgr/ev_epollex_linux.h',
'src/core/lib/iomgr/ev_poll_posix.h',
@ -726,6 +726,7 @@ Pod::Spec.new do |s|
'src/core/ext/filters/http/client/http_client_filter.h',
'src/core/ext/filters/http/client_authority_filter.h',
'src/core/ext/filters/http/message_compress/message_compress_filter.h',
'src/core/ext/filters/http/message_compress/message_decompress_filter.h',
'src/core/ext/filters/http/server/http_server_filter.h',
'src/core/ext/filters/max_age/max_age_filter.h',
'src/core/ext/filters/message_size/message_size_filter.h',
@ -897,7 +898,6 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/error.h',
'src/core/lib/iomgr/error_cfstream.h',
'src/core/lib/iomgr/error_internal.h',
'src/core/lib/iomgr/ev_apple.h',
'src/core/lib/iomgr/ev_epoll1_linux.h',
'src/core/lib/iomgr/ev_epollex_linux.h',
'src/core/lib/iomgr/ev_poll_posix.h',

@ -231,6 +231,7 @@ Pod::Spec.new do |s|
'src/core/ext/filters/client_channel/lb_policy/xds/eds.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/xds.h',
'src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.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',
@ -301,6 +302,8 @@ Pod::Spec.new do |s|
'src/core/ext/filters/http/http_filters_plugin.cc',
'src/core/ext/filters/http/message_compress/message_compress_filter.cc',
'src/core/ext/filters/http/message_compress/message_compress_filter.h',
'src/core/ext/filters/http/message_compress/message_decompress_filter.cc',
'src/core/ext/filters/http/message_compress/message_decompress_filter.h',
'src/core/ext/filters/http/server/http_server_filter.cc',
'src/core/ext/filters/http/server/http_server_filter.h',
'src/core/ext/filters/max_age/max_age_filter.cc',
@ -654,8 +657,6 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/error_cfstream.cc',
'src/core/lib/iomgr/error_cfstream.h',
'src/core/lib/iomgr/error_internal.h',
'src/core/lib/iomgr/ev_apple.cc',
'src/core/lib/iomgr/ev_apple.h',
'src/core/lib/iomgr/ev_epoll1_linux.cc',
'src/core/lib/iomgr/ev_epoll1_linux.h',
'src/core/lib/iomgr/ev_epollex_linux.cc',
@ -1080,6 +1081,7 @@ Pod::Spec.new do |s|
'src/core/ext/filters/http/client/http_client_filter.h',
'src/core/ext/filters/http/client_authority_filter.h',
'src/core/ext/filters/http/message_compress/message_compress_filter.h',
'src/core/ext/filters/http/message_compress/message_decompress_filter.h',
'src/core/ext/filters/http/server/http_server_filter.h',
'src/core/ext/filters/max_age/max_age_filter.h',
'src/core/ext/filters/message_size/message_size_filter.h',
@ -1251,7 +1253,6 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/error.h',
'src/core/lib/iomgr/error_cfstream.h',
'src/core/lib/iomgr/error_internal.h',
'src/core/lib/iomgr/ev_apple.h',
'src/core/lib/iomgr/ev_epoll1_linux.h',
'src/core/lib/iomgr/ev_epollex_linux.h',
'src/core/lib/iomgr/ev_poll_posix.h',

@ -153,6 +153,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/filters/client_channel/lb_policy/xds/eds.cc )
s.files += %w( src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc )
s.files += %w( src/core/ext/filters/client_channel/lb_policy/xds/xds.h )
s.files += %w( src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc )
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.cc )
s.files += %w( src/core/ext/filters/client_channel/lb_policy_registry.h )
@ -223,6 +224,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/filters/http/http_filters_plugin.cc )
s.files += %w( src/core/ext/filters/http/message_compress/message_compress_filter.cc )
s.files += %w( src/core/ext/filters/http/message_compress/message_compress_filter.h )
s.files += %w( src/core/ext/filters/http/message_compress/message_decompress_filter.cc )
s.files += %w( src/core/ext/filters/http/message_compress/message_decompress_filter.h )
s.files += %w( src/core/ext/filters/http/server/http_server_filter.cc )
s.files += %w( src/core/ext/filters/http/server/http_server_filter.h )
s.files += %w( src/core/ext/filters/max_age/max_age_filter.cc )
@ -576,8 +579,6 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/iomgr/error_cfstream.cc )
s.files += %w( src/core/lib/iomgr/error_cfstream.h )
s.files += %w( src/core/lib/iomgr/error_internal.h )
s.files += %w( src/core/lib/iomgr/ev_apple.cc )
s.files += %w( src/core/lib/iomgr/ev_apple.h )
s.files += %w( src/core/lib/iomgr/ev_epoll1_linux.cc )
s.files += %w( src/core/lib/iomgr/ev_epoll1_linux.h )
s.files += %w( src/core/lib/iomgr/ev_epollex_linux.cc )

@ -458,6 +458,7 @@
'src/core/ext/filters/client_channel/lb_policy/xds/cds.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/eds.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc',
'src/core/ext/filters/client_channel/lb_policy_registry.cc',
'src/core/ext/filters/client_channel/local_subchannel_pool.cc',
'src/core/ext/filters/client_channel/parse_address.cc',
@ -497,6 +498,7 @@
'src/core/ext/filters/http/client_authority_filter.cc',
'src/core/ext/filters/http/http_filters_plugin.cc',
'src/core/ext/filters/http/message_compress/message_compress_filter.cc',
'src/core/ext/filters/http/message_compress/message_decompress_filter.cc',
'src/core/ext/filters/http/server/http_server_filter.cc',
'src/core/ext/filters/max_age/max_age_filter.cc',
'src/core/ext/filters/message_size/message_size_filter.cc',
@ -640,7 +642,6 @@
'src/core/lib/iomgr/endpoint_pair_windows.cc',
'src/core/lib/iomgr/error.cc',
'src/core/lib/iomgr/error_cfstream.cc',
'src/core/lib/iomgr/ev_apple.cc',
'src/core/lib/iomgr/ev_epoll1_linux.cc',
'src/core/lib/iomgr/ev_epollex_linux.cc',
'src/core/lib/iomgr/ev_poll_posix.cc',
@ -954,6 +955,7 @@
'src/core/ext/filters/client_channel/lb_policy/xds/cds.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/eds.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc',
'src/core/ext/filters/client_channel/lb_policy_registry.cc',
'src/core/ext/filters/client_channel/local_subchannel_pool.cc',
'src/core/ext/filters/client_channel/parse_address.cc',
@ -993,6 +995,7 @@
'src/core/ext/filters/http/client_authority_filter.cc',
'src/core/ext/filters/http/http_filters_plugin.cc',
'src/core/ext/filters/http/message_compress/message_compress_filter.cc',
'src/core/ext/filters/http/message_compress/message_decompress_filter.cc',
'src/core/ext/filters/http/server/http_server_filter.cc',
'src/core/ext/filters/max_age/max_age_filter.cc',
'src/core/ext/filters/message_size/message_size_filter.cc',
@ -1130,7 +1133,6 @@
'src/core/lib/iomgr/endpoint_pair_windows.cc',
'src/core/lib/iomgr/error.cc',
'src/core/lib/iomgr/error_cfstream.cc',
'src/core/lib/iomgr/ev_apple.cc',
'src/core/lib/iomgr/ev_epoll1_linux.cc',
'src/core/lib/iomgr/ev_epollex_linux.cc',
'src/core/lib/iomgr/ev_poll_posix.cc',

@ -174,6 +174,11 @@ typedef struct {
/** Enable/disable support for per-message compression. Defaults to 1, unless
GRPC_ARG_MINIMAL_STACK is enabled, in which case it defaults to 0. */
#define GRPC_ARG_ENABLE_PER_MESSAGE_COMPRESSION "grpc.per_message_compression"
/** Experimental Arg. Enable/disable support for per-message decompression.
Defaults to 1. If disabled, decompression will not be performed and the
application will see the compressed message in the byte buffer. */
#define GRPC_ARG_ENABLE_PER_MESSAGE_DECOMPRESSION \
"grpc.per_message_decompression"
/** Enable/disable support for deadline checking. Defaults to 1, unless
GRPC_ARG_MINIMAL_STACK is enabled, in which case it defaults to 0 */
#define GRPC_ARG_ENABLE_DEADLINE_CHECKS "grpc.enable_deadline_checking"
@ -354,6 +359,11 @@ typedef struct {
* The default is 15 seconds. */
#define GRPC_ARG_XDS_RESOURCE_DOES_NOT_EXIST_TIMEOUT_MS \
"grpc.xds_resource_does_not_exist_timeout_ms"
/* If set, enable xds routing policy. This boolean argument is currently
* disabled by default; however, it will be changed to enabled by default
* once the functionality proves stable. This arg will eventually
* be removed completely. */
#define GRPC_ARG_XDS_ROUTING_ENABLED "grpc.xds_routing_enabled"
/** If non-zero, grpc server's cronet compression workaround will be enabled */
#define GRPC_ARG_WORKAROUND_CRONET_COMPRESSION \
"grpc.workaround.cronet_compression"

@ -133,6 +133,7 @@
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/xds/eds.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/xds/xds.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc" 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.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy_registry.h" role="src" />
@ -203,6 +204,8 @@
<file baseinstalldir="/" name="src/core/ext/filters/http/http_filters_plugin.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/http/message_compress/message_compress_filter.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/http/message_compress/message_compress_filter.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/http/message_compress/message_decompress_filter.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/http/message_compress/message_decompress_filter.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/http/server/http_server_filter.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/http/server/http_server_filter.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/max_age/max_age_filter.cc" role="src" />
@ -556,8 +559,6 @@
<file baseinstalldir="/" name="src/core/lib/iomgr/error_cfstream.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/error_cfstream.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/error_internal.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/ev_apple.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/ev_apple.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/ev_epoll1_linux.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/ev_epoll1_linux.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/ev_epollex_linux.cc" role="src" />

@ -0,0 +1,833 @@
//
// 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 <inttypes.h>
#include <limits.h>
#include <string.h>
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include <grpc/grpc.h>
#include "src/core/ext/filters/client_channel/lb_policy.h"
#include "src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h"
#include "src/core/ext/filters/client_channel/lb_policy_factory.h"
#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/iomgr/combiner.h"
#include "src/core/lib/iomgr/timer.h"
#define GRPC_XDS_ROUTING_CHILD_RETENTION_INTERVAL_MS (15 * 60 * 1000)
namespace grpc_core {
TraceFlag grpc_xds_routing_lb_trace(false, "xds_routing_lb");
namespace {
constexpr char kXdsRouting[] = "xds_routing_experimental";
// Config for xds_routing LB policy.
class XdsRoutingLbConfig : public LoadBalancingPolicy::Config {
public:
struct Matcher {
std::string service;
std::string method;
};
struct Route {
Matcher matcher;
std::string action;
};
using RouteTable = std::vector<Route>;
using ActionMap =
std::map<std::string, RefCountedPtr<LoadBalancingPolicy::Config>>;
XdsRoutingLbConfig(ActionMap action_map, RouteTable route_table)
: action_map_(std::move(action_map)),
route_table_(std::move(route_table)) {}
const char* name() const override { return kXdsRouting; }
const ActionMap& action_map() const { return action_map_; }
const RouteTable& route_table() const { return route_table_; }
private:
ActionMap action_map_;
RouteTable route_table_;
};
// xds_routing LB policy.
class XdsRoutingLb : public LoadBalancingPolicy {
public:
explicit XdsRoutingLb(Args args);
const char* name() const override { return kXdsRouting; }
void UpdateLocked(UpdateArgs args) override;
void ExitIdleLocked() override;
void ResetBackoffLocked() override;
private:
// A simple wrapper for ref-counting a picker from the child policy.
class ChildPickerWrapper : public RefCounted<ChildPickerWrapper> {
public:
ChildPickerWrapper(std::string name,
std::unique_ptr<SubchannelPicker> picker)
: name_(std::move(name)), picker_(std::move(picker)) {}
PickResult Pick(PickArgs args) { return picker_->Pick(args); }
const std::string& name() const { return name_; }
private:
std::string name_;
std::unique_ptr<SubchannelPicker> picker_;
};
// Picks a child using prefix or path matching and then delegates to that
// child's picker.
class RoutePicker : public SubchannelPicker {
public:
struct Route {
XdsRoutingLbConfig::Matcher matcher;
RefCountedPtr<ChildPickerWrapper> picker;
};
// Maintains an ordered xds route table as provided by RDS response.
using RouteTable = std::vector<Route>;
explicit RoutePicker(RouteTable route_table)
: route_table_(std::move(route_table)) {}
PickResult Pick(PickArgs args) override;
private:
RouteTable route_table_;
};
// Each XdsRoutingChild holds a ref to its parent XdsRoutingLb.
class XdsRoutingChild : public InternallyRefCounted<XdsRoutingChild> {
public:
XdsRoutingChild(RefCountedPtr<XdsRoutingLb> xds_routing_policy,
const std::string& name);
~XdsRoutingChild();
void Orphan() override;
void UpdateLocked(RefCountedPtr<LoadBalancingPolicy::Config> config,
const ServerAddressList& addresses,
const grpc_channel_args* args);
void ExitIdleLocked();
void ResetBackoffLocked();
void DeactivateLocked();
grpc_connectivity_state connectivity_state() const {
return connectivity_state_;
}
RefCountedPtr<ChildPickerWrapper> picker_wrapper() const {
return picker_wrapper_;
}
private:
class Helper : public ChannelControlHelper {
public:
explicit Helper(RefCountedPtr<XdsRoutingChild> xds_routing_child)
: xds_routing_child_(std::move(xds_routing_child)) {}
~Helper() { xds_routing_child_.reset(DEBUG_LOCATION, "Helper"); }
RefCountedPtr<SubchannelInterface> CreateSubchannel(
const grpc_channel_args& args) override;
void UpdateState(grpc_connectivity_state state,
std::unique_ptr<SubchannelPicker> picker) override;
void RequestReresolution() override;
void AddTraceEvent(TraceSeverity severity, StringView message) override;
private:
RefCountedPtr<XdsRoutingChild> xds_routing_child_;
};
// Methods for dealing with the child policy.
OrphanablePtr<LoadBalancingPolicy> CreateChildPolicyLocked(
const grpc_channel_args* args);
static void OnDelayedRemovalTimer(void* arg, grpc_error* error);
static void OnDelayedRemovalTimerLocked(void* arg, grpc_error* error);
// The owning LB policy.
RefCountedPtr<XdsRoutingLb> xds_routing_policy_;
// Points to the corresponding key in XdsRoutingLb::actions_.
const std::string& name_;
OrphanablePtr<LoadBalancingPolicy> child_policy_;
RefCountedPtr<ChildPickerWrapper> picker_wrapper_;
grpc_connectivity_state connectivity_state_ = GRPC_CHANNEL_IDLE;
bool seen_failure_since_ready_ = false;
// States for delayed removal.
grpc_timer delayed_removal_timer_;
grpc_closure on_delayed_removal_timer_;
bool delayed_removal_timer_callback_pending_ = false;
bool shutdown_ = false;
};
~XdsRoutingLb();
void ShutdownLocked() override;
void UpdateStateLocked();
// Current config from the resolver.
RefCountedPtr<XdsRoutingLbConfig> config_;
// Internal state.
bool shutting_down_ = false;
// Children.
std::map<std::string, OrphanablePtr<XdsRoutingChild>> actions_;
};
//
// XdsRoutingLb::RoutePicker
//
XdsRoutingLb::PickResult XdsRoutingLb::RoutePicker::Pick(PickArgs args) {
absl::string_view path;
// TODO(roth): Using const auto& here trigger a warning in a macos or windows
// build:
//*(args.initial_metadata) is returning values not references.
for (const auto p : *(args.initial_metadata)) {
if (p.first == ":path") {
path = p.second;
break;
}
}
std::vector<absl::string_view> path_elements =
absl::StrSplit(path.substr(1), '/');
for (const Route& route : route_table_) {
if ((path_elements[0] == route.matcher.service &&
(path_elements[1] == route.matcher.method ||
route.matcher.method.empty())) ||
(route.matcher.service.empty() && route.matcher.method.empty())) {
return route.picker.get()->Pick(args);
}
}
PickResult result;
result.type = PickResult::PICK_FAILED;
result.error =
grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"xds routing picker: no matching route"),
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_INTERNAL);
return result;
}
//
// XdsRoutingLb
//
XdsRoutingLb::XdsRoutingLb(Args args) : LoadBalancingPolicy(std::move(args)) {}
XdsRoutingLb::~XdsRoutingLb() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_routing_lb_trace)) {
gpr_log(GPR_INFO, "[xds_routing_lb %p] destroying xds_routing LB policy",
this);
}
}
void XdsRoutingLb::ShutdownLocked() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_routing_lb_trace)) {
gpr_log(GPR_INFO, "[xds_routing_lb %p] shutting down", this);
}
shutting_down_ = true;
actions_.clear();
}
void XdsRoutingLb::ExitIdleLocked() {
for (auto& p : actions_) p.second->ExitIdleLocked();
}
void XdsRoutingLb::ResetBackoffLocked() {
for (auto& p : actions_) p.second->ResetBackoffLocked();
}
void XdsRoutingLb::UpdateLocked(UpdateArgs args) {
if (shutting_down_) return;
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_routing_lb_trace)) {
gpr_log(GPR_INFO, "[xds_routing_lb %p] Received update", this);
}
// Update config.
config_ = std::move(args.config);
// Deactivate the actions not in the new config.
for (const auto& p : actions_) {
const std::string& name = p.first;
XdsRoutingChild* child = p.second.get();
if (config_->action_map().find(name) == config_->action_map().end()) {
child->DeactivateLocked();
}
}
// Add or update the actions in the new config.
for (const auto& p : config_->action_map()) {
const std::string& name = p.first;
const RefCountedPtr<LoadBalancingPolicy::Config>& config = p.second;
auto it = actions_.find(name);
if (it == actions_.end()) {
it = actions_.emplace(std::make_pair(name, nullptr)).first;
it->second = MakeOrphanable<XdsRoutingChild>(
Ref(DEBUG_LOCATION, "XdsRoutingChild"), it->first);
}
it->second->UpdateLocked(config, args.addresses, args.args);
}
}
void XdsRoutingLb::UpdateStateLocked() {
// Also count the number of children in each state, to determine the
// overall state.
size_t num_ready = 0;
size_t num_connecting = 0;
size_t num_idle = 0;
size_t num_transient_failures = 0;
for (const auto& p : actions_) {
const auto& child_name = p.first;
const XdsRoutingChild* child = p.second.get();
// Skip the actions that are not in the latest update.
if (config_->action_map().find(child_name) == config_->action_map().end()) {
continue;
}
switch (child->connectivity_state()) {
case GRPC_CHANNEL_READY: {
++num_ready;
break;
}
case GRPC_CHANNEL_CONNECTING: {
++num_connecting;
break;
}
case GRPC_CHANNEL_IDLE: {
++num_idle;
break;
}
case GRPC_CHANNEL_TRANSIENT_FAILURE: {
++num_transient_failures;
break;
}
default:
GPR_UNREACHABLE_CODE(return );
}
}
// Determine aggregated connectivity state.
grpc_connectivity_state connectivity_state;
if (num_ready > 0) {
connectivity_state = GRPC_CHANNEL_READY;
} else if (num_connecting > 0) {
connectivity_state = GRPC_CHANNEL_CONNECTING;
} else if (num_idle > 0) {
connectivity_state = GRPC_CHANNEL_IDLE;
} else {
connectivity_state = GRPC_CHANNEL_TRANSIENT_FAILURE;
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_routing_lb_trace)) {
gpr_log(GPR_INFO, "[xds_routing_lb %p] connectivity changed to %s", this,
ConnectivityStateName(connectivity_state));
}
std::unique_ptr<SubchannelPicker> picker;
switch (connectivity_state) {
case GRPC_CHANNEL_READY: {
RoutePicker::RouteTable route_table;
for (const auto& config_route : config_->route_table()) {
RoutePicker::Route route;
route.matcher = config_route.matcher;
route.picker = actions_[config_route.action]->picker_wrapper();
if (route.picker == nullptr) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_routing_lb_trace)) {
gpr_log(GPR_INFO,
"[xds_routing_lb %p] child %s has not yet returned a "
"picker; creating a QueuePicker.",
this, config_route.action.c_str());
}
route.picker = MakeRefCounted<ChildPickerWrapper>(
config_route.action, absl::make_unique<QueuePicker>(
Ref(DEBUG_LOCATION, "QueuePicker")));
}
route_table.push_back(std::move(route));
}
picker = absl::make_unique<RoutePicker>(std::move(route_table));
break;
}
case GRPC_CHANNEL_CONNECTING:
case GRPC_CHANNEL_IDLE:
picker =
absl::make_unique<QueuePicker>(Ref(DEBUG_LOCATION, "QueuePicker"));
break;
default:
picker = absl::make_unique<TransientFailurePicker>(grpc_error_set_int(
GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"TRANSIENT_FAILURE from XdsRoutingLb"),
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE));
}
channel_control_helper()->UpdateState(connectivity_state, std::move(picker));
}
//
// XdsRoutingLb::XdsRoutingChild
//
XdsRoutingLb::XdsRoutingChild::XdsRoutingChild(
RefCountedPtr<XdsRoutingLb> xds_routing_policy, const std::string& name)
: xds_routing_policy_(std::move(xds_routing_policy)), name_(name) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_routing_lb_trace)) {
gpr_log(GPR_INFO, "[xds_routing_lb %p] created XdsRoutingChild %p for %s",
xds_routing_policy_.get(), this, name_.c_str());
}
}
XdsRoutingLb::XdsRoutingChild::~XdsRoutingChild() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_routing_lb_trace)) {
gpr_log(GPR_INFO,
"[xds_routing_lb %p] XdsRoutingChild %p: destroying child",
xds_routing_policy_.get(), this);
}
xds_routing_policy_.reset(DEBUG_LOCATION, "XdsRoutingChild");
}
void XdsRoutingLb::XdsRoutingChild::Orphan() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_routing_lb_trace)) {
gpr_log(GPR_INFO,
"[xds_routing_lb %p] XdsRoutingChild %p %s: shutting down child",
xds_routing_policy_.get(), this, name_.c_str());
}
// Remove the child policy's interested_parties pollset_set from the
// xDS policy.
grpc_pollset_set_del_pollset_set(child_policy_->interested_parties(),
xds_routing_policy_->interested_parties());
child_policy_.reset();
// Drop our ref to the child's picker, in case it's holding a ref to
// the child.
picker_wrapper_.reset();
if (delayed_removal_timer_callback_pending_) {
grpc_timer_cancel(&delayed_removal_timer_);
}
shutdown_ = true;
Unref();
}
OrphanablePtr<LoadBalancingPolicy>
XdsRoutingLb::XdsRoutingChild::CreateChildPolicyLocked(
const grpc_channel_args* args) {
LoadBalancingPolicy::Args lb_policy_args;
lb_policy_args.combiner = xds_routing_policy_->combiner();
lb_policy_args.args = args;
lb_policy_args.channel_control_helper =
absl::make_unique<Helper>(this->Ref(DEBUG_LOCATION, "Helper"));
OrphanablePtr<LoadBalancingPolicy> lb_policy =
MakeOrphanable<ChildPolicyHandler>(std::move(lb_policy_args),
&grpc_xds_routing_lb_trace);
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_routing_lb_trace)) {
gpr_log(GPR_INFO,
"[xds_routing_lb %p] XdsRoutingChild %p %s: Created new child "
"policy handler %p",
xds_routing_policy_.get(), this, name_.c_str(), lb_policy.get());
}
// 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(lb_policy->interested_parties(),
xds_routing_policy_->interested_parties());
return lb_policy;
}
void XdsRoutingLb::XdsRoutingChild::UpdateLocked(
RefCountedPtr<LoadBalancingPolicy::Config> config,
const ServerAddressList& addresses, const grpc_channel_args* args) {
if (xds_routing_policy_->shutting_down_) return;
// Update child weight.
// Reactivate if needed.
if (delayed_removal_timer_callback_pending_) {
delayed_removal_timer_callback_pending_ = false;
grpc_timer_cancel(&delayed_removal_timer_);
}
// Create child policy if needed.
if (child_policy_ == nullptr) {
child_policy_ = CreateChildPolicyLocked(args);
}
// Construct update args.
UpdateArgs update_args;
update_args.config = std::move(config);
update_args.addresses = addresses;
update_args.args = grpc_channel_args_copy(args);
// Update the policy.
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_routing_lb_trace)) {
gpr_log(GPR_INFO,
"[xds_routing_lb %p] XdsRoutingChild %p %s: Updating child "
"policy handler %p",
xds_routing_policy_.get(), this, name_.c_str(),
child_policy_.get());
}
child_policy_->UpdateLocked(std::move(update_args));
}
void XdsRoutingLb::XdsRoutingChild::ExitIdleLocked() {
child_policy_->ExitIdleLocked();
}
void XdsRoutingLb::XdsRoutingChild::ResetBackoffLocked() {
child_policy_->ResetBackoffLocked();
}
void XdsRoutingLb::XdsRoutingChild::DeactivateLocked() {
// If already deactivated, don't do that again.
if (delayed_removal_timer_callback_pending_ == true) return;
// Set the child weight to 0 so that future picker won't contain this child.
// Start a timer to delete the child.
Ref(DEBUG_LOCATION, "XdsRoutingChild+timer").release();
GRPC_CLOSURE_INIT(&on_delayed_removal_timer_, OnDelayedRemovalTimer, this,
grpc_schedule_on_exec_ctx);
grpc_timer_init(
&delayed_removal_timer_,
ExecCtx::Get()->Now() + GRPC_XDS_ROUTING_CHILD_RETENTION_INTERVAL_MS,
&on_delayed_removal_timer_);
delayed_removal_timer_callback_pending_ = true;
}
void XdsRoutingLb::XdsRoutingChild::OnDelayedRemovalTimer(void* arg,
grpc_error* error) {
XdsRoutingChild* self = static_cast<XdsRoutingChild*>(arg);
self->xds_routing_policy_->combiner()->Run(
GRPC_CLOSURE_INIT(&self->on_delayed_removal_timer_,
OnDelayedRemovalTimerLocked, self, nullptr),
GRPC_ERROR_REF(error));
}
void XdsRoutingLb::XdsRoutingChild::OnDelayedRemovalTimerLocked(
void* arg, grpc_error* error) {
XdsRoutingChild* self = static_cast<XdsRoutingChild*>(arg);
self->delayed_removal_timer_callback_pending_ = false;
if (error == GRPC_ERROR_NONE && !self->shutdown_) {
self->xds_routing_policy_->actions_.erase(self->name_);
}
self->Unref(DEBUG_LOCATION, "XdsRoutingChild+timer");
}
//
// XdsRoutingLb::XdsRoutingChild::Helper
//
RefCountedPtr<SubchannelInterface>
XdsRoutingLb::XdsRoutingChild::Helper::CreateSubchannel(
const grpc_channel_args& args) {
if (xds_routing_child_->xds_routing_policy_->shutting_down_) return nullptr;
return xds_routing_child_->xds_routing_policy_->channel_control_helper()
->CreateSubchannel(args);
}
void XdsRoutingLb::XdsRoutingChild::Helper::UpdateState(
grpc_connectivity_state state, std::unique_ptr<SubchannelPicker> picker) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_routing_lb_trace)) {
gpr_log(GPR_INFO,
"[xds_routing_lb %p] child %s: received update: state=%s picker=%p",
xds_routing_child_->xds_routing_policy_.get(),
xds_routing_child_->name_.c_str(), ConnectivityStateName(state),
picker.get());
}
if (xds_routing_child_->xds_routing_policy_->shutting_down_) return;
// Cache the picker in the XdsRoutingChild.
xds_routing_child_->picker_wrapper_ = MakeRefCounted<ChildPickerWrapper>(
xds_routing_child_->name_, std::move(picker));
// Decide what state to report for aggregation purposes.
// If we haven't seen a failure since the last time we were in state
// READY, then we report the state change as-is. However, once we do see
// a failure, we report TRANSIENT_FAILURE and ignore any subsequent state
// changes until we go back into state READY.
if (!xds_routing_child_->seen_failure_since_ready_) {
if (state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
xds_routing_child_->seen_failure_since_ready_ = true;
}
} else {
if (state != GRPC_CHANNEL_READY) return;
xds_routing_child_->seen_failure_since_ready_ = false;
}
xds_routing_child_->connectivity_state_ = state;
// Notify the LB policy.
xds_routing_child_->xds_routing_policy_->UpdateStateLocked();
}
void XdsRoutingLb::XdsRoutingChild::Helper::RequestReresolution() {
if (xds_routing_child_->xds_routing_policy_->shutting_down_) return;
xds_routing_child_->xds_routing_policy_->channel_control_helper()
->RequestReresolution();
}
void XdsRoutingLb::XdsRoutingChild::Helper::AddTraceEvent(
TraceSeverity severity, StringView message) {
if (xds_routing_child_->xds_routing_policy_->shutting_down_) return;
xds_routing_child_->xds_routing_policy_->channel_control_helper()
->AddTraceEvent(severity, message);
}
//
// factory
//
class XdsRoutingLbFactory : public LoadBalancingPolicyFactory {
public:
OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
LoadBalancingPolicy::Args args) const override {
return MakeOrphanable<XdsRoutingLb>(std::move(args));
}
const char* name() const override { return kXdsRouting; }
RefCountedPtr<LoadBalancingPolicy::Config> ParseLoadBalancingConfig(
const Json& json, grpc_error** error) const override {
GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
if (json.type() == Json::Type::JSON_NULL) {
// xds_routing was mentioned as a policy in the deprecated
// loadBalancingPolicy field or in the client API.
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:loadBalancingPolicy error:xds_routing policy requires "
"configuration. Please use loadBalancingConfig field of service "
"config instead.");
return nullptr;
}
std::vector<grpc_error*> error_list;
// action map.
XdsRoutingLbConfig::ActionMap action_map;
std::set<std::string /*action_name*/> actions_to_be_used;
auto it = json.object_value().find("actions");
if (it == json.object_value().end()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:actions error:required field not present"));
} else if (it->second.type() != Json::Type::OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:actions error:type should be object"));
} else {
for (const auto& p : it->second.object_value()) {
if (p.first.empty()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:actions element error: name cannot be empty"));
continue;
}
RefCountedPtr<LoadBalancingPolicy::Config> child_config;
std::vector<grpc_error*> child_errors =
ParseChildConfig(p.second, &child_config);
if (!child_errors.empty()) {
// Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
// string is not static in this case.
grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("field:actions name:", p.first).c_str());
for (grpc_error* child_error : child_errors) {
error = grpc_error_add_child(error, child_error);
}
error_list.push_back(error);
} else {
action_map[p.first] = std::move(child_config);
actions_to_be_used.insert(p.first);
}
}
}
if (action_map.empty()) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("no valid actions configured"));
}
XdsRoutingLbConfig::RouteTable route_table;
it = json.object_value().find("routes");
if (it == json.object_value().end()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:routes error:required field not present"));
} else if (it->second.type() != Json::Type::ARRAY) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:routes error:type should be array"));
} else {
const Json::Array& array = it->second.array_value();
for (size_t i = 0; i < array.size(); ++i) {
XdsRoutingLbConfig::Route route;
std::vector<grpc_error*> route_errors =
ParseRoute(array[i], action_map, &route, &actions_to_be_used);
if (!route_errors.empty()) {
// Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
// string is not static in this case.
grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("field:routes element: ", i, " error").c_str());
for (grpc_error* route_error : route_errors) {
error = grpc_error_add_child(error, route_error);
}
error_list.push_back(error);
}
route_table.emplace_back(std::move(route));
}
}
if (route_table.empty()) {
grpc_error* error =
GRPC_ERROR_CREATE_FROM_STATIC_STRING("no valid routes configured");
error_list.push_back(error);
}
if (!route_table.back().matcher.service.empty() ||
!route_table.back().matcher.method.empty()) {
grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"default route must not contain service or method");
error_list.push_back(error);
}
if (!actions_to_be_used.empty()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"some actions were not referenced by any route"));
}
if (!error_list.empty()) {
*error = GRPC_ERROR_CREATE_FROM_VECTOR(
"xds_routing_experimental LB policy config", &error_list);
return nullptr;
}
return MakeRefCounted<XdsRoutingLbConfig>(std::move(action_map),
std::move(route_table));
}
private:
static std::vector<grpc_error*> ParseChildConfig(
const Json& json,
RefCountedPtr<LoadBalancingPolicy::Config>* child_config) {
std::vector<grpc_error*> error_list;
if (json.type() != Json::Type::OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"value should be of type object"));
return error_list;
}
auto it = json.object_value().find("child_policy");
if (it == json.object_value().end()) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("did not find childPolicy"));
} else {
grpc_error* parse_error = GRPC_ERROR_NONE;
*child_config = LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
it->second, &parse_error);
if (*child_config == nullptr) {
GPR_DEBUG_ASSERT(parse_error != GRPC_ERROR_NONE);
std::vector<grpc_error*> child_errors;
child_errors.push_back(parse_error);
error_list.push_back(
GRPC_ERROR_CREATE_FROM_VECTOR("field:childPolicy", &child_errors));
}
}
return error_list;
}
static std::vector<grpc_error*> ParseMethodName(
const Json& json, XdsRoutingLbConfig::Matcher* route_config) {
std::vector<grpc_error*> error_list;
if (json.type() != Json::Type::OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"value should be of type object"));
return error_list;
}
// Parse service
auto it = json.object_value().find("service");
if (it != json.object_value().end()) {
if (it->second.type() != Json::Type::STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:service error: should be string"));
} else {
route_config->service = it->second.string_value();
}
}
// Parse method
it = json.object_value().find("method");
if (it != json.object_value().end()) {
if (it->second.type() != Json::Type::STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:method error: should be string"));
} else {
route_config->method = it->second.string_value();
}
}
if (route_config->service.empty() && !route_config->method.empty()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"service is empty when method is not"));
}
return error_list;
}
static std::vector<grpc_error*> ParseRoute(
const Json& json, const XdsRoutingLbConfig::ActionMap& action_map,
XdsRoutingLbConfig::Route* route,
std::set<std::string /*action_name*/>* actions_to_be_used) {
std::vector<grpc_error*> error_list;
if (json.type() != Json::Type::OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"value should be of type object"));
return error_list;
}
// Parse MethodName.
auto it = json.object_value().find("methodName");
if (it == json.object_value().end()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:methodName error:required field missing"));
} else {
std::vector<grpc_error*> method_name_errors =
ParseMethodName(it->second, &route->matcher);
if (!method_name_errors.empty()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_VECTOR(
"field:methodName", &method_name_errors));
}
}
// Parse action.
it = json.object_value().find("action");
if (it == json.object_value().end()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:action error:required field missing"));
} else if (it->second.type() != Json::Type::STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:action error:should be of type string"));
} else {
route->action = it->second.string_value();
if (route->action.empty()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:action error:cannot be empty"));
} else {
// Validate action exists and mark it as used.
if (action_map.find(route->action) == action_map.end()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
absl::StrCat("field:action error:", route->action,
" does not exist")
.c_str()));
}
actions_to_be_used->erase(route->action);
}
}
return error_list;
}
};
} // namespace
} // namespace grpc_core
//
// Plugin registration
//
void grpc_lb_policy_xds_routing_init() {
grpc_core::LoadBalancingPolicyRegistry::Builder::
RegisterLoadBalancingPolicyFactory(
absl::make_unique<grpc_core::XdsRoutingLbFactory>());
}
void grpc_lb_policy_xds_routing_shutdown() {}

@ -24,6 +24,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include <grpc/impl/codegen/log.h>
#include <grpc/support/alloc.h>
@ -951,7 +952,8 @@ MatchType DomainPatternMatchType(const std::string& domain_pattern) {
grpc_error* RouteConfigParse(
XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_RouteConfiguration* route_config,
const std::string& expected_server_name, XdsApi::RdsUpdate* rds_update) {
const std::string& expected_server_name, const bool xds_routing_enabled,
XdsApi::RdsUpdate* rds_update) {
MaybeLogRouteConfiguration(client, tracer, route_config);
// Get the virtual hosts.
size_t size;
@ -1011,41 +1013,105 @@ grpc_error* RouteConfigParse(
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No route found in the virtual host.");
}
// Only look at the last one in the route list (the default route),
const envoy_api_v2_route_Route* route = routes[size - 1];
// Validate that the match field must have a prefix field which is an empty
// string.
const envoy_api_v2_route_RouteMatch* match =
envoy_api_v2_route_Route_match(route);
if (!envoy_api_v2_route_RouteMatch_has_prefix(match)) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No prefix field found in RouteMatch.");
}
const upb_strview prefix = envoy_api_v2_route_RouteMatch_prefix(match);
if (!upb_strview_eql(prefix, upb_strview_makez("")) &&
!upb_strview_eql(prefix, upb_strview_makez("/"))) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Prefix is not \"\" or \"/\".");
}
if (!envoy_api_v2_route_Route_has_route(route)) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No RouteAction found in route.");
// If xds_routing is not configured, only look at the last one in the route
// list (the default route)
size_t start_index = xds_routing_enabled ? 0 : size - 1;
for (size_t i = start_index; i < size; ++i) {
const envoy_api_v2_route_Route* route = routes[i];
const envoy_api_v2_route_RouteMatch* match =
envoy_api_v2_route_Route_match(route);
XdsApi::RdsRoute rds_route;
if (envoy_api_v2_route_RouteMatch_has_prefix(match)) {
upb_strview prefix = envoy_api_v2_route_RouteMatch_prefix(match);
// Empty prefix "" is accepted.
if (prefix.size > 0) {
// Prefix "/" is accepted.
if (prefix.data[0] != '/') {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Prefix does not start with a /");
}
if (prefix.size > 1) {
std::vector<absl::string_view> prefix_elements = absl::StrSplit(
absl::string_view(prefix.data, prefix.size).substr(1),
absl::MaxSplits('/', 1));
if (prefix_elements.size() != 2) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Prefix not in the required format of /service/");
} else if (!prefix_elements[1].empty()) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Prefix does not end with a /");
} else if (prefix_elements[0].empty()) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Prefix contains empty service name");
}
rds_route.service = std::string(prefix_elements[0]);
}
}
} else if (envoy_api_v2_route_RouteMatch_has_path(match)) {
upb_strview path = envoy_api_v2_route_RouteMatch_path(match);
if (path.size == 0) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Path if set cannot be empty");
}
if (path.data[0] != '/') {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Path does not start with a /");
}
std::vector<absl::string_view> path_elements = absl::StrSplit(
absl::string_view(path.data, path.size).substr(1), '/');
if (path_elements.size() != 2) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Path not in the required format of /service/method");
} else if (path_elements[0].empty()) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Path contains empty service name");
} else if (path_elements[1].empty()) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Path contains empty method name");
}
rds_route.service = std::string(path_elements[0]);
rds_route.method = std::string(path_elements[1]);
} else {
// TODO(donnadionne): We may change this behavior once we decide how to
// handle unsupported fields.
continue;
}
if (!envoy_api_v2_route_Route_has_route(route)) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No RouteAction found in route.");
}
const envoy_api_v2_route_RouteAction* route_action =
envoy_api_v2_route_Route_route(route);
// Get the cluster in the RouteAction.
if (!envoy_api_v2_route_RouteAction_has_cluster(route_action)) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No cluster found in RouteAction.");
}
const upb_strview action =
envoy_api_v2_route_RouteAction_cluster(route_action);
if (action.size == 0) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"RouteAction contains empty cluster.");
}
rds_route.cluster_name = std::string(action.data, action.size);
rds_update->routes.emplace_back(std::move(rds_route));
}
const envoy_api_v2_route_RouteAction* route_action =
envoy_api_v2_route_Route_route(route);
// Get the cluster in the RouteAction.
if (!envoy_api_v2_route_RouteAction_has_cluster(route_action)) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No cluster found in RouteAction.");
if (rds_update->routes.empty()) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid routes specified.");
} else {
if (!rds_update->routes.back().service.empty() ||
!rds_update->routes.back().method.empty()) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Default route must have empty service and method");
}
}
const upb_strview cluster =
envoy_api_v2_route_RouteAction_cluster(route_action);
rds_update->cluster_name = std::string(cluster.data, cluster.size);
return GRPC_ERROR_NONE;
}
grpc_error* LdsResponseParse(XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_DiscoveryResponse* response,
const std::string& expected_server_name,
const bool xds_routing_enabled,
absl::optional<XdsApi::LdsUpdate>* lds_update,
upb_arena* arena) {
// Get the resources from the response.
@ -1091,8 +1157,9 @@ grpc_error* LdsResponseParse(XdsClient* client, TraceFlag* tracer,
envoy_config_filter_network_http_connection_manager_v2_HttpConnectionManager_route_config(
http_connection_manager);
XdsApi::RdsUpdate rds_update;
grpc_error* error = RouteConfigParse(client, tracer, route_config,
expected_server_name, &rds_update);
grpc_error* error =
RouteConfigParse(client, tracer, route_config, expected_server_name,
xds_routing_enabled, &rds_update);
if (error != GRPC_ERROR_NONE) return error;
lds_update->emplace();
(*lds_update)->rds_update.emplace(std::move(rds_update));
@ -1123,6 +1190,7 @@ grpc_error* RdsResponseParse(XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_DiscoveryResponse* response,
const std::string& expected_server_name,
const std::string& expected_route_config_name,
const bool xds_routing_enabled,
absl::optional<XdsApi::RdsUpdate>* rds_update,
upb_arena* arena) {
// Get the resources from the response.
@ -1151,8 +1219,9 @@ grpc_error* RdsResponseParse(XdsClient* client, TraceFlag* tracer,
if (!upb_strview_eql(name, expected_name)) continue;
// Parse the route_config.
XdsApi::RdsUpdate local_rds_update;
grpc_error* error = RouteConfigParse(
client, tracer, route_config, expected_server_name, &local_rds_update);
grpc_error* error =
RouteConfigParse(client, tracer, route_config, expected_server_name,
xds_routing_enabled, &local_rds_update);
if (error != GRPC_ERROR_NONE) return error;
rds_update->emplace(std::move(local_rds_update));
return GRPC_ERROR_NONE;
@ -1432,6 +1501,7 @@ grpc_error* EdsResponseParse(
grpc_error* XdsApi::ParseAdsResponse(
const grpc_slice& encoded_response, const std::string& expected_server_name,
const std::string& expected_route_config_name,
const bool xds_routing_enabled,
const std::set<StringView>& expected_cluster_names,
const std::set<StringView>& expected_eds_service_names,
absl::optional<LdsUpdate>* lds_update,
@ -1463,11 +1533,11 @@ grpc_error* XdsApi::ParseAdsResponse(
// Parse the response according to the resource type.
if (*type_url == kLdsTypeUrl) {
return LdsResponseParse(client_, tracer_, response, expected_server_name,
lds_update, arena.ptr());
xds_routing_enabled, lds_update, arena.ptr());
} else if (*type_url == kRdsTypeUrl) {
return RdsResponseParse(client_, tracer_, response, expected_server_name,
expected_route_config_name, rds_update,
arena.ptr());
expected_route_config_name, xds_routing_enabled,
rds_update, arena.ptr());
} else if (*type_url == kCdsTypeUrl) {
return CdsResponseParse(client_, tracer_, response, expected_cluster_names,
cds_update_map, arena.ptr());

@ -44,12 +44,22 @@ class XdsApi {
static const char* kCdsTypeUrl;
static const char* kEdsTypeUrl;
struct RdsUpdate {
// The name to use in the CDS request.
struct RdsRoute {
std::string service;
std::string method;
std::string cluster_name;
bool operator==(const RdsRoute& other) const {
return (service == other.service && method == other.method &&
cluster_name == other.cluster_name);
}
};
struct RdsUpdate {
std::vector<RdsRoute> routes;
bool operator==(const RdsUpdate& other) const {
return cluster_name == other.cluster_name;
return routes == other.routes;
}
};
@ -247,6 +257,7 @@ class XdsApi {
const grpc_slice& encoded_response,
const std::string& expected_server_name,
const std::string& expected_route_config_name,
const bool xds_routing_enabled,
const std::set<StringView>& expected_cluster_names,
const std::set<StringView>& expected_eds_service_names,
absl::optional<LdsUpdate>* lds_update,

@ -22,6 +22,7 @@
#include <limits.h>
#include <string.h>
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include <grpc/byte_buffer_reader.h>
@ -896,15 +897,22 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO,
"[xds_client %p] LDS update received: route_config_name=%s, "
"cluster_name=%s",
"[xds_client %p] LDS update received: route_config_name=%s",
xds_client(),
(!lds_update->route_config_name.empty()
? lds_update->route_config_name.c_str()
: "<inlined>"),
(lds_update->rds_update.has_value()
? lds_update->rds_update->cluster_name.c_str()
: "<to be obtained via RDS>"));
: "<inlined>"));
if (lds_update->rds_update.has_value()) {
gpr_log(GPR_INFO, " RouteConfiguration contains %lu routes",
lds_update->rds_update.value().routes.size());
for (const auto& route : lds_update->rds_update.value().routes) {
gpr_log(GPR_INFO,
" route: { service=\"%s\", "
"method=\"%s\" }, cluster=\"%s\" }",
route.service.c_str(), route.method.c_str(),
route.cluster_name.c_str());
}
}
}
auto& lds_state = state_map_[XdsApi::kLdsTypeUrl];
auto& state = lds_state.subscribed_resources[xds_client()->server_name_];
@ -930,7 +938,7 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
// the watcher immediately.
RefCountedPtr<ServiceConfig> service_config;
grpc_error* error = xds_client()->CreateServiceConfig(
xds_client()->lds_result_->rds_update->cluster_name, &service_config);
xds_client()->lds_result_->rds_update.value(), &service_config);
if (error == GRPC_ERROR_NONE) {
xds_client()->service_config_watcher_->OnServiceConfigChanged(
std::move(service_config));
@ -956,8 +964,17 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
return;
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p] RDS update received: cluster_name=%s",
xds_client(), rds_update->cluster_name.c_str());
gpr_log(GPR_INFO,
"[xds_client %p] RDS update received; RouteConfiguration contains "
"%lu routes",
this, rds_update.value().routes.size());
for (const auto& route : rds_update.value().routes) {
gpr_log(GPR_INFO,
" route: { service=\"%s\", "
"method=\"%s\" }, cluster=\"%s\" }",
route.service.c_str(), route.method.c_str(),
route.cluster_name.c_str());
}
}
auto& rds_state = state_map_[XdsApi::kRdsTypeUrl];
auto& state =
@ -977,7 +994,7 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
// Notify the watcher.
RefCountedPtr<ServiceConfig> service_config;
grpc_error* error = xds_client()->CreateServiceConfig(
xds_client()->rds_result_->cluster_name, &service_config);
xds_client()->rds_result_.value(), &service_config);
if (error == GRPC_ERROR_NONE) {
xds_client()->service_config_watcher_->OnServiceConfigChanged(
std::move(service_config));
@ -1226,7 +1243,7 @@ void XdsClient::ChannelState::AdsCallState::OnResponseReceivedLocked(
(xds_client->lds_result_.has_value()
? xds_client->lds_result_->route_config_name
: ""),
ads_calld->ClusterNamesForRequest(),
xds_client->xds_routing_enabled_, ads_calld->ClusterNamesForRequest(),
ads_calld->EdsServiceNamesForRequest(), &lds_update, &rds_update,
&cds_update_map, &eds_update_map, &version, &nonce, &type_url);
grpc_slice_unref_internal(response_slice);
@ -1801,6 +1818,11 @@ grpc_millis GetRequestTimeout(const grpc_channel_args& args) {
{15000, 0, INT_MAX});
}
bool GetXdsRoutingEnabled(const grpc_channel_args& args) {
return grpc_channel_args_find_bool(&args, GRPC_ARG_XDS_ROUTING_ENABLED,
false);
}
} // namespace
XdsClient::XdsClient(Combiner* combiner, grpc_pollset_set* interested_parties,
@ -1809,6 +1831,7 @@ XdsClient::XdsClient(Combiner* combiner, grpc_pollset_set* interested_parties,
const grpc_channel_args& channel_args, grpc_error** error)
: InternallyRefCounted<XdsClient>(&grpc_xds_client_trace),
request_timeout_(GetRequestTimeout(channel_args)),
xds_routing_enabled_(GetXdsRoutingEnabled(channel_args)),
combiner_(GRPC_COMBINER_REF(combiner, "xds_client")),
interested_parties_(interested_parties),
bootstrap_(
@ -2034,22 +2057,69 @@ void XdsClient::ResetBackoff() {
}
}
namespace {
std::string CreateServiceConfigActionCluster(const std::string& cluster_name) {
return absl::StrFormat(
" \"cds:%s\":{\n"
" \"child_policy\":[ {\n"
" \"cds_experimental\":{\n"
" \"cluster\": \"%s\"\n"
" }\n"
" } ]\n"
" }",
cluster_name.c_str(), cluster_name.c_str());
}
std::string CreateServiceConfigRoute(const std::string& cluster_name,
const std::string& service,
const std::string& method) {
return absl::StrFormat(
" { \n"
" \"methodName\": {\n"
" \"service\": \"%s\",\n"
" \"method\": \"%s\"\n"
" },\n"
" \"action\": \"cds:%s\"\n"
" }",
service.c_str(), method.c_str(), cluster_name.c_str());
}
} // namespace
grpc_error* XdsClient::CreateServiceConfig(
const std::string& cluster_name,
const XdsApi::RdsUpdate& rds_update,
RefCountedPtr<ServiceConfig>* service_config) const {
char* json;
gpr_asprintf(&json,
"{\n"
" \"loadBalancingConfig\":[\n"
" { \"cds_experimental\":{\n"
" \"cluster\": \"%s\"\n"
" } }\n"
" ]\n"
"}",
cluster_name.c_str());
std::vector<std::string> config_parts;
config_parts.push_back(
"{\n"
" \"loadBalancingConfig\":[\n"
" { \"xds_routing_experimental\":{\n"
" \"actions\":{\n");
std::vector<std::string> actions_vector;
for (size_t i = 0; i < rds_update.routes.size(); ++i) {
auto route = rds_update.routes[i];
actions_vector.push_back(
CreateServiceConfigActionCluster(route.cluster_name.c_str()));
}
config_parts.push_back(absl::StrJoin(actions_vector, ",\n"));
config_parts.push_back(
" },\n"
" \"routes\":[\n");
std::vector<std::string> routes_vector;
for (size_t i = 0; i < rds_update.routes.size(); ++i) {
auto route_info = rds_update.routes[i];
routes_vector.push_back(CreateServiceConfigRoute(
route_info.cluster_name.c_str(), route_info.service.c_str(),
route_info.method.c_str()));
}
config_parts.push_back(absl::StrJoin(routes_vector, ",\n"));
config_parts.push_back(
" ]\n"
" } }\n"
" ]\n"
"}");
std::string json = absl::StrJoin(config_parts, "");
grpc_error* error = GRPC_ERROR_NONE;
*service_config = ServiceConfig::Create(json, &error);
gpr_free(json);
*service_config = ServiceConfig::Create(json.c_str(), &error);
return error;
}

@ -226,7 +226,7 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
void NotifyOnError(grpc_error* error);
grpc_error* CreateServiceConfig(
const std::string& cluster_name,
const XdsApi::RdsUpdate& rds_update,
RefCountedPtr<ServiceConfig>* service_config) const;
XdsApi::ClusterLoadReportMap BuildLoadReportSnapshot(
@ -241,6 +241,8 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
const grpc_millis request_timeout_;
const bool xds_routing_enabled_;
Combiner* combiner_;
grpc_pollset_set* interested_parties_;

@ -22,6 +22,7 @@
#include "src/core/ext/filters/http/client/http_client_filter.h"
#include "src/core/ext/filters/http/message_compress/message_compress_filter.h"
#include "src/core/ext/filters/http/message_compress/message_decompress_filter.h"
#include "src/core/ext/filters/http/server/http_server_filter.h"
#include "src/core/lib/channel/channel_stack_builder.h"
#include "src/core/lib/surface/call.h"
@ -36,12 +37,16 @@ typedef struct {
static optional_filter compress_filter = {
&grpc_message_compress_filter, GRPC_ARG_ENABLE_PER_MESSAGE_COMPRESSION};
static optional_filter decompress_filter = {
&grpc_message_decompress_filter, GRPC_ARG_ENABLE_PER_MESSAGE_DECOMPRESSION};
static bool is_building_http_like_transport(
grpc_channel_stack_builder* builder) {
grpc_transport* t = grpc_channel_stack_builder_get_transport(builder);
return t != nullptr && strstr(t->vtable->name, "http");
}
template <bool enable_in_minimal_stack>
static bool maybe_add_optional_filter(grpc_channel_stack_builder* builder,
void* arg) {
if (!is_building_http_like_transport(builder)) return true;
@ -50,7 +55,8 @@ static bool maybe_add_optional_filter(grpc_channel_stack_builder* builder,
grpc_channel_stack_builder_get_channel_arguments(builder);
bool enable = grpc_channel_arg_get_bool(
grpc_channel_args_find(channel_args, filtarg->control_channel_arg),
!grpc_channel_args_want_minimal_stack(channel_args));
enable_in_minimal_stack ||
!grpc_channel_args_want_minimal_stack(channel_args));
return enable ? grpc_channel_stack_builder_prepend_filter(
builder, filtarg->filter, nullptr, nullptr)
: true;
@ -66,15 +72,24 @@ static bool maybe_add_required_filter(grpc_channel_stack_builder* builder,
}
void grpc_http_filters_init(void) {
grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
maybe_add_optional_filter, &compress_filter);
grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL,
GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
maybe_add_optional_filter, &compress_filter);
grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL,
GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
maybe_add_optional_filter, &compress_filter);
grpc_channel_init_register_stage(
GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
maybe_add_optional_filter<false>, &compress_filter);
grpc_channel_init_register_stage(
GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
maybe_add_optional_filter<false>, &compress_filter);
grpc_channel_init_register_stage(
GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
maybe_add_optional_filter<false>, &compress_filter);
grpc_channel_init_register_stage(
GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
maybe_add_optional_filter<true>, &decompress_filter);
grpc_channel_init_register_stage(
GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
maybe_add_optional_filter<true>, &decompress_filter);
grpc_channel_init_register_stage(
GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
maybe_add_optional_filter<true>, &decompress_filter);
grpc_channel_init_register_stage(
GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
maybe_add_required_filter, (void*)&grpc_http_client_filter);

@ -0,0 +1,358 @@
//
//
// Copyright 2020 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <grpc/support/port_platform.h>
#include <assert.h>
#include <string.h>
#include <grpc/compression.h>
#include <grpc/slice_buffer.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include "src/core/ext/filters/http/message_compress/message_decompress_filter.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/compression/algorithm_metadata.h"
#include "src/core/lib/compression/compression_args.h"
#include "src/core/lib/compression/compression_internal.h"
#include "src/core/lib/compression/message_compress.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/slice/slice_string_helpers.h"
namespace {
class ChannelData {};
class CallData {
public:
explicit CallData(const grpc_call_element_args& args)
: call_combiner_(args.call_combiner) {
// Initialize state for recv_initial_metadata_ready callback
GRPC_CLOSURE_INIT(&on_recv_initial_metadata_ready_,
OnRecvInitialMetadataReady, this,
grpc_schedule_on_exec_ctx);
// Initialize state for recv_message_ready callback
grpc_slice_buffer_init(&recv_slices_);
GRPC_CLOSURE_INIT(&on_recv_message_next_done_, OnRecvMessageNextDone, this,
grpc_schedule_on_exec_ctx);
GRPC_CLOSURE_INIT(&on_recv_message_ready_, OnRecvMessageReady, this,
grpc_schedule_on_exec_ctx);
// Initialize state for recv_trailing_metadata_ready callback
GRPC_CLOSURE_INIT(&on_recv_trailing_metadata_ready_,
OnRecvTrailingMetadataReady, this,
grpc_schedule_on_exec_ctx);
}
~CallData() { grpc_slice_buffer_destroy_internal(&recv_slices_); }
void DecompressStartTransportStreamOpBatch(
grpc_call_element* elem, grpc_transport_stream_op_batch* batch);
private:
static void OnRecvInitialMetadataReady(void* arg, grpc_error* error);
// Methods for processing a receive message event
void MaybeResumeOnRecvMessageReady();
static void OnRecvMessageReady(void* arg, grpc_error* error);
static void OnRecvMessageNextDone(void* arg, grpc_error* error);
grpc_error* PullSliceFromRecvMessage();
void ContinueReadingRecvMessage();
void FinishRecvMessage();
void ContinueRecvMessageReadyCallback(grpc_error* error);
// Methods for processing a recv_trailing_metadata event
void MaybeResumeOnRecvTrailingMetadataReady();
static void OnRecvTrailingMetadataReady(void* arg, grpc_error* error);
grpc_core::CallCombiner* call_combiner_;
// Overall error for the call
grpc_error* error_ = GRPC_ERROR_NONE;
// Fields for handling recv_initial_metadata_ready callback
grpc_closure on_recv_initial_metadata_ready_;
grpc_closure* original_recv_initial_metadata_ready_ = nullptr;
grpc_metadata_batch* recv_initial_metadata_ = nullptr;
// Fields for handling recv_message_ready callback
bool seen_recv_message_ready_ = false;
grpc_message_compression_algorithm algorithm_ = GRPC_MESSAGE_COMPRESS_NONE;
grpc_closure on_recv_message_ready_;
grpc_closure* original_recv_message_ready_ = nullptr;
grpc_closure on_recv_message_next_done_;
grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message_ = nullptr;
// recv_slices_ holds the slices read from the original recv_message stream.
// It is initialized during construction and reset when a new stream is
// created using it.
grpc_slice_buffer recv_slices_;
std::aligned_storage<sizeof(grpc_core::SliceBufferByteStream),
alignof(grpc_core::SliceBufferByteStream)>::type
recv_replacement_stream_;
// Fields for handling recv_trailing_metadata_ready callback
bool seen_recv_trailing_metadata_ready_ = false;
grpc_closure on_recv_trailing_metadata_ready_;
grpc_closure* original_recv_trailing_metadata_ready_ = nullptr;
grpc_error* on_recv_trailing_metadata_ready_error_ = GRPC_ERROR_NONE;
};
grpc_message_compression_algorithm DecodeMessageCompressionAlgorithm(
grpc_mdelem md) {
grpc_message_compression_algorithm algorithm =
grpc_message_compression_algorithm_from_slice(GRPC_MDVALUE(md));
if (algorithm == GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT) {
char* md_c_str = grpc_slice_to_c_string(GRPC_MDVALUE(md));
gpr_log(GPR_ERROR,
"Invalid incoming message compression algorithm: '%s'. "
"Interpreting incoming data as uncompressed.",
md_c_str);
gpr_free(md_c_str);
return GRPC_MESSAGE_COMPRESS_NONE;
}
return algorithm;
}
void CallData::OnRecvInitialMetadataReady(void* arg, grpc_error* error) {
CallData* calld = static_cast<CallData*>(arg);
if (error == GRPC_ERROR_NONE) {
grpc_linked_mdelem* grpc_encoding =
calld->recv_initial_metadata_->idx.named.grpc_encoding;
if (grpc_encoding != nullptr) {
calld->algorithm_ = DecodeMessageCompressionAlgorithm(grpc_encoding->md);
}
}
calld->MaybeResumeOnRecvMessageReady();
calld->MaybeResumeOnRecvTrailingMetadataReady();
grpc_closure* closure = calld->original_recv_initial_metadata_ready_;
calld->original_recv_initial_metadata_ready_ = nullptr;
grpc_core::Closure::Run(DEBUG_LOCATION, closure, GRPC_ERROR_REF(error));
}
void CallData::MaybeResumeOnRecvMessageReady() {
if (seen_recv_message_ready_) {
seen_recv_message_ready_ = false;
GRPC_CALL_COMBINER_START(call_combiner_, &on_recv_message_ready_,
GRPC_ERROR_NONE,
"continue recv_message_ready callback");
}
}
void CallData::OnRecvMessageReady(void* arg, grpc_error* error) {
CallData* calld = static_cast<CallData*>(arg);
if (error == GRPC_ERROR_NONE) {
if (calld->original_recv_initial_metadata_ready_ != nullptr) {
calld->seen_recv_message_ready_ = true;
GRPC_CALL_COMBINER_STOP(calld->call_combiner_,
"Deferring OnRecvMessageReady until after "
"OnRecvInitialMetadataReady");
return;
}
if (calld->algorithm_ != GRPC_MESSAGE_COMPRESS_NONE) {
// recv_message can be NULL if trailing metadata is received instead of
// message, or it's possible that the message was not compressed.
if (*calld->recv_message_ == nullptr ||
(*calld->recv_message_)->length() == 0 ||
((*calld->recv_message_)->flags() & GRPC_WRITE_INTERNAL_COMPRESS) ==
0) {
return calld->ContinueRecvMessageReadyCallback(GRPC_ERROR_NONE);
}
grpc_slice_buffer_destroy_internal(&calld->recv_slices_);
grpc_slice_buffer_init(&calld->recv_slices_);
return calld->ContinueReadingRecvMessage();
}
}
calld->ContinueRecvMessageReadyCallback(GRPC_ERROR_REF(error));
}
void CallData::ContinueReadingRecvMessage() {
while ((*recv_message_)
->Next((*recv_message_)->length() - recv_slices_.length,
&on_recv_message_next_done_)) {
grpc_error* error = PullSliceFromRecvMessage();
if (error != GRPC_ERROR_NONE) {
return ContinueRecvMessageReadyCallback(error);
}
// We have read the entire message.
if (recv_slices_.length == (*recv_message_)->length()) {
return FinishRecvMessage();
}
}
}
grpc_error* CallData::PullSliceFromRecvMessage() {
grpc_slice incoming_slice;
grpc_error* error = (*recv_message_)->Pull(&incoming_slice);
if (error == GRPC_ERROR_NONE) {
grpc_slice_buffer_add(&recv_slices_, incoming_slice);
}
return error;
}
void CallData::OnRecvMessageNextDone(void* arg, grpc_error* error) {
CallData* calld = static_cast<CallData*>(arg);
if (error != GRPC_ERROR_NONE) {
return calld->ContinueRecvMessageReadyCallback(GRPC_ERROR_REF(error));
}
error = calld->PullSliceFromRecvMessage();
if (error != GRPC_ERROR_NONE) {
return calld->ContinueRecvMessageReadyCallback(error);
}
if (calld->recv_slices_.length == (*calld->recv_message_)->length()) {
calld->FinishRecvMessage();
} else {
calld->ContinueReadingRecvMessage();
}
}
void CallData::FinishRecvMessage() {
grpc_slice_buffer decompressed_slices;
grpc_slice_buffer_init(&decompressed_slices);
if (grpc_msg_decompress(algorithm_, &recv_slices_, &decompressed_slices) ==
0) {
char* msg;
gpr_asprintf(
&msg,
"Unexpected error decompressing data for algorithm with enum value %d",
algorithm_);
GPR_DEBUG_ASSERT(error_ == GRPC_ERROR_NONE);
error_ = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
gpr_free(msg);
grpc_slice_buffer_destroy_internal(&decompressed_slices);
} else {
uint32_t recv_flags =
((*recv_message_)->flags() & (~GRPC_WRITE_INTERNAL_COMPRESS)) |
GRPC_WRITE_INTERNAL_TEST_ONLY_WAS_COMPRESSED;
// Swap out the original receive byte stream with our new one and send the
// batch down.
// Initializing recv_replacement_stream_ with decompressed_slices removes
// all the slices from decompressed_slices leaving it empty.
new (&recv_replacement_stream_)
grpc_core::SliceBufferByteStream(&decompressed_slices, recv_flags);
recv_message_->reset(reinterpret_cast<grpc_core::SliceBufferByteStream*>(
&recv_replacement_stream_));
recv_message_ = nullptr;
}
ContinueRecvMessageReadyCallback(GRPC_ERROR_REF(error_));
}
void CallData::ContinueRecvMessageReadyCallback(grpc_error* error) {
MaybeResumeOnRecvTrailingMetadataReady();
// The surface will clean up the receiving stream if there is an error.
grpc_closure* closure = original_recv_message_ready_;
original_recv_message_ready_ = nullptr;
grpc_core::Closure::Run(DEBUG_LOCATION, closure, error);
}
void CallData::MaybeResumeOnRecvTrailingMetadataReady() {
if (seen_recv_trailing_metadata_ready_) {
seen_recv_trailing_metadata_ready_ = false;
grpc_error* error = on_recv_trailing_metadata_ready_error_;
on_recv_trailing_metadata_ready_error_ = GRPC_ERROR_NONE;
GRPC_CALL_COMBINER_START(call_combiner_, &on_recv_trailing_metadata_ready_,
error, "Continuing OnRecvTrailingMetadataReady");
}
}
void CallData::OnRecvTrailingMetadataReady(void* arg, grpc_error* error) {
CallData* calld = static_cast<CallData*>(arg);
if (calld->original_recv_initial_metadata_ready_ != nullptr ||
calld->original_recv_message_ready_ != nullptr) {
calld->seen_recv_trailing_metadata_ready_ = true;
calld->on_recv_trailing_metadata_ready_error_ = GRPC_ERROR_REF(error);
GRPC_CALL_COMBINER_STOP(
calld->call_combiner_,
"Deferring OnRecvTrailingMetadataReady until after "
"OnRecvInitialMetadataReady and OnRecvMessageReady");
return;
}
error = grpc_error_add_child(GRPC_ERROR_REF(error), calld->error_);
calld->error_ = GRPC_ERROR_NONE;
grpc_closure* closure = calld->original_recv_trailing_metadata_ready_;
calld->original_recv_trailing_metadata_ready_ = nullptr;
grpc_core::Closure::Run(DEBUG_LOCATION, closure, error);
}
void CallData::DecompressStartTransportStreamOpBatch(
grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
// Handle recv_initial_metadata.
if (batch->recv_initial_metadata) {
recv_initial_metadata_ =
batch->payload->recv_initial_metadata.recv_initial_metadata;
original_recv_initial_metadata_ready_ =
batch->payload->recv_initial_metadata.recv_initial_metadata_ready;
batch->payload->recv_initial_metadata.recv_initial_metadata_ready =
&on_recv_initial_metadata_ready_;
}
// Handle recv_message
if (batch->recv_message) {
recv_message_ = batch->payload->recv_message.recv_message;
original_recv_message_ready_ =
batch->payload->recv_message.recv_message_ready;
batch->payload->recv_message.recv_message_ready = &on_recv_message_ready_;
}
// Handle recv_trailing_metadata
if (batch->recv_trailing_metadata) {
original_recv_trailing_metadata_ready_ =
batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
&on_recv_trailing_metadata_ready_;
}
// Pass control down the stack.
grpc_call_next_op(elem, batch);
}
void DecompressStartTransportStreamOpBatch(
grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
GPR_TIMER_SCOPE("decompress_start_transport_stream_op_batch", 0);
CallData* calld = static_cast<CallData*>(elem->call_data);
calld->DecompressStartTransportStreamOpBatch(elem, batch);
}
static grpc_error* DecompressInitCallElem(grpc_call_element* elem,
const grpc_call_element_args* args) {
new (elem->call_data) CallData(*args);
return GRPC_ERROR_NONE;
}
static void DecompressDestroyCallElem(
grpc_call_element* elem, const grpc_call_final_info* /*final_info*/,
grpc_closure* /*ignored*/) {
CallData* calld = static_cast<CallData*>(elem->call_data);
calld->~CallData();
}
static grpc_error* DecompressInitChannelElem(
grpc_channel_element* /*elem*/, grpc_channel_element_args* /*args*/) {
return GRPC_ERROR_NONE;
}
void DecompressDestroyChannelElem(grpc_channel_element* /*elem*/) {}
} // namespace
const grpc_channel_filter grpc_message_decompress_filter = {
DecompressStartTransportStreamOpBatch,
grpc_channel_next_op,
sizeof(CallData),
DecompressInitCallElem,
grpc_call_stack_ignore_set_pollset_or_pollset_set,
DecompressDestroyCallElem,
0, // sizeof(ChannelData)
DecompressInitChannelElem,
DecompressDestroyChannelElem,
grpc_channel_next_get_info,
"message_decompress"};

@ -0,0 +1,29 @@
//
//
// Copyright 2020 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_DECOMPRESS_FILTER_H
#define GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_DECOMPRESS_FILTER_H
#include <grpc/support/port_platform.h>
#include "src/core/lib/channel/channel_stack.h"
extern const grpc_channel_filter grpc_message_decompress_filter;
#endif /* GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_DECOMPRESS_FILTER_H \
*/

@ -32,7 +32,6 @@
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/iomgr/error_cfstream.h"
#include "src/core/lib/iomgr/ev_apple.h"
#include "src/core/lib/iomgr/exec_ctx.h"
extern grpc_core::TraceFlag grpc_tcp_trace;
@ -148,8 +147,8 @@ CFStreamHandle::CFStreamHandle(CFReadStreamRef read_stream,
kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes |
kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
CFStreamHandle::WriteCallback, &ctx);
grpc_apple_register_read_stream(read_stream, dispatch_queue_);
grpc_apple_register_write_stream(write_stream, dispatch_queue_);
CFReadStreamSetDispatchQueue(read_stream, dispatch_queue_);
CFWriteStreamSetDispatchQueue(write_stream, dispatch_queue_);
}
CFStreamHandle::~CFStreamHandle() {

@ -1,356 +0,0 @@
/*
*
* Copyright 2020 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/// Event engine based on Apple's CFRunLoop API family. If the CFRunLoop engine
/// is enabled (see iomgr_posix_cfstream.cc), a global thread is started to
/// handle and trigger all the CFStream events. The CFStream streams register
/// themselves with the run loop with functions grpc_apple_register_read_stream
/// and grpc_apple_register_read_stream. Pollsets are dummy and block on a
/// condition variable in pollset_work().
#include <grpc/support/port_platform.h>
#include "src/core/lib/iomgr/port.h"
#ifdef GRPC_APPLE_EV
#include <CoreFoundation/CoreFoundation.h>
#include <list>
#include "src/core/lib/gprpp/thd.h"
#include "src/core/lib/iomgr/ev_apple.h"
grpc_core::DebugOnlyTraceFlag grpc_apple_polling_trace(false, "apple_polling");
#ifndef NDEBUG
#define GRPC_POLLING_TRACE(format, ...) \
if (GRPC_TRACE_FLAG_ENABLED(grpc_apple_polling_trace)) { \
gpr_log(GPR_DEBUG, "(polling) " format, __VA_ARGS__); \
}
#else
#define GRPC_POLLING_TRACE(...)
#endif // NDEBUG
#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker*)1)
struct GlobalRunLoopContext {
grpc_core::CondVar init_cv;
grpc_core::CondVar input_source_cv;
grpc_core::Mutex mu;
// Whether an input source registration is pending. Protected by mu.
bool input_source_registered = false;
// The reference to the global run loop object. Protected by mu.
CFRunLoopRef run_loop;
// Whether the pollset has been globally shut down. Protected by mu.
bool is_shutdown = false;
};
struct GrpcAppleWorker {
// The condition varible to kick the worker. Works with the pollset's lock
// (GrpcApplePollset.mu).
grpc_core::CondVar cv;
// Whether the worker is kicked. Protected by the pollset's lock
// (GrpcApplePollset.mu).
bool kicked = false;
};
struct GrpcApplePollset {
grpc_core::Mutex mu;
// Tracks the current workers in the pollset. Protected by mu.
std::list<GrpcAppleWorker*> workers;
// Whether the pollset is shut down. Protected by mu.
bool is_shutdown = false;
// Closure to call when shutdown is done. Protected by mu.
grpc_closure* shutdown_closure;
// Whether there's an outstanding kick that was not processed. Protected by
// mu.
bool kicked_without_poller = false;
};
static GlobalRunLoopContext* gGlobalRunLoopContext = nullptr;
static grpc_core::Thread* gGlobalRunLoopThread = nullptr;
/// Register the stream with the dispatch queue. Callbacks of the stream will be
/// issued to the dispatch queue when a network event happens and will be
/// managed by Grand Central Dispatch.
static void grpc_apple_register_read_stream_queue(
CFReadStreamRef read_stream, dispatch_queue_t dispatch_queue) {
CFReadStreamSetDispatchQueue(read_stream, dispatch_queue);
}
/// Register the stream with the dispatch queue. Callbacks of the stream will be
/// issued to the dispatch queue when a network event happens and will be
/// managed by Grand Central Dispatch.
static void grpc_apple_register_write_stream_queue(
CFWriteStreamRef write_stream, dispatch_queue_t dispatch_queue) {
CFWriteStreamSetDispatchQueue(write_stream, dispatch_queue);
}
/// Register the stream with the global run loop. Callbacks of the stream will
/// be issued to the run loop when a network event happens and will be driven by
/// the global run loop thread gGlobalRunLoopThread.
static void grpc_apple_register_read_stream_run_loop(
CFReadStreamRef read_stream, dispatch_queue_t dispatch_queue) {
GRPC_POLLING_TRACE("Register read stream: %p", read_stream);
grpc_core::MutexLock lock(&gGlobalRunLoopContext->mu);
CFReadStreamScheduleWithRunLoop(read_stream, gGlobalRunLoopContext->run_loop,
kCFRunLoopDefaultMode);
gGlobalRunLoopContext->input_source_registered = true;
gGlobalRunLoopContext->input_source_cv.Signal();
}
/// Register the stream with the global run loop. Callbacks of the stream will
/// be issued to the run loop when a network event happens, and will be driven
/// by the global run loop thread gGlobalRunLoopThread.
static void grpc_apple_register_write_stream_run_loop(
CFWriteStreamRef write_stream, dispatch_queue_t dispatch_queue) {
GRPC_POLLING_TRACE("Register write stream: %p", write_stream);
grpc_core::MutexLock lock(&gGlobalRunLoopContext->mu);
CFWriteStreamScheduleWithRunLoop(
write_stream, gGlobalRunLoopContext->run_loop, kCFRunLoopDefaultMode);
gGlobalRunLoopContext->input_source_registered = true;
gGlobalRunLoopContext->input_source_cv.Signal();
}
/// The default implementation of stream registration is to register the stream
/// to a dispatch queue. However, if the CFRunLoop based pollset is enabled (by
/// macro and environment variable, see docs in iomgr_posix_cfstream.cc), the
/// CFStream streams are registered with the global run loop instead (see
/// pollset_global_init below).
static void (*grpc_apple_register_read_stream_impl)(
CFReadStreamRef, dispatch_queue_t) = grpc_apple_register_read_stream_queue;
static void (*grpc_apple_register_write_stream_impl)(CFWriteStreamRef,
dispatch_queue_t) =
grpc_apple_register_write_stream_queue;
void grpc_apple_register_read_stream(CFReadStreamRef read_stream,
dispatch_queue_t dispatch_queue) {
grpc_apple_register_read_stream_impl(read_stream, dispatch_queue);
}
void grpc_apple_register_write_stream(CFWriteStreamRef write_stream,
dispatch_queue_t dispatch_queue) {
grpc_apple_register_write_stream_impl(write_stream, dispatch_queue);
}
/// Drive the run loop in a global singleton thread until the global run loop is
/// shutdown.
static void GlobalRunLoopFunc(void* arg) {
grpc_core::ReleasableMutexLock lock(&gGlobalRunLoopContext->mu);
gGlobalRunLoopContext->run_loop = CFRunLoopGetCurrent();
gGlobalRunLoopContext->init_cv.Signal();
while (!gGlobalRunLoopContext->is_shutdown) {
// CFRunLoopRun() will return immediately if no stream is registered on it.
// So we wait on a conditional variable until a stream is registered;
// otherwise we'll be running a spinning loop.
while (!gGlobalRunLoopContext->input_source_registered) {
gGlobalRunLoopContext->input_source_cv.Wait(&gGlobalRunLoopContext->mu);
}
gGlobalRunLoopContext->input_source_registered = false;
lock.Unlock();
CFRunLoopRun();
lock.Lock();
}
lock.Unlock();
}
// pollset implementation
static void pollset_global_init(void) {
gGlobalRunLoopContext = new GlobalRunLoopContext;
grpc_apple_register_read_stream_impl =
grpc_apple_register_read_stream_run_loop;
grpc_apple_register_write_stream_impl =
grpc_apple_register_write_stream_run_loop;
grpc_core::MutexLock lock(&gGlobalRunLoopContext->mu);
gGlobalRunLoopThread =
new grpc_core::Thread("apple_ev", GlobalRunLoopFunc, nullptr);
gGlobalRunLoopThread->Start();
while (gGlobalRunLoopContext->run_loop == NULL)
gGlobalRunLoopContext->init_cv.Wait(&gGlobalRunLoopContext->mu);
}
static void pollset_global_shutdown(void) {
{
grpc_core::MutexLock lock(&gGlobalRunLoopContext->mu);
gGlobalRunLoopContext->is_shutdown = true;
CFRunLoopStop(gGlobalRunLoopContext->run_loop);
}
gGlobalRunLoopThread->Join();
delete gGlobalRunLoopThread;
delete gGlobalRunLoopContext;
}
/// The caller must acquire the lock GrpcApplePollset.mu before calling this
/// function. The lock may be temporarily released when waiting on the condition
/// variable but will be re-acquired before the function returns.
///
/// The Apple pollset simply waits on a condition variable until it is kicked.
/// The network events are handled in the global run loop thread. Processing of
/// these events will eventually trigger the kick.
static grpc_error* pollset_work(grpc_pollset* pollset,
grpc_pollset_worker** worker,
grpc_millis deadline) {
GRPC_POLLING_TRACE("pollset work: %p, worker: %p, deadline: %" PRIu64,
pollset, worker, deadline);
GrpcApplePollset* apple_pollset =
reinterpret_cast<GrpcApplePollset*>(pollset);
GrpcAppleWorker actual_worker;
if (worker) {
*worker = reinterpret_cast<grpc_pollset_worker*>(&actual_worker);
}
if (apple_pollset->kicked_without_poller) {
// Process the outstanding kick and reset the flag. Do not block.
apple_pollset->kicked_without_poller = false;
} else {
// Block until kicked, timed out, or the pollset shuts down.
apple_pollset->workers.push_front(&actual_worker);
auto it = apple_pollset->workers.begin();
while (!actual_worker.kicked && !apple_pollset->is_shutdown) {
if (actual_worker.cv.Wait(
&apple_pollset->mu,
grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME))) {
// timed out
break;
}
}
apple_pollset->workers.erase(it);
// If the pollset is shut down asynchronously and this is the last pending
// worker, the shutdown process is complete at this moment and the shutdown
// callback will be called.
if (apple_pollset->is_shutdown && apple_pollset->workers.empty()) {
grpc_core::ExecCtx::Run(DEBUG_LOCATION, apple_pollset->shutdown_closure,
GRPC_ERROR_NONE);
}
}
return GRPC_ERROR_NONE;
}
/// Kick a specific worker. The caller must acquire the lock GrpcApplePollset.mu
/// before calling this function.
static void kick_worker(GrpcAppleWorker* worker) {
worker->kicked = true;
worker->cv.Signal();
}
/// The caller must acquire the lock GrpcApplePollset.mu before calling this
/// function. The kick action simply signals the condition variable of the
/// worker.
static grpc_error* pollset_kick(grpc_pollset* pollset,
grpc_pollset_worker* specific_worker) {
GrpcApplePollset* apple_pollset =
reinterpret_cast<GrpcApplePollset*>(pollset);
GRPC_POLLING_TRACE("pollset kick: %p, worker:%p", pollset, specific_worker);
if (specific_worker == nullptr) {
if (apple_pollset->workers.empty()) {
apple_pollset->kicked_without_poller = true;
} else {
GrpcAppleWorker* actual_worker = apple_pollset->workers.front();
kick_worker(actual_worker);
}
} else if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) {
for (auto& actual_worker : apple_pollset->workers) {
kick_worker(actual_worker);
}
} else {
GrpcAppleWorker* actual_worker =
reinterpret_cast<GrpcAppleWorker*>(specific_worker);
kick_worker(actual_worker);
}
return GRPC_ERROR_NONE;
}
static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) {
GRPC_POLLING_TRACE("pollset init: %p", pollset);
GrpcApplePollset* apple_pollset = new (pollset) GrpcApplePollset();
*mu = apple_pollset->mu.get();
}
/// The caller must acquire the lock GrpcApplePollset.mu before calling this
/// function.
static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) {
GRPC_POLLING_TRACE("pollset shutdown: %p", pollset);
GrpcApplePollset* apple_pollset =
reinterpret_cast<GrpcApplePollset*>(pollset);
apple_pollset->is_shutdown = true;
pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
// If there is any worker blocked, shutdown will be done asynchronously.
if (apple_pollset->workers.empty()) {
grpc_core::ExecCtx::Run(DEBUG_LOCATION, closure, GRPC_ERROR_NONE);
} else {
apple_pollset->shutdown_closure = closure;
}
}
static void pollset_destroy(grpc_pollset* pollset) {
GRPC_POLLING_TRACE("pollset destroy: %p", pollset);
GrpcApplePollset* apple_pollset =
reinterpret_cast<GrpcApplePollset*>(pollset);
apple_pollset->~GrpcApplePollset();
}
size_t pollset_size(void) { return sizeof(GrpcApplePollset); }
grpc_pollset_vtable grpc_apple_pollset_vtable = {
pollset_global_init, pollset_global_shutdown,
pollset_init, pollset_shutdown,
pollset_destroy, pollset_work,
pollset_kick, pollset_size};
// pollset_set implementation
grpc_pollset_set* pollset_set_create(void) { return nullptr; }
void pollset_set_destroy(grpc_pollset_set* pollset_set) {}
void pollset_set_add_pollset(grpc_pollset_set* pollset_set,
grpc_pollset* pollset) {}
void pollset_set_del_pollset(grpc_pollset_set* pollset_set,
grpc_pollset* pollset) {}
void pollset_set_add_pollset_set(grpc_pollset_set* bag,
grpc_pollset_set* item) {}
void pollset_set_del_pollset_set(grpc_pollset_set* bag,
grpc_pollset_set* item) {}
grpc_pollset_set_vtable grpc_apple_pollset_set_vtable = {
pollset_set_create, pollset_set_destroy,
pollset_set_add_pollset, pollset_set_del_pollset,
pollset_set_add_pollset_set, pollset_set_del_pollset_set};
#endif

@ -1,43 +0,0 @@
/*
*
* Copyright 2020 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef GRPC_CORE_LIB_IOMGR_EV_APPLE_H
#define GRPC_CORE_LIB_IOMGR_EV_APPLE_H
#include <grpc/support/port_platform.h>
#ifdef GRPC_APPLE_EV
#include <CoreFoundation/CoreFoundation.h>
#include "src/core/lib/iomgr/pollset.h"
#include "src/core/lib/iomgr/pollset_set.h"
void grpc_apple_register_read_stream(CFReadStreamRef read_stream,
dispatch_queue_t dispatch_queue);
void grpc_apple_register_write_stream(CFWriteStreamRef write_stream,
dispatch_queue_t dispatch_queue);
extern grpc_pollset_vtable grpc_apple_pollset_vtable;
extern grpc_pollset_set_vtable grpc_apple_pollset_set_vtable;
#endif
#endif

@ -16,20 +16,6 @@
*
*/
/// CFStream is build-enabled on iOS by default and disabled by default on other
/// platforms (see port_platform.h). To enable CFStream build on another
/// platform, the users need to define macro "GRPC_CFSTREAM=1" when building
/// gRPC.
///
/// When CFStream is to be built (either by default on iOS or by macro on other
/// platforms), the users can disable CFStream with environment variable
/// "grpc_cfstream=0". This will let gRPC to fallback to use POSIX sockets. In
/// addition, the users may choose to use an alternative CFRunLoop based pollset
/// "ev_apple" by setting environment variable "grpc_cfstream_run_loop=1". This
/// pollset resolves a bug from Apple when CFStream streams dispatch events to
/// dispatch queues. The caveat of this pollset is that users may not be able to
/// run a gRPC server in the same process.
#include <grpc/support/port_platform.h>
#include "src/core/lib/iomgr/port.h"
@ -37,7 +23,6 @@
#ifdef GRPC_CFSTREAM_IOMGR
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/iomgr/ev_apple.h"
#include "src/core/lib/iomgr/ev_posix.h"
#include "src/core/lib/iomgr/iomgr_internal.h"
#include "src/core/lib/iomgr/iomgr_posix.h"
@ -48,7 +33,6 @@
#include "src/core/lib/iomgr/timer.h"
static const char* grpc_cfstream_env_var = "grpc_cfstream";
static const char* grpc_cfstream_run_loop_env_var = "GRPC_CFSTREAM_RUN_LOOP";
extern grpc_tcp_server_vtable grpc_posix_tcp_server_vtable;
extern grpc_tcp_client_vtable grpc_posix_tcp_client_vtable;
@ -58,33 +42,6 @@ extern grpc_pollset_vtable grpc_posix_pollset_vtable;
extern grpc_pollset_set_vtable grpc_posix_pollset_set_vtable;
extern grpc_address_resolver_vtable grpc_posix_resolver_vtable;
static void apple_iomgr_platform_init(void) { grpc_pollset_global_init(); }
static void apple_iomgr_platform_flush(void) {}
static void apple_iomgr_platform_shutdown(void) {
grpc_pollset_global_shutdown();
}
static void apple_iomgr_platform_shutdown_background_closure(void) {}
static bool apple_iomgr_platform_is_any_background_poller_thread(void) {
return false;
}
static bool apple_iomgr_platform_add_closure_to_background_poller(
grpc_closure* closure, grpc_error* error) {
return false;
}
static grpc_iomgr_platform_vtable apple_vtable = {
apple_iomgr_platform_init,
apple_iomgr_platform_flush,
apple_iomgr_platform_shutdown,
apple_iomgr_platform_shutdown_background_closure,
apple_iomgr_platform_is_any_background_poller_thread,
apple_iomgr_platform_add_closure_to_background_poller};
static void iomgr_platform_init(void) {
grpc_wakeup_fd_global_init();
grpc_event_engine_init();
@ -119,53 +76,32 @@ static grpc_iomgr_platform_vtable vtable = {
iomgr_platform_add_closure_to_background_poller};
void grpc_set_default_iomgr_platform() {
char* enable_cfstream_str = getenv(grpc_cfstream_env_var);
bool enable_cfstream =
enable_cfstream_str == nullptr || enable_cfstream_str[0] != '0';
char* enable_cfstream_run_loop_str = getenv(grpc_cfstream_run_loop_env_var);
// CFStream run-loop is disabled by default. The user has to enable it
// explicitly with environment variable.
bool enable_cfstream_run_loop = enable_cfstream_run_loop_str != nullptr &&
enable_cfstream_run_loop_str[0] == '1';
if (!enable_cfstream) {
// Use POSIX sockets for both client and server
grpc_set_tcp_client_impl(&grpc_posix_tcp_client_vtable);
grpc_set_tcp_server_impl(&grpc_posix_tcp_server_vtable);
grpc_set_pollset_vtable(&grpc_posix_pollset_vtable);
grpc_set_pollset_set_vtable(&grpc_posix_pollset_set_vtable);
grpc_set_iomgr_platform_vtable(&vtable);
} else if (enable_cfstream && !enable_cfstream_run_loop) {
// Use CFStream with dispatch queue for client; use POSIX sockets for server
grpc_set_tcp_client_impl(&grpc_cfstream_client_vtable);
grpc_set_tcp_server_impl(&grpc_posix_tcp_server_vtable);
grpc_set_pollset_vtable(&grpc_posix_pollset_vtable);
grpc_set_pollset_set_vtable(&grpc_posix_pollset_set_vtable);
grpc_set_iomgr_platform_vtable(&vtable);
} else {
// Use CFStream with CFRunLoop for client; server not supported
grpc_set_tcp_client_impl(&grpc_cfstream_client_vtable);
grpc_set_pollset_vtable(&grpc_apple_pollset_vtable);
grpc_set_pollset_set_vtable(&grpc_apple_pollset_set_vtable);
grpc_set_iomgr_platform_vtable(&apple_vtable);
char* enable_cfstream = getenv(grpc_cfstream_env_var);
grpc_tcp_client_vtable* client_vtable = &grpc_posix_tcp_client_vtable;
// CFStream is enabled by default on iOS, and disabled by default on other
// platforms. Defaults can be overriden by setting the grpc_cfstream
// environment variable.
#if TARGET_OS_IPHONE
if (enable_cfstream == nullptr || enable_cfstream[0] == '1') {
client_vtable = &grpc_cfstream_client_vtable;
}
#else
if (enable_cfstream != nullptr && enable_cfstream[0] == '1') {
client_vtable = &grpc_cfstream_client_vtable;
}
#endif
grpc_set_tcp_client_impl(client_vtable);
grpc_set_tcp_server_impl(&grpc_posix_tcp_server_vtable);
grpc_set_timer_impl(&grpc_generic_timer_vtable);
grpc_set_pollset_vtable(&grpc_posix_pollset_vtable);
grpc_set_pollset_set_vtable(&grpc_posix_pollset_set_vtable);
grpc_set_resolver_impl(&grpc_posix_resolver_vtable);
grpc_set_iomgr_platform_vtable(&vtable);
}
bool grpc_iomgr_run_in_background() {
char* enable_cfstream_str = getenv(grpc_cfstream_env_var);
bool enable_cfstream =
enable_cfstream_str == nullptr || enable_cfstream_str[0] != '0';
char* enable_cfstream_run_loop_str = getenv(grpc_cfstream_run_loop_env_var);
// CFStream run-loop is disabled by default. The user has to enable it
// explicitly with environment variable.
bool enable_cfstream_run_loop = enable_cfstream_run_loop_str != nullptr &&
enable_cfstream_run_loop_str[0] == '1';
if (enable_cfstream && enable_cfstream_run_loop) {
return false;
} else {
return grpc_event_engine_run_in_background();
}
return grpc_event_engine_run_in_background();
}
#endif /* GRPC_CFSTREAM_IOMGR */

@ -22,23 +22,23 @@
#include "src/core/lib/iomgr/pollset_set.h"
static grpc_pollset_set* pollset_set_create(void) {
grpc_pollset_set* pollset_set_create(void) {
return (grpc_pollset_set*)((intptr_t)0xdeafbeef);
}
static void pollset_set_destroy(grpc_pollset_set* /*pollset_set*/) {}
void pollset_set_destroy(grpc_pollset_set* /*pollset_set*/) {}
static void pollset_set_add_pollset(grpc_pollset_set* /*pollset_set*/,
grpc_pollset* /*pollset*/) {}
void pollset_set_add_pollset(grpc_pollset_set* /*pollset_set*/,
grpc_pollset* /*pollset*/) {}
static void pollset_set_del_pollset(grpc_pollset_set* /*pollset_set*/,
grpc_pollset* /*pollset*/) {}
void pollset_set_del_pollset(grpc_pollset_set* /*pollset_set*/,
grpc_pollset* /*pollset*/) {}
static void pollset_set_add_pollset_set(grpc_pollset_set* /*bag*/,
grpc_pollset_set* /*item*/) {}
void pollset_set_add_pollset_set(grpc_pollset_set* /*bag*/,
grpc_pollset_set* /*item*/) {}
static void pollset_set_del_pollset_set(grpc_pollset_set* /*bag*/,
grpc_pollset_set* /*item*/) {}
void pollset_set_del_pollset_set(grpc_pollset_set* /*bag*/,
grpc_pollset_set* /*item*/) {}
static grpc_pollset_set_vtable vtable = {
pollset_set_create, pollset_set_destroy,

@ -129,7 +129,6 @@
#define GRPC_CFSTREAM_IOMGR 1
#define GRPC_CFSTREAM_CLIENT 1
#define GRPC_CFSTREAM_ENDPOINT 1
#define GRPC_APPLE_EV 1
#define GRPC_POSIX_SOCKET_ARES_EV_DRIVER 1
#define GRPC_POSIX_SOCKET_EV 1
#define GRPC_POSIX_SOCKET_EV_EPOLL1 1

@ -22,73 +22,28 @@
#include <string.h>
#include <grpc/byte_buffer.h>
#include <grpc/compression.h>
#include <grpc/grpc.h>
#include <grpc/slice_buffer.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "src/core/lib/compression/message_compress.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/slice/slice_internal.h"
static int is_compressed(grpc_byte_buffer* buffer) {
switch (buffer->type) {
case GRPC_BB_RAW:
if (buffer->data.raw.compression == GRPC_COMPRESS_NONE) {
return 0 /* GPR_FALSE */;
}
break;
}
return 1 /* GPR_TRUE */;
}
int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader,
grpc_byte_buffer* buffer) {
grpc_core::ExecCtx exec_ctx;
grpc_slice_buffer decompressed_slices_buffer;
reader->buffer_in = buffer;
switch (reader->buffer_in->type) {
case GRPC_BB_RAW:
grpc_slice_buffer_init(&decompressed_slices_buffer);
if (is_compressed(reader->buffer_in)) {
if (grpc_msg_decompress(
grpc_compression_algorithm_to_message_compression_algorithm(
reader->buffer_in->data.raw.compression),
&reader->buffer_in->data.raw.slice_buffer,
&decompressed_slices_buffer) == 0) {
gpr_log(GPR_ERROR,
"Unexpected error decompressing data for algorithm with enum "
"value '%d'.",
reader->buffer_in->data.raw.compression);
memset(reader, 0, sizeof(*reader));
return 0;
} else { /* all fine */
reader->buffer_out =
grpc_raw_byte_buffer_create(decompressed_slices_buffer.slices,
decompressed_slices_buffer.count);
}
grpc_slice_buffer_destroy_internal(&decompressed_slices_buffer);
} else { /* not compressed, use the input buffer as output */
reader->buffer_out = reader->buffer_in;
}
reader->buffer_out = reader->buffer_in;
reader->current.index = 0;
break;
}
return 1;
}
void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader* reader) {
switch (reader->buffer_in->type) {
case GRPC_BB_RAW:
/* keeping the same if-else structure as in the init function */
if (is_compressed(reader->buffer_in)) {
grpc_byte_buffer_destroy(reader->buffer_out);
}
break;
}
reader->buffer_out = nullptr;
}
int grpc_byte_buffer_reader_peek(grpc_byte_buffer_reader* reader,

@ -268,7 +268,7 @@ struct grpc_server {
(((channel_data*)(elem)->channel_data)->server)
namespace {
void publish_new_rpc(void* calld, grpc_error* error);
void publish_new_rpc(void* arg, grpc_error* error);
void fail_call(grpc_server* server, size_t cq_idx, requested_call* rc,
grpc_error* error);
/* Before calling maybe_finish_shutdown, we must hold mu_global and not

@ -26,10 +26,15 @@
#include "src/core/lib/iomgr/closure.h"
/** Internal bit flag for grpc_begin_message's \a flags signaling the use of
* compression for the message */
* compression for the message. (Does not apply for stream compression.) */
#define GRPC_WRITE_INTERNAL_COMPRESS (0x80000000u)
/** Internal bit flag for determining whether the message was compressed and had
* to be decompressed by the message_decompress filter. (Does not apply for
* stream compression.) */
#define GRPC_WRITE_INTERNAL_TEST_ONLY_WAS_COMPRESSED (0x40000000u)
/** Mask of all valid internal flags. */
#define GRPC_WRITE_INTERNAL_USED_MASK (GRPC_WRITE_INTERNAL_COMPRESS)
#define GRPC_WRITE_INTERNAL_USED_MASK \
(GRPC_WRITE_INTERNAL_COMPRESS | GRPC_WRITE_INTERNAL_TEST_ONLY_WAS_COMPRESSED)
namespace grpc_core {

@ -44,6 +44,8 @@ void grpc_lb_policy_priority_init(void);
void grpc_lb_policy_priority_shutdown(void);
void grpc_lb_policy_weighted_target_init(void);
void grpc_lb_policy_weighted_target_shutdown(void);
void grpc_lb_policy_xds_routing_init(void);
void grpc_lb_policy_xds_routing_shutdown(void);
void grpc_lb_policy_pick_first_init(void);
void grpc_lb_policy_pick_first_shutdown(void);
void grpc_lb_policy_round_robin_init(void);
@ -92,6 +94,8 @@ void grpc_register_built_in_plugins(void) {
grpc_lb_policy_priority_shutdown);
grpc_register_plugin(grpc_lb_policy_weighted_target_init,
grpc_lb_policy_weighted_target_shutdown);
grpc_register_plugin(grpc_lb_policy_xds_routing_init,
grpc_lb_policy_xds_routing_shutdown);
grpc_register_plugin(grpc_lb_policy_pick_first_init,
grpc_lb_policy_pick_first_shutdown);
grpc_register_plugin(grpc_lb_policy_round_robin_init,

@ -52,6 +52,8 @@ void grpc_lb_policy_priority_init(void);
void grpc_lb_policy_priority_shutdown(void);
void grpc_lb_policy_weighted_target_init(void);
void grpc_lb_policy_weighted_target_shutdown(void);
void grpc_lb_policy_xds_routing_init(void);
void grpc_lb_policy_xds_routing_shutdown(void);
void grpc_lb_policy_pick_first_init(void);
void grpc_lb_policy_pick_first_shutdown(void);
void grpc_lb_policy_round_robin_init(void);
@ -100,6 +102,8 @@ void grpc_register_built_in_plugins(void) {
grpc_lb_policy_priority_shutdown);
grpc_register_plugin(grpc_lb_policy_weighted_target_init,
grpc_lb_policy_weighted_target_shutdown);
grpc_register_plugin(grpc_lb_policy_xds_routing_init,
grpc_lb_policy_xds_routing_shutdown);
grpc_register_plugin(grpc_lb_policy_pick_first_init,
grpc_lb_policy_pick_first_shutdown);
grpc_register_plugin(grpc_lb_policy_round_robin_init,

@ -66,13 +66,8 @@
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "GRPC_CFSTREAM_RUN_LOOP"
value = "1"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Test"

@ -69,13 +69,8 @@
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "GRPC_CFSTREAM_RUN_LOOP"
value = "1"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

@ -96,13 +96,8 @@
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "GRPC_CFSTREAM_RUN_LOOP"
value = "1"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

@ -66,13 +66,8 @@
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "GRPC_CFSTREAM_RUN_LOOP"
value = "1"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

@ -61,13 +61,8 @@
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "GRPC_CFSTREAM_RUN_LOOP"
value = "1"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

@ -22,6 +22,34 @@ package grpc.testing;
service EchoTestService {
rpc Echo(EchoRequest) returns (EchoResponse);
rpc Echo1(EchoRequest) returns (EchoResponse);
rpc Echo2(EchoRequest) returns (EchoResponse);
// A service which checks that the initial metadata sent over contains some
// expected key value pair
rpc CheckClientInitialMetadata(SimpleRequest) returns (SimpleResponse);
rpc RequestStream(stream EchoRequest) returns (EchoResponse);
rpc ResponseStream(EchoRequest) returns (stream EchoResponse);
rpc BidiStream(stream EchoRequest) returns (stream EchoResponse);
rpc Unimplemented(EchoRequest) returns (EchoResponse);
}
service EchoTest1Service {
rpc Echo(EchoRequest) returns (EchoResponse);
rpc Echo1(EchoRequest) returns (EchoResponse);
rpc Echo2(EchoRequest) returns (EchoResponse);
// A service which checks that the initial metadata sent over contains some
// expected key value pair
rpc CheckClientInitialMetadata(SimpleRequest) returns (SimpleResponse);
rpc RequestStream(stream EchoRequest) returns (EchoResponse);
rpc ResponseStream(EchoRequest) returns (stream EchoResponse);
rpc BidiStream(stream EchoRequest) returns (stream EchoResponse);
rpc Unimplemented(EchoRequest) returns (EchoResponse);
}
service EchoTest2Service {
rpc Echo(EchoRequest) returns (EchoResponse);
rpc Echo1(EchoRequest) returns (EchoResponse);
rpc Echo2(EchoRequest) returns (EchoResponse);
// A service which checks that the initial metadata sent over contains some
// expected key value pair
rpc CheckClientInitialMetadata(SimpleRequest) returns (SimpleResponse);

@ -34,6 +34,7 @@ message RouteMatch {
// If specified, the route is a prefix rule meaning that the prefix must
// match the beginning of the *:path* header.
string prefix = 1;
string path = 2;
}
}

@ -43,6 +43,7 @@ CORE_SOURCE_FILES = [
'src/core/ext/filters/client_channel/lb_policy/xds/cds.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/eds.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc',
'src/core/ext/filters/client_channel/lb_policy_registry.cc',
'src/core/ext/filters/client_channel/local_subchannel_pool.cc',
'src/core/ext/filters/client_channel/parse_address.cc',
@ -82,6 +83,7 @@ CORE_SOURCE_FILES = [
'src/core/ext/filters/http/client_authority_filter.cc',
'src/core/ext/filters/http/http_filters_plugin.cc',
'src/core/ext/filters/http/message_compress/message_compress_filter.cc',
'src/core/ext/filters/http/message_compress/message_decompress_filter.cc',
'src/core/ext/filters/http/server/http_server_filter.cc',
'src/core/ext/filters/max_age/max_age_filter.cc',
'src/core/ext/filters/message_size/message_size_filter.cc',
@ -264,7 +266,6 @@ CORE_SOURCE_FILES = [
'src/core/lib/iomgr/endpoint_pair_windows.cc',
'src/core/lib/iomgr/error.cc',
'src/core/lib/iomgr/error_cfstream.cc',
'src/core/lib/iomgr/ev_apple.cc',
'src/core/lib/iomgr/ev_epoll1_linux.cc',
'src/core/lib/iomgr/ev_epollex_linux.cc',
'src/core/lib/iomgr/ev_poll_posix.cc',

@ -17,7 +17,7 @@ py_grpc_library(
py_library(
name = "grpc_reflection",
srcs = glob(["*.py"]),
srcs = ["reflection.py"],
imports = ["../../"],
deps = [
":reflection_py_pb2",

@ -1,57 +0,0 @@
# Copyright 2020 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The AsyncIO version of the reflection servicer."""
from typing import AsyncIterable
import grpc
from grpc_reflection.v1alpha import reflection_pb2 as _reflection_pb2
from grpc_reflection.v1alpha._base import BaseReflectionServicer
class ReflectionServicer(BaseReflectionServicer):
"""Servicer handling RPCs for service statuses."""
async def ServerReflectionInfo(
self, request_iterator: AsyncIterable[
_reflection_pb2.ServerReflectionRequest], unused_context
) -> AsyncIterable[_reflection_pb2.ServerReflectionResponse]:
async for request in request_iterator:
if request.HasField('file_by_filename'):
yield self._file_by_filename(request.file_by_filename)
elif request.HasField('file_containing_symbol'):
yield self._file_containing_symbol(
request.file_containing_symbol)
elif request.HasField('file_containing_extension'):
yield self._file_containing_extension(
request.file_containing_extension.containing_type,
request.file_containing_extension.extension_number)
elif request.HasField('all_extension_numbers_of_type'):
yield self._all_extension_numbers_of_type(
request.all_extension_numbers_of_type)
elif request.HasField('list_services'):
yield self._list_services()
else:
yield _reflection_pb2.ServerReflectionResponse(
error_response=_reflection_pb2.ErrorResponse(
error_code=grpc.StatusCode.INVALID_ARGUMENT.value[0],
error_message=grpc.StatusCode.INVALID_ARGUMENT.value[1].
encode(),
))
__all__ = [
"ReflectionServicer",
]

@ -1,110 +0,0 @@
# Copyright 2020 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Base implementation of reflection servicer."""
import grpc
from google.protobuf import descriptor_pb2
from google.protobuf import descriptor_pool
from grpc_reflection.v1alpha import reflection_pb2 as _reflection_pb2
from grpc_reflection.v1alpha import reflection_pb2_grpc as _reflection_pb2_grpc
_POOL = descriptor_pool.Default()
def _not_found_error():
return _reflection_pb2.ServerReflectionResponse(
error_response=_reflection_pb2.ErrorResponse(
error_code=grpc.StatusCode.NOT_FOUND.value[0],
error_message=grpc.StatusCode.NOT_FOUND.value[1].encode(),
))
def _file_descriptor_response(descriptor):
proto = descriptor_pb2.FileDescriptorProto()
descriptor.CopyToProto(proto)
serialized_proto = proto.SerializeToString()
return _reflection_pb2.ServerReflectionResponse(
file_descriptor_response=_reflection_pb2.FileDescriptorResponse(
file_descriptor_proto=(serialized_proto,)),)
class BaseReflectionServicer(_reflection_pb2_grpc.ServerReflectionServicer):
"""Base class for reflection servicer."""
def __init__(self, service_names, pool=None):
"""Constructor.
Args:
service_names: Iterable of fully-qualified service names available.
pool: An optional DescriptorPool instance.
"""
self._service_names = tuple(sorted(service_names))
self._pool = _POOL if pool is None else pool
def _file_by_filename(self, filename):
try:
descriptor = self._pool.FindFileByName(filename)
except KeyError:
return _not_found_error()
else:
return _file_descriptor_response(descriptor)
def _file_containing_symbol(self, fully_qualified_name):
try:
descriptor = self._pool.FindFileContainingSymbol(
fully_qualified_name)
except KeyError:
return _not_found_error()
else:
return _file_descriptor_response(descriptor)
def _file_containing_extension(self, containing_type, extension_number):
try:
message_descriptor = self._pool.FindMessageTypeByName(
containing_type)
extension_descriptor = self._pool.FindExtensionByNumber(
message_descriptor, extension_number)
descriptor = self._pool.FindFileContainingSymbol(
extension_descriptor.full_name)
except KeyError:
return _not_found_error()
else:
return _file_descriptor_response(descriptor)
def _all_extension_numbers_of_type(self, containing_type):
try:
message_descriptor = self._pool.FindMessageTypeByName(
containing_type)
extension_numbers = tuple(
sorted(extension.number for extension in
self._pool.FindAllExtensions(message_descriptor)))
except KeyError:
return _not_found_error()
else:
return _reflection_pb2.ServerReflectionResponse(
all_extension_numbers_response=_reflection_pb2.
ExtensionNumberResponse(
base_type_name=message_descriptor.full_name,
extension_number=extension_numbers))
def _list_services(self):
return _reflection_pb2.ServerReflectionResponse(
list_services_response=_reflection_pb2.ListServiceResponse(service=[
_reflection_pb2.ServiceResponse(name=service_name)
for service_name in self._service_names
]))
__all__ = ['BaseReflectionServicer']

@ -13,21 +13,100 @@
# limitations under the License.
"""Reference implementation for reflection in gRPC Python."""
import sys
import grpc
from google.protobuf import descriptor_pb2
from google.protobuf import descriptor_pool
from grpc_reflection.v1alpha import reflection_pb2 as _reflection_pb2
from grpc_reflection.v1alpha import reflection_pb2_grpc as _reflection_pb2_grpc
from grpc_reflection.v1alpha._base import BaseReflectionServicer
_POOL = descriptor_pool.Default()
SERVICE_NAME = _reflection_pb2.DESCRIPTOR.services_by_name[
'ServerReflection'].full_name
class ReflectionServicer(BaseReflectionServicer):
def _not_found_error():
return _reflection_pb2.ServerReflectionResponse(
error_response=_reflection_pb2.ErrorResponse(
error_code=grpc.StatusCode.NOT_FOUND.value[0],
error_message=grpc.StatusCode.NOT_FOUND.value[1].encode(),
))
def _file_descriptor_response(descriptor):
proto = descriptor_pb2.FileDescriptorProto()
descriptor.CopyToProto(proto)
serialized_proto = proto.SerializeToString()
return _reflection_pb2.ServerReflectionResponse(
file_descriptor_response=_reflection_pb2.FileDescriptorResponse(
file_descriptor_proto=(serialized_proto,)),)
class ReflectionServicer(_reflection_pb2_grpc.ServerReflectionServicer):
"""Servicer handling RPCs for service statuses."""
def __init__(self, service_names, pool=None):
"""Constructor.
Args:
service_names: Iterable of fully-qualified service names available.
"""
self._service_names = tuple(sorted(service_names))
self._pool = _POOL if pool is None else pool
def _file_by_filename(self, filename):
try:
descriptor = self._pool.FindFileByName(filename)
except KeyError:
return _not_found_error()
else:
return _file_descriptor_response(descriptor)
def _file_containing_symbol(self, fully_qualified_name):
try:
descriptor = self._pool.FindFileContainingSymbol(
fully_qualified_name)
except KeyError:
return _not_found_error()
else:
return _file_descriptor_response(descriptor)
def _file_containing_extension(self, containing_type, extension_number):
try:
message_descriptor = self._pool.FindMessageTypeByName(
containing_type)
extension_descriptor = self._pool.FindExtensionByNumber(
message_descriptor, extension_number)
descriptor = self._pool.FindFileContainingSymbol(
extension_descriptor.full_name)
except KeyError:
return _not_found_error()
else:
return _file_descriptor_response(descriptor)
def _all_extension_numbers_of_type(self, containing_type):
try:
message_descriptor = self._pool.FindMessageTypeByName(
containing_type)
extension_numbers = tuple(
sorted(extension.number for extension in
self._pool.FindAllExtensions(message_descriptor)))
except KeyError:
return _not_found_error()
else:
return _reflection_pb2.ServerReflectionResponse(
all_extension_numbers_response=_reflection_pb2.
ExtensionNumberResponse(
base_type_name=message_descriptor.full_name,
extension_number=extension_numbers))
def _list_services(self):
return _reflection_pb2.ServerReflectionResponse(
list_services_response=_reflection_pb2.ListServiceResponse(service=[
_reflection_pb2.ServiceResponse(name=service_name)
for service_name in self._service_names
]))
def ServerReflectionInfo(self, request_iterator, context):
# pylint: disable=unused-argument
for request in request_iterator:
@ -54,45 +133,13 @@ class ReflectionServicer(BaseReflectionServicer):
))
_enable_server_reflection_doc = """Enables server reflection on a server.
def enable_server_reflection(service_names, server, pool=None):
"""Enables server reflection on a server.
Args:
service_names: Iterable of fully-qualified service names available.
server: grpc.Server to which reflection service will be added.
pool: DescriptorPool object to use (descriptor_pool.Default() if None).
"""
if sys.version_info[0] >= 3 and sys.version_info[1] >= 6:
# Exposes AsyncReflectionServicer as public API.
from . import _async as aio
from grpc.experimental import aio as grpc_aio # pylint: disable=ungrouped-imports
def enable_server_reflection(service_names, server, pool=None):
if isinstance(server, grpc_aio.Server):
_reflection_pb2_grpc.add_ServerReflectionServicer_to_server(
aio.ReflectionServicer(service_names, pool=pool), server)
else:
_reflection_pb2_grpc.add_ServerReflectionServicer_to_server(
ReflectionServicer(service_names, pool=pool), server)
enable_server_reflection.__doc__ = _enable_server_reflection_doc
__all__ = [
"SERVICE_NAME",
"ReflectionServicer",
"enable_server_reflection",
"aio",
]
else:
def enable_server_reflection(service_names, server, pool=None):
_reflection_pb2_grpc.add_ServerReflectionServicer_to_server(
ReflectionServicer(service_names, pool=pool), server)
enable_server_reflection.__doc__ = _enable_server_reflection_doc
__all__ = [
"SERVICE_NAME",
"ReflectionServicer",
"enable_server_reflection",
]
Args:
service_names: Iterable of fully-qualified service names available.
server: grpc.Server to which reflection service will be added.
pool: DescriptorPool object to use (descriptor_pool.Default() if None).
"""
_reflection_pb2_grpc.add_ServerReflectionServicer_to_server(
ReflectionServicer(service_names, pool=pool), server)

@ -1,30 +0,0 @@
# Copyright 2020 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
package(default_testonly = 1)
py_test(
name = "reflection_servicer_test",
srcs = ["reflection_servicer_test.py"],
imports = ["../../"],
python_version = "PY3",
deps = [
"//src/proto/grpc/testing:empty_py_pb2",
"//src/proto/grpc/testing/proto2:empty2_extensions_proto",
"//src/proto/grpc/testing/proto2:empty2_proto",
"//src/python/grpcio/grpc:grpcio",
"//src/python/grpcio_reflection/grpc_reflection/v1alpha:grpc_reflection",
"//src/python/grpcio_tests/tests_aio/unit:_test_base",
],
)

@ -1,13 +0,0 @@
# Copyright 2016 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

@ -1,193 +0,0 @@
# Copyright 2016 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests of grpc_reflection.v1alpha.reflection."""
import logging
import unittest
import grpc
from google.protobuf import descriptor_pb2, descriptor_pool
from grpc.experimental import aio
from grpc_reflection.v1alpha import (reflection, reflection_pb2,
reflection_pb2_grpc)
from src.proto.grpc.testing import empty_pb2
from src.proto.grpc.testing.proto2 import empty2_extensions_pb2
from tests_aio.unit._test_base import AioTestBase
_EMPTY_PROTO_FILE_NAME = 'src/proto/grpc/testing/empty.proto'
_EMPTY_PROTO_SYMBOL_NAME = 'grpc.testing.Empty'
_SERVICE_NAMES = ('Angstrom', 'Bohr', 'Curie', 'Dyson', 'Einstein', 'Feynman',
'Galilei')
_EMPTY_EXTENSIONS_SYMBOL_NAME = 'grpc.testing.proto2.EmptyWithExtensions'
_EMPTY_EXTENSIONS_NUMBERS = (
124,
125,
126,
127,
128,
)
def _file_descriptor_to_proto(descriptor):
proto = descriptor_pb2.FileDescriptorProto()
descriptor.CopyToProto(proto)
return proto.SerializeToString()
class ReflectionServicerTest(AioTestBase):
async def setUp(self):
self._server = aio.server()
reflection.enable_server_reflection(_SERVICE_NAMES, self._server)
port = self._server.add_insecure_port('[::]:0')
await self._server.start()
self._channel = aio.insecure_channel('localhost:%d' % port)
self._stub = reflection_pb2_grpc.ServerReflectionStub(self._channel)
async def tearDown(self):
await self._server.stop(None)
await self._channel.close()
async def test_file_by_name(self):
requests = (
reflection_pb2.ServerReflectionRequest(
file_by_filename=_EMPTY_PROTO_FILE_NAME),
reflection_pb2.ServerReflectionRequest(
file_by_filename='i-donut-exist'),
)
responses = []
async for response in self._stub.ServerReflectionInfo(iter(requests)):
responses.append(response)
expected_responses = (
reflection_pb2.ServerReflectionResponse(
valid_host='',
file_descriptor_response=reflection_pb2.FileDescriptorResponse(
file_descriptor_proto=(
_file_descriptor_to_proto(empty_pb2.DESCRIPTOR),))),
reflection_pb2.ServerReflectionResponse(
valid_host='',
error_response=reflection_pb2.ErrorResponse(
error_code=grpc.StatusCode.NOT_FOUND.value[0],
error_message=grpc.StatusCode.NOT_FOUND.value[1].encode(),
)),
)
self.assertSequenceEqual(expected_responses, responses)
async def test_file_by_symbol(self):
requests = (
reflection_pb2.ServerReflectionRequest(
file_containing_symbol=_EMPTY_PROTO_SYMBOL_NAME),
reflection_pb2.ServerReflectionRequest(
file_containing_symbol='i.donut.exist.co.uk.org.net.me.name.foo'
),
)
responses = []
async for response in self._stub.ServerReflectionInfo(iter(requests)):
responses.append(response)
expected_responses = (
reflection_pb2.ServerReflectionResponse(
valid_host='',
file_descriptor_response=reflection_pb2.FileDescriptorResponse(
file_descriptor_proto=(
_file_descriptor_to_proto(empty_pb2.DESCRIPTOR),))),
reflection_pb2.ServerReflectionResponse(
valid_host='',
error_response=reflection_pb2.ErrorResponse(
error_code=grpc.StatusCode.NOT_FOUND.value[0],
error_message=grpc.StatusCode.NOT_FOUND.value[1].encode(),
)),
)
self.assertSequenceEqual(expected_responses, responses)
async def test_file_containing_extension(self):
requests = (
reflection_pb2.ServerReflectionRequest(
file_containing_extension=reflection_pb2.ExtensionRequest(
containing_type=_EMPTY_EXTENSIONS_SYMBOL_NAME,
extension_number=125,
),),
reflection_pb2.ServerReflectionRequest(
file_containing_extension=reflection_pb2.ExtensionRequest(
containing_type='i.donut.exist.co.uk.org.net.me.name.foo',
extension_number=55,
),),
)
responses = []
async for response in self._stub.ServerReflectionInfo(iter(requests)):
responses.append(response)
expected_responses = (
reflection_pb2.ServerReflectionResponse(
valid_host='',
file_descriptor_response=reflection_pb2.FileDescriptorResponse(
file_descriptor_proto=(_file_descriptor_to_proto(
empty2_extensions_pb2.DESCRIPTOR),))),
reflection_pb2.ServerReflectionResponse(
valid_host='',
error_response=reflection_pb2.ErrorResponse(
error_code=grpc.StatusCode.NOT_FOUND.value[0],
error_message=grpc.StatusCode.NOT_FOUND.value[1].encode(),
)),
)
self.assertSequenceEqual(expected_responses, responses)
async def test_extension_numbers_of_type(self):
requests = (
reflection_pb2.ServerReflectionRequest(
all_extension_numbers_of_type=_EMPTY_EXTENSIONS_SYMBOL_NAME),
reflection_pb2.ServerReflectionRequest(
all_extension_numbers_of_type='i.donut.exist.co.uk.net.name.foo'
),
)
responses = []
async for response in self._stub.ServerReflectionInfo(iter(requests)):
responses.append(response)
expected_responses = (
reflection_pb2.ServerReflectionResponse(
valid_host='',
all_extension_numbers_response=reflection_pb2.
ExtensionNumberResponse(
base_type_name=_EMPTY_EXTENSIONS_SYMBOL_NAME,
extension_number=_EMPTY_EXTENSIONS_NUMBERS)),
reflection_pb2.ServerReflectionResponse(
valid_host='',
error_response=reflection_pb2.ErrorResponse(
error_code=grpc.StatusCode.NOT_FOUND.value[0],
error_message=grpc.StatusCode.NOT_FOUND.value[1].encode(),
)),
)
self.assertSequenceEqual(expected_responses, responses)
async def test_list_services(self):
requests = (reflection_pb2.ServerReflectionRequest(list_services='',),)
responses = []
async for response in self._stub.ServerReflectionInfo(iter(requests)):
responses.append(response)
expected_responses = (reflection_pb2.ServerReflectionResponse(
valid_host='',
list_services_response=reflection_pb2.ListServiceResponse(
service=tuple(
reflection_pb2.ServiceResponse(name=name)
for name in _SERVICE_NAMES))),)
self.assertSequenceEqual(expected_responses, responses)
def test_reflection_service_name(self):
self.assertEqual(reflection.SERVICE_NAME,
'grpc.reflection.v1alpha.ServerReflection')
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
unittest.main(verbosity=2)

@ -3,7 +3,6 @@
"health_check.health_servicer_test.HealthServicerTest",
"interop.local_interop_test.InsecureLocalInteropTest",
"interop.local_interop_test.SecureLocalInteropTest",
"reflection.reflection_servicer_test.ReflectionServicerTest",
"status.grpc_status_test.StatusTest",
"unit._metadata_test.TestTypeMetadata",
"unit.abort_test.TestAbort",

@ -73,13 +73,15 @@ int main(int argc, char** argv) {
"authority", "connected", NULL);
errors += CHECK_STACK("unknown", &minimal_stack_args, GRPC_SERVER_CHANNEL,
"server", "connected", NULL);
errors +=
CHECK_STACK("chttp2", &minimal_stack_args, GRPC_CLIENT_DIRECT_CHANNEL,
"authority", "http-client", "connected", NULL);
errors += CHECK_STACK("chttp2", &minimal_stack_args,
GRPC_CLIENT_DIRECT_CHANNEL, "authority", "http-client",
"message_decompress", "connected", NULL);
errors += CHECK_STACK("chttp2", &minimal_stack_args, GRPC_CLIENT_SUBCHANNEL,
"authority", "http-client", "connected", NULL);
errors += CHECK_STACK("chttp2", &minimal_stack_args, GRPC_SERVER_CHANNEL,
"server", "http-server", "connected", NULL);
"authority", "http-client", "message_decompress",
"connected", NULL);
errors +=
CHECK_STACK("chttp2", &minimal_stack_args, GRPC_SERVER_CHANNEL, "server",
"http-server", "message_decompress", "connected", NULL);
errors += CHECK_STACK(nullptr, &minimal_stack_args, GRPC_CLIENT_CHANNEL,
"client-channel", NULL);
@ -91,15 +93,17 @@ int main(int argc, char** argv) {
"message_size", "connected", NULL);
errors += CHECK_STACK("unknown", nullptr, GRPC_SERVER_CHANNEL, "server",
"message_size", "deadline", "connected", NULL);
errors += CHECK_STACK("chttp2", nullptr, GRPC_CLIENT_DIRECT_CHANNEL,
"authority", "message_size", "deadline", "http-client",
"message_compress", "connected", NULL);
errors +=
CHECK_STACK("chttp2", nullptr, GRPC_CLIENT_DIRECT_CHANNEL, "authority",
"message_size", "deadline", "http-client",
"message_decompress", "message_compress", "connected", NULL);
errors += CHECK_STACK("chttp2", nullptr, GRPC_CLIENT_SUBCHANNEL, "authority",
"message_size", "http-client", "message_compress",
"connected", NULL);
errors += CHECK_STACK("chttp2", nullptr, GRPC_SERVER_CHANNEL, "server",
"message_size", "deadline", "http-server",
"message_size", "http-client", "message_decompress",
"message_compress", "connected", NULL);
errors +=
CHECK_STACK("chttp2", nullptr, GRPC_SERVER_CHANNEL, "server",
"message_size", "deadline", "http-server",
"message_decompress", "message_compress", "connected", NULL);
errors += CHECK_STACK(nullptr, nullptr, GRPC_CLIENT_CHANNEL, "client-channel",
NULL);

@ -29,6 +29,8 @@
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/time.h>
#include "src/core/lib/compression/compression_internal.h"
#include "src/core/lib/compression/message_compress.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/surface/event_string.h"
@ -145,33 +147,25 @@ int raw_byte_buffer_eq_slice(grpc_byte_buffer* rbb, grpc_slice b) {
}
int byte_buffer_eq_slice(grpc_byte_buffer* bb, grpc_slice b) {
grpc_byte_buffer_reader reader;
grpc_byte_buffer* rbb;
int res;
GPR_ASSERT(grpc_byte_buffer_reader_init(&reader, bb) &&
"Couldn't init byte buffer reader");
rbb = grpc_raw_byte_buffer_from_reader(&reader);
res = raw_byte_buffer_eq_slice(rbb, b);
grpc_byte_buffer_reader_destroy(&reader);
grpc_byte_buffer_destroy(rbb);
return res;
if (bb->data.raw.compression > GRPC_COMPRESS_NONE) {
grpc_slice_buffer decompressed_buffer;
grpc_slice_buffer_init(&decompressed_buffer);
GPR_ASSERT(grpc_msg_decompress(
grpc_compression_algorithm_to_message_compression_algorithm(
bb->data.raw.compression),
&bb->data.raw.slice_buffer, &decompressed_buffer));
grpc_byte_buffer* rbb = grpc_raw_byte_buffer_create(
decompressed_buffer.slices, decompressed_buffer.count);
int ret_val = raw_byte_buffer_eq_slice(rbb, b);
grpc_byte_buffer_destroy(rbb);
grpc_slice_buffer_destroy(&decompressed_buffer);
return ret_val;
}
return raw_byte_buffer_eq_slice(bb, b);
}
int byte_buffer_eq_string(grpc_byte_buffer* bb, const char* str) {
grpc_byte_buffer_reader reader;
grpc_byte_buffer* rbb;
int res;
GPR_ASSERT(grpc_byte_buffer_reader_init(&reader, bb) &&
"Couldn't init byte buffer reader");
rbb = grpc_raw_byte_buffer_from_reader(&reader);
res = raw_byte_buffer_eq_slice(rbb, grpc_slice_from_copied_string(str));
grpc_byte_buffer_reader_destroy(&reader);
grpc_byte_buffer_destroy(rbb);
return res;
return byte_buffer_eq_slice(bb, grpc_slice_from_copied_string(str));
}
static bool is_probably_integer(void* p) { return ((uintptr_t)p) < 1000000; }

@ -41,9 +41,12 @@ static void* tag(intptr_t t) { return (void*)t; }
static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
const char* test_name,
grpc_channel_args* client_args,
grpc_channel_args* server_args) {
grpc_channel_args* server_args,
bool decompress_in_core) {
grpc_end2end_test_fixture f;
gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
gpr_log(GPR_INFO, "Running test: %s%s/%s", test_name,
decompress_in_core ? "" : "_with_decompression_disabled",
config.name);
f = config.create_fixture(client_args, server_args);
config.init_server(&f, server_args);
config.init_client(&f, client_args);
@ -97,7 +100,8 @@ static void request_for_disabled_algorithm(
uint32_t send_flags_bitmask,
grpc_compression_algorithm algorithm_to_disable,
grpc_compression_algorithm requested_client_compression_algorithm,
grpc_status_code expected_error, grpc_metadata* client_metadata) {
grpc_status_code expected_error, grpc_metadata* client_metadata,
bool decompress_in_core) {
grpc_call* c;
grpc_call* s;
grpc_slice request_payload_slice;
@ -128,13 +132,24 @@ static void request_for_disabled_algorithm(
nullptr, requested_client_compression_algorithm);
server_args = grpc_channel_args_set_channel_default_compression_algorithm(
nullptr, GRPC_COMPRESS_NONE);
{
grpc_core::ExecCtx exec_ctx;
server_args = grpc_channel_args_compression_algorithm_set_state(
&server_args, algorithm_to_disable, false);
server_args = grpc_channel_args_compression_algorithm_set_state(
&server_args, algorithm_to_disable, false);
if (!decompress_in_core) {
grpc_arg disable_decompression_in_core_arg =
grpc_channel_arg_integer_create(
const_cast<char*>(GRPC_ARG_ENABLE_PER_MESSAGE_DECOMPRESSION), 0);
grpc_channel_args* old_client_args = client_args;
grpc_channel_args* old_server_args = server_args;
client_args = grpc_channel_args_copy_and_add(
client_args, &disable_decompression_in_core_arg, 1);
server_args = grpc_channel_args_copy_and_add(
server_args, &disable_decompression_in_core_arg, 1);
grpc_channel_args_destroy(old_client_args);
grpc_channel_args_destroy(old_server_args);
}
f = begin_test(config, test_name, client_args, server_args);
f = begin_test(config, test_name, client_args, server_args,
decompress_in_core);
cqv = cq_verifier_create(f.cq);
gpr_timespec deadline = five_seconds_from_now();
@ -253,18 +268,13 @@ static void request_for_disabled_algorithm(
grpc_slice_unref(request_payload_slice);
grpc_byte_buffer_destroy(request_payload);
grpc_byte_buffer_destroy(request_payload_recv);
{
grpc_core::ExecCtx exec_ctx;
grpc_channel_args_destroy(client_args);
grpc_channel_args_destroy(server_args);
}
grpc_channel_args_destroy(client_args);
grpc_channel_args_destroy(server_args);
end_test(&f);
config.tear_down_data(&f);
}
static void request_with_payload_template(
static void request_with_payload_template_inner(
grpc_end2end_test_config config, const char* test_name,
uint32_t client_send_flags_bitmask,
grpc_compression_algorithm default_client_channel_compression_algorithm,
@ -273,7 +283,7 @@ static void request_with_payload_template(
grpc_compression_algorithm expected_algorithm_from_server,
grpc_metadata* client_init_metadata, bool set_server_level,
grpc_compression_level server_compression_level,
bool send_message_before_initial_metadata) {
bool send_message_before_initial_metadata, bool decompress_in_core) {
grpc_call* c;
grpc_call* s;
grpc_slice request_payload_slice;
@ -312,8 +322,21 @@ static void request_with_payload_template(
nullptr, default_client_channel_compression_algorithm);
server_args = grpc_channel_args_set_channel_default_compression_algorithm(
nullptr, default_server_channel_compression_algorithm);
f = begin_test(config, test_name, client_args, server_args);
if (!decompress_in_core) {
grpc_arg disable_decompression_in_core_arg =
grpc_channel_arg_integer_create(
const_cast<char*>(GRPC_ARG_ENABLE_PER_MESSAGE_DECOMPRESSION), 0);
grpc_channel_args* old_client_args = client_args;
grpc_channel_args* old_server_args = server_args;
client_args = grpc_channel_args_copy_and_add(
client_args, &disable_decompression_in_core_arg, 1);
server_args = grpc_channel_args_copy_and_add(
server_args, &disable_decompression_in_core_arg, 1);
grpc_channel_args_destroy(old_client_args);
grpc_channel_args_destroy(old_server_args);
}
f = begin_test(config, test_name, client_args, server_args,
decompress_in_core);
cqv = cq_verifier_create(f.cq);
gpr_timespec deadline = five_seconds_from_now();
@ -341,7 +364,6 @@ static void request_with_payload_template(
GPR_ASSERT(GRPC_CALL_OK == error);
CQ_EXPECT_COMPLETION(cqv, tag(2), true);
}
memset(ops, 0, sizeof(ops));
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
@ -385,7 +407,6 @@ static void request_with_payload_template(
GRPC_COMPRESS_DEFLATE) != 0);
GPR_ASSERT(GPR_BITGET(grpc_call_test_only_get_encodings_accepted_by_peer(s),
GRPC_COMPRESS_GZIP) != 0);
memset(ops, 0, sizeof(ops));
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
@ -406,7 +427,6 @@ static void request_with_payload_template(
error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(101),
nullptr);
GPR_ASSERT(GRPC_CALL_OK == error);
for (int i = 0; i < 2; i++) {
response_payload = grpc_raw_byte_buffer_create(&response_payload_slice, 1);
@ -442,7 +462,8 @@ static void request_with_payload_template(
GPR_ASSERT(request_payload_recv->type == GRPC_BB_RAW);
GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, request_str));
GPR_ASSERT(request_payload_recv->data.raw.compression ==
expected_algorithm_from_client);
(decompress_in_core ? GRPC_COMPRESS_NONE
: expected_algorithm_from_client));
memset(ops, 0, sizeof(ops));
op = ops;
@ -475,11 +496,13 @@ static void request_with_payload_template(
if (server_compression_level > GRPC_COMPRESS_LEVEL_NONE) {
const grpc_compression_algorithm algo_for_server_level =
grpc_call_compression_for_level(s, server_compression_level);
GPR_ASSERT(response_payload_recv->data.raw.compression ==
algo_for_server_level);
GPR_ASSERT(
response_payload_recv->data.raw.compression ==
(decompress_in_core ? GRPC_COMPRESS_NONE : algo_for_server_level));
} else {
GPR_ASSERT(response_payload_recv->data.raw.compression ==
expected_algorithm_from_server);
(decompress_in_core ? GRPC_COMPRESS_NONE
: expected_algorithm_from_server));
}
grpc_byte_buffer_destroy(request_payload);
@ -487,7 +510,6 @@ static void request_with_payload_template(
grpc_byte_buffer_destroy(request_payload_recv);
grpc_byte_buffer_destroy(response_payload_recv);
}
grpc_slice_unref(request_payload_slice);
grpc_slice_unref(response_payload_slice);
@ -536,17 +558,38 @@ static void request_with_payload_template(
grpc_call_unref(s);
cq_verifier_destroy(cqv);
{
grpc_core::ExecCtx exec_ctx;
grpc_channel_args_destroy(client_args);
grpc_channel_args_destroy(server_args);
}
grpc_channel_args_destroy(client_args);
grpc_channel_args_destroy(server_args);
end_test(&f);
config.tear_down_data(&f);
}
static void request_with_payload_template(
grpc_end2end_test_config config, const char* test_name,
uint32_t client_send_flags_bitmask,
grpc_compression_algorithm default_client_channel_compression_algorithm,
grpc_compression_algorithm default_server_channel_compression_algorithm,
grpc_compression_algorithm expected_algorithm_from_client,
grpc_compression_algorithm expected_algorithm_from_server,
grpc_metadata* client_init_metadata, bool set_server_level,
grpc_compression_level server_compression_level,
bool send_message_before_initial_metadata) {
request_with_payload_template_inner(
config, test_name, client_send_flags_bitmask,
default_client_channel_compression_algorithm,
default_server_channel_compression_algorithm,
expected_algorithm_from_client, expected_algorithm_from_server,
client_init_metadata, set_server_level, server_compression_level,
send_message_before_initial_metadata, false);
request_with_payload_template_inner(
config, test_name, client_send_flags_bitmask,
default_client_channel_compression_algorithm,
default_server_channel_compression_algorithm,
expected_algorithm_from_client, expected_algorithm_from_server,
client_init_metadata, set_server_level, server_compression_level,
send_message_before_initial_metadata, true);
}
static void test_invoke_request_with_exceptionally_uncompressed_payload(
grpc_end2end_test_config config) {
request_with_payload_template(
@ -634,7 +677,11 @@ static void test_invoke_request_with_disabled_algorithm(
request_for_disabled_algorithm(config,
"test_invoke_request_with_disabled_algorithm",
0, GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP,
GRPC_STATUS_UNIMPLEMENTED, nullptr);
GRPC_STATUS_UNIMPLEMENTED, nullptr, false);
request_for_disabled_algorithm(config,
"test_invoke_request_with_disabled_algorithm",
0, GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP,
GRPC_STATUS_UNIMPLEMENTED, nullptr, true);
}
void compressed_payload(grpc_end2end_test_config config) {

@ -100,8 +100,8 @@ static void request_with_payload_template(
grpc_compression_algorithm expected_algorithm_from_client,
grpc_compression_algorithm expected_algorithm_from_server,
grpc_metadata* client_init_metadata, bool set_server_level,
grpc_compression_level server_compression_level,
char* user_agent_override) {
grpc_compression_level server_compression_level, char* user_agent_override,
bool decompress_in_core) {
grpc_call* c;
grpc_call* s;
grpc_slice request_payload_slice;
@ -140,9 +140,21 @@ static void request_with_payload_template(
nullptr, default_client_channel_compression_algorithm);
server_args = grpc_channel_args_set_channel_default_compression_algorithm(
nullptr, default_server_channel_compression_algorithm);
if (!decompress_in_core) {
grpc_arg disable_decompression_in_core_arg =
grpc_channel_arg_integer_create(
const_cast<char*>(GRPC_ARG_ENABLE_PER_MESSAGE_DECOMPRESSION), 0);
grpc_channel_args* old_client_args = client_args;
grpc_channel_args* old_server_args = server_args;
client_args = grpc_channel_args_copy_and_add(
client_args, &disable_decompression_in_core_arg, 1);
server_args = grpc_channel_args_copy_and_add(
server_args, &disable_decompression_in_core_arg, 1);
grpc_channel_args_destroy(old_client_args);
grpc_channel_args_destroy(old_server_args);
}
if (user_agent_override) {
grpc_core::ExecCtx exec_ctx;
grpc_channel_args* client_args_old = client_args;
grpc_arg arg;
arg.key = const_cast<char*>(GRPC_ARG_PRIMARY_USER_AGENT_STRING);
@ -267,7 +279,8 @@ static void request_with_payload_template(
GPR_ASSERT(request_payload_recv->type == GRPC_BB_RAW);
GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, request_str));
GPR_ASSERT(request_payload_recv->data.raw.compression ==
expected_algorithm_from_client);
(decompress_in_core ? GRPC_COMPRESS_NONE
: expected_algorithm_from_client));
memset(ops, 0, sizeof(ops));
op = ops;
@ -288,11 +301,13 @@ static void request_with_payload_template(
if (server_compression_level > GRPC_COMPRESS_LEVEL_NONE) {
const grpc_compression_algorithm algo_for_server_level =
grpc_call_compression_for_level(s, server_compression_level);
GPR_ASSERT(response_payload_recv->data.raw.compression ==
algo_for_server_level);
GPR_ASSERT(
response_payload_recv->data.raw.compression ==
(decompress_in_core ? GRPC_COMPRESS_NONE : algo_for_server_level));
} else {
GPR_ASSERT(response_payload_recv->data.raw.compression ==
expected_algorithm_from_server);
(decompress_in_core ? GRPC_COMPRESS_NONE
: expected_algorithm_from_server));
}
grpc_byte_buffer_destroy(request_payload);
@ -349,13 +364,8 @@ static void request_with_payload_template(
grpc_call_unref(s);
cq_verifier_destroy(cqv);
{
grpc_core::ExecCtx exec_ctx;
grpc_channel_args_destroy(client_args);
grpc_channel_args_destroy(server_args);
}
grpc_channel_args_destroy(client_args);
grpc_channel_args_destroy(server_args);
end_test(&f);
config.tear_down_data(&f);
}
@ -387,7 +397,14 @@ static void test_workaround_cronet_compression(
GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP,
workaround_configs[i].expected_algorithm_from_server, nullptr, false,
/* ignored */ GRPC_COMPRESS_LEVEL_NONE,
workaround_configs[i].user_agent_override);
workaround_configs[i].user_agent_override, true);
request_with_payload_template(
config,
"test_invoke_request_with_compressed_payload_with_compression_disabled",
0, GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP, GRPC_COMPRESS_GZIP,
workaround_configs[i].expected_algorithm_from_server, nullptr, false,
/* ignored */ GRPC_COMPRESS_LEVEL_NONE,
workaround_configs[i].user_agent_override, false);
}
}

@ -25,7 +25,6 @@
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include "src/core/lib/compression/message_compress.h"
#include "src/core/lib/gprpp/thd.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "test/core/util/test_config.h"
@ -168,75 +167,6 @@ static void test_peek_none_compressed_slice(void) {
grpc_byte_buffer_destroy(buffer);
}
static void test_read_corrupted_slice(void) {
grpc_slice slice;
grpc_byte_buffer* buffer;
grpc_byte_buffer_reader reader;
LOG_TEST("test_read_corrupted_slice");
slice = grpc_slice_from_copied_string("test");
buffer = grpc_raw_byte_buffer_create(&slice, 1);
buffer->data.raw.compression = GRPC_COMPRESS_GZIP; /* lies! */
grpc_slice_unref(slice);
GPR_ASSERT(!grpc_byte_buffer_reader_init(&reader, buffer));
grpc_byte_buffer_destroy(buffer);
}
static void read_compressed_slice(grpc_compression_algorithm algorithm,
size_t input_size) {
grpc_slice input_slice;
grpc_slice_buffer sliceb_in;
grpc_slice_buffer sliceb_out;
grpc_byte_buffer* buffer;
grpc_byte_buffer_reader reader;
grpc_slice read_slice;
size_t read_count = 0;
grpc_slice_buffer_init(&sliceb_in);
grpc_slice_buffer_init(&sliceb_out);
input_slice = grpc_slice_malloc(input_size);
memset(GRPC_SLICE_START_PTR(input_slice), 'a', input_size);
grpc_slice_buffer_add(&sliceb_in, input_slice); /* takes ownership */
{
grpc_core::ExecCtx exec_ctx;
GPR_ASSERT(grpc_msg_compress(
grpc_compression_algorithm_to_message_compression_algorithm(algorithm),
&sliceb_in, &sliceb_out));
}
buffer = grpc_raw_compressed_byte_buffer_create(sliceb_out.slices,
sliceb_out.count, algorithm);
GPR_ASSERT(grpc_byte_buffer_reader_init(&reader, buffer) &&
"Couldn't init byte buffer reader");
while (grpc_byte_buffer_reader_next(&reader, &read_slice)) {
GPR_ASSERT(memcmp(GRPC_SLICE_START_PTR(read_slice),
GRPC_SLICE_START_PTR(input_slice) + read_count,
GRPC_SLICE_LENGTH(read_slice)) == 0);
read_count += GRPC_SLICE_LENGTH(read_slice);
grpc_slice_unref(read_slice);
}
GPR_ASSERT(read_count == input_size);
grpc_byte_buffer_reader_destroy(&reader);
grpc_byte_buffer_destroy(buffer);
grpc_slice_buffer_destroy(&sliceb_out);
grpc_slice_buffer_destroy(&sliceb_in);
}
static void test_read_gzip_compressed_slice(void) {
const size_t INPUT_SIZE = 2048;
LOG_TEST("test_read_gzip_compressed_slice");
read_compressed_slice(GRPC_COMPRESS_GZIP, INPUT_SIZE);
}
static void test_read_deflate_compressed_slice(void) {
const size_t INPUT_SIZE = 2048;
LOG_TEST("test_read_deflate_compressed_slice");
read_compressed_slice(GRPC_COMPRESS_DEFLATE, INPUT_SIZE);
}
static void test_byte_buffer_from_reader(void) {
grpc_slice slice;
grpc_byte_buffer *buffer, *buffer_from_reader;
@ -342,9 +272,6 @@ int main(int argc, char** argv) {
test_peek_one_slice();
test_peek_one_slice_malloc();
test_peek_none_compressed_slice();
test_read_gzip_compressed_slice();
test_read_deflate_compressed_slice();
test_read_corrupted_slice();
test_byte_buffer_from_reader();
test_byte_buffer_copy();
test_readall();

@ -34,7 +34,7 @@ using std::chrono::system_clock;
namespace grpc {
namespace testing {
namespace {
namespace internal {
// When echo_deadline is requested, deadline seen in the ServerContext is set in
// the response in seconds.
@ -84,9 +84,7 @@ int MetadataMatchCount(
}
return count;
}
} // namespace
namespace {
int GetIntValueFromMetadataHelper(
const char* key,
const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
@ -125,293 +123,7 @@ void ServerTryCancelNonblocking(experimental::CallbackServerContext* context) {
"Server called TryCancelNonblocking() to cancel the request");
}
} // namespace
Status TestServiceImpl::Echo(ServerContext* context, const EchoRequest* request,
EchoResponse* response) {
if (request->has_param() &&
request->param().server_notify_client_when_started()) {
signaller_.SignalClientThatRpcStarted();
signaller_.ServerWaitToContinue();
}
// A bit of sleep to make sure that short deadline tests fail
if (request->has_param() && request->param().server_sleep_us() > 0) {
gpr_sleep_until(
gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_micros(request->param().server_sleep_us(),
GPR_TIMESPAN)));
}
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();
return 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
ServerTryCancel(context);
return Status::CANCELLED;
}
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;
}
while (!context->IsCancelled()) {
gpr_sleep_until(gpr_time_add(
gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_micros(request->param().client_cancel_after_us(),
GPR_TIMESPAN)));
}
return Status::CANCELLED;
} else if (request->has_param() &&
request->param().server_cancel_after_us()) {
gpr_sleep_until(gpr_time_add(
gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_micros(request->param().server_cancel_after_us(),
GPR_TIMESPAN)));
return Status::CANCELLED;
} else if (!request->has_param() ||
!request->param().skip_cancelled_check()) {
EXPECT_FALSE(context->IsCancelled());
}
if (request->has_param() && request->param().echo_metadata_initially()) {
const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
context->client_metadata();
for (const auto& metadatum : client_metadata) {
context->AddInitialMetadata(ToString(metadatum.first),
ToString(metadatum.second));
}
}
if (request->has_param() && request->param().echo_metadata()) {
const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
context->client_metadata();
for (const auto& metadatum : client_metadata) {
context->AddTrailingMetadata(ToString(metadatum.first),
ToString(metadatum.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);
return 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());
}
return Status::OK;
}
Status TestServiceImpl::CheckClientInitialMetadata(
ServerContext* context, const SimpleRequest* /*request*/,
SimpleResponse* /*response*/) {
EXPECT_EQ(MetadataMatchCount(context->client_metadata(),
kCheckClientInitialMetadataKey,
kCheckClientInitialMetadataVal),
1);
EXPECT_EQ(1u,
context->client_metadata().count(kCheckClientInitialMetadataKey));
return Status::OK;
}
// Unimplemented is left unimplemented to test the returned error.
Status TestServiceImpl::RequestStream(ServerContext* context,
ServerReader<EchoRequest>* reader,
EchoResponse* response) {
// If 'server_try_cancel' is set in the metadata, the RPC is cancelled by
// the server by calling ServerContext::TryCancel() depending on the value:
// CANCEL_BEFORE_PROCESSING: The RPC is cancelled before the server reads
// any message from the client
// CANCEL_DURING_PROCESSING: The RPC is cancelled while the server is
// reading messages from the client
// CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads
// all the messages from the client
int server_try_cancel = GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
EchoRequest request;
response->set_message("");
if (server_try_cancel == CANCEL_BEFORE_PROCESSING) {
ServerTryCancel(context);
return Status::CANCELLED;
}
std::thread* server_try_cancel_thd = nullptr;
if (server_try_cancel == CANCEL_DURING_PROCESSING) {
server_try_cancel_thd =
new std::thread([context] { ServerTryCancel(context); });
}
int num_msgs_read = 0;
while (reader->Read(&request)) {
response->mutable_message()->append(request.message());
}
gpr_log(GPR_INFO, "Read: %d messages", num_msgs_read);
if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
return Status::CANCELLED;
}
if (server_try_cancel == CANCEL_AFTER_PROCESSING) {
ServerTryCancel(context);
return Status::CANCELLED;
}
return Status::OK;
}
// Return 'kNumResponseStreamMsgs' messages.
// TODO(yangg) make it generic by adding a parameter into EchoRequest
Status TestServiceImpl::ResponseStream(ServerContext* context,
const EchoRequest* request,
ServerWriter<EchoResponse>* writer) {
// If server_try_cancel is set in the metadata, the RPC is cancelled by the
// server by calling ServerContext::TryCancel() depending on the value:
// CANCEL_BEFORE_PROCESSING: The RPC is cancelled before the server writes
// any messages to the client
// CANCEL_DURING_PROCESSING: The RPC is cancelled while the server is
// writing messages to the client
// CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server writes
// all the messages to the client
int server_try_cancel = GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
int server_coalescing_api = GetIntValueFromMetadata(
kServerUseCoalescingApi, context->client_metadata(), 0);
int server_responses_to_send = GetIntValueFromMetadata(
kServerResponseStreamsToSend, context->client_metadata(),
kServerDefaultResponseStreamsToSend);
if (server_try_cancel == CANCEL_BEFORE_PROCESSING) {
ServerTryCancel(context);
return Status::CANCELLED;
}
EchoResponse response;
std::thread* server_try_cancel_thd = nullptr;
if (server_try_cancel == CANCEL_DURING_PROCESSING) {
server_try_cancel_thd =
new std::thread([context] { ServerTryCancel(context); });
}
for (int i = 0; i < server_responses_to_send; i++) {
response.set_message(request->message() + grpc::to_string(i));
if (i == server_responses_to_send - 1 && server_coalescing_api != 0) {
writer->WriteLast(response, WriteOptions());
} else {
writer->Write(response);
}
}
if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
return Status::CANCELLED;
}
if (server_try_cancel == CANCEL_AFTER_PROCESSING) {
ServerTryCancel(context);
return Status::CANCELLED;
}
return Status::OK;
}
Status TestServiceImpl::BidiStream(
ServerContext* context,
ServerReaderWriter<EchoResponse, EchoRequest>* stream) {
// If server_try_cancel is set in the metadata, the RPC is cancelled by the
// server by calling ServerContext::TryCancel() depending on the value:
// CANCEL_BEFORE_PROCESSING: The RPC is cancelled before the server reads/
// writes any messages from/to the client
// CANCEL_DURING_PROCESSING: The RPC is cancelled while the server is
// reading/writing messages from/to the client
// CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server
// reads/writes all messages from/to the client
int server_try_cancel = GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
EchoRequest request;
EchoResponse response;
if (server_try_cancel == CANCEL_BEFORE_PROCESSING) {
ServerTryCancel(context);
return Status::CANCELLED;
}
std::thread* server_try_cancel_thd = nullptr;
if (server_try_cancel == CANCEL_DURING_PROCESSING) {
server_try_cancel_thd =
new std::thread([context] { ServerTryCancel(context); });
}
// kServerFinishAfterNReads suggests after how many reads, the server should
// write the last message and send status (coalesced using WriteLast)
int server_write_last = GetIntValueFromMetadata(
kServerFinishAfterNReads, context->client_metadata(), 0);
int read_counts = 0;
while (stream->Read(&request)) {
read_counts++;
gpr_log(GPR_INFO, "recv msg %s", request.message().c_str());
response.set_message(request.message());
if (read_counts == server_write_last) {
stream->WriteLast(response, WriteOptions());
} else {
stream->Write(response);
}
}
if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
return Status::CANCELLED;
}
if (server_try_cancel == CANCEL_AFTER_PROCESSING) {
ServerTryCancel(context);
return Status::CANCELLED;
}
return Status::OK;
}
} // namespace internal
experimental::ServerUnaryReactor* CallbackTestServiceImpl::Echo(
experimental::CallbackServerContext* context, const EchoRequest* request,
@ -500,7 +212,7 @@ experimental::ServerUnaryReactor* CallbackTestServiceImpl::Echo(
error.error_message(), error.binary_error_details()));
return;
}
int server_try_cancel = GetIntValueFromMetadata(
int server_try_cancel = internal::GetIntValueFromMetadata(
kServerTryCancelRequest, ctx_->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,
@ -515,7 +227,7 @@ experimental::ServerUnaryReactor* CallbackTestServiceImpl::Echo(
}
gpr_log(GPR_DEBUG, "Request message was %s", req_->message().c_str());
resp_->set_message(req_->message());
MaybeEchoDeadline(ctx_, req_, resp_);
internal::MaybeEchoDeadline(ctx_, req_, resp_);
if (service_->host_) {
resp_->mutable_param()->set_host(*service_->host_);
}
@ -569,9 +281,9 @@ experimental::ServerUnaryReactor* CallbackTestServiceImpl::Echo(
if (req_->has_param() &&
(req_->param().expected_client_identity().length() > 0 ||
req_->param().check_auth_context())) {
CheckServerAuthContext(ctx_,
req_->param().expected_transport_security_type(),
req_->param().expected_client_identity());
internal::CheckServerAuthContext(
ctx_, req_->param().expected_transport_security_type(),
req_->param().expected_client_identity());
}
if (req_->has_param() && req_->param().response_message_length() > 0) {
resp_->set_message(
@ -615,9 +327,9 @@ CallbackTestServiceImpl::CheckClientInitialMetadata(
class Reactor : public ::grpc::experimental::ServerUnaryReactor {
public:
explicit Reactor(experimental::CallbackServerContext* ctx) {
EXPECT_EQ(MetadataMatchCount(ctx->client_metadata(),
kCheckClientInitialMetadataKey,
kCheckClientInitialMetadataVal),
EXPECT_EQ(internal::MetadataMatchCount(ctx->client_metadata(),
kCheckClientInitialMetadataKey,
kCheckClientInitialMetadataVal),
1);
EXPECT_EQ(ctx->client_metadata().count(kCheckClientInitialMetadataKey),
1u);
@ -640,10 +352,10 @@ CallbackTestServiceImpl::RequestStream(
// is cancelled while the server is reading messages from the client
// CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads
// all the messages from the client
int server_try_cancel = GetIntValueFromMetadata(
int server_try_cancel = internal::GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
if (server_try_cancel == CANCEL_BEFORE_PROCESSING) {
ServerTryCancelNonblocking(context);
internal::ServerTryCancelNonblocking(context);
// Don't need to provide a reactor since the RPC is canceled
return nullptr;
}
@ -684,7 +396,7 @@ CallbackTestServiceImpl::RequestStream(
return;
}
if (server_try_cancel_ == CANCEL_AFTER_PROCESSING) {
ServerTryCancelNonblocking(ctx_);
internal::ServerTryCancelNonblocking(ctx_);
return;
}
FinishOnce(Status::OK);
@ -726,10 +438,10 @@ CallbackTestServiceImpl::ResponseStream(
// is cancelled while the server is reading messages from the client
// CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads
// all the messages from the client
int server_try_cancel = GetIntValueFromMetadata(
int server_try_cancel = internal::GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
if (server_try_cancel == CANCEL_BEFORE_PROCESSING) {
ServerTryCancelNonblocking(context);
internal::ServerTryCancelNonblocking(context);
}
class Reactor
@ -738,9 +450,9 @@ CallbackTestServiceImpl::ResponseStream(
Reactor(experimental::CallbackServerContext* ctx,
const EchoRequest* request, int server_try_cancel)
: ctx_(ctx), request_(request), server_try_cancel_(server_try_cancel) {
server_coalescing_api_ = GetIntValueFromMetadata(
server_coalescing_api_ = internal::GetIntValueFromMetadata(
kServerUseCoalescingApi, ctx->client_metadata(), 0);
server_responses_to_send_ = GetIntValueFromMetadata(
server_responses_to_send_ = internal::GetIntValueFromMetadata(
kServerResponseStreamsToSend, ctx->client_metadata(),
kServerDefaultResponseStreamsToSend);
if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
@ -767,7 +479,7 @@ CallbackTestServiceImpl::ResponseStream(
} else if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
// Let OnCancel recover this
} else if (server_try_cancel_ == CANCEL_AFTER_PROCESSING) {
ServerTryCancelNonblocking(ctx_);
internal::ServerTryCancelNonblocking(ctx_);
} else {
FinishOnce(Status::OK);
}
@ -825,12 +537,12 @@ CallbackTestServiceImpl::BidiStream(
// is cancelled while the server is reading messages from the client
// CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads
// all the messages from the client
server_try_cancel_ = GetIntValueFromMetadata(
server_try_cancel_ = internal::GetIntValueFromMetadata(
kServerTryCancelRequest, ctx->client_metadata(), DO_NOT_CANCEL);
server_write_last_ = GetIntValueFromMetadata(kServerFinishAfterNReads,
ctx->client_metadata(), 0);
server_write_last_ = internal::GetIntValueFromMetadata(
kServerFinishAfterNReads, ctx->client_metadata(), 0);
if (server_try_cancel_ == CANCEL_BEFORE_PROCESSING) {
ServerTryCancelNonblocking(ctx);
internal::ServerTryCancelNonblocking(ctx);
} else {
if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
ctx->TryCancel();
@ -870,7 +582,7 @@ CallbackTestServiceImpl::BidiStream(
if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
// Let OnCancel handle this
} else if (server_try_cancel_ == CANCEL_AFTER_PROCESSING) {
ServerTryCancelNonblocking(ctx_);
internal::ServerTryCancelNonblocking(ctx_);
} else {
FinishOnce(Status::OK);
}

@ -15,6 +15,7 @@
* limitations under the License.
*
*/
#ifndef GRPC_TEST_CPP_END2END_TEST_SERVICE_IMPL_H
#define GRPC_TEST_CPP_END2END_TEST_SERVICE_IMPL_H
@ -23,9 +24,19 @@
#include <mutex>
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include <grpcpp/alarm.h>
#include <grpcpp/security/credentials.h>
#include <grpcpp/server_context.h>
#include <gtest/gtest.h>
#include <string>
#include <thread>
#include "src/proto/grpc/testing/echo.grpc.pb.h"
#include "test/cpp/util/string_ref_helper.h"
using std::chrono::system_clock;
namespace grpc {
namespace testing {
@ -46,6 +57,36 @@ typedef enum {
CANCEL_AFTER_PROCESSING
} ServerTryCancelRequestPhase;
namespace internal {
// When echo_deadline is requested, deadline seen in the ServerContext is set in
// the response in seconds.
void MaybeEchoDeadline(experimental::ServerContextBase* context,
const EchoRequest* request, EchoResponse* response);
void CheckServerAuthContext(
const experimental::ServerContextBase* context,
const grpc::string& expected_transport_security_type,
const grpc::string& expected_client_identity);
// Returns the number of pairs in metadata that exactly match the given
// key-value pair. Returns -1 if the pair wasn't found.
int MetadataMatchCount(
const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
const grpc::string& key, const grpc::string& value);
int GetIntValueFromMetadataHelper(
const char* key,
const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
int default_value);
int GetIntValueFromMetadata(
const char* key,
const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
int default_value);
void ServerTryCancel(ServerContext* context);
} // namespace internal
class TestServiceSignaller {
public:
void ClientWaitUntilRpcStarted() {
@ -75,32 +116,310 @@ class TestServiceSignaller {
bool server_should_continue_ /* GUARDED_BY(mu_) */ = false;
};
class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
template <typename RpcService>
class TestMultipleServiceImpl : public RpcService {
public:
TestServiceImpl() : signal_client_(false), host_() {}
explicit TestServiceImpl(const grpc::string& host)
TestMultipleServiceImpl() : signal_client_(false), host_() {}
explicit TestMultipleServiceImpl(const grpc::string& host)
: signal_client_(false), host_(new grpc::string(host)) {}
Status Echo(ServerContext* context, const EchoRequest* request,
EchoResponse* response) override;
EchoResponse* response) {
if (request->has_param() &&
request->param().server_notify_client_when_started()) {
signaller_.SignalClientThatRpcStarted();
signaller_.ServerWaitToContinue();
}
// A bit of sleep to make sure that short deadline tests fail
if (request->has_param() && request->param().server_sleep_us() > 0) {
gpr_sleep_until(
gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_micros(request->param().server_sleep_us(),
GPR_TIMESPAN)));
}
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();
return Status(static_cast<StatusCode>(error.code()),
error.error_message(), error.binary_error_details());
}
int server_try_cancel = internal::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
internal::ServerTryCancel(context);
return Status::CANCELLED;
}
response->set_message(request->message());
internal::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;
}
while (!context->IsCancelled()) {
gpr_sleep_until(gpr_time_add(
gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_micros(request->param().client_cancel_after_us(),
GPR_TIMESPAN)));
}
return Status::CANCELLED;
} else if (request->has_param() &&
request->param().server_cancel_after_us()) {
gpr_sleep_until(gpr_time_add(
gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_micros(request->param().server_cancel_after_us(),
GPR_TIMESPAN)));
return Status::CANCELLED;
} else if (!request->has_param() ||
!request->param().skip_cancelled_check()) {
EXPECT_FALSE(context->IsCancelled());
}
if (request->has_param() && request->param().echo_metadata_initially()) {
const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
context->client_metadata();
for (const auto& metadatum : client_metadata) {
context->AddInitialMetadata(ToString(metadatum.first),
ToString(metadatum.second));
}
}
if (request->has_param() && request->param().echo_metadata()) {
const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
context->client_metadata();
for (const auto& metadatum : client_metadata) {
context->AddTrailingMetadata(ToString(metadatum.first),
ToString(metadatum.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);
return Status::CANCELLED;
}
}
if (request->has_param() &&
(request->param().expected_client_identity().length() > 0 ||
request->param().check_auth_context())) {
internal::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());
}
return Status::OK;
}
Status Echo1(ServerContext* context, const EchoRequest* request,
EchoResponse* response) {
return Echo(context, request, response);
}
Status Echo2(ServerContext* context, const EchoRequest* request,
EchoResponse* response) {
return Echo(context, request, response);
}
Status CheckClientInitialMetadata(ServerContext* context,
const SimpleRequest* request,
SimpleResponse* response) override;
const SimpleRequest* /*request*/,
SimpleResponse* /*response*/) {
EXPECT_EQ(internal::MetadataMatchCount(context->client_metadata(),
kCheckClientInitialMetadataKey,
kCheckClientInitialMetadataVal),
1);
EXPECT_EQ(1u,
context->client_metadata().count(kCheckClientInitialMetadataKey));
return Status::OK;
}
// Unimplemented is left unimplemented to test the returned error.
Status RequestStream(ServerContext* context,
ServerReader<EchoRequest>* reader,
EchoResponse* response) override;
EchoResponse* response) {
// If 'server_try_cancel' is set in the metadata, the RPC is cancelled by
// the server by calling ServerContext::TryCancel() depending on the value:
// CANCEL_BEFORE_PROCESSING: The RPC is cancelled before the server reads
// any message from the client
// CANCEL_DURING_PROCESSING: The RPC is cancelled while the server is
// reading messages from the client
// CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads
// all the messages from the client
int server_try_cancel = internal::GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
EchoRequest request;
response->set_message("");
if (server_try_cancel == CANCEL_BEFORE_PROCESSING) {
internal::ServerTryCancel(context);
return Status::CANCELLED;
}
std::thread* server_try_cancel_thd = nullptr;
if (server_try_cancel == CANCEL_DURING_PROCESSING) {
server_try_cancel_thd =
new std::thread([context] { internal::ServerTryCancel(context); });
}
int num_msgs_read = 0;
while (reader->Read(&request)) {
response->mutable_message()->append(request.message());
}
gpr_log(GPR_INFO, "Read: %d messages", num_msgs_read);
if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
return Status::CANCELLED;
}
if (server_try_cancel == CANCEL_AFTER_PROCESSING) {
internal::ServerTryCancel(context);
return Status::CANCELLED;
}
return Status::OK;
}
// Return 'kNumResponseStreamMsgs' messages.
// TODO(yangg) make it generic by adding a parameter into EchoRequest
Status ResponseStream(ServerContext* context, const EchoRequest* request,
ServerWriter<EchoResponse>* writer) override;
ServerWriter<EchoResponse>* writer) {
// If server_try_cancel is set in the metadata, the RPC is cancelled by the
// server by calling ServerContext::TryCancel() depending on the value:
// CANCEL_BEFORE_PROCESSING: The RPC is cancelled before the server writes
// any messages to the client
// CANCEL_DURING_PROCESSING: The RPC is cancelled while the server is
// writing messages to the client
// CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server writes
// all the messages to the client
int server_try_cancel = internal::GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
int server_coalescing_api = internal::GetIntValueFromMetadata(
kServerUseCoalescingApi, context->client_metadata(), 0);
int server_responses_to_send = internal::GetIntValueFromMetadata(
kServerResponseStreamsToSend, context->client_metadata(),
kServerDefaultResponseStreamsToSend);
if (server_try_cancel == CANCEL_BEFORE_PROCESSING) {
internal::ServerTryCancel(context);
return Status::CANCELLED;
}
EchoResponse response;
std::thread* server_try_cancel_thd = nullptr;
if (server_try_cancel == CANCEL_DURING_PROCESSING) {
server_try_cancel_thd =
new std::thread([context] { internal::ServerTryCancel(context); });
}
for (int i = 0; i < server_responses_to_send; i++) {
response.set_message(request->message() + grpc::to_string(i));
if (i == server_responses_to_send - 1 && server_coalescing_api != 0) {
writer->WriteLast(response, WriteOptions());
} else {
writer->Write(response);
}
}
if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
return Status::CANCELLED;
}
if (server_try_cancel == CANCEL_AFTER_PROCESSING) {
internal::ServerTryCancel(context);
return Status::CANCELLED;
}
return Status::OK;
}
Status BidiStream(ServerContext* context,
ServerReaderWriter<EchoResponse, EchoRequest>* stream) {
// If server_try_cancel is set in the metadata, the RPC is cancelled by the
// server by calling ServerContext::TryCancel() depending on the value:
// CANCEL_BEFORE_PROCESSING: The RPC is cancelled before the server reads/
// writes any messages from/to the client
// CANCEL_DURING_PROCESSING: The RPC is cancelled while the server is
// reading/writing messages from/to the client
// CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server
// reads/writes all messages from/to the client
int server_try_cancel = internal::GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
EchoRequest request;
EchoResponse response;
if (server_try_cancel == CANCEL_BEFORE_PROCESSING) {
internal::ServerTryCancel(context);
return Status::CANCELLED;
}
std::thread* server_try_cancel_thd = nullptr;
if (server_try_cancel == CANCEL_DURING_PROCESSING) {
server_try_cancel_thd =
new std::thread([context] { internal::ServerTryCancel(context); });
}
// kServerFinishAfterNReads suggests after how many reads, the server should
// write the last message and send status (coalesced using WriteLast)
int server_write_last = internal::GetIntValueFromMetadata(
kServerFinishAfterNReads, context->client_metadata(), 0);
Status BidiStream(
ServerContext* context,
ServerReaderWriter<EchoResponse, EchoRequest>* stream) override;
int read_counts = 0;
while (stream->Read(&request)) {
read_counts++;
gpr_log(GPR_INFO, "recv msg %s", request.message().c_str());
response.set_message(request.message());
if (read_counts == server_write_last) {
stream->WriteLast(response, WriteOptions());
} else {
stream->Write(response);
}
}
if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
return Status::CANCELLED;
}
if (server_try_cancel == CANCEL_AFTER_PROCESSING) {
internal::ServerTryCancel(context);
return Status::CANCELLED;
}
return Status::OK;
}
// Unimplemented is left unimplemented to test the returned error.
bool signal_client() {
std::unique_lock<std::mutex> lock(mu_);
return signal_client_;
@ -156,6 +475,9 @@ class CallbackTestServiceImpl
std::unique_ptr<grpc::string> host_;
};
using TestServiceImpl =
TestMultipleServiceImpl<::grpc::testing::EchoTestService::Service>;
} // namespace testing
} // namespace grpc

@ -233,13 +233,14 @@ class CountedService : public ServiceType {
size_t response_count_ = 0;
};
using BackendService = CountedService<TestServiceImpl>;
using LrsService = CountedService<LoadReportingService::Service>;
const char g_kCallCredsMdKey[] = "Balancer should not ...";
const char g_kCallCredsMdValue[] = "... receive me";
class BackendServiceImpl : public BackendService {
template <typename RpcService>
class BackendServiceImpl
: public CountedService<TestMultipleServiceImpl<RpcService>> {
public:
BackendServiceImpl() {}
@ -252,13 +253,25 @@ class BackendServiceImpl : public BackendService {
if (call_credentials_entry != context->client_metadata().end()) {
EXPECT_EQ(call_credentials_entry->second, g_kCallCredsMdValue);
}
IncreaseRequestCount();
const auto status = TestServiceImpl::Echo(context, request, response);
IncreaseResponseCount();
CountedService<TestMultipleServiceImpl<RpcService>>::IncreaseRequestCount();
const auto status =
TestMultipleServiceImpl<RpcService>::Echo(context, request, response);
CountedService<
TestMultipleServiceImpl<RpcService>>::IncreaseResponseCount();
AddClient(context->peer());
return status;
}
Status Echo1(ServerContext* context, const EchoRequest* request,
EchoResponse* response) override {
return Echo(context, request, response);
}
Status Echo2(ServerContext* context, const EchoRequest* request,
EchoResponse* response) override {
return Echo(context, request, response);
}
void Start() {}
void Shutdown() {}
@ -1146,7 +1159,8 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
void ResetStub(int failover_timeout = 0,
const grpc::string& expected_targets = "",
int xds_resource_does_not_exist_timeout = 0) {
int xds_resource_does_not_exist_timeout = 0,
bool xds_routing_enabled = false) {
ChannelArguments args;
if (failover_timeout > 0) {
args.SetInt(GRPC_ARG_PRIORITY_FAILOVER_TIMEOUT_MS, failover_timeout);
@ -1155,6 +1169,9 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
args.SetInt(GRPC_ARG_XDS_RESOURCE_DOES_NOT_EXIST_TIMEOUT_MS,
xds_resource_does_not_exist_timeout);
}
if (xds_routing_enabled) {
args.SetInt(GRPC_ARG_XDS_ROUTING_ENABLED, 1);
}
// If the parent channel is using the fake resolver, we inject the
// response generator for the parent here, and then SetNextResolution()
// will inject the xds channel's response generator via the parent's
@ -1187,6 +1204,8 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
channel_creds->Unref();
channel_ = ::grpc::CreateCustomChannel(uri.str(), creds, args);
stub_ = grpc::testing::EchoTestService::NewStub(channel_);
stub1_ = grpc::testing::EchoTest1Service::NewStub(channel_);
stub2_ = grpc::testing::EchoTest2Service::NewStub(channel_);
}
void ResetBackendCounters() {
@ -1340,29 +1359,105 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
return backend_ports;
}
Status SendRpc(EchoResponse* response = nullptr, int timeout_ms = 1000,
bool wait_for_ready = false, bool server_fail = false) {
enum RpcService {
SERVICE_ECHO,
SERVICE_ECHO1,
SERVICE_ECHO2,
};
enum RpcMethod {
METHOD_ECHO,
METHOD_ECHO1,
METHOD_ECHO2,
};
struct RpcOptions {
RpcService service = SERVICE_ECHO;
RpcMethod method = METHOD_ECHO;
int timeout_ms = 1000;
bool wait_for_ready = false;
bool server_fail = false;
RpcOptions() {}
RpcOptions& set_rpc_service(RpcService rpc_service) {
service = rpc_service;
return *this;
}
RpcOptions& set_rpc_method(RpcMethod rpc_method) {
method = rpc_method;
return *this;
}
RpcOptions& set_timeout_ms(int rpc_timeout_ms) {
timeout_ms = rpc_timeout_ms;
return *this;
}
RpcOptions& set_wait_for_ready(bool rpc_wait_for_ready) {
wait_for_ready = rpc_wait_for_ready;
return *this;
}
RpcOptions& set_server_fail(bool rpc_server_fail) {
server_fail = rpc_server_fail;
return *this;
}
};
template <typename Stub>
Status SendRpcMethod(Stub* stub, const RpcOptions& rpc_options,
ClientContext* context, EchoRequest& request,
EchoResponse* response) {
switch (rpc_options.method) {
case METHOD_ECHO:
return (*stub)->Echo(context, request, response);
case METHOD_ECHO1:
return (*stub)->Echo1(context, request, response);
case METHOD_ECHO2:
return (*stub)->Echo2(context, request, response);
}
}
Status SendRpc(const RpcOptions& rpc_options = RpcOptions(),
EchoResponse* response = nullptr) {
const bool local_response = (response == nullptr);
if (local_response) response = new EchoResponse;
EchoRequest request;
ClientContext context;
context.set_deadline(
grpc_timeout_milliseconds_to_deadline(rpc_options.timeout_ms));
if (rpc_options.wait_for_ready) context.set_wait_for_ready(true);
request.set_message(kRequestMessage_);
if (server_fail) {
if (rpc_options.server_fail) {
request.mutable_param()->mutable_expected_error()->set_code(
GRPC_STATUS_FAILED_PRECONDITION);
}
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);
Status status;
switch (rpc_options.service) {
case SERVICE_ECHO:
status =
SendRpcMethod(&stub_, rpc_options, &context, request, response);
break;
case SERVICE_ECHO1:
status =
SendRpcMethod(&stub1_, rpc_options, &context, request, response);
break;
case SERVICE_ECHO2:
status =
SendRpcMethod(&stub2_, rpc_options, &context, request, response);
break;
}
if (local_response) delete response;
return status;
}
void CheckRpcSendOk(const size_t times = 1, const int timeout_ms = 1000,
bool wait_for_ready = false) {
void CheckRpcSendOk(const size_t times = 1,
const RpcOptions& rpc_options = RpcOptions()) {
for (size_t i = 0; i < times; ++i) {
EchoResponse response;
const Status status = SendRpc(&response, timeout_ms, wait_for_ready);
const Status status = SendRpc(rpc_options, &response);
EXPECT_TRUE(status.ok()) << "code=" << status.error_code()
<< " message=" << status.error_message();
EXPECT_EQ(response.message(), kRequestMessage_);
@ -1371,7 +1466,7 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
void CheckRpcSendFailure(const size_t times = 1, bool server_fail = false) {
for (size_t i = 0; i < times; ++i) {
const Status status = SendRpc(nullptr, 1000, false, server_fail);
const Status status = SendRpc(RpcOptions().set_server_fail(server_fail));
EXPECT_FALSE(status.ok());
}
}
@ -1451,20 +1546,46 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
class BackendServerThread : public ServerThread {
public:
BackendServiceImpl* backend_service() { return &backend_service_; }
BackendServiceImpl<::grpc::testing::EchoTestService::Service>*
backend_service() {
return &backend_service_;
}
BackendServiceImpl<::grpc::testing::EchoTest1Service::Service>*
backend_service1() {
return &backend_service1_;
}
BackendServiceImpl<::grpc::testing::EchoTest2Service::Service>*
backend_service2() {
return &backend_service2_;
}
private:
void RegisterAllServices(ServerBuilder* builder) override {
builder->RegisterService(&backend_service_);
builder->RegisterService(&backend_service1_);
builder->RegisterService(&backend_service2_);
}
void StartAllServices() override { backend_service_.Start(); }
void StartAllServices() override {
backend_service_.Start();
backend_service1_.Start();
backend_service2_.Start();
}
void ShutdownAllServices() override { backend_service_.Shutdown(); }
void ShutdownAllServices() override {
backend_service_.Shutdown();
backend_service1_.Shutdown();
backend_service2_.Shutdown();
}
const char* Type() override { return "Backend"; }
BackendServiceImpl backend_service_;
BackendServiceImpl<::grpc::testing::EchoTestService::Service>
backend_service_;
BackendServiceImpl<::grpc::testing::EchoTest1Service::Service>
backend_service1_;
BackendServiceImpl<::grpc::testing::EchoTest2Service::Service>
backend_service2_;
};
class BalancerServerThread : public ServerThread {
@ -1503,6 +1624,8 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
const int client_load_reporting_interval_seconds_;
std::shared_ptr<Channel> channel_;
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
std::unique_ptr<grpc::testing::EchoTest1Service::Stub> stub1_;
std::unique_ptr<grpc::testing::EchoTest2Service::Stub> stub2_;
std::vector<std::unique_ptr<BackendServerThread>> backends_;
std::vector<std::unique_ptr<BalancerServerThread>> balancers_;
grpc_core::RefCountedPtr<grpc_core::FakeResolverResponseGenerator>
@ -1560,9 +1683,9 @@ TEST_P(BasicTest, Vanilla) {
backends_[i]->backend_service()->request_count());
}
// Check LB policy name for the channel.
EXPECT_EQ(
(GetParam().use_xds_resolver() ? "cds_experimental" : "eds_experimental"),
channel_->GetLoadBalancingPolicyName());
EXPECT_EQ((GetParam().use_xds_resolver() ? "xds_routing_experimental"
: "eds_experimental"),
channel_->GetLoadBalancingPolicyName());
}
TEST_P(BasicTest, IgnoresUnhealthyEndpoints) {
@ -1639,7 +1762,8 @@ TEST_P(BasicTest, InitiallyEmptyServerlist) {
kDefaultResourceName));
const auto t0 = system_clock::now();
// Client will block: LB will initially send empty serverlist.
CheckRpcSendOk(1, kCallDeadlineMs, true /* wait_for_ready */);
CheckRpcSendOk(
1, RpcOptions().set_timeout_ms(kCallDeadlineMs).set_wait_for_ready(true));
const auto ellapsed_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(
system_clock::now() - t0);
@ -1687,8 +1811,7 @@ TEST_P(BasicTest, BackendsRestart) {
CheckRpcSendFailure();
// Restart all backends. RPCs should start succeeding again.
StartAllBackends();
CheckRpcSendOk(1 /* times */, 2000 /* timeout_ms */,
true /* wait_for_ready */);
CheckRpcSendOk(1, RpcOptions().set_timeout_ms(2000).set_wait_for_ready(true));
}
using XdsResolverOnlyTest = BasicTest;
@ -2096,7 +2219,7 @@ TEST_P(LdsTest, ChooseLastRoute) {
}
// Tests that LDS client should send a NACK if route match has non-empty prefix
// in the LDS response.
// as the only route (default) in the LDS response.
TEST_P(LdsTest, RouteMatchHasNonemptyPrefix) {
RouteConfiguration route_config =
balancers_[0]->ads_service()->default_route_config();
@ -2113,6 +2236,247 @@ TEST_P(LdsTest, RouteMatchHasNonemptyPrefix) {
AdsServiceImpl::NACKED);
}
// Tests that LDS client should send a NACK if route match has a prefix
// string with no "/".
TEST_P(LdsTest, RouteMatchHasInvalidPrefixNonEmptyNoSlash) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
RouteConfiguration route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = route_config.mutable_virtual_hosts(0)->mutable_routes(0);
route1->mutable_match()->set_prefix("grpc.testing.EchoTest1Service");
auto* default_route = route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultResourceName);
balancers_[0]->ads_service()->SetLdsResource(
AdsServiceImpl::BuildListener(route_config), kDefaultResourceName);
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
CheckRpcSendFailure();
EXPECT_EQ(balancers_[0]->ads_service()->lds_response_state(),
AdsServiceImpl::NACKED);
}
// Tests that LDS client should send a NACK if route match has a prefix
// string does not end with "/".
TEST_P(LdsTest, RouteMatchHasInvalidPrefixNoEndingSlash) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
RouteConfiguration route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = route_config.mutable_virtual_hosts(0)->mutable_routes(0);
route1->mutable_match()->set_prefix("/grpc.testing.EchoTest1Service");
balancers_[0]->ads_service()->SetLdsResource(
AdsServiceImpl::BuildListener(route_config), kDefaultResourceName);
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
CheckRpcSendFailure();
EXPECT_EQ(balancers_[0]->ads_service()->lds_response_state(),
AdsServiceImpl::NACKED);
}
// Tests that LDS client should send a NACK if route match has a prefix
// string does not start with "/".
TEST_P(LdsTest, RouteMatchHasInvalidPrefixNoLeadingSlash) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
RouteConfiguration route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = route_config.mutable_virtual_hosts(0)->mutable_routes(0);
route1->mutable_match()->set_prefix("grpc.testing.EchoTest1Service/");
balancers_[0]->ads_service()->SetLdsResource(
AdsServiceImpl::BuildListener(route_config), kDefaultResourceName);
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
CheckRpcSendFailure();
EXPECT_EQ(balancers_[0]->ads_service()->lds_response_state(),
AdsServiceImpl::NACKED);
}
// Tests that LDS client should send a NACK if route match has a prefix
// string with extra content outside of "/service/".
TEST_P(LdsTest, RouteMatchHasInvalidPrefixExtraContent) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
RouteConfiguration route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = route_config.mutable_virtual_hosts(0)->mutable_routes(0);
route1->mutable_match()->set_prefix("/grpc.testing.EchoTest1Service/Echo1");
balancers_[0]->ads_service()->SetLdsResource(
AdsServiceImpl::BuildListener(route_config), kDefaultResourceName);
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
CheckRpcSendFailure();
EXPECT_EQ(balancers_[0]->ads_service()->lds_response_state(),
AdsServiceImpl::NACKED);
}
// Tests that LDS client should send a NACK if route match has a prefix
// string "//".
TEST_P(LdsTest, RouteMatchHasInvalidPrefixNoContent) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
RouteConfiguration route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = route_config.mutable_virtual_hosts(0)->mutable_routes(0);
route1->mutable_match()->set_prefix("//");
balancers_[0]->ads_service()->SetLdsResource(
AdsServiceImpl::BuildListener(route_config), kDefaultResourceName);
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
CheckRpcSendFailure();
EXPECT_EQ(balancers_[0]->ads_service()->lds_response_state(),
AdsServiceImpl::NACKED);
}
// Tests that LDS client should send a NACK if route match has path
// but it's empty.
TEST_P(LdsTest, RouteMatchHasInvalidPathEmptyPath) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
RouteConfiguration route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = route_config.mutable_virtual_hosts(0)->mutable_routes(0);
auto* default_route = route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultResourceName);
route1->mutable_match()->set_path("");
balancers_[0]->ads_service()->SetLdsResource(
AdsServiceImpl::BuildListener(route_config), kDefaultResourceName);
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
CheckRpcSendFailure();
EXPECT_EQ(balancers_[0]->ads_service()->lds_response_state(),
AdsServiceImpl::NACKED);
}
// Tests that LDS client should send a NACK if route match has path
// string does not start with "/".
TEST_P(LdsTest, RouteMatchHasInvalidPathNoLeadingSlash) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
RouteConfiguration route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = route_config.mutable_virtual_hosts(0)->mutable_routes(0);
auto* default_route = route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultResourceName);
route1->mutable_match()->set_path("grpc.testing.EchoTest1Service/Echo1");
balancers_[0]->ads_service()->SetLdsResource(
AdsServiceImpl::BuildListener(route_config), kDefaultResourceName);
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
CheckRpcSendFailure();
EXPECT_EQ(balancers_[0]->ads_service()->lds_response_state(),
AdsServiceImpl::NACKED);
}
// Tests that LDS client should send a NACK if route match has path
// string that ends with "/".
TEST_P(LdsTest, RouteMatchHasInvalidPathEndsWithSlash) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
RouteConfiguration route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = route_config.mutable_virtual_hosts(0)->mutable_routes(0);
auto* default_route = route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultResourceName);
route1->mutable_match()->set_path("/grpc.testing.EchoTest1Service/Echo1/");
balancers_[0]->ads_service()->SetLdsResource(
AdsServiceImpl::BuildListener(route_config), kDefaultResourceName);
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
CheckRpcSendFailure();
EXPECT_EQ(balancers_[0]->ads_service()->lds_response_state(),
AdsServiceImpl::NACKED);
}
// Tests that LDS client should send a NACK if route match has path
// string that misses "/" between service and method.
TEST_P(LdsTest, RouteMatchHasInvalidPathMissingMiddleSlash) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
RouteConfiguration route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = route_config.mutable_virtual_hosts(0)->mutable_routes(0);
auto* default_route = route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultResourceName);
route1->mutable_match()->set_path("/grpc.testing.EchoTest1Service.Echo1");
balancers_[0]->ads_service()->SetLdsResource(
AdsServiceImpl::BuildListener(route_config), kDefaultResourceName);
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
CheckRpcSendFailure();
EXPECT_EQ(balancers_[0]->ads_service()->lds_response_state(),
AdsServiceImpl::NACKED);
}
// Tests that LDS client should send a NACK if route match has path
// string that is missing service.
TEST_P(LdsTest, RouteMatchHasInvalidPathMissingService) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
RouteConfiguration route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = route_config.mutable_virtual_hosts(0)->mutable_routes(0);
auto* default_route = route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultResourceName);
route1->mutable_match()->set_path("//Echo1");
balancers_[0]->ads_service()->SetLdsResource(
AdsServiceImpl::BuildListener(route_config), kDefaultResourceName);
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
CheckRpcSendFailure();
EXPECT_EQ(balancers_[0]->ads_service()->lds_response_state(),
AdsServiceImpl::NACKED);
}
// Tests that LDS client should send a NACK if route match has path
// string that is missing method.
TEST_P(LdsTest, RouteMatchHasInvalidPathMissingMethod) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
RouteConfiguration route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = route_config.mutable_virtual_hosts(0)->mutable_routes(0);
auto* default_route = route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultResourceName);
route1->mutable_match()->set_path("/grpc.testing.EchoTest1Service/");
balancers_[0]->ads_service()->SetLdsResource(
AdsServiceImpl::BuildListener(route_config), kDefaultResourceName);
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
CheckRpcSendFailure();
EXPECT_EQ(balancers_[0]->ads_service()->lds_response_state(),
AdsServiceImpl::NACKED);
}
// Tests that LDS client should send a NACK if route has an action other than
// RouteAction in the LDS response.
TEST_P(LdsTest, RouteHasNoRouteAction) {
@ -2128,6 +2492,9 @@ TEST_P(LdsTest, RouteHasNoRouteAction) {
AdsServiceImpl::NACKED);
}
// TODO@donnadionne: Add more invalid config tests to cover all errors in
// xds_api.cc
// Tests that LDS client should send a NACK if RouteAction has a
// cluster_specifier other than cluster in the LDS response.
TEST_P(LdsTest, RouteActionHasNoCluster) {
@ -2155,6 +2522,160 @@ TEST_P(LdsTest, Timeout) {
CheckRpcSendFailure();
}
// Tests that LDS client should choose the default route (with no matching
// specified) after unable to find a match with previous routes.
TEST_P(LdsTest, XdsRoutingPathMatching) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
const char* kNewCluster1Name = "new_cluster_1";
const char* kNewCluster2Name = "new_cluster_2";
const size_t kNumEcho1Rpcs = 10;
const size_t kNumEcho2Rpcs = 20;
const size_t kNumEchoRpcs = 30;
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
// Populate new EDS resources.
AdsServiceImpl::EdsResourceArgs args({
{"locality0", GetBackendPorts(0, 2)},
});
AdsServiceImpl::EdsResourceArgs args1({
{"locality0", GetBackendPorts(2, 3)},
});
AdsServiceImpl::EdsResourceArgs args2({
{"locality0", GetBackendPorts(3, 4)},
});
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args), kDefaultResourceName);
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args1, kNewCluster1Name),
kNewCluster1Name);
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args2, kNewCluster2Name),
kNewCluster2Name);
// Populate new CDS resources.
Cluster new_cluster1 = balancers_[0]->ads_service()->default_cluster();
new_cluster1.set_name(kNewCluster1Name);
balancers_[0]->ads_service()->SetCdsResource(new_cluster1, kNewCluster1Name);
Cluster new_cluster2 = balancers_[0]->ads_service()->default_cluster();
new_cluster2.set_name(kNewCluster2Name);
balancers_[0]->ads_service()->SetCdsResource(new_cluster2, kNewCluster2Name);
// Populating Route Configurations for LDS.
RouteConfiguration new_route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
route1->mutable_match()->set_path("/grpc.testing.EchoTest1Service/Echo1");
route1->mutable_route()->set_cluster(kNewCluster1Name);
auto* route2 = new_route_config.mutable_virtual_hosts(0)->add_routes();
route2->mutable_match()->set_path("/grpc.testing.EchoTest2Service/Echo2");
route2->mutable_route()->set_cluster(kNewCluster2Name);
auto* default_route = new_route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultResourceName);
Listener listener =
balancers_[0]->ads_service()->BuildListener(new_route_config);
balancers_[0]->ads_service()->SetLdsResource(listener, kDefaultResourceName);
WaitForAllBackends(0, 2);
CheckRpcSendOk(kNumEchoRpcs, RpcOptions().set_wait_for_ready(true));
CheckRpcSendOk(kNumEcho1Rpcs, RpcOptions()
.set_rpc_service(SERVICE_ECHO1)
.set_rpc_method(METHOD_ECHO1)
.set_wait_for_ready(true));
CheckRpcSendOk(kNumEcho2Rpcs, RpcOptions()
.set_rpc_service(SERVICE_ECHO2)
.set_rpc_method(METHOD_ECHO2)
.set_wait_for_ready(true));
// Make sure RPCs all go to the correct backend.
for (size_t i = 0; i < 2; ++i) {
EXPECT_EQ(kNumEchoRpcs / 2,
backends_[i]->backend_service()->request_count());
EXPECT_EQ(0, backends_[i]->backend_service1()->request_count());
EXPECT_EQ(0, backends_[i]->backend_service2()->request_count());
}
EXPECT_EQ(0, backends_[2]->backend_service()->request_count());
EXPECT_EQ(kNumEcho1Rpcs, backends_[2]->backend_service1()->request_count());
EXPECT_EQ(0, backends_[2]->backend_service2()->request_count());
EXPECT_EQ(0, backends_[3]->backend_service()->request_count());
EXPECT_EQ(0, backends_[3]->backend_service1()->request_count());
EXPECT_EQ(kNumEcho2Rpcs, backends_[3]->backend_service2()->request_count());
}
TEST_P(LdsTest, XdsRoutingPrefixMatching) {
ResetStub(/*failover_timeout=*/0,
/*expected_targets=*/"",
/*xds_resource_does_not_exist_timeout*/ 0,
/*xds_routing_enabled=*/true);
const char* kNewCluster1Name = "new_cluster_1";
const char* kNewCluster2Name = "new_cluster_2";
const size_t kNumEcho1Rpcs = 10;
const size_t kNumEcho2Rpcs = 20;
const size_t kNumEchoRpcs = 30;
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
// Populate new EDS resources.
AdsServiceImpl::EdsResourceArgs args({
{"locality0", GetBackendPorts(0, 2)},
});
AdsServiceImpl::EdsResourceArgs args1({
{"locality0", GetBackendPorts(2, 3)},
});
AdsServiceImpl::EdsResourceArgs args2({
{"locality0", GetBackendPorts(3, 4)},
});
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args), kDefaultResourceName);
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args1, kNewCluster1Name),
kNewCluster1Name);
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args2, kNewCluster2Name),
kNewCluster2Name);
// Populate new CDS resources.
Cluster new_cluster1 = balancers_[0]->ads_service()->default_cluster();
new_cluster1.set_name(kNewCluster1Name);
balancers_[0]->ads_service()->SetCdsResource(new_cluster1, kNewCluster1Name);
Cluster new_cluster2 = balancers_[0]->ads_service()->default_cluster();
new_cluster2.set_name(kNewCluster2Name);
balancers_[0]->ads_service()->SetCdsResource(new_cluster2, kNewCluster2Name);
// Populating Route Configurations for LDS.
RouteConfiguration new_route_config =
balancers_[0]->ads_service()->default_route_config();
auto* route1 = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
route1->mutable_match()->set_prefix("/grpc.testing.EchoTest1Service/");
route1->mutable_route()->set_cluster(kNewCluster1Name);
auto* route2 = new_route_config.mutable_virtual_hosts(0)->add_routes();
route2->mutable_match()->set_prefix("/grpc.testing.EchoTest2Service/");
route2->mutable_route()->set_cluster(kNewCluster2Name);
auto* default_route = new_route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultResourceName);
Listener listener =
balancers_[0]->ads_service()->BuildListener(new_route_config);
balancers_[0]->ads_service()->SetLdsResource(listener, kDefaultResourceName);
WaitForAllBackends(0, 2);
CheckRpcSendOk(kNumEchoRpcs, RpcOptions().set_wait_for_ready(true));
CheckRpcSendOk(
kNumEcho1Rpcs,
RpcOptions().set_rpc_service(SERVICE_ECHO1).set_wait_for_ready(true));
CheckRpcSendOk(
kNumEcho2Rpcs,
RpcOptions().set_rpc_service(SERVICE_ECHO2).set_wait_for_ready(true));
// Make sure RPCs all go to the correct backend.
for (size_t i = 0; i < 2; ++i) {
EXPECT_EQ(kNumEchoRpcs / 2,
backends_[i]->backend_service()->request_count());
EXPECT_EQ(0, backends_[i]->backend_service1()->request_count());
EXPECT_EQ(0, backends_[i]->backend_service2()->request_count());
}
EXPECT_EQ(0, backends_[2]->backend_service()->request_count());
EXPECT_EQ(kNumEcho1Rpcs, backends_[2]->backend_service1()->request_count());
EXPECT_EQ(0, backends_[2]->backend_service2()->request_count());
EXPECT_EQ(0, backends_[3]->backend_service()->request_count());
EXPECT_EQ(0, backends_[3]->backend_service1()->request_count());
EXPECT_EQ(kNumEcho2Rpcs, backends_[3]->backend_service2()->request_count());
}
using RdsTest = BasicTest;
// Tests that RDS client should send an ACK upon correct RDS response.
@ -2228,7 +2749,7 @@ TEST_P(RdsTest, ChooseLastRoute) {
}
// Tests that RDS client should send a NACK if route match has non-empty prefix
// in the RDS response.
// as the only route (default) in the RDS response.
TEST_P(RdsTest, RouteMatchHasNonemptyPrefix) {
balancers_[0]->ads_service()->SetLdsToUseDynamicRds();
RouteConfiguration route_config =
@ -2236,7 +2757,7 @@ TEST_P(RdsTest, RouteMatchHasNonemptyPrefix) {
route_config.mutable_virtual_hosts(0)
->mutable_routes(0)
->mutable_match()
->set_prefix("nonempty_prefix");
->set_prefix("/nonempty_prefix/");
balancers_[0]->ads_service()->SetRdsResource(route_config,
kDefaultResourceName);
SetNextResolution({});
@ -2849,7 +3370,7 @@ TEST_P(DropTest, Vanilla) {
size_t num_drops = 0;
for (size_t i = 0; i < kNumRpcs; ++i) {
EchoResponse response;
const Status status = SendRpc(&response);
const Status status = SendRpc(RpcOptions(), &response);
if (!status.ok() &&
status.error_message() == "Call dropped by load balancing policy") {
++num_drops;
@ -2889,7 +3410,7 @@ TEST_P(DropTest, DropPerHundred) {
size_t num_drops = 0;
for (size_t i = 0; i < kNumRpcs; ++i) {
EchoResponse response;
const Status status = SendRpc(&response);
const Status status = SendRpc(RpcOptions(), &response);
if (!status.ok() &&
status.error_message() == "Call dropped by load balancing policy") {
++num_drops;
@ -2928,7 +3449,7 @@ TEST_P(DropTest, DropPerTenThousand) {
size_t num_drops = 0;
for (size_t i = 0; i < kNumRpcs; ++i) {
EchoResponse response;
const Status status = SendRpc(&response);
const Status status = SendRpc(RpcOptions(), &response);
if (!status.ok() &&
status.error_message() == "Call dropped by load balancing policy") {
++num_drops;
@ -2971,7 +3492,7 @@ TEST_P(DropTest, Update) {
gpr_log(GPR_INFO, "========= BEFORE FIRST BATCH ==========");
for (size_t i = 0; i < kNumRpcs; ++i) {
EchoResponse response;
const Status status = SendRpc(&response);
const Status status = SendRpc(RpcOptions(), &response);
if (!status.ok() &&
status.error_message() == "Call dropped by load balancing policy") {
++num_drops;
@ -3003,7 +3524,7 @@ TEST_P(DropTest, Update) {
size_t num_rpcs = kNumRpcs;
while (seen_drop_rate < kDropRateThreshold) {
EchoResponse response;
const Status status = SendRpc(&response);
const Status status = SendRpc(RpcOptions(), &response);
++num_rpcs;
if (!status.ok() &&
status.error_message() == "Call dropped by load balancing policy") {
@ -3020,7 +3541,7 @@ TEST_P(DropTest, Update) {
gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
for (size_t i = 0; i < kNumRpcs; ++i) {
EchoResponse response;
const Status status = SendRpc(&response);
const Status status = SendRpc(RpcOptions(), &response);
if (!status.ok() &&
status.error_message() == "Call dropped by load balancing policy") {
++num_drops;
@ -3057,7 +3578,7 @@ TEST_P(DropTest, DropAll) {
// Send kNumRpcs RPCs and all of them are dropped.
for (size_t i = 0; i < kNumRpcs; ++i) {
EchoResponse response;
const Status status = SendRpc(&response);
const Status status = SendRpc(RpcOptions(), &response);
EXPECT_EQ(status.error_code(), StatusCode::UNAVAILABLE);
EXPECT_EQ(status.error_message(), "Call dropped by load balancing policy");
}
@ -3452,7 +3973,7 @@ TEST_P(ClientLoadReportingWithDropTest, Vanilla) {
// Send kNumRpcs RPCs and count the drops.
for (size_t i = 0; i < kNumRpcs; ++i) {
EchoResponse response;
const Status status = SendRpc(&response);
const Status status = SendRpc(RpcOptions(), &response);
if (!status.ok() &&
status.error_message() == "Call dropped by load balancing policy") {
++num_drops;

@ -27,6 +27,7 @@
#include <grpcpp/client_context.h>
#include "src/core/lib/surface/call_test_only.h"
#include "src/core/lib/transport/byte_stream.h"
namespace grpc {
namespace testing {
@ -54,8 +55,11 @@ class InteropClientContextInspector {
return grpc_call_test_only_get_compression_algorithm(context_.call_);
}
uint32_t GetMessageFlags() const {
return grpc_call_test_only_get_message_flags(context_.call_);
bool WasCompressed() const {
return (grpc_call_test_only_get_message_flags(context_.call_) &
GRPC_WRITE_INTERNAL_COMPRESS) ||
(grpc_call_test_only_get_message_flags(context_.call_) &
GRPC_WRITE_INTERNAL_TEST_ONLY_WAS_COMPRESSED);
}
private:

@ -30,7 +30,6 @@
#include <grpcpp/client_context.h>
#include <grpcpp/security/credentials.h>
#include "src/core/lib/transport/byte_stream.h"
#include "src/proto/grpc/testing/empty.pb.h"
#include "src/proto/grpc/testing/messages.pb.h"
#include "src/proto/grpc/testing/test.grpc.pb.h"
@ -67,10 +66,10 @@ void UnaryCompressionChecks(const InteropClientContextInspector& inspector,
"from server.");
abort();
}
GPR_ASSERT(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS);
GPR_ASSERT(inspector.WasCompressed());
} else {
// Didn't request compression -> make sure the response is uncompressed
GPR_ASSERT(!(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS));
GPR_ASSERT(!(inspector.WasCompressed()));
}
}
} // namespace
@ -577,10 +576,10 @@ bool InteropClient::DoServerCompressedStreaming() {
GPR_ASSERT(request.response_parameters(k).has_compressed());
if (request.response_parameters(k).compressed().value()) {
GPR_ASSERT(inspector.GetCallCompressionAlgorithm() > GRPC_COMPRESS_NONE);
GPR_ASSERT(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS);
GPR_ASSERT(inspector.WasCompressed());
} else {
// requested *no* compression.
GPR_ASSERT(!(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS));
GPR_ASSERT(!(inspector.WasCompressed()));
}
++k;
}

@ -31,7 +31,6 @@
#include <grpcpp/server_context.h>
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/transport/byte_stream.h"
#include "src/proto/grpc/testing/empty.pb.h"
#include "src/proto/grpc/testing/messages.pb.h"
#include "src/proto/grpc/testing/test.grpc.pb.h"
@ -118,7 +117,7 @@ bool CheckExpectedCompression(const ServerContext& context,
"Expected compression but got uncompressed request from client.");
return false;
}
if (!(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS)) {
if (!(inspector.WasCompressed())) {
gpr_log(GPR_ERROR,
"Failure: Requested compression in a compressable request, but "
"compression bit in message flags not set.");
@ -126,7 +125,7 @@ bool CheckExpectedCompression(const ServerContext& context,
}
} else {
// Didn't expect compression -> make sure the request is uncompressed
if (inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS) {
if (inspector.WasCompressed()) {
gpr_log(GPR_ERROR,
"Failure: Didn't requested compression, but compression bit in "
"message flags set.");

@ -24,6 +24,7 @@
#include <grpcpp/security/server_credentials.h>
#include "src/core/lib/surface/call_test_only.h"
#include "src/core/lib/transport/byte_stream.h"
#include "test/cpp/util/test_credentials_provider.h"
DECLARE_bool(use_alts);
@ -60,8 +61,11 @@ uint32_t InteropServerContextInspector::GetEncodingsAcceptedByClient() const {
return grpc_call_test_only_get_encodings_accepted_by_peer(context_.call_);
}
uint32_t InteropServerContextInspector::GetMessageFlags() const {
return grpc_call_test_only_get_message_flags(context_.call_);
bool InteropServerContextInspector::WasCompressed() const {
return (grpc_call_test_only_get_message_flags(context_.call_) &
GRPC_WRITE_INTERNAL_COMPRESS) ||
(grpc_call_test_only_get_message_flags(context_.call_) &
GRPC_WRITE_INTERNAL_TEST_ONLY_WAS_COMPRESSED);
}
std::shared_ptr<const AuthContext>

@ -44,7 +44,7 @@ class InteropServerContextInspector {
bool IsCancelled() const;
grpc_compression_algorithm GetCallCompressionAlgorithm() const;
uint32_t GetEncodingsAcceptedByClient() const;
uint32_t GetMessageFlags() const;
bool WasCompressed() const;
private:
const ::grpc::ServerContext& context_;

@ -72,7 +72,7 @@ post_install do |installer|
# GPR_UNREACHABLE_CODE causes "Control may reach end of non-void
# function" warning
config.build_settings['GCC_WARN_ABOUT_RETURN_TYPE'] = 'NO'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) COCOAPODS=1 GRPC_CRONET_WITH_PACKET_COALESCING=1'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) COCOAPODS=1 GRPC_CRONET_WITH_PACKET_COALESCING=1 GRPC_CFSTREAM=1'
end
end

@ -48,29 +48,35 @@ using grpc::testing::EchoResponse;
#define ECHO_TEST_SERVICE_SUMMARY \
"Echo\n" \
"Echo1\n" \
"Echo2\n" \
"CheckClientInitialMetadata\n" \
"RequestStream\n" \
"ResponseStream\n" \
"BidiStream\n" \
"Unimplemented\n"
#define ECHO_TEST_SERVICE_DESCRIPTION \
"filename: src/proto/grpc/testing/echo.proto\n" \
"package: grpc.testing;\n" \
"service EchoTestService {\n" \
" rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \
"{}\n" \
" rpc CheckClientInitialMetadata(grpc.testing.SimpleRequest) returns " \
"(grpc.testing.SimpleResponse) {}\n" \
" rpc RequestStream(stream grpc.testing.EchoRequest) returns " \
"(grpc.testing.EchoResponse) {}\n" \
" rpc ResponseStream(grpc.testing.EchoRequest) returns (stream " \
"grpc.testing.EchoResponse) {}\n" \
" rpc BidiStream(stream grpc.testing.EchoRequest) returns (stream " \
"grpc.testing.EchoResponse) {}\n" \
" rpc Unimplemented(grpc.testing.EchoRequest) returns " \
"(grpc.testing.EchoResponse) {}\n" \
"}\n" \
#define ECHO_TEST_SERVICE_DESCRIPTION \
"filename: src/proto/grpc/testing/echo.proto\n" \
"package: grpc.testing;\n" \
"service EchoTestService {\n" \
" rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \
"{}\n" \
" rpc Echo1(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \
"{}\n" \
" rpc Echo2(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \
"{}\n" \
" rpc CheckClientInitialMetadata(grpc.testing.SimpleRequest) returns " \
"(grpc.testing.SimpleResponse) {}\n" \
" rpc RequestStream(stream grpc.testing.EchoRequest) returns " \
"(grpc.testing.EchoResponse) {}\n" \
" rpc ResponseStream(grpc.testing.EchoRequest) returns (stream " \
"grpc.testing.EchoResponse) {}\n" \
" rpc BidiStream(stream grpc.testing.EchoRequest) returns (stream " \
"grpc.testing.EchoResponse) {}\n" \
" rpc Unimplemented(grpc.testing.EchoRequest) returns " \
"(grpc.testing.EchoResponse) {}\n" \
"}\n" \
"\n"
#define ECHO_METHOD_DESCRIPTION \
@ -1103,7 +1109,8 @@ TEST_F(GrpcToolTest, CallCommandWithMetadata) {
TEST_F(GrpcToolTest, CallCommandWithBadMetadata) {
// Test input "grpc_cli call localhost:10000 Echo "message: 'Hello'"
const char* argv[] = {"grpc_cli", "call", "localhost:10000", "Echo",
const char* argv[] = {"grpc_cli", "call", "localhost:10000",
"grpc.testing.EchoTestService.Echo",
"message: 'Hello'"};
FLAGS_protofiles = "src/proto/grpc/testing/echo.proto";
char* test_srcdir = gpr_getenv("TEST_SRCDIR");

@ -1116,6 +1116,7 @@ src/core/ext/filters/client_channel/lb_policy/xds/cds.cc \
src/core/ext/filters/client_channel/lb_policy/xds/eds.cc \
src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc \
src/core/ext/filters/client_channel/lb_policy/xds/xds.h \
src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.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 \
@ -1186,6 +1187,8 @@ src/core/ext/filters/http/client_authority_filter.h \
src/core/ext/filters/http/http_filters_plugin.cc \
src/core/ext/filters/http/message_compress/message_compress_filter.cc \
src/core/ext/filters/http/message_compress/message_compress_filter.h \
src/core/ext/filters/http/message_compress/message_decompress_filter.cc \
src/core/ext/filters/http/message_compress/message_decompress_filter.h \
src/core/ext/filters/http/server/http_server_filter.cc \
src/core/ext/filters/http/server/http_server_filter.h \
src/core/ext/filters/max_age/max_age_filter.cc \
@ -1539,8 +1542,6 @@ src/core/lib/iomgr/error.h \
src/core/lib/iomgr/error_cfstream.cc \
src/core/lib/iomgr/error_cfstream.h \
src/core/lib/iomgr/error_internal.h \
src/core/lib/iomgr/ev_apple.cc \
src/core/lib/iomgr/ev_apple.h \
src/core/lib/iomgr/ev_epoll1_linux.cc \
src/core/lib/iomgr/ev_epoll1_linux.h \
src/core/lib/iomgr/ev_epollex_linux.cc \

@ -913,6 +913,7 @@ src/core/ext/filters/client_channel/lb_policy/xds/cds.cc \
src/core/ext/filters/client_channel/lb_policy/xds/eds.cc \
src/core/ext/filters/client_channel/lb_policy/xds/lrs.cc \
src/core/ext/filters/client_channel/lb_policy/xds/xds.h \
src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.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 \
@ -986,6 +987,8 @@ src/core/ext/filters/http/client_authority_filter.h \
src/core/ext/filters/http/http_filters_plugin.cc \
src/core/ext/filters/http/message_compress/message_compress_filter.cc \
src/core/ext/filters/http/message_compress/message_compress_filter.h \
src/core/ext/filters/http/message_compress/message_decompress_filter.cc \
src/core/ext/filters/http/message_compress/message_decompress_filter.h \
src/core/ext/filters/http/server/http_server_filter.cc \
src/core/ext/filters/http/server/http_server_filter.h \
src/core/ext/filters/max_age/max_age_filter.cc \
@ -1351,8 +1354,6 @@ src/core/lib/iomgr/error.h \
src/core/lib/iomgr/error_cfstream.cc \
src/core/lib/iomgr/error_cfstream.h \
src/core/lib/iomgr/error_internal.h \
src/core/lib/iomgr/ev_apple.cc \
src/core/lib/iomgr/ev_apple.h \
src/core/lib/iomgr/ev_epoll1_linux.cc \
src/core/lib/iomgr/ev_epoll1_linux.h \
src/core/lib/iomgr/ev_epollex_linux.cc \

@ -17,7 +17,7 @@
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/macos/grpc_basictests_python.sh"
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
timeout_mins: 60
timeout_mins: 90
action {
define_artifacts {
regex: "**/*sponge_log.*"

@ -17,7 +17,7 @@
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/macos/grpc_basictests_python.sh"
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
timeout_mins: 60
timeout_mins: 90
action {
define_artifacts {
regex: "**/*sponge_log.*"

Loading…
Cancel
Save