Merge branch 'master' into tsi_grpc

pull/11977/head
jiangtaoli2016 8 years ago
commit f3f7d3ba6c
  1. 3
      .pylintrc
  2. 5
      BUILD
  3. 18
      CMakeLists.txt
  4. 18
      Makefile
  5. 3
      binding.gyp
  6. 5
      build.yaml
  7. 3
      config.m4
  8. 3
      config.w32
  9. 1
      doc/environment_variables.md
  10. 2
      doc/epoll-polling-engine.md
  11. 7
      gRPC-Core.podspec
  12. 23
      gRPC.podspec
  13. 5
      grpc.gemspec
  14. 37
      include/grpc++/impl/codegen/call.h
  15. 14
      include/grpc++/support/slice.h
  16. 6
      include/grpc/impl/codegen/grpc_types.h
  17. 5
      package.xml
  18. 125
      src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c
  19. 86
      src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c
  20. 27
      src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h
  21. 7
      src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c
  22. 17
      src/core/ext/transport/cronet/transport/cronet_transport.c
  23. 23
      src/core/lib/iomgr/ev_epoll1_linux.c
  24. 26
      src/core/lib/iomgr/gethostname.h
  25. 27
      src/core/lib/iomgr/gethostname_fallback.c
  26. 37
      src/core/lib/iomgr/gethostname_host_name_max.c
  27. 37
      src/core/lib/iomgr/gethostname_sysconf.c
  28. 9
      src/core/lib/iomgr/port.h
  29. 2
      src/core/lib/iomgr/tcp_server_utils_posix_common.c
  30. 73
      src/core/lib/surface/alarm.c
  31. 40
      src/core/lib/surface/alarm_internal.h
  32. 2
      src/core/lib/surface/init.c
  33. 7
      src/cpp/util/slice_cc.cc
  34. 29
      src/objective-c/GRPCClient/GRPCCall+GID.h
  35. 28
      src/objective-c/GRPCClient/GRPCCall+GID.m
  36. 15
      src/objective-c/GRPCClient/GRPCCall+OAuth2.h
  37. 11
      src/objective-c/GRPCClient/GRPCCall+OAuth2.m
  38. 2
      src/objective-c/GRPCClient/GRPCCall.h
  39. 73
      src/objective-c/GRPCClient/GRPCCall.m
  40. 1
      src/objective-c/GRPCClient/private/GRPCRequestHeaders.m
  41. 8
      src/php/ext/grpc/call.c
  42. 12
      src/php/ext/grpc/call_credentials.c
  43. 290
      src/php/ext/grpc/channel.c
  44. 27
      src/php/ext/grpc/channel.h
  45. 32
      src/php/ext/grpc/channel_credentials.c
  46. 2
      src/php/ext/grpc/channel_credentials.h
  47. 28
      src/php/ext/grpc/php7_wrapper.h
  48. 2
      src/php/ext/grpc/php_grpc.c
  49. 4
      src/php/ext/grpc/php_grpc.h
  50. 3
      src/php/tests/unit_tests/CallTest.php
  51. 457
      src/php/tests/unit_tests/ChannelTest.php
  52. 7
      src/php/tests/unit_tests/EndToEndTest.php
  53. 3
      src/php/tests/unit_tests/SecureEndToEndTest.php
  54. 3
      src/python/grpcio/grpc_core_dependencies.py
  55. 289
      src/python/grpcio_testing/grpc_testing/__init__.py
  56. 23
      src/python/grpcio_testing/grpc_testing/_channel/__init__.py
  57. 62
      src/python/grpcio_testing/grpc_testing/_channel/_channel.py
  58. 119
      src/python/grpcio_testing/grpc_testing/_channel/_channel_rpc.py
  59. 48
      src/python/grpcio_testing/grpc_testing/_channel/_channel_state.py
  60. 322
      src/python/grpcio_testing/grpc_testing/_channel/_invocation.py
  61. 115
      src/python/grpcio_testing/grpc_testing/_channel/_multi_callable.py
  62. 193
      src/python/grpcio_testing/grpc_testing/_channel/_rpc_state.py
  63. 92
      src/python/grpcio_testing/grpc_testing/_common.py
  64. 4
      src/python/grpcio_tests/setup.py
  65. 36
      src/python/grpcio_tests/tests/testing/_application_common.py
  66. 33
      src/python/grpcio_tests/tests/testing/_application_testing_common.py
  67. 260
      src/python/grpcio_tests/tests/testing/_client_application.py
  68. 306
      src/python/grpcio_tests/tests/testing/_client_test.py
  69. 13
      src/python/grpcio_tests/tests/testing/proto/__init__.py
  70. 29
      src/python/grpcio_tests/tests/testing/proto/requests.proto
  71. 42
      src/python/grpcio_tests/tests/testing/proto/services.proto
  72. 1
      src/python/grpcio_tests/tests/tests.json
  73. 23
      templates/gRPC.podspec.template
  74. 3
      test/core/client_channel/resolvers/dns_resolver_connectivity_test.c
  75. 3
      test/core/end2end/fuzzers/api_fuzzer.c
  76. 8
      test/core/end2end/goaway_server_test.c
  77. 15
      test/core/surface/alarm_test.c
  78. 24
      test/core/util/BUILD
  79. 31
      test/core/util/port.c
  80. 9
      test/core/util/port.h
  81. 2
      test/cpp/common/BUILD
  82. 10
      test/cpp/end2end/grpclb_end2end_test.cc
  83. 4
      test/cpp/microbenchmarks/BUILD
  84. 8
      test/cpp/qps/client_async.cc
  85. 8
      test/cpp/server/BUILD
  86. 45
      test/cpp/util/BUILD
  87. 36
      test/cpp/util/slice_test.cc
  88. 2
      tools/doxygen/Doxyfile.c++.internal
  89. 5
      tools/doxygen/Doxyfile.core.internal
  90. 38
      tools/internal_ci/helper_scripts/prepare_build_macos_interop_rc
  91. 1
      tools/internal_ci/helper_scripts/prepare_build_macos_rc
  92. 8
      tools/internal_ci/linux/grpc_build_boringssl_at_head.cfg
  93. 31
      tools/internal_ci/linux/grpc_build_protobuf_at_head.cfg
  94. 30
      tools/internal_ci/linux/grpc_build_submodule_at_head.sh
  95. 3
      tools/internal_ci/macos/grpc_interop.sh
  96. 93
      tools/run_tests/dockerize/build_interop_stress_image.sh
  97. 7
      tools/run_tests/generated/sources_and_headers.json
  98. 28
      tools/run_tests/run_tests.py
  99. 2
      vsprojects/vcxproj/grpc++/grpc++.vcxproj
  100. 6
      vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
  101. Some files were not shown because too many files have changed in this diff Show More

@ -38,6 +38,9 @@ disable=
# TODO(https://github.com/grpc/grpc/issues/261): This doesn't seem to # TODO(https://github.com/grpc/grpc/issues/261): This doesn't seem to
# work for now? Try with a later pylint? # work for now? Try with a later pylint?
locally-disabled, locally-disabled,
# NOTE(nathaniel): What even is this? *Enabling* an inspection results
# in a warning? How does that encourage more analysis and coverage?
locally-enabled,
# NOTE(nathaniel): We don't write doc strings for most private code # NOTE(nathaniel): We don't write doc strings for most private code
# elements. # elements.
missing-docstring, missing-docstring,

@ -593,6 +593,9 @@ grpc_cc_library(
"src/core/lib/iomgr/ev_windows.c", "src/core/lib/iomgr/ev_windows.c",
"src/core/lib/iomgr/exec_ctx.c", "src/core/lib/iomgr/exec_ctx.c",
"src/core/lib/iomgr/executor.c", "src/core/lib/iomgr/executor.c",
"src/core/lib/iomgr/gethostname_host_name_max.c",
"src/core/lib/iomgr/gethostname_sysconf.c",
"src/core/lib/iomgr/gethostname_fallback.c",
"src/core/lib/iomgr/iocp_windows.c", "src/core/lib/iomgr/iocp_windows.c",
"src/core/lib/iomgr/iomgr.c", "src/core/lib/iomgr/iomgr.c",
"src/core/lib/iomgr/iomgr_posix.c", "src/core/lib/iomgr/iomgr_posix.c",
@ -718,6 +721,7 @@ grpc_cc_library(
"src/core/lib/iomgr/ev_posix.h", "src/core/lib/iomgr/ev_posix.h",
"src/core/lib/iomgr/exec_ctx.h", "src/core/lib/iomgr/exec_ctx.h",
"src/core/lib/iomgr/executor.h", "src/core/lib/iomgr/executor.h",
"src/core/lib/iomgr/gethostname.h",
"src/core/lib/iomgr/iocp_windows.h", "src/core/lib/iomgr/iocp_windows.h",
"src/core/lib/iomgr/iomgr.h", "src/core/lib/iomgr/iomgr.h",
"src/core/lib/iomgr/iomgr_internal.h", "src/core/lib/iomgr/iomgr_internal.h",
@ -774,6 +778,7 @@ grpc_cc_library(
"src/core/lib/slice/slice_hash_table.h", "src/core/lib/slice/slice_hash_table.h",
"src/core/lib/slice/slice_internal.h", "src/core/lib/slice/slice_internal.h",
"src/core/lib/slice/slice_string_helpers.h", "src/core/lib/slice/slice_string_helpers.h",
"src/core/lib/surface/alarm_internal.h",
"src/core/lib/surface/api_trace.h", "src/core/lib/surface/api_trace.h",
"src/core/lib/surface/call.h", "src/core/lib/surface/call.h",
"src/core/lib/surface/call_test_only.h", "src/core/lib/surface/call_test_only.h",

@ -978,6 +978,9 @@ add_library(grpc
src/core/lib/iomgr/ev_windows.c src/core/lib/iomgr/ev_windows.c
src/core/lib/iomgr/exec_ctx.c src/core/lib/iomgr/exec_ctx.c
src/core/lib/iomgr/executor.c src/core/lib/iomgr/executor.c
src/core/lib/iomgr/gethostname_fallback.c
src/core/lib/iomgr/gethostname_host_name_max.c
src/core/lib/iomgr/gethostname_sysconf.c
src/core/lib/iomgr/iocp_windows.c src/core/lib/iomgr/iocp_windows.c
src/core/lib/iomgr/iomgr.c src/core/lib/iomgr/iomgr.c
src/core/lib/iomgr/iomgr_posix.c src/core/lib/iomgr/iomgr_posix.c
@ -1323,6 +1326,9 @@ add_library(grpc_cronet
src/core/lib/iomgr/ev_windows.c src/core/lib/iomgr/ev_windows.c
src/core/lib/iomgr/exec_ctx.c src/core/lib/iomgr/exec_ctx.c
src/core/lib/iomgr/executor.c src/core/lib/iomgr/executor.c
src/core/lib/iomgr/gethostname_fallback.c
src/core/lib/iomgr/gethostname_host_name_max.c
src/core/lib/iomgr/gethostname_sysconf.c
src/core/lib/iomgr/iocp_windows.c src/core/lib/iomgr/iocp_windows.c
src/core/lib/iomgr/iomgr.c src/core/lib/iomgr/iomgr.c
src/core/lib/iomgr/iomgr_posix.c src/core/lib/iomgr/iomgr_posix.c
@ -1636,6 +1642,9 @@ add_library(grpc_test_util
src/core/lib/iomgr/ev_windows.c src/core/lib/iomgr/ev_windows.c
src/core/lib/iomgr/exec_ctx.c src/core/lib/iomgr/exec_ctx.c
src/core/lib/iomgr/executor.c src/core/lib/iomgr/executor.c
src/core/lib/iomgr/gethostname_fallback.c
src/core/lib/iomgr/gethostname_host_name_max.c
src/core/lib/iomgr/gethostname_sysconf.c
src/core/lib/iomgr/iocp_windows.c src/core/lib/iomgr/iocp_windows.c
src/core/lib/iomgr/iomgr.c src/core/lib/iomgr/iomgr.c
src/core/lib/iomgr/iomgr_posix.c src/core/lib/iomgr/iomgr_posix.c
@ -1893,6 +1902,9 @@ add_library(grpc_test_util_unsecure
src/core/lib/iomgr/ev_windows.c src/core/lib/iomgr/ev_windows.c
src/core/lib/iomgr/exec_ctx.c src/core/lib/iomgr/exec_ctx.c
src/core/lib/iomgr/executor.c src/core/lib/iomgr/executor.c
src/core/lib/iomgr/gethostname_fallback.c
src/core/lib/iomgr/gethostname_host_name_max.c
src/core/lib/iomgr/gethostname_sysconf.c
src/core/lib/iomgr/iocp_windows.c src/core/lib/iomgr/iocp_windows.c
src/core/lib/iomgr/iomgr.c src/core/lib/iomgr/iomgr.c
src/core/lib/iomgr/iomgr_posix.c src/core/lib/iomgr/iomgr_posix.c
@ -2136,6 +2148,9 @@ add_library(grpc_unsecure
src/core/lib/iomgr/ev_windows.c src/core/lib/iomgr/ev_windows.c
src/core/lib/iomgr/exec_ctx.c src/core/lib/iomgr/exec_ctx.c
src/core/lib/iomgr/executor.c src/core/lib/iomgr/executor.c
src/core/lib/iomgr/gethostname_fallback.c
src/core/lib/iomgr/gethostname_host_name_max.c
src/core/lib/iomgr/gethostname_sysconf.c
src/core/lib/iomgr/iocp_windows.c src/core/lib/iomgr/iocp_windows.c
src/core/lib/iomgr/iomgr.c src/core/lib/iomgr/iomgr.c
src/core/lib/iomgr/iomgr_posix.c src/core/lib/iomgr/iomgr_posix.c
@ -2829,6 +2844,9 @@ add_library(grpc++_cronet
src/core/lib/iomgr/ev_windows.c src/core/lib/iomgr/ev_windows.c
src/core/lib/iomgr/exec_ctx.c src/core/lib/iomgr/exec_ctx.c
src/core/lib/iomgr/executor.c src/core/lib/iomgr/executor.c
src/core/lib/iomgr/gethostname_fallback.c
src/core/lib/iomgr/gethostname_host_name_max.c
src/core/lib/iomgr/gethostname_sysconf.c
src/core/lib/iomgr/iocp_windows.c src/core/lib/iomgr/iocp_windows.c
src/core/lib/iomgr/iomgr.c src/core/lib/iomgr/iomgr.c
src/core/lib/iomgr/iomgr_posix.c src/core/lib/iomgr/iomgr_posix.c

@ -2925,6 +2925,9 @@ LIBGRPC_SRC = \
src/core/lib/iomgr/ev_windows.c \ src/core/lib/iomgr/ev_windows.c \
src/core/lib/iomgr/exec_ctx.c \ src/core/lib/iomgr/exec_ctx.c \
src/core/lib/iomgr/executor.c \ src/core/lib/iomgr/executor.c \
src/core/lib/iomgr/gethostname_fallback.c \
src/core/lib/iomgr/gethostname_host_name_max.c \
src/core/lib/iomgr/gethostname_sysconf.c \
src/core/lib/iomgr/iocp_windows.c \ src/core/lib/iomgr/iocp_windows.c \
src/core/lib/iomgr/iomgr.c \ src/core/lib/iomgr/iomgr.c \
src/core/lib/iomgr/iomgr_posix.c \ src/core/lib/iomgr/iomgr_posix.c \
@ -3268,6 +3271,9 @@ LIBGRPC_CRONET_SRC = \
src/core/lib/iomgr/ev_windows.c \ src/core/lib/iomgr/ev_windows.c \
src/core/lib/iomgr/exec_ctx.c \ src/core/lib/iomgr/exec_ctx.c \
src/core/lib/iomgr/executor.c \ src/core/lib/iomgr/executor.c \
src/core/lib/iomgr/gethostname_fallback.c \
src/core/lib/iomgr/gethostname_host_name_max.c \
src/core/lib/iomgr/gethostname_sysconf.c \
src/core/lib/iomgr/iocp_windows.c \ src/core/lib/iomgr/iocp_windows.c \
src/core/lib/iomgr/iomgr.c \ src/core/lib/iomgr/iomgr.c \
src/core/lib/iomgr/iomgr_posix.c \ src/core/lib/iomgr/iomgr_posix.c \
@ -3578,6 +3584,9 @@ LIBGRPC_TEST_UTIL_SRC = \
src/core/lib/iomgr/ev_windows.c \ src/core/lib/iomgr/ev_windows.c \
src/core/lib/iomgr/exec_ctx.c \ src/core/lib/iomgr/exec_ctx.c \
src/core/lib/iomgr/executor.c \ src/core/lib/iomgr/executor.c \
src/core/lib/iomgr/gethostname_fallback.c \
src/core/lib/iomgr/gethostname_host_name_max.c \
src/core/lib/iomgr/gethostname_sysconf.c \
src/core/lib/iomgr/iocp_windows.c \ src/core/lib/iomgr/iocp_windows.c \
src/core/lib/iomgr/iomgr.c \ src/core/lib/iomgr/iomgr.c \
src/core/lib/iomgr/iomgr_posix.c \ src/core/lib/iomgr/iomgr_posix.c \
@ -3824,6 +3833,9 @@ LIBGRPC_TEST_UTIL_UNSECURE_SRC = \
src/core/lib/iomgr/ev_windows.c \ src/core/lib/iomgr/ev_windows.c \
src/core/lib/iomgr/exec_ctx.c \ src/core/lib/iomgr/exec_ctx.c \
src/core/lib/iomgr/executor.c \ src/core/lib/iomgr/executor.c \
src/core/lib/iomgr/gethostname_fallback.c \
src/core/lib/iomgr/gethostname_host_name_max.c \
src/core/lib/iomgr/gethostname_sysconf.c \
src/core/lib/iomgr/iocp_windows.c \ src/core/lib/iomgr/iocp_windows.c \
src/core/lib/iomgr/iomgr.c \ src/core/lib/iomgr/iomgr.c \
src/core/lib/iomgr/iomgr_posix.c \ src/core/lib/iomgr/iomgr_posix.c \
@ -4043,6 +4055,9 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/iomgr/ev_windows.c \ src/core/lib/iomgr/ev_windows.c \
src/core/lib/iomgr/exec_ctx.c \ src/core/lib/iomgr/exec_ctx.c \
src/core/lib/iomgr/executor.c \ src/core/lib/iomgr/executor.c \
src/core/lib/iomgr/gethostname_fallback.c \
src/core/lib/iomgr/gethostname_host_name_max.c \
src/core/lib/iomgr/gethostname_sysconf.c \
src/core/lib/iomgr/iocp_windows.c \ src/core/lib/iomgr/iocp_windows.c \
src/core/lib/iomgr/iomgr.c \ src/core/lib/iomgr/iomgr.c \
src/core/lib/iomgr/iomgr_posix.c \ src/core/lib/iomgr/iomgr_posix.c \
@ -4719,6 +4734,9 @@ LIBGRPC++_CRONET_SRC = \
src/core/lib/iomgr/ev_windows.c \ src/core/lib/iomgr/ev_windows.c \
src/core/lib/iomgr/exec_ctx.c \ src/core/lib/iomgr/exec_ctx.c \
src/core/lib/iomgr/executor.c \ src/core/lib/iomgr/executor.c \
src/core/lib/iomgr/gethostname_fallback.c \
src/core/lib/iomgr/gethostname_host_name_max.c \
src/core/lib/iomgr/gethostname_sysconf.c \
src/core/lib/iomgr/iocp_windows.c \ src/core/lib/iomgr/iocp_windows.c \
src/core/lib/iomgr/iomgr.c \ src/core/lib/iomgr/iomgr.c \
src/core/lib/iomgr/iomgr_posix.c \ src/core/lib/iomgr/iomgr_posix.c \

@ -687,6 +687,9 @@
'src/core/lib/iomgr/ev_windows.c', 'src/core/lib/iomgr/ev_windows.c',
'src/core/lib/iomgr/exec_ctx.c', 'src/core/lib/iomgr/exec_ctx.c',
'src/core/lib/iomgr/executor.c', 'src/core/lib/iomgr/executor.c',
'src/core/lib/iomgr/gethostname_fallback.c',
'src/core/lib/iomgr/gethostname_host_name_max.c',
'src/core/lib/iomgr/gethostname_sysconf.c',
'src/core/lib/iomgr/iocp_windows.c', 'src/core/lib/iomgr/iocp_windows.c',
'src/core/lib/iomgr/iomgr.c', 'src/core/lib/iomgr/iomgr.c',
'src/core/lib/iomgr/iomgr_posix.c', 'src/core/lib/iomgr/iomgr_posix.c',

@ -214,6 +214,9 @@ filegroups:
- src/core/lib/iomgr/ev_windows.c - src/core/lib/iomgr/ev_windows.c
- src/core/lib/iomgr/exec_ctx.c - src/core/lib/iomgr/exec_ctx.c
- src/core/lib/iomgr/executor.c - src/core/lib/iomgr/executor.c
- src/core/lib/iomgr/gethostname_fallback.c
- src/core/lib/iomgr/gethostname_host_name_max.c
- src/core/lib/iomgr/gethostname_sysconf.c
- src/core/lib/iomgr/iocp_windows.c - src/core/lib/iomgr/iocp_windows.c
- src/core/lib/iomgr/iomgr.c - src/core/lib/iomgr/iomgr.c
- src/core/lib/iomgr/iomgr_posix.c - src/core/lib/iomgr/iomgr_posix.c
@ -359,6 +362,7 @@ filegroups:
- src/core/lib/iomgr/ev_posix.h - src/core/lib/iomgr/ev_posix.h
- src/core/lib/iomgr/exec_ctx.h - src/core/lib/iomgr/exec_ctx.h
- src/core/lib/iomgr/executor.h - src/core/lib/iomgr/executor.h
- src/core/lib/iomgr/gethostname.h
- src/core/lib/iomgr/iocp_windows.h - src/core/lib/iomgr/iocp_windows.h
- src/core/lib/iomgr/iomgr.h - src/core/lib/iomgr/iomgr.h
- src/core/lib/iomgr/iomgr_internal.h - src/core/lib/iomgr/iomgr_internal.h
@ -415,6 +419,7 @@ filegroups:
- src/core/lib/slice/slice_hash_table.h - src/core/lib/slice/slice_hash_table.h
- src/core/lib/slice/slice_internal.h - src/core/lib/slice/slice_internal.h
- src/core/lib/slice/slice_string_helpers.h - src/core/lib/slice/slice_string_helpers.h
- src/core/lib/surface/alarm_internal.h
- src/core/lib/surface/api_trace.h - src/core/lib/surface/api_trace.h
- src/core/lib/surface/call.h - src/core/lib/surface/call.h
- src/core/lib/surface/call_test_only.h - src/core/lib/surface/call_test_only.h

@ -116,6 +116,9 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/iomgr/ev_windows.c \ src/core/lib/iomgr/ev_windows.c \
src/core/lib/iomgr/exec_ctx.c \ src/core/lib/iomgr/exec_ctx.c \
src/core/lib/iomgr/executor.c \ src/core/lib/iomgr/executor.c \
src/core/lib/iomgr/gethostname_fallback.c \
src/core/lib/iomgr/gethostname_host_name_max.c \
src/core/lib/iomgr/gethostname_sysconf.c \
src/core/lib/iomgr/iocp_windows.c \ src/core/lib/iomgr/iocp_windows.c \
src/core/lib/iomgr/iomgr.c \ src/core/lib/iomgr/iomgr.c \
src/core/lib/iomgr/iomgr_posix.c \ src/core/lib/iomgr/iomgr_posix.c \

@ -93,6 +93,9 @@ if (PHP_GRPC != "no") {
"src\\core\\lib\\iomgr\\ev_windows.c " + "src\\core\\lib\\iomgr\\ev_windows.c " +
"src\\core\\lib\\iomgr\\exec_ctx.c " + "src\\core\\lib\\iomgr\\exec_ctx.c " +
"src\\core\\lib\\iomgr\\executor.c " + "src\\core\\lib\\iomgr\\executor.c " +
"src\\core\\lib\\iomgr\\gethostname_fallback.c " +
"src\\core\\lib\\iomgr\\gethostname_host_name_max.c " +
"src\\core\\lib\\iomgr\\gethostname_sysconf.c " +
"src\\core\\lib\\iomgr\\iocp_windows.c " + "src\\core\\lib\\iomgr\\iocp_windows.c " +
"src\\core\\lib\\iomgr\\iomgr.c " + "src\\core\\lib\\iomgr\\iomgr.c " +
"src\\core\\lib\\iomgr\\iomgr_posix.c " + "src\\core\\lib\\iomgr\\iomgr_posix.c " +

@ -69,6 +69,7 @@ some configuration as environment variables that can be set.
The following tracers will only run in binaries built in DEBUG mode. This is The following tracers will only run in binaries built in DEBUG mode. This is
accomplished by invoking `CONFIG=dbg make <target>` accomplished by invoking `CONFIG=dbg make <target>`
- alarm_refcount - refcounting traces for grpc_alarm structure
- metadata - tracks creation and mutation of metadata - metadata - tracks creation and mutation of metadata
- closure - tracks closure creation, scheduling, and completion - closure - tracks closure creation, scheduling, and completion
- pending_tags - traces still-in-progress tags on completion queues - pending_tags - traces still-in-progress tags on completion queues

@ -5,7 +5,7 @@ Sree Kuchibhotla (sreek@) [May - 2016]
> Status: As of June 2016, this change is implemented and merged. > Status: As of June 2016, this change is implemented and merged.
> * The bulk of the functionality is in: [ev_poll_linux.c](https://github.com/grpc/grpc/blob/master/src/core/lib/iomgr/ev_epoll_linux.c) > * The bulk of the functionality is in: [ev_epollsig_linux.c](https://github.com/grpc/grpc/blob/master/src/core/lib/iomgr/ev_epollsig_linux.c)
> * Pull request: https://github.com/grpc/grpc/pull/6803 > * Pull request: https://github.com/grpc/grpc/pull/6803
## 1. Introduction ## 1. Introduction

@ -345,6 +345,7 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/ev_posix.h', 'src/core/lib/iomgr/ev_posix.h',
'src/core/lib/iomgr/exec_ctx.h', 'src/core/lib/iomgr/exec_ctx.h',
'src/core/lib/iomgr/executor.h', 'src/core/lib/iomgr/executor.h',
'src/core/lib/iomgr/gethostname.h',
'src/core/lib/iomgr/iocp_windows.h', 'src/core/lib/iomgr/iocp_windows.h',
'src/core/lib/iomgr/iomgr.h', 'src/core/lib/iomgr/iomgr.h',
'src/core/lib/iomgr/iomgr_internal.h', 'src/core/lib/iomgr/iomgr_internal.h',
@ -401,6 +402,7 @@ Pod::Spec.new do |s|
'src/core/lib/slice/slice_hash_table.h', 'src/core/lib/slice/slice_hash_table.h',
'src/core/lib/slice/slice_internal.h', 'src/core/lib/slice/slice_internal.h',
'src/core/lib/slice/slice_string_helpers.h', 'src/core/lib/slice/slice_string_helpers.h',
'src/core/lib/surface/alarm_internal.h',
'src/core/lib/surface/api_trace.h', 'src/core/lib/surface/api_trace.h',
'src/core/lib/surface/call.h', 'src/core/lib/surface/call.h',
'src/core/lib/surface/call_test_only.h', 'src/core/lib/surface/call_test_only.h',
@ -493,6 +495,9 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/ev_windows.c', 'src/core/lib/iomgr/ev_windows.c',
'src/core/lib/iomgr/exec_ctx.c', 'src/core/lib/iomgr/exec_ctx.c',
'src/core/lib/iomgr/executor.c', 'src/core/lib/iomgr/executor.c',
'src/core/lib/iomgr/gethostname_fallback.c',
'src/core/lib/iomgr/gethostname_host_name_max.c',
'src/core/lib/iomgr/gethostname_sysconf.c',
'src/core/lib/iomgr/iocp_windows.c', 'src/core/lib/iomgr/iocp_windows.c',
'src/core/lib/iomgr/iomgr.c', 'src/core/lib/iomgr/iomgr.c',
'src/core/lib/iomgr/iomgr_posix.c', 'src/core/lib/iomgr/iomgr_posix.c',
@ -834,6 +839,7 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/ev_posix.h', 'src/core/lib/iomgr/ev_posix.h',
'src/core/lib/iomgr/exec_ctx.h', 'src/core/lib/iomgr/exec_ctx.h',
'src/core/lib/iomgr/executor.h', 'src/core/lib/iomgr/executor.h',
'src/core/lib/iomgr/gethostname.h',
'src/core/lib/iomgr/iocp_windows.h', 'src/core/lib/iomgr/iocp_windows.h',
'src/core/lib/iomgr/iomgr.h', 'src/core/lib/iomgr/iomgr.h',
'src/core/lib/iomgr/iomgr_internal.h', 'src/core/lib/iomgr/iomgr_internal.h',
@ -890,6 +896,7 @@ Pod::Spec.new do |s|
'src/core/lib/slice/slice_hash_table.h', 'src/core/lib/slice/slice_hash_table.h',
'src/core/lib/slice/slice_internal.h', 'src/core/lib/slice/slice_internal.h',
'src/core/lib/slice/slice_string_helpers.h', 'src/core/lib/slice/slice_string_helpers.h',
'src/core/lib/surface/alarm_internal.h',
'src/core/lib/surface/api_trace.h', 'src/core/lib/surface/api_trace.h',
'src/core/lib/surface/call.h', 'src/core/lib/surface/call.h',
'src/core/lib/surface/call_test_only.h', 'src/core/lib/surface/call_test_only.h',

@ -40,12 +40,9 @@ Pod::Spec.new do |s|
s.header_dir = name s.header_dir = name
src_dir = 'src/objective-c/GRPCClient' src_dir = 'src/objective-c/GRPCClient'
s.source_files = "#{src_dir}/*.{h,m}", "#{src_dir}/**/*.{h,m}"
s.private_header_files = "#{src_dir}/private/*.h"
s.header_mappings_dir = "#{src_dir}"
s.dependency 'gRPC-Core', version
s.dependency 'gRPC-RxLibrary', version s.dependency 'gRPC-RxLibrary', version
s.default_subspec = 'Main'
# Certificates, to be able to establish TLS connections: # Certificates, to be able to establish TLS connections:
s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] } s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] }
@ -54,4 +51,22 @@ Pod::Spec.new do |s|
# This is needed by all pods that depend on gRPC-RxLibrary: # This is needed by all pods that depend on gRPC-RxLibrary:
'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES', 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
} }
s.subspec 'Main' do |ss|
ss.header_mappings_dir = "#{src_dir}"
ss.source_files = "#{src_dir}/*.{h,m}", "#{src_dir}/**/*.{h,m}"
ss.exclude_files = "#{src_dir}/GRPCCall+GID.{h,m}"
ss.private_header_files = "#{src_dir}/private/*.h"
ss.dependency 'gRPC-Core', version
end
s.subspec 'GID' do |ss|
ss.header_mappings_dir = "#{src_dir}"
ss.source_files = "#{src_dir}/GRPCCall+GID.{h,m}"
ss.dependency 'Google/SignIn'
end
end end

@ -277,6 +277,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/iomgr/ev_posix.h ) s.files += %w( src/core/lib/iomgr/ev_posix.h )
s.files += %w( src/core/lib/iomgr/exec_ctx.h ) s.files += %w( src/core/lib/iomgr/exec_ctx.h )
s.files += %w( src/core/lib/iomgr/executor.h ) s.files += %w( src/core/lib/iomgr/executor.h )
s.files += %w( src/core/lib/iomgr/gethostname.h )
s.files += %w( src/core/lib/iomgr/iocp_windows.h ) s.files += %w( src/core/lib/iomgr/iocp_windows.h )
s.files += %w( src/core/lib/iomgr/iomgr.h ) s.files += %w( src/core/lib/iomgr/iomgr.h )
s.files += %w( src/core/lib/iomgr/iomgr_internal.h ) s.files += %w( src/core/lib/iomgr/iomgr_internal.h )
@ -333,6 +334,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/slice/slice_hash_table.h ) s.files += %w( src/core/lib/slice/slice_hash_table.h )
s.files += %w( src/core/lib/slice/slice_internal.h ) s.files += %w( src/core/lib/slice/slice_internal.h )
s.files += %w( src/core/lib/slice/slice_string_helpers.h ) s.files += %w( src/core/lib/slice/slice_string_helpers.h )
s.files += %w( src/core/lib/surface/alarm_internal.h )
s.files += %w( src/core/lib/surface/api_trace.h ) s.files += %w( src/core/lib/surface/api_trace.h )
s.files += %w( src/core/lib/surface/call.h ) s.files += %w( src/core/lib/surface/call.h )
s.files += %w( src/core/lib/surface/call_test_only.h ) s.files += %w( src/core/lib/surface/call_test_only.h )
@ -425,6 +427,9 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/iomgr/ev_windows.c ) s.files += %w( src/core/lib/iomgr/ev_windows.c )
s.files += %w( src/core/lib/iomgr/exec_ctx.c ) s.files += %w( src/core/lib/iomgr/exec_ctx.c )
s.files += %w( src/core/lib/iomgr/executor.c ) s.files += %w( src/core/lib/iomgr/executor.c )
s.files += %w( src/core/lib/iomgr/gethostname_fallback.c )
s.files += %w( src/core/lib/iomgr/gethostname_host_name_max.c )
s.files += %w( src/core/lib/iomgr/gethostname_sysconf.c )
s.files += %w( src/core/lib/iomgr/iocp_windows.c ) s.files += %w( src/core/lib/iomgr/iocp_windows.c )
s.files += %w( src/core/lib/iomgr/iomgr.c ) s.files += %w( src/core/lib/iomgr/iomgr.c )
s.files += %w( src/core/lib/iomgr/iomgr_posix.c ) s.files += %w( src/core/lib/iomgr/iomgr_posix.c )

@ -349,6 +349,28 @@ class CallOpRecvMessage {
bool allow_not_getting_message_; bool allow_not_getting_message_;
}; };
namespace CallOpGenericRecvMessageHelper {
class DeserializeFunc {
public:
virtual Status Deserialize(grpc_byte_buffer* buf) = 0;
virtual ~DeserializeFunc() {}
};
template <class R>
class DeserializeFuncType final : public DeserializeFunc {
public:
DeserializeFuncType(R* message) : message_(message) {}
Status Deserialize(grpc_byte_buffer* buf) override {
return SerializationTraits<R>::Deserialize(buf, message_);
}
~DeserializeFuncType() override {}
private:
R* message_; // Not a managed pointer because management is external to this
};
} // namespace CallOpGenericRecvMessageHelper
class CallOpGenericRecvMessage { class CallOpGenericRecvMessage {
public: public:
CallOpGenericRecvMessage() CallOpGenericRecvMessage()
@ -356,9 +378,11 @@ class CallOpGenericRecvMessage {
template <class R> template <class R>
void RecvMessage(R* message) { void RecvMessage(R* message) {
deserialize_ = [message](grpc_byte_buffer* buf) -> Status { // Use an explicit base class pointer to avoid resolution error in the
return SerializationTraits<R>::Deserialize(buf, message); // following unique_ptr::reset for some old implementations.
}; CallOpGenericRecvMessageHelper::DeserializeFunc* func =
new CallOpGenericRecvMessageHelper::DeserializeFuncType<R>(message);
deserialize_.reset(func);
} }
// Do not change status if no message is received. // Do not change status if no message is received.
@ -381,7 +405,7 @@ class CallOpGenericRecvMessage {
if (recv_buf_) { if (recv_buf_) {
if (*status) { if (*status) {
got_message = true; got_message = true;
*status = deserialize_(recv_buf_).ok(); *status = deserialize_->Deserialize(recv_buf_).ok();
} else { } else {
got_message = false; got_message = false;
g_core_codegen_interface->grpc_byte_buffer_destroy(recv_buf_); g_core_codegen_interface->grpc_byte_buffer_destroy(recv_buf_);
@ -392,12 +416,11 @@ class CallOpGenericRecvMessage {
*status = false; *status = false;
} }
} }
deserialize_ = DeserializeFunc(); deserialize_.reset();
} }
private: private:
typedef std::function<Status(grpc_byte_buffer*)> DeserializeFunc; std::unique_ptr<CallOpGenericRecvMessageHelper::DeserializeFunc> deserialize_;
DeserializeFunc deserialize_;
grpc_byte_buffer* recv_buf_; grpc_byte_buffer* recv_buf_;
bool allow_not_getting_message_; bool allow_not_getting_message_;
}; };

@ -67,6 +67,20 @@ class Slice final {
return *this; return *this;
} }
/// Create a slice pointing at some data. Calls malloc to allocate a refcount
/// for the object, and arranges that destroy will be called with the
/// user data pointer passed in at destruction. Can be the same as buf or
/// different (e.g., if data is part of a larger structure that must be
/// destroyed when the data is no longer needed)
Slice(void* buf, size_t len, void (*destroy)(void*), void* user_data);
/// Specialization of above for common case where buf == user_data
Slice(void* buf, size_t len, void (*destroy)(void*))
: Slice(buf, len, destroy, buf) {}
/// Similar to the above but has a destroy that also takes slice length
Slice(void* buf, size_t len, void (*destroy)(void*, size_t));
/// Byte size. /// Byte size.
size_t size() const { return GRPC_SLICE_LENGTH(slice_); } size_t size() const { return GRPC_SLICE_LENGTH(slice_); }

@ -258,8 +258,12 @@ typedef struct {
#define GRPC_ARG_RESOURCE_QUOTA "grpc.resource_quota" #define GRPC_ARG_RESOURCE_QUOTA "grpc.resource_quota"
/** If non-zero, expand wildcard addresses to a list of local addresses. */ /** If non-zero, expand wildcard addresses to a list of local addresses. */
#define GRPC_ARG_EXPAND_WILDCARD_ADDRS "grpc.expand_wildcard_addrs" #define GRPC_ARG_EXPAND_WILDCARD_ADDRS "grpc.expand_wildcard_addrs"
/** Service config data in JSON form. Not intended for use outside of tests. */ /** Service config data in JSON form.
This value will be ignored if the name resolver returns a service config. */
#define GRPC_ARG_SERVICE_CONFIG "grpc.service_config" #define GRPC_ARG_SERVICE_CONFIG "grpc.service_config"
/** Disable looking up the service config via the name resolver. */
#define GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION \
"grpc.service_config_disable_resolution"
/** LB policy name. */ /** LB policy name. */
#define GRPC_ARG_LB_POLICY_NAME "grpc.lb_policy_name" #define GRPC_ARG_LB_POLICY_NAME "grpc.lb_policy_name"
/** The grpc_socket_mutator instance that set the socket options. A pointer. */ /** The grpc_socket_mutator instance that set the socket options. A pointer. */

@ -291,6 +291,7 @@
<file baseinstalldir="/" name="src/core/lib/iomgr/ev_posix.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/ev_posix.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/exec_ctx.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/exec_ctx.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/executor.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/executor.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/gethostname.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/iocp_windows.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/iocp_windows.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/iomgr.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/iomgr.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/iomgr_internal.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/iomgr_internal.h" role="src" />
@ -347,6 +348,7 @@
<file baseinstalldir="/" name="src/core/lib/slice/slice_hash_table.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/slice/slice_hash_table.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/slice/slice_internal.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/slice/slice_internal.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/slice/slice_string_helpers.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/slice/slice_string_helpers.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/alarm_internal.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/api_trace.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/surface/api_trace.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/call.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/surface/call.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/call_test_only.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/surface/call_test_only.h" role="src" />
@ -439,6 +441,9 @@
<file baseinstalldir="/" name="src/core/lib/iomgr/ev_windows.c" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/ev_windows.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/exec_ctx.c" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/exec_ctx.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/executor.c" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/executor.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/gethostname_fallback.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/gethostname_host_name_max.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/gethostname_sysconf.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/iocp_windows.c" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/iocp_windows.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/iomgr.c" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/iomgr.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/iomgr_posix.c" role="src" /> <file baseinstalldir="/" name="src/core/lib/iomgr/iomgr_posix.c" role="src" />

@ -19,7 +19,10 @@
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
#if GRPC_ARES == 1 && !defined(GRPC_UV) #if GRPC_ARES == 1 && !defined(GRPC_UV)
#include <limits.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/host_port.h> #include <grpc/support/host_port.h>
@ -31,11 +34,14 @@
#include "src/core/ext/filters/client_channel/resolver_registry.h" #include "src/core/ext/filters/client_channel/resolver_registry.h"
#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/iomgr/combiner.h" #include "src/core/lib/iomgr/combiner.h"
#include "src/core/lib/iomgr/gethostname.h"
#include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/resolve_address.h"
#include "src/core/lib/iomgr/timer.h" #include "src/core/lib/iomgr/timer.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/support/backoff.h" #include "src/core/lib/support/backoff.h"
#include "src/core/lib/support/env.h" #include "src/core/lib/support/env.h"
#include "src/core/lib/support/string.h" #include "src/core/lib/support/string.h"
#include "src/core/lib/transport/service_config.h"
#define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1 #define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1
#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1 #define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1
@ -54,6 +60,8 @@ typedef struct {
char *default_port; char *default_port;
/** channel args. */ /** channel args. */
grpc_channel_args *channel_args; grpc_channel_args *channel_args;
/** whether to request the service config */
bool request_service_config;
/** pollset_set to drive the name resolution process */ /** pollset_set to drive the name resolution process */
grpc_pollset_set *interested_parties; grpc_pollset_set *interested_parties;
@ -85,6 +93,8 @@ typedef struct {
/** currently resolving addresses */ /** currently resolving addresses */
grpc_lb_addresses *lb_addresses; grpc_lb_addresses *lb_addresses;
/** currently resolving service config */
char *service_config_json;
} ares_dns_resolver; } ares_dns_resolver;
static void dns_ares_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); static void dns_ares_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r);
@ -144,6 +154,77 @@ static void dns_ares_on_retry_timer_locked(grpc_exec_ctx *exec_ctx, void *arg,
GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "retry-timer"); GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "retry-timer");
} }
static bool value_in_json_array(grpc_json *array, const char *value) {
for (grpc_json *entry = array->child; entry != NULL; entry = entry->next) {
if (entry->type == GRPC_JSON_STRING && strcmp(entry->value, value) == 0) {
return true;
}
}
return false;
}
static char *choose_service_config(char *service_config_choice_json) {
grpc_json *choices_json = grpc_json_parse_string(service_config_choice_json);
if (choices_json == NULL || choices_json->type != GRPC_JSON_ARRAY) {
gpr_log(GPR_ERROR, "cannot parse service config JSON string");
return NULL;
}
char *service_config = NULL;
for (grpc_json *choice = choices_json->child; choice != NULL;
choice = choice->next) {
if (choice->type != GRPC_JSON_OBJECT) {
gpr_log(GPR_ERROR, "cannot parse service config JSON string");
break;
}
grpc_json *service_config_json = NULL;
for (grpc_json *field = choice->child; field != NULL; field = field->next) {
// Check client language, if specified.
if (strcmp(field->key, "clientLanguage") == 0) {
if (field->type != GRPC_JSON_ARRAY ||
!value_in_json_array(field, "c++")) {
service_config_json = NULL;
break;
}
}
// Check client hostname, if specified.
if (strcmp(field->key, "clientHostname") == 0) {
char *hostname = grpc_gethostname();
if (hostname == NULL || field->type != GRPC_JSON_ARRAY ||
!value_in_json_array(field, hostname)) {
service_config_json = NULL;
break;
}
}
// Check percentage, if specified.
if (strcmp(field->key, "percentage") == 0) {
if (field->type != GRPC_JSON_NUMBER) {
service_config_json = NULL;
break;
}
int random_pct = rand() % 100;
int percentage;
if (sscanf(field->value, "%d", &percentage) != 1 ||
random_pct > percentage) {
service_config_json = NULL;
break;
}
}
// Save service config.
if (strcmp(field->key, "serviceConfig") == 0) {
if (field->type == GRPC_JSON_OBJECT) {
service_config_json = field;
}
}
}
if (service_config_json != NULL) {
service_config = grpc_json_dump_to_string(service_config_json, 0);
break;
}
}
grpc_json_destroy(choices_json);
return service_config;
}
static void dns_ares_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg, static void dns_ares_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error) { grpc_error *error) {
ares_dns_resolver *r = arg; ares_dns_resolver *r = arg;
@ -152,8 +233,40 @@ static void dns_ares_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg,
r->resolving = false; r->resolving = false;
r->pending_request = NULL; r->pending_request = NULL;
if (r->lb_addresses != NULL) { if (r->lb_addresses != NULL) {
grpc_arg new_arg = grpc_lb_addresses_create_channel_arg(r->lb_addresses); static const char *args_to_remove[2];
result = grpc_channel_args_copy_and_add(r->channel_args, &new_arg, 1); size_t num_args_to_remove = 0;
grpc_arg new_args[3];
size_t num_args_to_add = 0;
new_args[num_args_to_add++] =
grpc_lb_addresses_create_channel_arg(r->lb_addresses);
grpc_service_config *service_config = NULL;
char *service_config_string = NULL;
if (r->service_config_json != NULL) {
service_config_string = choose_service_config(r->service_config_json);
gpr_free(r->service_config_json);
if (service_config_string != NULL) {
gpr_log(GPR_INFO, "selected service config choice: %s",
service_config_string);
args_to_remove[num_args_to_remove++] = GRPC_ARG_SERVICE_CONFIG;
new_args[num_args_to_add++] = grpc_channel_arg_string_create(
GRPC_ARG_SERVICE_CONFIG, service_config_string);
service_config = grpc_service_config_create(service_config_string);
if (service_config != NULL) {
const char *lb_policy_name =
grpc_service_config_get_lb_policy_name(service_config);
if (lb_policy_name != NULL) {
args_to_remove[num_args_to_remove++] = GRPC_ARG_LB_POLICY_NAME;
new_args[num_args_to_add++] = grpc_channel_arg_string_create(
GRPC_ARG_LB_POLICY_NAME, (char *)lb_policy_name);
}
}
}
}
result = grpc_channel_args_copy_and_add_and_remove(
r->channel_args, args_to_remove, num_args_to_remove, new_args,
num_args_to_add);
if (service_config != NULL) grpc_service_config_destroy(service_config);
gpr_free(service_config_string);
grpc_lb_addresses_destroy(exec_ctx, r->lb_addresses); grpc_lb_addresses_destroy(exec_ctx, r->lb_addresses);
} else { } else {
const char *msg = grpc_error_string(error); const char *msg = grpc_error_string(error);
@ -207,10 +320,12 @@ static void dns_ares_start_resolving_locked(grpc_exec_ctx *exec_ctx,
GPR_ASSERT(!r->resolving); GPR_ASSERT(!r->resolving);
r->resolving = true; r->resolving = true;
r->lb_addresses = NULL; r->lb_addresses = NULL;
r->service_config_json = NULL;
r->pending_request = grpc_dns_lookup_ares( r->pending_request = grpc_dns_lookup_ares(
exec_ctx, r->dns_server, r->name_to_resolve, r->default_port, exec_ctx, r->dns_server, r->name_to_resolve, r->default_port,
r->interested_parties, &r->dns_ares_on_resolved_locked, &r->lb_addresses, r->interested_parties, &r->dns_ares_on_resolved_locked, &r->lb_addresses,
true /* check_grpclb */); true /* check_grpclb */,
r->request_service_config ? &r->service_config_json : NULL);
} }
static void dns_ares_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, static void dns_ares_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx,
@ -256,6 +371,10 @@ static grpc_resolver *dns_ares_create(grpc_exec_ctx *exec_ctx,
r->name_to_resolve = gpr_strdup(path); r->name_to_resolve = gpr_strdup(path);
r->default_port = gpr_strdup(default_port); r->default_port = gpr_strdup(default_port);
r->channel_args = grpc_channel_args_copy(args->args); r->channel_args = grpc_channel_args_copy(args->args);
const grpc_arg *arg = grpc_channel_args_find(
r->channel_args, GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION);
r->request_service_config = !grpc_channel_arg_get_integer(
arg, (grpc_integer_options){false, false, true});
r->interested_parties = grpc_pollset_set_create(); r->interested_parties = grpc_pollset_set_create();
if (args->pollset_set != NULL) { if (args->pollset_set != NULL) {
grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties, grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties,

@ -54,6 +54,8 @@ struct grpc_ares_request {
grpc_closure *on_done; grpc_closure *on_done;
/** the pointer to receive the resolved addresses */ /** the pointer to receive the resolved addresses */
grpc_lb_addresses **lb_addrs_out; grpc_lb_addresses **lb_addrs_out;
/** the pointer to receive the service config in JSON */
char **service_config_json_out;
/** the evernt driver used by this request */ /** the evernt driver used by this request */
grpc_ares_ev_driver *ev_driver; grpc_ares_ev_driver *ev_driver;
/** number of ongoing queries */ /** number of ongoing queries */
@ -266,10 +268,68 @@ static void on_srv_query_done_cb(void *arg, int status, int timeouts,
grpc_exec_ctx_finish(&exec_ctx); grpc_exec_ctx_finish(&exec_ctx);
} }
static const char g_service_config_attribute_prefix[] = "grpc_config=";
static void on_txt_done_cb(void *arg, int status, int timeouts,
unsigned char *buf, int len) {
gpr_log(GPR_DEBUG, "on_txt_done_cb");
char *error_msg;
grpc_ares_request *r = (grpc_ares_request *)arg;
gpr_mu_lock(&r->mu);
if (status != ARES_SUCCESS) goto fail;
struct ares_txt_ext *reply = NULL;
status = ares_parse_txt_reply_ext(buf, len, &reply);
if (status != ARES_SUCCESS) goto fail;
// Find service config in TXT record.
const size_t prefix_len = sizeof(g_service_config_attribute_prefix) - 1;
struct ares_txt_ext *result;
for (result = reply; result != NULL; result = result->next) {
if (result->record_start &&
memcmp(result->txt, g_service_config_attribute_prefix, prefix_len) ==
0) {
break;
}
}
// Found a service config record.
if (result != NULL) {
size_t service_config_len = result->length - prefix_len;
*r->service_config_json_out = gpr_malloc(service_config_len + 1);
memcpy(*r->service_config_json_out, result->txt + prefix_len,
service_config_len);
for (result = result->next; result != NULL && !result->record_start;
result = result->next) {
*r->service_config_json_out = gpr_realloc(
*r->service_config_json_out, service_config_len + result->length + 1);
memcpy(*r->service_config_json_out + service_config_len, result->txt,
result->length);
service_config_len += result->length;
}
(*r->service_config_json_out)[service_config_len] = '\0';
gpr_log(GPR_INFO, "found service config: %s", *r->service_config_json_out);
}
// Clean up.
ares_free_data(reply);
goto done;
fail:
gpr_asprintf(&error_msg, "C-ares TXT lookup status is not ARES_SUCCESS: %s",
ares_strerror(status));
grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
gpr_free(error_msg);
if (r->error == GRPC_ERROR_NONE) {
r->error = error;
} else {
r->error = grpc_error_add_child(error, r->error);
}
done:
gpr_mu_unlock(&r->mu);
grpc_ares_request_unref(NULL, r);
}
static grpc_ares_request *grpc_dns_lookup_ares_impl( static grpc_ares_request *grpc_dns_lookup_ares_impl(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
const char *default_port, grpc_pollset_set *interested_parties, const char *default_port, grpc_pollset_set *interested_parties,
grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb) { grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb,
char **service_config_json) {
grpc_error *error = GRPC_ERROR_NONE; grpc_error *error = GRPC_ERROR_NONE;
/* TODO(zyc): Enable tracing after #9603 is checked in */ /* TODO(zyc): Enable tracing after #9603 is checked in */
/* if (grpc_dns_trace) { /* if (grpc_dns_trace) {
@ -300,11 +360,12 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl(
error = grpc_ares_ev_driver_create(&ev_driver, interested_parties); error = grpc_ares_ev_driver_create(&ev_driver, interested_parties);
if (error != GRPC_ERROR_NONE) goto error_cleanup; if (error != GRPC_ERROR_NONE) goto error_cleanup;
grpc_ares_request *r = gpr_malloc(sizeof(grpc_ares_request)); grpc_ares_request *r = gpr_zalloc(sizeof(grpc_ares_request));
gpr_mu_init(&r->mu); gpr_mu_init(&r->mu);
r->ev_driver = ev_driver; r->ev_driver = ev_driver;
r->on_done = on_done; r->on_done = on_done;
r->lb_addrs_out = addrs; r->lb_addrs_out = addrs;
r->service_config_json_out = service_config_json;
r->success = false; r->success = false;
r->error = GRPC_ERROR_NONE; r->error = GRPC_ERROR_NONE;
ares_channel *channel = grpc_ares_ev_driver_get_channel(r->ev_driver); ares_channel *channel = grpc_ares_ev_driver_get_channel(r->ev_driver);
@ -315,13 +376,17 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl(
grpc_resolved_address addr; grpc_resolved_address addr;
if (grpc_parse_ipv4_hostport(dns_server, &addr, false /* log_errors */)) { if (grpc_parse_ipv4_hostport(dns_server, &addr, false /* log_errors */)) {
r->dns_server_addr.family = AF_INET; r->dns_server_addr.family = AF_INET;
memcpy(&r->dns_server_addr.addr.addr4, addr.addr, addr.len); struct sockaddr_in *in = (struct sockaddr_in *)addr.addr;
memcpy(&r->dns_server_addr.addr.addr4, &in->sin_addr,
sizeof(struct in_addr));
r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr); r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr);
r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr); r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr);
} else if (grpc_parse_ipv6_hostport(dns_server, &addr, } else if (grpc_parse_ipv6_hostport(dns_server, &addr,
false /* log_errors */)) { false /* log_errors */)) {
r->dns_server_addr.family = AF_INET6; r->dns_server_addr.family = AF_INET6;
memcpy(&r->dns_server_addr.addr.addr6, addr.addr, addr.len); struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr.addr;
memcpy(&r->dns_server_addr.addr.addr6, &in6->sin6_addr,
sizeof(struct in6_addr));
r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr); r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr);
r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr); r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr);
} else { } else {
@ -342,8 +407,6 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl(
goto error_cleanup; goto error_cleanup;
} }
} }
// An extra reference is put here to avoid destroying the request in
// on_done_cb before calling grpc_ares_ev_driver_start.
gpr_ref_init(&r->pending_queries, 1); gpr_ref_init(&r->pending_queries, 1);
if (grpc_ipv6_loopback_available()) { if (grpc_ipv6_loopback_available()) {
grpc_ares_hostbyname_request *hr = create_hostbyname_request( grpc_ares_hostbyname_request *hr = create_hostbyname_request(
@ -362,6 +425,10 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl(
r); r);
gpr_free(service_name); gpr_free(service_name);
} }
if (service_config_json != NULL) {
grpc_ares_request_ref(r);
ares_search(*channel, hr->host, ns_c_in, ns_t_txt, on_txt_done_cb, r);
}
/* TODO(zyc): Handle CNAME records here. */ /* TODO(zyc): Handle CNAME records here. */
grpc_ares_ev_driver_start(exec_ctx, r->ev_driver); grpc_ares_ev_driver_start(exec_ctx, r->ev_driver);
grpc_ares_request_unref(exec_ctx, r); grpc_ares_request_unref(exec_ctx, r);
@ -379,8 +446,8 @@ error_cleanup:
grpc_ares_request *(*grpc_dns_lookup_ares)( grpc_ares_request *(*grpc_dns_lookup_ares)(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
const char *default_port, grpc_pollset_set *interested_parties, const char *default_port, grpc_pollset_set *interested_parties,
grpc_closure *on_done, grpc_lb_addresses **addrs, grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb,
bool check_grpclb) = grpc_dns_lookup_ares_impl; char **service_config_json) = grpc_dns_lookup_ares_impl;
void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) { void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) {
if (grpc_dns_lookup_ares == grpc_dns_lookup_ares_impl) { if (grpc_dns_lookup_ares == grpc_dns_lookup_ares_impl) {
@ -465,7 +532,8 @@ static void grpc_resolve_address_ares_impl(grpc_exec_ctx *exec_ctx,
grpc_schedule_on_exec_ctx); grpc_schedule_on_exec_ctx);
grpc_dns_lookup_ares(exec_ctx, NULL /* dns_server */, name, default_port, grpc_dns_lookup_ares(exec_ctx, NULL /* dns_server */, name, default_port,
interested_parties, &r->on_dns_lookup_done, &r->lb_addrs, interested_parties, &r->on_dns_lookup_done, &r->lb_addrs,
false /* check_grpclb */); false /* check_grpclb */,
NULL /* service_config_json */);
} }
void (*grpc_resolve_address_ares)( void (*grpc_resolve_address_ares)(

@ -27,29 +27,30 @@
typedef struct grpc_ares_request grpc_ares_request; typedef struct grpc_ares_request grpc_ares_request;
/* Asynchronously resolve addr. Use \a default_port if a port isn't designated /* Asynchronously resolve \a name. Use \a default_port if a port isn't
in addr, otherwise use the port in addr. grpc_ares_init() must be called at designated in \a name, otherwise use the port in \a name. grpc_ares_init()
least once before this function. \a on_done may be called directly in this must be called at least once before this function. \a on_done may be
function without being scheduled with \a exec_ctx, it must not try to acquire called directly in this function without being scheduled with \a exec_ctx,
locks that are being held by the caller. */ so it must not try to acquire locks that are being held by the caller. */
extern void (*grpc_resolve_address_ares)(grpc_exec_ctx *exec_ctx, extern void (*grpc_resolve_address_ares)(grpc_exec_ctx *exec_ctx,
const char *addr, const char *name,
const char *default_port, const char *default_port,
grpc_pollset_set *interested_parties, grpc_pollset_set *interested_parties,
grpc_closure *on_done, grpc_closure *on_done,
grpc_resolved_addresses **addresses); grpc_resolved_addresses **addresses);
/* Asynchronously resolve addr. It will try to resolve grpclb SRV records in /* Asynchronously resolve \a name. It will try to resolve grpclb SRV records in
addition to the normal address records. For normal address records, it uses addition to the normal address records. For normal address records, it uses
\a default_port if a port isn't designated in \a addr, otherwise it uses the \a default_port if a port isn't designated in \a name, otherwise it uses the
port in \a addr. grpc_ares_init() must be called at least once before this port in \a name. grpc_ares_init() must be called at least once before this
function. \a on_done may be called directly in this function without being function. \a on_done may be called directly in this function without being
scheduled with \a exec_ctx, it must not try to acquire locks that are being scheduled with \a exec_ctx, so it must not try to acquire locks that are
held by the caller. */ being held by the caller. */
extern grpc_ares_request *(*grpc_dns_lookup_ares)( extern grpc_ares_request *(*grpc_dns_lookup_ares)(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr, grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
const char *default_port, grpc_pollset_set *interested_parties, const char *default_port, grpc_pollset_set *interested_parties,
grpc_closure *on_done, grpc_lb_addresses **addresses, bool check_grpclb); grpc_closure *on_done, grpc_lb_addresses **addresses, bool check_grpclb,
char **service_config_json);
/* Cancel the pending grpc_ares_request \a request */ /* Cancel the pending grpc_ares_request \a request */
void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx,

@ -28,15 +28,16 @@ struct grpc_ares_request {
static grpc_ares_request *grpc_dns_lookup_ares_impl( static grpc_ares_request *grpc_dns_lookup_ares_impl(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
const char *default_port, grpc_pollset_set *interested_parties, const char *default_port, grpc_pollset_set *interested_parties,
grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb) { grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb,
char **service_config_json) {
return NULL; return NULL;
} }
grpc_ares_request *(*grpc_dns_lookup_ares)( grpc_ares_request *(*grpc_dns_lookup_ares)(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
const char *default_port, grpc_pollset_set *interested_parties, const char *default_port, grpc_pollset_set *interested_parties,
grpc_closure *on_done, grpc_lb_addresses **addrs, grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb,
bool check_grpclb) = grpc_dns_lookup_ares_impl; char **service_config_json) = grpc_dns_lookup_ares_impl;
void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) {} void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) {}

@ -637,7 +637,8 @@ static void on_response_trailers_received(
Utility function that takes the data from s->write_slice_buffer and assembles Utility function that takes the data from s->write_slice_buffer and assembles
into a contiguous byte stream with 5 byte gRPC header prepended. into a contiguous byte stream with 5 byte gRPC header prepended.
*/ */
static void create_grpc_frame(grpc_slice_buffer *write_slice_buffer, static void create_grpc_frame(grpc_exec_ctx *exec_ctx,
grpc_slice_buffer *write_slice_buffer,
char **pp_write_buffer, char **pp_write_buffer,
size_t *p_write_buffer_size, uint32_t flags) { size_t *p_write_buffer_size, uint32_t flags) {
grpc_slice slice = grpc_slice_buffer_take_first(write_slice_buffer); grpc_slice slice = grpc_slice_buffer_take_first(write_slice_buffer);
@ -657,6 +658,7 @@ static void create_grpc_frame(grpc_slice_buffer *write_slice_buffer,
*p++ = (uint8_t)(length); *p++ = (uint8_t)(length);
/* append actual data */ /* append actual data */
memcpy(p, GRPC_SLICE_START_PTR(slice), length); memcpy(p, GRPC_SLICE_START_PTR(slice), length);
grpc_slice_unref_internal(exec_ctx, slice);
} }
/* /*
@ -1017,14 +1019,15 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
} }
if (write_slice_buffer.count > 0) { if (write_slice_buffer.count > 0) {
size_t write_buffer_size; size_t write_buffer_size;
create_grpc_frame(&write_slice_buffer, &stream_state->ws.write_buffer, create_grpc_frame(exec_ctx, &write_slice_buffer,
&write_buffer_size, &stream_state->ws.write_buffer, &write_buffer_size,
stream_op->payload->send_message.send_message->flags); stream_op->payload->send_message.send_message->flags);
CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, %p)", s->cbs, CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, %p)", s->cbs,
stream_state->ws.write_buffer); stream_state->ws.write_buffer);
stream_state->state_callback_received[OP_SEND_MESSAGE] = false; stream_state->state_callback_received[OP_SEND_MESSAGE] = false;
bidirectional_stream_write(s->cbs, stream_state->ws.write_buffer, bidirectional_stream_write(s->cbs, stream_state->ws.write_buffer,
(int)write_buffer_size, false); (int)write_buffer_size, false);
grpc_slice_buffer_destroy_internal(exec_ctx, &write_slice_buffer);
if (t->use_packet_coalescing) { if (t->use_packet_coalescing) {
if (!stream_op->send_trailing_metadata) { if (!stream_op->send_trailing_metadata) {
CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs); CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs);
@ -1153,6 +1156,9 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
} else { } else {
stream_state->rs.remaining_bytes = 0; stream_state->rs.remaining_bytes = 0;
CRONET_LOG(GPR_DEBUG, "read operation complete. Empty response."); CRONET_LOG(GPR_DEBUG, "read operation complete. Empty response.");
/* Clean up read_slice_buffer in case there is unread data. */
grpc_slice_buffer_destroy_internal(
exec_ctx, &stream_state->rs.read_slice_buffer);
grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer); grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer);
grpc_slice_buffer_stream_init(&stream_state->rs.sbs, grpc_slice_buffer_stream_init(&stream_state->rs.sbs,
&stream_state->rs.read_slice_buffer, 0); &stream_state->rs.read_slice_buffer, 0);
@ -1206,6 +1212,9 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
memcpy(dst_p, stream_state->rs.read_buffer, memcpy(dst_p, stream_state->rs.read_buffer,
(size_t)stream_state->rs.length_field); (size_t)stream_state->rs.length_field);
null_and_maybe_free_read_buffer(s); null_and_maybe_free_read_buffer(s);
/* Clean up read_slice_buffer in case there is unread data. */
grpc_slice_buffer_destroy_internal(exec_ctx,
&stream_state->rs.read_slice_buffer);
grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer); grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer);
grpc_slice_buffer_add(&stream_state->rs.read_slice_buffer, grpc_slice_buffer_add(&stream_state->rs.read_slice_buffer,
read_data_slice); read_data_slice);
@ -1369,6 +1378,8 @@ static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
grpc_closure *then_schedule_closure) { grpc_closure *then_schedule_closure) {
stream_obj *s = (stream_obj *)gs; stream_obj *s = (stream_obj *)gs;
null_and_maybe_free_read_buffer(s); null_and_maybe_free_read_buffer(s);
/* Clean up read_slice_buffer in case there is unread data. */
grpc_slice_buffer_destroy_internal(exec_ctx, &s->state.rs.read_slice_buffer);
GRPC_ERROR_UNREF(s->state.cancel_error); GRPC_ERROR_UNREF(s->state.cancel_error);
GRPC_CLOSURE_SCHED(exec_ctx, then_schedule_closure, GRPC_ERROR_NONE); GRPC_CLOSURE_SCHED(exec_ctx, then_schedule_closure, GRPC_ERROR_NONE);
} }

@ -237,28 +237,41 @@ static grpc_fd *fd_create(int fd, const char *name) {
static int fd_wrapped_fd(grpc_fd *fd) { return fd->fd; } static int fd_wrapped_fd(grpc_fd *fd) { return fd->fd; }
/* Might be called multiple times */ /* if 'releasing_fd' is true, it means that we are going to detach the internal
static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { * fd from grpc_fd structure (i.e which means we should not be calling
* shutdown() syscall on that fd) */
static void fd_shutdown_internal(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
grpc_error *why, bool releasing_fd) {
if (grpc_lfev_set_shutdown(exec_ctx, &fd->read_closure, if (grpc_lfev_set_shutdown(exec_ctx, &fd->read_closure,
GRPC_ERROR_REF(why))) { GRPC_ERROR_REF(why))) {
shutdown(fd->fd, SHUT_RDWR); if (!releasing_fd) {
shutdown(fd->fd, SHUT_RDWR);
}
grpc_lfev_set_shutdown(exec_ctx, &fd->write_closure, GRPC_ERROR_REF(why)); grpc_lfev_set_shutdown(exec_ctx, &fd->write_closure, GRPC_ERROR_REF(why));
} }
GRPC_ERROR_UNREF(why); GRPC_ERROR_UNREF(why);
} }
/* Might be called multiple times */
static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) {
fd_shutdown_internal(exec_ctx, fd, why, false);
}
static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
grpc_closure *on_done, int *release_fd, grpc_closure *on_done, int *release_fd,
bool already_closed, const char *reason) { bool already_closed, const char *reason) {
grpc_error *error = GRPC_ERROR_NONE; grpc_error *error = GRPC_ERROR_NONE;
bool is_release_fd = (release_fd != NULL);
if (!grpc_lfev_is_shutdown(&fd->read_closure)) { if (!grpc_lfev_is_shutdown(&fd->read_closure)) {
fd_shutdown(exec_ctx, fd, GRPC_ERROR_CREATE_FROM_COPIED_STRING(reason)); fd_shutdown_internal(exec_ctx, fd,
GRPC_ERROR_CREATE_FROM_COPIED_STRING(reason),
is_release_fd);
} }
/* If release_fd is not NULL, we should be relinquishing control of the file /* If release_fd is not NULL, we should be relinquishing control of the file
descriptor fd->fd (but we still own the grpc_fd structure). */ descriptor fd->fd (but we still own the grpc_fd structure). */
if (release_fd != NULL) { if (is_release_fd) {
*release_fd = fd->fd; *release_fd = fd->fd;
} else if (!already_closed) { } else if (!already_closed) {
close(fd->fd); close(fd->fd);

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

@ -0,0 +1,27 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "src/core/lib/iomgr/port.h"
#ifdef GRPC_GETHOSTNAME_FALLBACK
#include <stddef.h>
char *grpc_gethostname() { return NULL; }
#endif // GRPC_GETHOSTNAME_FALLBACK

@ -0,0 +1,37 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "src/core/lib/iomgr/port.h"
#ifdef GRPC_POSIX_HOST_NAME_MAX
#include <limits.h>
#include <unistd.h>
#include <grpc/support/alloc.h>
char *grpc_gethostname() {
char *hostname = (char *)gpr_malloc(HOST_NAME_MAX);
if (gethostname(hostname, HOST_NAME_MAX) != 0) {
gpr_free(hostname);
return NULL;
}
return hostname;
}
#endif // GRPC_POSIX_HOST_NAME_MAX

@ -0,0 +1,37 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "src/core/lib/iomgr/port.h"
#ifdef GRPC_POSIX_SYSCONF
#include <unistd.h>
#include <grpc/support/alloc.h>
char *grpc_gethostname() {
size_t host_name_max = (size_t)sysconf(_SC_HOST_NAME_MAX);
char *hostname = (char *)gpr_malloc(host_name_max);
if (gethostname(hostname, host_name_max) != 0) {
gpr_free(hostname);
return NULL;
}
return hostname;
}
#endif // GRPC_POSIX_SYSCONF

@ -59,6 +59,7 @@
#define GRPC_HAVE_MSG_NOSIGNAL 1 #define GRPC_HAVE_MSG_NOSIGNAL 1
#define GRPC_HAVE_UNIX_SOCKET 1 #define GRPC_HAVE_UNIX_SOCKET 1
#define GRPC_LINUX_MULTIPOLL_WITH_EPOLL 1 #define GRPC_LINUX_MULTIPOLL_WITH_EPOLL 1
#define GRPC_POSIX_HOST_NAME_MAX 1
#define GRPC_POSIX_SOCKET 1 #define GRPC_POSIX_SOCKET 1
#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETADDR 1
#define GRPC_POSIX_WAKEUP_FD 1 #define GRPC_POSIX_WAKEUP_FD 1
@ -93,6 +94,7 @@
#define GRPC_POSIX_SOCKET 1 #define GRPC_POSIX_SOCKET 1
#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETADDR 1
#define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_SOCKETUTILS 1
#define GRPC_POSIX_SYSCONF 1
#define GRPC_POSIX_WAKEUP_FD 1 #define GRPC_POSIX_WAKEUP_FD 1
#define GRPC_TIMER_USE_GENERIC 1 #define GRPC_TIMER_USE_GENERIC 1
#elif defined(GPR_FREEBSD) #elif defined(GPR_FREEBSD)
@ -125,4 +127,11 @@
#error Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GPR_CUSTOM_SOCKET #error Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GPR_CUSTOM_SOCKET
#endif #endif
#if defined(GRPC_POSIX_HOST_NAME_MAX) && defined(GRPC_POSIX_SYSCONF)
#error "Cannot define both GRPC_POSIX_HOST_NAME_MAX and GRPC_POSIX_SYSCONF"
#endif
#if !defined(GRPC_POSIX_HOST_NAME_MAX) && !defined(GRPC_POSIX_SYSCONF)
#define GRPC_GETHOSTNAME_FALLBACK 1
#endif
#endif /* GRPC_CORE_LIB_IOMGR_PORT_H */ #endif /* GRPC_CORE_LIB_IOMGR_PORT_H */

@ -39,7 +39,7 @@
#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 #define MIN_SAFE_ACCEPT_QUEUE_SIZE 100
static gpr_once s_init_max_accept_queue_size; static gpr_once s_init_max_accept_queue_size = GPR_ONCE_INIT;
static int s_max_accept_queue_size; static int s_max_accept_queue_size;
/* get max listen queue size on linux */ /* get max listen queue size on linux */

@ -15,6 +15,7 @@
* limitations under the License. * limitations under the License.
* *
*/ */
#include "src/core/lib/surface/alarm_internal.h"
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
@ -22,7 +23,13 @@
#include "src/core/lib/iomgr/timer.h" #include "src/core/lib/iomgr/timer.h"
#include "src/core/lib/surface/completion_queue.h" #include "src/core/lib/surface/completion_queue.h"
#ifndef NDEBUG
grpc_tracer_flag grpc_trace_alarm_refcount =
GRPC_TRACER_INITIALIZER(false, "alarm_refcount");
#endif
struct grpc_alarm { struct grpc_alarm {
gpr_refcount refs;
grpc_timer alarm; grpc_timer alarm;
grpc_closure on_alarm; grpc_closure on_alarm;
grpc_cq_completion completion; grpc_cq_completion completion;
@ -32,13 +39,58 @@ struct grpc_alarm {
void *tag; void *tag;
}; };
static void do_nothing_end_completion(grpc_exec_ctx *exec_ctx, void *arg, static void alarm_ref(grpc_alarm *alarm) { gpr_ref(&alarm->refs); }
grpc_cq_completion *c) {}
static void alarm_unref(grpc_alarm *alarm) {
if (gpr_unref(&alarm->refs)) {
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
GRPC_CQ_INTERNAL_UNREF(&exec_ctx, alarm->cq, "alarm");
grpc_exec_ctx_finish(&exec_ctx);
gpr_free(alarm);
}
}
#ifndef NDEBUG
static void alarm_ref_dbg(grpc_alarm *alarm, const char *reason,
const char *file, int line) {
if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) {
gpr_atm val = gpr_atm_no_barrier_load(&alarm->refs.count);
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"Alarm:%p ref %" PRIdPTR " -> %" PRIdPTR " %s", alarm, val,
val + 1, reason);
}
alarm_ref(alarm);
}
static void alarm_unref_dbg(grpc_alarm *alarm, const char *reason,
const char *file, int line) {
if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) {
gpr_atm val = gpr_atm_no_barrier_load(&alarm->refs.count);
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"Alarm:%p Unref %" PRIdPTR " -> %" PRIdPTR " %s", alarm, val,
val - 1, reason);
}
alarm_unref(alarm);
}
#endif
static void alarm_end_completion(grpc_exec_ctx *exec_ctx, void *arg,
grpc_cq_completion *c) {
grpc_alarm *alarm = arg;
GRPC_ALARM_UNREF(alarm, "dequeue-end-op");
}
static void alarm_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { static void alarm_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
grpc_alarm *alarm = arg; grpc_alarm *alarm = arg;
grpc_cq_end_op(exec_ctx, alarm->cq, alarm->tag, error,
do_nothing_end_completion, NULL, &alarm->completion); /* We are queuing an op on completion queue. This means, the alarm's structure
cannot be destroyed until the op is dequeued. Adding an extra ref
here and unref'ing when the op is dequeued will achieve this */
GRPC_ALARM_REF(alarm, "queue-end-op");
grpc_cq_end_op(exec_ctx, alarm->cq, alarm->tag, error, alarm_end_completion,
(void *)alarm, &alarm->completion);
} }
grpc_alarm *grpc_alarm_create(grpc_completion_queue *cq, gpr_timespec deadline, grpc_alarm *grpc_alarm_create(grpc_completion_queue *cq, gpr_timespec deadline,
@ -46,6 +98,14 @@ grpc_alarm *grpc_alarm_create(grpc_completion_queue *cq, gpr_timespec deadline,
grpc_alarm *alarm = gpr_malloc(sizeof(grpc_alarm)); grpc_alarm *alarm = gpr_malloc(sizeof(grpc_alarm));
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
gpr_ref_init(&alarm->refs, 1);
#ifndef NDEBUG
if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) {
gpr_log(GPR_DEBUG, "Alarm:%p created (ref: 1)", alarm);
}
#endif
GRPC_CQ_INTERNAL_REF(cq, "alarm"); GRPC_CQ_INTERNAL_REF(cq, "alarm");
alarm->cq = cq; alarm->cq = cq;
alarm->tag = tag; alarm->tag = tag;
@ -67,9 +127,6 @@ void grpc_alarm_cancel(grpc_alarm *alarm) {
} }
void grpc_alarm_destroy(grpc_alarm *alarm) { void grpc_alarm_destroy(grpc_alarm *alarm) {
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_alarm_cancel(alarm); grpc_alarm_cancel(alarm);
GRPC_CQ_INTERNAL_UNREF(&exec_ctx, alarm->cq, "alarm"); GRPC_ALARM_UNREF(alarm, "alarm_destroy");
gpr_free(alarm);
grpc_exec_ctx_finish(&exec_ctx);
} }

@ -0,0 +1,40 @@
/*
*
* Copyright 2015-2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef GRPC_CORE_LIB_SURFACE_ALARM_INTERNAL_H
#define GRPC_CORE_LIB_SURFACE_ALARM_INTERNAL_H
#include <grpc/support/log.h>
#include "src/core/lib/debug/trace.h"
#ifndef NDEBUG
extern grpc_tracer_flag grpc_trace_alarm_refcount;
#define GRPC_ALARM_REF(a, reason) alarm_ref_dbg(a, reason, __FILE__, __LINE__)
#define GRPC_ALARM_UNREF(a, reason) \
alarm_unref_dbg(a, reason, __FILE__, __LINE__)
#else /* !defined(NDEBUG) */
#define GRPC_ALARM_REF(a, reason) alarm_ref(a)
#define GRPC_ALARM_UNREF(a, reason) alarm_unref(a)
#endif /* defined(NDEBUG) */
#endif /* GRPC_CORE_LIB_SURFACE_ALARM_INTERNAL_H */

@ -36,6 +36,7 @@
#include "src/core/lib/iomgr/resource_quota.h" #include "src/core/lib/iomgr/resource_quota.h"
#include "src/core/lib/profiling/timers.h" #include "src/core/lib/profiling/timers.h"
#include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/surface/alarm_internal.h"
#include "src/core/lib/surface/api_trace.h" #include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/call.h" #include "src/core/lib/surface/call.h"
#include "src/core/lib/surface/channel_init.h" #include "src/core/lib/surface/channel_init.h"
@ -135,6 +136,7 @@ void grpc_init(void) {
grpc_register_tracer(&grpc_call_error_trace); grpc_register_tracer(&grpc_call_error_trace);
#ifndef NDEBUG #ifndef NDEBUG
grpc_register_tracer(&grpc_trace_pending_tags); grpc_register_tracer(&grpc_trace_pending_tags);
grpc_register_tracer(&grpc_trace_alarm_refcount);
grpc_register_tracer(&grpc_trace_cq_refcount); grpc_register_tracer(&grpc_trace_cq_refcount);
grpc_register_tracer(&grpc_trace_closure); grpc_register_tracer(&grpc_trace_closure);
grpc_register_tracer(&grpc_trace_error_refcount); grpc_register_tracer(&grpc_trace_error_refcount);

@ -17,6 +17,7 @@
*/ */
#include <grpc++/support/slice.h> #include <grpc++/support/slice.h>
#include <grpc/slice.h>
namespace grpc { namespace grpc {
@ -43,4 +44,10 @@ Slice::Slice(const void* buf, size_t len, StaticSlice)
Slice::Slice(const Slice& other) : slice_(grpc_slice_ref(other.slice_)) {} Slice::Slice(const Slice& other) : slice_(grpc_slice_ref(other.slice_)) {}
Slice::Slice(void* buf, size_t len, void (*destroy)(void*), void* user_data)
: slice_(grpc_slice_new_with_user_data(buf, len, destroy, user_data)) {}
Slice::Slice(void* buf, size_t len, void (*destroy)(void*, size_t))
: slice_(grpc_slice_new_with_len(buf, len, destroy)) {}
} // namespace grpc } // namespace grpc

@ -0,0 +1,29 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#import "GRPCCall.h"
#import "GRPCCall+OAuth2.h"
#import <Google/SignIn.h>
/**
* Extend GIDSignIn class to comply GRPCAuthorizationProtocol
*/
@interface GIDSignIn (GRPC) <GRPCAuthorizationProtocol>
- (void)getTokenWithHandler:(void (^)(NSString *token))hander;
@end

@ -0,0 +1,28 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#import "GRPCCall+GID.h"
@implementation GIDSignIn (GRPC)
- (void)getTokenWithHandler:(void (^)(NSString *token))handler {
NSString *token = self.currentUser.authentication.accessToken;
handler(token);
}
@end

@ -18,6 +18,13 @@
#import "GRPCCall.h" #import "GRPCCall.h"
/**
* The protocol of an OAuth2 token object from which GRPCCall can acquire a token.
*/
@protocol GRPCAuthorizationProtocol
- (void)getTokenWithHandler:(void (^)(NSString *token))hander;
@end
/** Helpers for setting and reading headers compatible with OAuth2. */ /** Helpers for setting and reading headers compatible with OAuth2. */
@interface GRPCCall (OAuth2) @interface GRPCCall (OAuth2)
@ -33,4 +40,12 @@
/** Returns the value (if any) of the "www-authenticate" response header (the challenge header). */ /** Returns the value (if any) of the "www-authenticate" response header (the challenge header). */
@property(atomic, readonly) NSString *oauth2ChallengeHeader; @property(atomic, readonly) NSString *oauth2ChallengeHeader;
/**
* The authorization token object to be used when starting the call. If the value is set to nil, no
* oauth authentication will be used.
*
* If tokenProvider exists, it takes precedence over the token set by oauth2AccessToken.
*/
@property(atomic, strong) id<GRPCAuthorizationProtocol> tokenProvider;
@end @end

@ -16,6 +16,8 @@
* *
*/ */
#import <objc/runtime.h>
#import "GRPCCall+OAuth2.h" #import "GRPCCall+OAuth2.h"
static NSString * const kAuthorizationHeader = @"authorization"; static NSString * const kAuthorizationHeader = @"authorization";
@ -23,6 +25,7 @@ static NSString * const kBearerPrefix = @"Bearer ";
static NSString * const kChallengeHeader = @"www-authenticate"; static NSString * const kChallengeHeader = @"www-authenticate";
@implementation GRPCCall (OAuth2) @implementation GRPCCall (OAuth2)
@dynamic tokenProvider;
- (NSString *)oauth2AccessToken { - (NSString *)oauth2AccessToken {
NSString *headerValue = self.requestHeaders[kAuthorizationHeader]; NSString *headerValue = self.requestHeaders[kAuthorizationHeader];
@ -45,4 +48,12 @@ static NSString * const kChallengeHeader = @"www-authenticate";
return self.responseHeaders[kChallengeHeader]; return self.responseHeaders[kChallengeHeader];
} }
- (void)setTokenProvider:(id<GRPCAuthorizationProtocol>)tokenProvider {
objc_setAssociatedObject(self, @selector(tokenProvider), tokenProvider, OBJC_ASSOCIATION_RETAIN);
}
- (id<GRPCAuthorizationProtocol>)tokenProvider {
return objc_getAssociatedObject(self, @selector(tokenProvider));
}
@end @end

@ -167,7 +167,7 @@ extern id const kGRPCTrailersKey;
* The authority for the RPC. If nil, the default authority will be used. This property must be nil * The authority for the RPC. If nil, the default authority will be used. This property must be nil
* when Cronet transport is enabled. * when Cronet transport is enabled.
*/ */
@property (atomic, readwrite) NSString *serverName; @property (atomic, copy, readwrite) NSString *serverName;
/** /**
* The container of the request headers of an RPC conforms to this protocol, which is a subset of * The container of the request headers of an RPC conforms to this protocol, which is a subset of

@ -18,6 +18,8 @@
#import "GRPCCall.h" #import "GRPCCall.h"
#import "GRPCCall+OAuth2.h"
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
#import <RxLibrary/GRXConcurrentWriteable.h> #import <RxLibrary/GRXConcurrentWriteable.h>
@ -40,10 +42,14 @@ NSString * const kGRPCHeadersKey = @"io.grpc.HeadersKey";
NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
static NSMutableDictionary *callFlags; static NSMutableDictionary *callFlags;
static NSString * const kAuthorizationHeader = @"authorization";
static NSString * const kBearerPrefix = @"Bearer ";
@interface GRPCCall () <GRXWriteable> @interface GRPCCall () <GRXWriteable>
// Make them read-write. // Make them read-write.
@property(atomic, strong) NSDictionary *responseHeaders; @property(atomic, strong) NSDictionary *responseHeaders;
@property(atomic, strong) NSDictionary *responseTrailers; @property(atomic, strong) NSDictionary *responseTrailers;
@property(atomic) BOOL isWaitingForToken;
@end @end
// The following methods of a C gRPC call object aren't reentrant, and thus // The following methods of a C gRPC call object aren't reentrant, and thus
@ -181,9 +187,6 @@ static NSMutableDictionary *callFlags;
- (void)finishWithError:(NSError *)errorOrNil { - (void)finishWithError:(NSError *)errorOrNil {
@synchronized(self) { @synchronized(self) {
if (_state == GRXWriterStateFinished) {
return;
}
_state = GRXWriterStateFinished; _state = GRXWriterStateFinished;
} }
@ -211,7 +214,11 @@ static NSMutableDictionary *callFlags;
[self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain [self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
code:GRPCErrorCodeCancelled code:GRPCErrorCodeCancelled
userInfo:@{NSLocalizedDescriptionKey: @"Canceled by app"}]]; userInfo:@{NSLocalizedDescriptionKey: @"Canceled by app"}]];
[self cancelCall]; if (!self.isWaitingForToken) {
[self cancelCall];
} else {
self.isWaitingForToken = NO;
}
} }
- (void)dealloc { - (void)dealloc {
@ -410,22 +417,13 @@ static NSMutableDictionary *callFlags;
#pragma mark GRXWriter implementation #pragma mark GRXWriter implementation
- (void)startWithWriteable:(id<GRXWriteable>)writeable { - (void)startCallWithWriteable:(id<GRXWriteable>)writeable {
@synchronized(self) {
_state = GRXWriterStateStarted;
}
// Create a retain cycle so that this instance lives until the RPC finishes (or is cancelled).
// This makes RPCs in which the call isn't externally retained possible (as long as it is started
// before being autoreleased).
// Care is taken not to retain self strongly in any of the blocks used in this implementation, so
// that the life of the instance is determined by this retain cycle.
_retainSelf = self;
_responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable _responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable
dispatchQueue:_responseQueue]; dispatchQueue:_responseQueue];
_wrappedCall = [[GRPCWrappedCall alloc] initWithHost:_host serverName:_serverName path:_path]; _wrappedCall = [[GRPCWrappedCall alloc] initWithHost:_host
serverName:_serverName
path:_path];
NSAssert(_wrappedCall, @"Error allocating RPC objects. Low memory?"); NSAssert(_wrappedCall, @"Error allocating RPC objects. Low memory?");
[self sendHeaders:_requestHeaders]; [self sendHeaders:_requestHeaders];
@ -437,20 +435,49 @@ static NSMutableDictionary *callFlags;
// TODO(jcanizales): Check this on init. // TODO(jcanizales): Check this on init.
[NSException raise:NSInvalidArgumentException format:@"host of %@ is nil", _host]; [NSException raise:NSInvalidArgumentException format:@"host of %@ is nil", _host];
} }
__weak typeof(self) weakSelf = self;
_connectivityMonitor = [GRPCConnectivityMonitor monitorWithHost:host]; _connectivityMonitor = [GRPCConnectivityMonitor monitorWithHost:host];
__weak typeof(self) weakSelf = self;
void (^handler)() = ^{ void (^handler)() = ^{
typeof(self) strongSelf = weakSelf; typeof(self) strongSelf = weakSelf;
if (strongSelf) { [strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
[strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain code:GRPCErrorCodeUnavailable
code:GRPCErrorCodeUnavailable userInfo:@{ NSLocalizedDescriptionKey : @"Connectivity lost." }]];
userInfo:@{ NSLocalizedDescriptionKey : @"Connectivity lost." }]];
}
}; };
[_connectivityMonitor handleLossWithHandler:handler [_connectivityMonitor handleLossWithHandler:handler
wifiStatusChangeHandler:nil]; wifiStatusChangeHandler:nil];
} }
- (void)startWithWriteable:(id<GRXWriteable>)writeable {
@synchronized(self) {
_state = GRXWriterStateStarted;
}
// Create a retain cycle so that this instance lives until the RPC finishes (or is cancelled).
// This makes RPCs in which the call isn't externally retained possible (as long as it is started
// before being autoreleased).
// Care is taken not to retain self strongly in any of the blocks used in this implementation, so
// that the life of the instance is determined by this retain cycle.
_retainSelf = self;
if (self.tokenProvider != nil) {
self.isWaitingForToken = YES;
__weak typeof(self) weakSelf = self;
[self.tokenProvider getTokenWithHandler:^(NSString *token){
typeof(self) strongSelf = weakSelf;
if (strongSelf && strongSelf.isWaitingForToken) {
if (token) {
NSString *t = [kBearerPrefix stringByAppendingString:token];
strongSelf.requestHeaders[kAuthorizationHeader] = t;
}
[strongSelf startCallWithWriteable:writeable];
strongSelf.isWaitingForToken = NO;
}
}];
} else {
[self startCallWithWriteable:writeable];
}
}
- (void)setState:(GRXWriterState)newState { - (void)setState:(GRXWriterState)newState {
@synchronized(self) { @synchronized(self) {
// Manual transitions are only allowed from the started or paused states. // Manual transitions are only allowed from the started or paused states.

@ -103,7 +103,6 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) {
} }
- (void)setObject:(id)obj forKey:(NSString *)key { - (void)setObject:(id)obj forKey:(NSString *)key {
[self checkCallIsNotStarted];
CheckIsNonNilASCII(@"Header name", key); CheckIsNonNilASCII(@"Header name", key);
key = key.lowercaseString; key = key.lowercaseString;
CheckKeyValuePairIsValid(key, obj); CheckKeyValuePairIsValid(key, obj);

@ -214,10 +214,12 @@ PHP_METHOD(Call, __construct) {
return; return;
} }
wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(channel_obj); wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(channel_obj);
if (channel->wrapped == NULL) { gpr_mu_lock(&channel->wrapper->mu);
if (channel->wrapper->wrapped == NULL) {
zend_throw_exception(spl_ce_InvalidArgumentException, zend_throw_exception(spl_ce_InvalidArgumentException,
"Call cannot be constructed from a closed Channel", "Call cannot be constructed from a closed Channel",
1 TSRMLS_CC); 1 TSRMLS_CC);
gpr_mu_unlock(&channel->wrapper->mu);
return; return;
} }
add_property_zval(getThis(), "channel", channel_obj); add_property_zval(getThis(), "channel", channel_obj);
@ -226,13 +228,15 @@ PHP_METHOD(Call, __construct) {
grpc_slice host_slice = host_override != NULL ? grpc_slice host_slice = host_override != NULL ?
grpc_slice_from_copied_string(host_override) : grpc_empty_slice(); grpc_slice_from_copied_string(host_override) : grpc_empty_slice();
call->wrapped = call->wrapped =
grpc_channel_create_call(channel->wrapped, NULL, GRPC_PROPAGATE_DEFAULTS, grpc_channel_create_call(channel->wrapper->wrapped, NULL,
GRPC_PROPAGATE_DEFAULTS,
completion_queue, method_slice, completion_queue, method_slice,
host_override != NULL ? &host_slice : NULL, host_override != NULL ? &host_slice : NULL,
deadline->wrapped, NULL); deadline->wrapped, NULL);
grpc_slice_unref(method_slice); grpc_slice_unref(method_slice);
grpc_slice_unref(host_slice); grpc_slice_unref(host_slice);
call->owned = true; call->owned = true;
gpr_mu_unlock(&channel->wrapper->mu);
} }
/** /**

@ -109,8 +109,8 @@ PHP_METHOD(CallCredentials, createFromPlugin) {
zend_fcall_info *fci; zend_fcall_info *fci;
zend_fcall_info_cache *fci_cache; zend_fcall_info_cache *fci_cache;
fci = (zend_fcall_info *)emalloc(sizeof(zend_fcall_info)); fci = (zend_fcall_info *)malloc(sizeof(zend_fcall_info));
fci_cache = (zend_fcall_info_cache *)emalloc(sizeof(zend_fcall_info_cache)); fci_cache = (zend_fcall_info_cache *)malloc(sizeof(zend_fcall_info_cache));
memset(fci, 0, sizeof(zend_fcall_info)); memset(fci, 0, sizeof(zend_fcall_info));
memset(fci_cache, 0, sizeof(zend_fcall_info_cache)); memset(fci_cache, 0, sizeof(zend_fcall_info_cache));
@ -123,7 +123,7 @@ PHP_METHOD(CallCredentials, createFromPlugin) {
} }
plugin_state *state; plugin_state *state;
state = (plugin_state *)emalloc(sizeof(plugin_state)); state = (plugin_state *)malloc(sizeof(plugin_state));
memset(state, 0, sizeof(plugin_state)); memset(state, 0, sizeof(plugin_state));
/* save the user provided PHP callback function */ /* save the user provided PHP callback function */
@ -210,13 +210,13 @@ void plugin_get_metadata(void *ptr, grpc_auth_metadata_context context,
/* Cleanup function for plugin creds API */ /* Cleanup function for plugin creds API */
void plugin_destroy_state(void *ptr) { void plugin_destroy_state(void *ptr) {
plugin_state *state = (plugin_state *)ptr; plugin_state *state = (plugin_state *)ptr;
efree(state->fci); free(state->fci);
efree(state->fci_cache); free(state->fci_cache);
#if PHP_MAJOR_VERSION < 7 #if PHP_MAJOR_VERSION < 7
PHP_GRPC_FREE_STD_ZVAL(state->fci->params); PHP_GRPC_FREE_STD_ZVAL(state->fci->params);
PHP_GRPC_FREE_STD_ZVAL(state->fci->retval); PHP_GRPC_FREE_STD_ZVAL(state->fci->retval);
#endif #endif
efree(state); free(state);
} }
ZEND_BEGIN_ARG_INFO_EX(arginfo_createComposite, 0, 0, 2) ZEND_BEGIN_ARG_INFO_EX(arginfo_createComposite, 0, 0, 2)

@ -25,6 +25,13 @@
#include <php.h> #include <php.h>
#include <php_ini.h> #include <php_ini.h>
#include <ext/standard/info.h> #include <ext/standard/info.h>
#include <ext/standard/php_var.h>
#include <ext/standard/sha1.h>
#if PHP_MAJOR_VERSION < 7
#include <ext/standard/php_smart_str.h>
#else
#include <zend_smart_str.h>
#endif
#include <ext/spl/spl_exceptions.h> #include <ext/spl/spl_exceptions.h>
#include "php_grpc.h" #include "php_grpc.h"
@ -44,11 +51,25 @@ zend_class_entry *grpc_ce_channel;
#if PHP_MAJOR_VERSION >= 7 #if PHP_MAJOR_VERSION >= 7
static zend_object_handlers channel_ce_handlers; static zend_object_handlers channel_ce_handlers;
#endif #endif
static gpr_mu global_persistent_list_mu;
int le_plink;
/* Frees and destroys an instance of wrapped_grpc_channel */ /* Frees and destroys an instance of wrapped_grpc_channel */
PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_channel) PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_channel)
if (p->wrapped != NULL) { if (p->wrapper != NULL) {
grpc_channel_destroy(p->wrapped); gpr_mu_lock(&p->wrapper->mu);
if (p->wrapper->wrapped != NULL) {
php_grpc_zend_resource *rsrc;
php_grpc_int key_len = strlen(p->wrapper->key);
// only destroy the channel here if not found in the persistent list
gpr_mu_lock(&global_persistent_list_mu);
if (!(PHP_GRPC_PERSISTENT_LIST_FIND(&EG(persistent_list), p->wrapper->key,
key_len, rsrc))) {
grpc_channel_destroy(p->wrapper->wrapped);
}
gpr_mu_unlock(&global_persistent_list_mu);
}
gpr_mu_unlock(&p->wrapper->mu);
} }
PHP_GRPC_FREE_WRAPPED_FUNC_END() PHP_GRPC_FREE_WRAPPED_FUNC_END()
@ -62,15 +83,15 @@ php_grpc_zend_object create_wrapped_grpc_channel(zend_class_entry *class_type
PHP_GRPC_FREE_CLASS_OBJECT(wrapped_grpc_channel, channel_ce_handlers); PHP_GRPC_FREE_CLASS_OBJECT(wrapped_grpc_channel, channel_ce_handlers);
} }
void php_grpc_read_args_array(zval *args_array, int php_grpc_read_args_array(zval *args_array,
grpc_channel_args *args TSRMLS_DC) { grpc_channel_args *args TSRMLS_DC) {
HashTable *array_hash; HashTable *array_hash;
int args_index; int args_index;
array_hash = Z_ARRVAL_P(args_array); array_hash = Z_ARRVAL_P(args_array);
if (!array_hash) { if (!array_hash) {
zend_throw_exception(spl_ce_InvalidArgumentException, zend_throw_exception(spl_ce_InvalidArgumentException,
"array_hash is NULL", 1 TSRMLS_CC); "array_hash is NULL", 1 TSRMLS_CC);
return; return FAILURE;
} }
args->num_args = zend_hash_num_elements(array_hash); args->num_args = zend_hash_num_elements(array_hash);
args->args = ecalloc(args->num_args, sizeof(grpc_arg)); args->args = ecalloc(args->num_args, sizeof(grpc_arg));
@ -84,7 +105,7 @@ void php_grpc_read_args_array(zval *args_array,
if (key_type != HASH_KEY_IS_STRING) { if (key_type != HASH_KEY_IS_STRING) {
zend_throw_exception(spl_ce_InvalidArgumentException, zend_throw_exception(spl_ce_InvalidArgumentException,
"args keys must be strings", 1 TSRMLS_CC); "args keys must be strings", 1 TSRMLS_CC);
return; return FAILURE;
} }
args->args[args_index].key = key; args->args[args_index].key = key;
switch (Z_TYPE_P(data)) { switch (Z_TYPE_P(data)) {
@ -99,16 +120,78 @@ void php_grpc_read_args_array(zval *args_array,
default: default:
zend_throw_exception(spl_ce_InvalidArgumentException, zend_throw_exception(spl_ce_InvalidArgumentException,
"args values must be int or string", 1 TSRMLS_CC); "args values must be int or string", 1 TSRMLS_CC);
return; return FAILURE;
} }
args_index++; args_index++;
PHP_GRPC_HASH_FOREACH_END() PHP_GRPC_HASH_FOREACH_END()
return SUCCESS;
}
void generate_sha1_str(char *sha1str, char *str, php_grpc_int len) {
PHP_SHA1_CTX context;
unsigned char digest[20];
sha1str[0] = '\0';
PHP_SHA1Init(&context);
PHP_GRPC_SHA1Update(&context, str, len);
PHP_SHA1Final(digest, &context);
make_sha1_digest(sha1str, digest);
}
void create_channel(
wrapped_grpc_channel *channel,
char *target,
grpc_channel_args args,
wrapped_grpc_channel_credentials *creds) {
if (creds == NULL) {
channel->wrapper->wrapped = grpc_insecure_channel_create(target, &args,
NULL);
} else {
channel->wrapper->wrapped =
grpc_secure_channel_create(creds->wrapped, target, &args, NULL);
}
efree(args.args);
}
void create_and_add_channel_to_persistent_list(
wrapped_grpc_channel *channel,
char *target,
grpc_channel_args args,
wrapped_grpc_channel_credentials *creds,
char *key,
php_grpc_int key_len) {
php_grpc_zend_resource new_rsrc;
channel_persistent_le_t *le;
// this links each persistent list entry to a destructor
new_rsrc.type = le_plink;
le = malloc(sizeof(channel_persistent_le_t));
create_channel(channel, target, args, creds);
le->channel = channel->wrapper;
new_rsrc.ptr = le;
gpr_mu_lock(&global_persistent_list_mu);
PHP_GRPC_PERSISTENT_LIST_UPDATE(&EG(persistent_list), key, key_len,
(void *)&new_rsrc);
gpr_mu_unlock(&global_persistent_list_mu);
} }
/** /**
* Construct an instance of the Channel class. If the $args array contains a * Construct an instance of the Channel class.
* "credentials" key mapping to a ChannelCredentials object, a secure channel *
* will be created with those credentials. * By default, the underlying grpc_channel is "persistent". That is, given
* the same set of parameters passed to the constructor, the same underlying
* grpc_channel will be returned.
*
* If the $args array contains a "credentials" key mapping to a
* ChannelCredentials object, a secure channel will be created with those
* credentials.
*
* If the $args array contains a "force_new" key mapping to a boolean value
* of "true", a new underlying grpc_channel will be created regardless. If
* there are any opened channels on the same hostname, user must manually
* call close() on those dangling channels before the end of the PHP
* script.
*
* @param string $target The hostname to associate with this channel * @param string $target The hostname to associate with this channel
* @param array $args_array The arguments to pass to the Channel * @param array $args_array The arguments to pass to the Channel
*/ */
@ -121,6 +204,9 @@ PHP_METHOD(Channel, __construct) {
grpc_channel_args args; grpc_channel_args args;
HashTable *array_hash; HashTable *array_hash;
wrapped_grpc_channel_credentials *creds = NULL; wrapped_grpc_channel_credentials *creds = NULL;
php_grpc_zend_resource *rsrc;
bool force_new = false;
zval *force_new_obj = NULL;
/* "sa" == 1 string, 1 array */ /* "sa" == 1 string, 1 array */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &target, if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &target,
@ -131,7 +217,7 @@ PHP_METHOD(Channel, __construct) {
} }
array_hash = Z_ARRVAL_P(args_array); array_hash = Z_ARRVAL_P(args_array);
if (php_grpc_zend_hash_find(array_hash, "credentials", sizeof("credentials"), if (php_grpc_zend_hash_find(array_hash, "credentials", sizeof("credentials"),
(void **)&creds_obj) == SUCCESS) { (void **)&creds_obj) == SUCCESS) {
if (Z_TYPE_P(creds_obj) == IS_NULL) { if (Z_TYPE_P(creds_obj) == IS_NULL) {
creds = NULL; creds = NULL;
php_grpc_zend_hash_del(array_hash, "credentials", sizeof("credentials")); php_grpc_zend_hash_del(array_hash, "credentials", sizeof("credentials"));
@ -146,14 +232,82 @@ PHP_METHOD(Channel, __construct) {
php_grpc_zend_hash_del(array_hash, "credentials", sizeof("credentials")); php_grpc_zend_hash_del(array_hash, "credentials", sizeof("credentials"));
} }
} }
php_grpc_read_args_array(args_array, &args TSRMLS_CC); if (php_grpc_zend_hash_find(array_hash, "force_new", sizeof("force_new"),
if (creds == NULL) { (void **)&force_new_obj) == SUCCESS) {
channel->wrapped = grpc_insecure_channel_create(target, &args, NULL); if (PHP_GRPC_BVAL_IS_TRUE(force_new_obj)) {
force_new = true;
}
php_grpc_zend_hash_del(array_hash, "force_new", sizeof("force_new"));
}
// parse the rest of the channel args array
if (php_grpc_read_args_array(args_array, &args TSRMLS_CC) == FAILURE) {
return;
}
// Construct a hashkey for the persistent channel
// Currently, the hashkey contains 3 parts:
// 1. hostname
// 2. hash value of the channel args array (excluding "credentials"
// and "force_new")
// 3. (optional) hash value of the ChannelCredentials object
php_serialize_data_t var_hash;
smart_str buf = {0};
PHP_VAR_SERIALIZE_INIT(var_hash);
PHP_GRPC_VAR_SERIALIZE(&buf, args_array, &var_hash);
PHP_VAR_SERIALIZE_DESTROY(var_hash);
char sha1str[41];
generate_sha1_str(sha1str, PHP_GRPC_SERIALIZED_BUF_STR(buf),
PHP_GRPC_SERIALIZED_BUF_LEN(buf));
php_grpc_int key_len = target_length + strlen(sha1str);
if (creds != NULL && creds->hashstr != NULL) {
key_len += strlen(creds->hashstr);
}
char *key = malloc(key_len + 1);
strcpy(key, target);
strcat(key, sha1str);
if (creds != NULL && creds->hashstr != NULL) {
strcat(key, creds->hashstr);
}
channel->wrapper = malloc(sizeof(grpc_channel_wrapper));
channel->wrapper->key = key;
channel->wrapper->target = target;
channel->wrapper->args_hashstr = sha1str;
if (creds != NULL && creds->hashstr != NULL) {
channel->wrapper->creds_hashstr = creds->hashstr;
}
gpr_mu_init(&channel->wrapper->mu);
smart_str_free(&buf);
if (force_new) {
php_grpc_delete_persistent_list_entry(key, key_len TSRMLS_CC);
}
if (creds != NULL && creds->has_call_creds) {
// If the ChannelCredentials object was composed with a CallCredentials
// object, there is no way we can tell them apart. Do NOT persist
// them. They should be individually destroyed.
create_channel(channel, target, args, creds);
} else if (!(PHP_GRPC_PERSISTENT_LIST_FIND(&EG(persistent_list), key,
key_len, rsrc))) {
create_and_add_channel_to_persistent_list(
channel, target, args, creds, key, key_len);
} else { } else {
channel->wrapped = // Found a previously stored channel in the persistent list
grpc_secure_channel_create(creds->wrapped, target, &args, NULL); channel_persistent_le_t *le = (channel_persistent_le_t *)rsrc->ptr;
if (strcmp(target, le->channel->target) != 0 ||
strcmp(sha1str, le->channel->args_hashstr) != 0 ||
(creds != NULL && creds->hashstr != NULL &&
strcmp(creds->hashstr, le->channel->creds_hashstr) != 0)) {
// somehow hash collision
create_and_add_channel_to_persistent_list(
channel, target, args, creds, key, key_len);
} else {
channel->wrapper = le->channel;
}
} }
efree(args.args);
} }
/** /**
@ -162,7 +316,16 @@ PHP_METHOD(Channel, __construct) {
*/ */
PHP_METHOD(Channel, getTarget) { PHP_METHOD(Channel, getTarget) {
wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis()); wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
PHP_GRPC_RETURN_STRING(grpc_channel_get_target(channel->wrapped), 1); gpr_mu_lock(&channel->wrapper->mu);
if (channel->wrapper->wrapped == NULL) {
zend_throw_exception(spl_ce_RuntimeException,
"Channel already closed", 1 TSRMLS_CC);
gpr_mu_unlock(&channel->wrapper->mu);
return;
}
char *target = grpc_channel_get_target(channel->wrapper->wrapped);
gpr_mu_unlock(&channel->wrapper->mu);
PHP_GRPC_RETURN_STRING(target, 1);
} }
/** /**
@ -172,6 +335,14 @@ PHP_METHOD(Channel, getTarget) {
*/ */
PHP_METHOD(Channel, getConnectivityState) { PHP_METHOD(Channel, getConnectivityState) {
wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis()); wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
gpr_mu_lock(&channel->wrapper->mu);
if (channel->wrapper->wrapped == NULL) {
zend_throw_exception(spl_ce_RuntimeException,
"Channel already closed", 1 TSRMLS_CC);
gpr_mu_unlock(&channel->wrapper->mu);
return;
}
bool try_to_connect = false; bool try_to_connect = false;
/* "|b" == 1 optional bool */ /* "|b" == 1 optional bool */
@ -179,10 +350,18 @@ PHP_METHOD(Channel, getConnectivityState) {
== FAILURE) { == FAILURE) {
zend_throw_exception(spl_ce_InvalidArgumentException, zend_throw_exception(spl_ce_InvalidArgumentException,
"getConnectivityState expects a bool", 1 TSRMLS_CC); "getConnectivityState expects a bool", 1 TSRMLS_CC);
gpr_mu_unlock(&channel->wrapper->mu);
return; return;
} }
RETURN_LONG(grpc_channel_check_connectivity_state(channel->wrapped, int state = grpc_channel_check_connectivity_state(channel->wrapper->wrapped,
(int)try_to_connect)); (int)try_to_connect);
// this can happen if another shared Channel object close the underlying
// channel
if (state == GRPC_CHANNEL_SHUTDOWN) {
channel->wrapper->wrapped = NULL;
}
gpr_mu_unlock(&channel->wrapper->mu);
RETURN_LONG(state);
} }
/** /**
@ -194,25 +373,37 @@ PHP_METHOD(Channel, getConnectivityState) {
*/ */
PHP_METHOD(Channel, watchConnectivityState) { PHP_METHOD(Channel, watchConnectivityState) {
wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis()); wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
gpr_mu_lock(&channel->wrapper->mu);
if (channel->wrapper->wrapped == NULL) {
zend_throw_exception(spl_ce_RuntimeException,
"Channel already closed", 1 TSRMLS_CC);
gpr_mu_unlock(&channel->wrapper->mu);
return;
}
php_grpc_long last_state; php_grpc_long last_state;
zval *deadline_obj; zval *deadline_obj;
/* "lO" == 1 long 1 object */ /* "lO" == 1 long 1 object */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lO", if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lO",
&last_state, &deadline_obj, grpc_ce_timeval) == FAILURE) { &last_state, &deadline_obj,
grpc_ce_timeval) == FAILURE) {
zend_throw_exception(spl_ce_InvalidArgumentException, zend_throw_exception(spl_ce_InvalidArgumentException,
"watchConnectivityState expects 1 long 1 timeval", 1 TSRMLS_CC); "watchConnectivityState expects 1 long 1 timeval",
1 TSRMLS_CC);
gpr_mu_unlock(&channel->wrapper->mu);
return; return;
} }
wrapped_grpc_timeval *deadline = Z_WRAPPED_GRPC_TIMEVAL_P(deadline_obj); wrapped_grpc_timeval *deadline = Z_WRAPPED_GRPC_TIMEVAL_P(deadline_obj);
grpc_channel_watch_connectivity_state(channel->wrapped, grpc_channel_watch_connectivity_state(channel->wrapper->wrapped,
(grpc_connectivity_state)last_state, (grpc_connectivity_state)last_state,
deadline->wrapped, completion_queue, deadline->wrapped, completion_queue,
NULL); NULL);
grpc_event event = grpc_event event =
grpc_completion_queue_pluck(completion_queue, NULL, grpc_completion_queue_pluck(completion_queue, NULL,
gpr_inf_future(GPR_CLOCK_REALTIME), NULL); gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
gpr_mu_unlock(&channel->wrapper->mu);
RETURN_BOOL(event.success); RETURN_BOOL(event.success);
} }
@ -222,10 +413,48 @@ PHP_METHOD(Channel, watchConnectivityState) {
*/ */
PHP_METHOD(Channel, close) { PHP_METHOD(Channel, close) {
wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis()); wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
if (channel->wrapped != NULL) { gpr_mu_lock(&channel->wrapper->mu);
grpc_channel_destroy(channel->wrapped); if (channel->wrapper->wrapped != NULL) {
channel->wrapped = NULL; grpc_channel_destroy(channel->wrapper->wrapped);
channel->wrapper->wrapped = NULL;
}
php_grpc_delete_persistent_list_entry(channel->wrapper->key,
strlen(channel->wrapper->key)
TSRMLS_CC);
gpr_mu_unlock(&channel->wrapper->mu);
}
// Delete an entry from the persistent list
// Note: this does not destroy or close the underlying grpc_channel
void php_grpc_delete_persistent_list_entry(char *key, php_grpc_int key_len
TSRMLS_DC) {
php_grpc_zend_resource *rsrc;
gpr_mu_lock(&global_persistent_list_mu);
if (PHP_GRPC_PERSISTENT_LIST_FIND(&EG(persistent_list), key,
key_len, rsrc)) {
channel_persistent_le_t *le;
le = (channel_persistent_le_t *)rsrc->ptr;
le->channel = NULL;
php_grpc_zend_hash_del(&EG(persistent_list), key, key_len+1);
}
gpr_mu_unlock(&global_persistent_list_mu);
}
// A destructor associated with each list entry from the persistent list
static void php_grpc_channel_plink_dtor(php_grpc_zend_resource *rsrc
TSRMLS_DC) {
channel_persistent_le_t *le = (channel_persistent_le_t *)rsrc->ptr;
if (le->channel != NULL) {
gpr_mu_lock(&le->channel->mu);
if (le->channel->wrapped != NULL) {
grpc_channel_destroy(le->channel->wrapped);
free(le->channel->key);
free(le->channel);
}
gpr_mu_unlock(&le->channel->mu);
} }
free(le);
} }
ZEND_BEGIN_ARG_INFO_EX(arginfo_construct, 0, 0, 2) ZEND_BEGIN_ARG_INFO_EX(arginfo_construct, 0, 0, 2)
@ -262,10 +491,13 @@ static zend_function_entry channel_methods[] = {
PHP_FE_END PHP_FE_END
}; };
void grpc_init_channel(TSRMLS_D) { GRPC_STARTUP_FUNCTION(channel) {
zend_class_entry ce; zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "Grpc\\Channel", channel_methods); INIT_CLASS_ENTRY(ce, "Grpc\\Channel", channel_methods);
ce.create_object = create_wrapped_grpc_channel; ce.create_object = create_wrapped_grpc_channel;
grpc_ce_channel = zend_register_internal_class(&ce TSRMLS_CC); grpc_ce_channel = zend_register_internal_class(&ce TSRMLS_CC);
le_plink = zend_register_list_destructors_ex(
NULL, php_grpc_channel_plink_dtor, "Persistent Channel", module_number);
PHP_GRPC_INIT_HANDLER(wrapped_grpc_channel, channel_ce_handlers); PHP_GRPC_INIT_HANDLER(wrapped_grpc_channel, channel_ce_handlers);
return SUCCESS;
} }

@ -33,9 +33,18 @@
/* Class entry for the PHP Channel class */ /* Class entry for the PHP Channel class */
extern zend_class_entry *grpc_ce_channel; extern zend_class_entry *grpc_ce_channel;
typedef struct _grpc_channel_wrapper {
grpc_channel *wrapped;
char *key;
char *target;
char *args_hashstr;
char *creds_hashstr;
gpr_mu mu;
} grpc_channel_wrapper;
/* Wrapper struct for grpc_channel that can be associated with a PHP object */ /* Wrapper struct for grpc_channel that can be associated with a PHP object */
PHP_GRPC_WRAP_OBJECT_START(wrapped_grpc_channel) PHP_GRPC_WRAP_OBJECT_START(wrapped_grpc_channel)
grpc_channel *wrapped; grpc_channel_wrapper *wrapper;
PHP_GRPC_WRAP_OBJECT_END(wrapped_grpc_channel) PHP_GRPC_WRAP_OBJECT_END(wrapped_grpc_channel)
#if PHP_MAJOR_VERSION < 7 #if PHP_MAJOR_VERSION < 7
@ -57,10 +66,20 @@ static inline wrapped_grpc_channel
#endif /* PHP_MAJOR_VERSION */ #endif /* PHP_MAJOR_VERSION */
/* Initializes the Channel class */ /* Initializes the Channel class */
void grpc_init_channel(TSRMLS_D); GRPC_STARTUP_FUNCTION(channel);
/* Iterates through a PHP array and populates args with the contents */ /* Iterates through a PHP array and populates args with the contents */
void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args int php_grpc_read_args_array(zval *args_array, grpc_channel_args *args
TSRMLS_DC); TSRMLS_DC);
void generate_sha1_str(char *sha1str, char *str, php_grpc_int len);
void php_grpc_delete_persistent_list_entry(char *key, php_grpc_int key_len
TSRMLS_DC);
typedef struct _channel_persistent_le {
grpc_channel_wrapper *channel;
} channel_persistent_le_t;
#endif /* NET_GRPC_PHP_GRPC_CHANNEL_H_ */ #endif /* NET_GRPC_PHP_GRPC_CHANNEL_H_ */

@ -26,7 +26,9 @@
#include <php.h> #include <php.h>
#include <php_ini.h> #include <php_ini.h>
#include <ext/standard/info.h> #include <ext/standard/info.h>
#include <ext/standard/sha1.h>
#include <ext/spl/spl_exceptions.h> #include <ext/spl/spl_exceptions.h>
#include "channel.h"
#include "php_grpc.h" #include "php_grpc.h"
#include <zend_exceptions.h> #include <zend_exceptions.h>
@ -69,14 +71,17 @@ php_grpc_zend_object create_wrapped_grpc_channel_credentials(
channel_credentials_ce_handlers); channel_credentials_ce_handlers);
} }
zval *grpc_php_wrap_channel_credentials(grpc_channel_credentials zval *grpc_php_wrap_channel_credentials(grpc_channel_credentials *wrapped,
*wrapped TSRMLS_DC) { char *hashstr,
zend_bool has_call_creds TSRMLS_DC) {
zval *credentials_object; zval *credentials_object;
PHP_GRPC_MAKE_STD_ZVAL(credentials_object); PHP_GRPC_MAKE_STD_ZVAL(credentials_object);
object_init_ex(credentials_object, grpc_ce_channel_credentials); object_init_ex(credentials_object, grpc_ce_channel_credentials);
wrapped_grpc_channel_credentials *credentials = wrapped_grpc_channel_credentials *credentials =
Z_WRAPPED_GRPC_CHANNEL_CREDS_P(credentials_object); Z_WRAPPED_GRPC_CHANNEL_CREDS_P(credentials_object);
credentials->wrapped = wrapped; credentials->wrapped = wrapped;
credentials->hashstr = hashstr;
credentials->has_call_creds = has_call_creds;
return credentials_object; return credentials_object;
} }
@ -106,7 +111,8 @@ PHP_METHOD(ChannelCredentials, setDefaultRootsPem) {
*/ */
PHP_METHOD(ChannelCredentials, createDefault) { PHP_METHOD(ChannelCredentials, createDefault) {
grpc_channel_credentials *creds = grpc_google_default_credentials_create(); grpc_channel_credentials *creds = grpc_google_default_credentials_create();
zval *creds_object = grpc_php_wrap_channel_credentials(creds TSRMLS_CC); zval *creds_object = grpc_php_wrap_channel_credentials(creds, NULL, false
TSRMLS_CC);
RETURN_DESTROY_ZVAL(creds_object); RETURN_DESTROY_ZVAL(creds_object);
} }
@ -140,10 +146,24 @@ PHP_METHOD(ChannelCredentials, createSsl) {
"createSsl expects 3 optional strings", 1 TSRMLS_CC); "createSsl expects 3 optional strings", 1 TSRMLS_CC);
return; return;
} }
php_grpc_int hashkey_len = root_certs_length + cert_chain_length;
char hashkey[hashkey_len];
if (root_certs_length > 0) {
strcpy(hashkey, pem_root_certs);
}
if (cert_chain_length > 0) {
strcpy(hashkey, pem_key_cert_pair.cert_chain);
}
char *hashstr = malloc(41);
generate_sha1_str(hashstr, hashkey, hashkey_len);
grpc_channel_credentials *creds = grpc_ssl_credentials_create( grpc_channel_credentials *creds = grpc_ssl_credentials_create(
pem_root_certs, pem_root_certs,
pem_key_cert_pair.private_key == NULL ? NULL : &pem_key_cert_pair, NULL); pem_key_cert_pair.private_key == NULL ? NULL : &pem_key_cert_pair, NULL);
zval *creds_object = grpc_php_wrap_channel_credentials(creds TSRMLS_CC); zval *creds_object = grpc_php_wrap_channel_credentials(creds, hashstr, false
TSRMLS_CC);
RETURN_DESTROY_ZVAL(creds_object); RETURN_DESTROY_ZVAL(creds_object);
} }
@ -172,7 +192,9 @@ PHP_METHOD(ChannelCredentials, createComposite) {
grpc_channel_credentials *creds = grpc_channel_credentials *creds =
grpc_composite_channel_credentials_create(cred1->wrapped, cred2->wrapped, grpc_composite_channel_credentials_create(cred1->wrapped, cred2->wrapped,
NULL); NULL);
zval *creds_object = grpc_php_wrap_channel_credentials(creds TSRMLS_CC); zval *creds_object =
grpc_php_wrap_channel_credentials(creds, cred1->hashstr, true
TSRMLS_CC);
RETURN_DESTROY_ZVAL(creds_object); RETURN_DESTROY_ZVAL(creds_object);
} }

@ -38,6 +38,8 @@ extern zend_class_entry *grpc_ce_channel_credentials;
* with a PHP object */ * with a PHP object */
PHP_GRPC_WRAP_OBJECT_START(wrapped_grpc_channel_credentials) PHP_GRPC_WRAP_OBJECT_START(wrapped_grpc_channel_credentials)
grpc_channel_credentials *wrapped; grpc_channel_credentials *wrapped;
char *hashstr;
zend_bool has_call_creds;
PHP_GRPC_WRAP_OBJECT_END(wrapped_grpc_channel_credentials) PHP_GRPC_WRAP_OBJECT_END(wrapped_grpc_channel_credentials)
#if PHP_MAJOR_VERSION < 7 #if PHP_MAJOR_VERSION < 7

@ -113,6 +113,20 @@ static inline int php_grpc_zend_hash_find(HashTable *ht, char *key, int len,
} }
#define php_grpc_zend_hash_del zend_hash_del #define php_grpc_zend_hash_del zend_hash_del
#define php_grpc_zend_resource zend_rsrc_list_entry
#define PHP_GRPC_BVAL_IS_TRUE(zv) Z_LVAL_P(zv)
#define PHP_GRPC_VAR_SERIALIZE(buf, zv, hash) \
php_var_serialize(buf, &zv, hash TSRMLS_CC)
#define PHP_GRPC_SERIALIZED_BUF_STR(buf) buf.c
#define PHP_GRPC_SERIALIZED_BUF_LEN(buf) buf.len
#define PHP_GRPC_SHA1Update(cxt, str, len) \
PHP_SHA1Update(cxt, (const unsigned char *)str, len)
#define PHP_GRPC_PERSISTENT_LIST_FIND(plist, key, len, rsrc) \
zend_hash_find(plist, key, len+1, (void **)&rsrc) != FAILURE
#define PHP_GRPC_PERSISTENT_LIST_UPDATE(plist, key, len, rsrc) \
zend_hash_update(plist, key, len+1, rsrc, sizeof(php_grpc_zend_resource), \
NULL)
#define PHP_GRPC_GET_CLASS_ENTRY(object) zend_get_class_entry(object TSRMLS_CC) #define PHP_GRPC_GET_CLASS_ENTRY(object) zend_get_class_entry(object TSRMLS_CC)
@ -200,6 +214,20 @@ static inline int php_grpc_zend_hash_find(HashTable *ht, char *key, int len,
static inline int php_grpc_zend_hash_del(HashTable *ht, char *key, int len) { static inline int php_grpc_zend_hash_del(HashTable *ht, char *key, int len) {
return zend_hash_str_del(ht, key, len - 1); return zend_hash_str_del(ht, key, len - 1);
} }
#define php_grpc_zend_resource zend_resource
#define PHP_GRPC_BVAL_IS_TRUE(zv) Z_TYPE_P(zv) == IS_TRUE
#define PHP_GRPC_VAR_SERIALIZE(buf, zv, hash) \
php_var_serialize(buf, zv, hash)
#define PHP_GRPC_SERIALIZED_BUF_STR(buf) ZSTR_VAL(buf.s)
#define PHP_GRPC_SERIALIZED_BUF_LEN(buf) ZSTR_LEN(buf.s)
#define PHP_GRPC_SHA1Update(cxt, str, len) \
PHP_SHA1Update(cxt, (unsigned char *)str, len)
#define PHP_GRPC_PERSISTENT_LIST_FIND(plist, key, len, rsrc) \
(rsrc = zend_hash_str_find_ptr(plist, key, len)) != NULL
#define PHP_GRPC_PERSISTENT_LIST_UPDATE(plist, key, len, rsrc) \
zend_hash_str_update_mem(plist, key, len, rsrc, \
sizeof(php_grpc_zend_resource))
#define PHP_GRPC_GET_CLASS_ENTRY(object) Z_OBJ_P(object)->ce #define PHP_GRPC_GET_CLASS_ENTRY(object) Z_OBJ_P(object)->ce

@ -221,7 +221,7 @@ PHP_MINIT_FUNCTION(grpc) {
CONST_CS | CONST_PERSISTENT); CONST_CS | CONST_PERSISTENT);
grpc_init_call(TSRMLS_C); grpc_init_call(TSRMLS_C);
grpc_init_channel(TSRMLS_C); GRPC_STARTUP(channel);
grpc_init_server(TSRMLS_C); grpc_init_server(TSRMLS_C);
grpc_init_timeval(TSRMLS_C); grpc_init_timeval(TSRMLS_C);
grpc_init_channel_credentials(TSRMLS_C); grpc_init_channel_credentials(TSRMLS_C);

@ -74,4 +74,8 @@ ZEND_END_MODULE_GLOBALS(grpc)
#define GRPC_G(v) (grpc_globals.v) #define GRPC_G(v) (grpc_globals.v)
#endif #endif
#define GRPC_STARTUP_FUNCTION(module) ZEND_MINIT_FUNCTION(grpc_##module)
#define GRPC_STARTUP(module) \
ZEND_MODULE_STARTUP_N(grpc_##module)(INIT_FUNC_ARGS_PASSTHRU)
#endif /* PHP_GRPC_H */ #endif /* PHP_GRPC_H */

@ -37,8 +37,7 @@ class CallTest extends PHPUnit_Framework_TestCase
public function tearDown() public function tearDown()
{ {
unset($this->call); $this->channel->close();
unset($this->channel);
} }
public function testConstructor() public function testConstructor()

@ -25,17 +25,15 @@ class ChannelTest extends PHPUnit_Framework_TestCase
public function tearDown() public function tearDown()
{ {
unset($this->channel); if (!empty($this->channel)) {
$this->channel->close();
}
} }
public function testInsecureCredentials() public function testInsecureCredentials()
{ {
$this->channel = new Grpc\Channel( $this->channel = new Grpc\Channel('localhost:0',
'localhost:0', ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
[
'credentials' => Grpc\ChannelCredentials::createInsecure(),
]
);
$this->assertSame('Grpc\Channel', get_class($this->channel)); $this->assertSame('Grpc\Channel', get_class($this->channel));
} }
@ -111,7 +109,7 @@ class ChannelTest extends PHPUnit_Framework_TestCase
*/ */
public function testInvalidConstructorWith() public function testInvalidConstructorWith()
{ {
$this->channel = new Grpc\Channel('localhost', 'invalid'); $this->channel = new Grpc\Channel('localhost:0', 'invalid');
$this->assertNull($this->channel); $this->assertNull($this->channel);
} }
@ -120,12 +118,8 @@ class ChannelTest extends PHPUnit_Framework_TestCase
*/ */
public function testInvalidCredentials() public function testInvalidCredentials()
{ {
$this->channel = new Grpc\Channel( $this->channel = new Grpc\Channel('localhost:0',
'localhost:0', ['credentials' => new Grpc\Timeval(100)]);
[
'credentials' => new Grpc\Timeval(100),
]
);
} }
/** /**
@ -133,12 +127,8 @@ class ChannelTest extends PHPUnit_Framework_TestCase
*/ */
public function testInvalidOptionsArray() public function testInvalidOptionsArray()
{ {
$this->channel = new Grpc\Channel( $this->channel = new Grpc\Channel('localhost:0',
'localhost:0', ['abc' => []]);
[
'abc' => [],
]
);
} }
/** /**
@ -170,4 +160,431 @@ class ChannelTest extends PHPUnit_Framework_TestCase
['credentials' => Grpc\ChannelCredentials::createInsecure()]); ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
$this->channel->watchConnectivityState(1, 'hi'); $this->channel->watchConnectivityState(1, 'hi');
} }
public function assertConnecting($state) {
$this->assertTrue($state == GRPC\CHANNEL_CONNECTING ||
$state == GRPC\CHANNEL_TRANSIENT_FAILURE);
}
public function waitUntilNotIdle($channel) {
for ($i = 0; $i < 10; $i++) {
$now = Grpc\Timeval::now();
$deadline = $now->add(new Grpc\Timeval(1000));
if ($channel->watchConnectivityState(GRPC\CHANNEL_IDLE,
$deadline)) {
return true;
}
}
$this->assertTrue(false);
}
public function testPersistentChannelSameHost()
{
$this->channel1 = new Grpc\Channel('localhost:1', []);
// the underlying grpc channel is the same by default
// when connecting to the same host
$this->channel2 = new Grpc\Channel('localhost:1', []);
// both channels should be IDLE
$state = $this->channel1->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$state = $this->channel2->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
// try to connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
// both channels should now be in the CONNECTING state
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel2->getConnectivityState();
$this->assertConnecting($state);
$this->channel1->close();
$this->channel2->close();
}
public function testPersistentChannelDifferentHost()
{
// two different underlying channels because different hostname
$this->channel1 = new Grpc\Channel('localhost:1', []);
$this->channel2 = new Grpc\Channel('localhost:2', []);
// both channels should be IDLE
$state = $this->channel1->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$state = $this->channel2->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
// try to connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
// channel1 should now be in the CONNECTING state
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
// channel2 should still be in the IDLE state
$state = $this->channel2->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$this->channel1->close();
$this->channel2->close();
}
public function testPersistentChannelSameArgs()
{
$this->channel1 = new Grpc\Channel('localhost:1', ["abc" => "def"]);
$this->channel2 = new Grpc\Channel('localhost:1', ["abc" => "def"]);
// try to connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel2->getConnectivityState();
$this->assertConnecting($state);
$this->channel1->close();
$this->channel2->close();
}
public function testPersistentChannelDifferentArgs()
{
$this->channel1 = new Grpc\Channel('localhost:1', []);
$this->channel2 = new Grpc\Channel('localhost:1', ["abc" => "def"]);
// try to connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel2->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$this->channel1->close();
$this->channel2->close();
}
public function testPersistentChannelSameChannelCredentials()
{
$creds1 = Grpc\ChannelCredentials::createSsl();
$creds2 = Grpc\ChannelCredentials::createSsl();
$this->channel1 = new Grpc\Channel('localhost:1',
["credentials" => $creds1]);
$this->channel2 = new Grpc\Channel('localhost:1',
["credentials" => $creds2]);
// try to connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel2->getConnectivityState();
$this->assertConnecting($state);
$this->channel1->close();
$this->channel2->close();
}
public function testPersistentChannelDifferentChannelCredentials()
{
$creds1 = Grpc\ChannelCredentials::createSsl();
$creds2 = Grpc\ChannelCredentials::createSsl(
file_get_contents(dirname(__FILE__).'/../data/ca.pem'));
$this->channel1 = new Grpc\Channel('localhost:1',
["credentials" => $creds1]);
$this->channel2 = new Grpc\Channel('localhost:1',
["credentials" => $creds2]);
// try to connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel2->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$this->channel1->close();
$this->channel2->close();
}
public function testPersistentChannelSameChannelCredentialsRootCerts()
{
$creds1 = Grpc\ChannelCredentials::createSsl(
file_get_contents(dirname(__FILE__).'/../data/ca.pem'));
$creds2 = Grpc\ChannelCredentials::createSsl(
file_get_contents(dirname(__FILE__).'/../data/ca.pem'));
$this->channel1 = new Grpc\Channel('localhost:1',
["credentials" => $creds1]);
$this->channel2 = new Grpc\Channel('localhost:1',
["credentials" => $creds2]);
// try to connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel2->getConnectivityState();
$this->assertConnecting($state);
$this->channel1->close();
$this->channel2->close();
}
public function testPersistentChannelDifferentSecureChannelCredentials()
{
$creds1 = Grpc\ChannelCredentials::createSsl();
$creds2 = Grpc\ChannelCredentials::createInsecure();
$this->channel1 = new Grpc\Channel('localhost:1',
["credentials" => $creds1]);
$this->channel2 = new Grpc\Channel('localhost:1',
["credentials" => $creds2]);
// try to connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel2->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$this->channel1->close();
$this->channel2->close();
}
/**
* @expectedException RuntimeException
*/
public function testPersistentChannelSharedChannelClose()
{
// same underlying channel
$this->channel1 = new Grpc\Channel('localhost:1', []);
$this->channel2 = new Grpc\Channel('localhost:1', []);
// close channel1
$this->channel1->close();
// channel2 is now in SHUTDOWN state
$state = $this->channel2->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_FATAL_FAILURE, $state);
// calling it again will result in an exception because the
// channel is already closed
$state = $this->channel2->getConnectivityState();
}
public function testPersistentChannelCreateAfterClose()
{
$this->channel1 = new Grpc\Channel('localhost:1', []);
$this->channel1->close();
$this->channel2 = new Grpc\Channel('localhost:1', []);
$state = $this->channel2->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$this->channel2->close();
}
public function testPersistentChannelSharedMoreThanTwo()
{
$this->channel1 = new Grpc\Channel('localhost:1', []);
$this->channel2 = new Grpc\Channel('localhost:1', []);
$this->channel3 = new Grpc\Channel('localhost:1', []);
// try to connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
// all 3 channels should be in CONNECTING state
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel2->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel3->getConnectivityState();
$this->assertConnecting($state);
$this->channel1->close();
}
public function callbackFunc($context)
{
return [];
}
public function callbackFunc2($context)
{
return ["k1" => "v1"];
}
public function testPersistentChannelWithCallCredentials()
{
$creds = Grpc\ChannelCredentials::createSsl();
$callCreds = Grpc\CallCredentials::createFromPlugin(
[$this, 'callbackFunc']);
$credsWithCallCreds = Grpc\ChannelCredentials::createComposite(
$creds, $callCreds);
// If a ChannelCredentials object is composed with a
// CallCredentials object, the underlying grpc channel will
// always be created new and NOT persisted.
$this->channel1 = new Grpc\Channel('localhost:1',
["credentials" =>
$credsWithCallCreds]);
$this->channel2 = new Grpc\Channel('localhost:1',
["credentials" =>
$credsWithCallCreds]);
// try to connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel2->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$this->channel1->close();
$this->channel2->close();
}
public function testPersistentChannelWithDifferentCallCredentials()
{
$callCreds1 = Grpc\CallCredentials::createFromPlugin(
[$this, 'callbackFunc']);
$callCreds2 = Grpc\CallCredentials::createFromPlugin(
[$this, 'callbackFunc2']);
$creds1 = Grpc\ChannelCredentials::createSsl();
$creds2 = Grpc\ChannelCredentials::createComposite(
$creds1, $callCreds1);
$creds3 = Grpc\ChannelCredentials::createComposite(
$creds1, $callCreds2);
// Similar to the test above, anytime a ChannelCredentials
// object is composed with a CallCredentials object, the
// underlying grpc channel will always be separate and not
// persisted
$this->channel1 = new Grpc\Channel('localhost:1',
["credentials" => $creds1]);
$this->channel2 = new Grpc\Channel('localhost:1',
["credentials" => $creds2]);
$this->channel3 = new Grpc\Channel('localhost:1',
["credentials" => $creds3]);
// try to connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel2->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$state = $this->channel3->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$this->channel1->close();
$this->channel2->close();
$this->channel3->close();
}
public function testPersistentChannelForceNew()
{
$this->channel1 = new Grpc\Channel('localhost:1', []);
// even though all the channel params are the same, channel2
// has a new and different underlying channel
$this->channel2 = new Grpc\Channel('localhost:1',
["force_new" => true]);
// try to connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel2->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
// any dangling old connection to the same host must be
// manually closed
$this->channel1->close();
$this->channel2->close();
}
public function testPersistentChannelForceNewOldChannelIdle()
{
$this->channel1 = new Grpc\Channel('localhost:1', []);
$this->channel2 = new Grpc\Channel('localhost:1',
["force_new" => true]);
$this->channel3 = new Grpc\Channel('localhost:1', []);
// try to connect on channel2
$state = $this->channel2->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel2);
$state = $this->channel1->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$state = $this->channel2->getConnectivityState();
$this->assertConnecting($state);
$state = $this->channel3->getConnectivityState();
$this->assertConnecting($state);
$this->channel1->close();
$this->channel2->close();
}
public function testPersistentChannelForceNewOldChannelClose()
{
$this->channel1 = new Grpc\Channel('localhost:1', []);
$this->channel2 = new Grpc\Channel('localhost:1',
["force_new" => true]);
$this->channel3 = new Grpc\Channel('localhost:1', []);
$this->channel1->close();
$state = $this->channel2->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$state = $this->channel3->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
$this->channel2->close();
$this->channel3->close();
}
public function testPersistentChannelForceNewNewChannelClose()
{
$this->channel1 = new Grpc\Channel('localhost:1', []);
$this->channel2 = new Grpc\Channel('localhost:1',
["force_new" => true]);
$this->channel3 = new Grpc\Channel('localhost:1', []);
$this->channel2->close();
$state = $this->channel1->getConnectivityState();
$this->assertEquals(GRPC\CHANNEL_IDLE, $state);
// can still connect on channel1
$state = $this->channel1->getConnectivityState(true);
$this->waitUntilNotIdle($this->channel1);
$state = $this->channel1->getConnectivityState();
$this->assertConnecting($state);
$this->channel1->close();
}
} }

@ -28,8 +28,7 @@ class EndToEndTest extends PHPUnit_Framework_TestCase
public function tearDown() public function tearDown()
{ {
unset($this->channel); $this->channel->close();
unset($this->server);
} }
public function testSimpleRequestBody() public function testSimpleRequestBody()
@ -516,7 +515,7 @@ class EndToEndTest extends PHPUnit_Framework_TestCase
$this->assertTrue($idle_state == Grpc\CHANNEL_IDLE); $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
$now = Grpc\Timeval::now(); $now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(500000); // should timeout $delta = new Grpc\Timeval(50000); // should timeout
$deadline = $now->add($delta); $deadline = $now->add($delta);
$this->assertFalse($this->channel->watchConnectivityState( $this->assertFalse($this->channel->watchConnectivityState(
@ -545,7 +544,7 @@ class EndToEndTest extends PHPUnit_Framework_TestCase
$this->assertTrue($idle_state == Grpc\CHANNEL_IDLE); $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
$now = Grpc\Timeval::now(); $now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(100000); $delta = new Grpc\Timeval(50000);
$deadline = $now->add($delta); $deadline = $now->add($delta);
$this->assertFalse($this->channel->watchConnectivityState( $this->assertFalse($this->channel->watchConnectivityState(

@ -43,8 +43,7 @@ class SecureEndToEndTest extends PHPUnit_Framework_TestCase
public function tearDown() public function tearDown()
{ {
unset($this->channel); $this->channel->close();
unset($this->server);
} }
public function testSimpleRequestBody() public function testSimpleRequestBody()

@ -92,6 +92,9 @@ CORE_SOURCE_FILES = [
'src/core/lib/iomgr/ev_windows.c', 'src/core/lib/iomgr/ev_windows.c',
'src/core/lib/iomgr/exec_ctx.c', 'src/core/lib/iomgr/exec_ctx.c',
'src/core/lib/iomgr/executor.c', 'src/core/lib/iomgr/executor.c',
'src/core/lib/iomgr/gethostname_fallback.c',
'src/core/lib/iomgr/gethostname_host_name_max.c',
'src/core/lib/iomgr/gethostname_sysconf.c',
'src/core/lib/iomgr/iocp_windows.c', 'src/core/lib/iomgr/iocp_windows.c',
'src/core/lib/iomgr/iomgr.c', 'src/core/lib/iomgr/iomgr.c',
'src/core/lib/iomgr/iomgr_posix.c', 'src/core/lib/iomgr/iomgr_posix.c',

@ -15,11 +15,284 @@
import abc import abc
from google.protobuf import descriptor
import six import six
import grpc import grpc
class UnaryUnaryChannelRpc(six.with_metaclass(abc.ABCMeta)):
"""Fixture for a unary-unary RPC invoked by a system under test.
Enables users to "play server" for the RPC.
"""
@abc.abstractmethod
def send_initial_metadata(self, initial_metadata):
"""Sends the RPC's initial metadata to the system under test.
Args:
initial_metadata: The RPC's initial metadata to be "sent" to
the system under test.
"""
raise NotImplementedError()
@abc.abstractmethod
def cancelled(self):
"""Blocks until the system under test has cancelled the RPC."""
raise NotImplementedError()
@abc.abstractmethod
def terminate(self, response, trailing_metadata, code, details):
"""Terminates the RPC.
Args:
response: The response for the RPC.
trailing_metadata: The RPC's trailing metadata.
code: The RPC's status code.
details: The RPC's status details.
"""
raise NotImplementedError()
class UnaryStreamChannelRpc(six.with_metaclass(abc.ABCMeta)):
"""Fixture for a unary-stream RPC invoked by a system under test.
Enables users to "play server" for the RPC.
"""
@abc.abstractmethod
def send_initial_metadata(self, initial_metadata):
"""Sends the RPC's initial metadata to the system under test.
Args:
initial_metadata: The RPC's initial metadata to be "sent" to
the system under test.
"""
raise NotImplementedError()
@abc.abstractmethod
def send_response(self, response):
"""Sends a response to the system under test.
Args:
response: A response message to be "sent" to the system under test.
"""
raise NotImplementedError()
@abc.abstractmethod
def cancelled(self):
"""Blocks until the system under test has cancelled the RPC."""
raise NotImplementedError()
@abc.abstractmethod
def terminate(self, trailing_metadata, code, details):
"""Terminates the RPC.
Args:
trailing_metadata: The RPC's trailing metadata.
code: The RPC's status code.
details: The RPC's status details.
"""
raise NotImplementedError()
class StreamUnaryChannelRpc(six.with_metaclass(abc.ABCMeta)):
"""Fixture for a stream-unary RPC invoked by a system under test.
Enables users to "play server" for the RPC.
"""
@abc.abstractmethod
def send_initial_metadata(self, initial_metadata):
"""Sends the RPC's initial metadata to the system under test.
Args:
initial_metadata: The RPC's initial metadata to be "sent" to
the system under test.
"""
raise NotImplementedError()
@abc.abstractmethod
def take_request(self):
"""Draws one of the requests added to the RPC by the system under test.
This method blocks until the system under test has added to the RPC
the request to be returned.
Successive calls to this method return requests in the same order in
which the system under test added them to the RPC.
Returns:
A request message added to the RPC by the system under test.
"""
raise NotImplementedError()
@abc.abstractmethod
def requests_closed(self):
"""Blocks until the system under test has closed the request stream."""
raise NotImplementedError()
@abc.abstractmethod
def cancelled(self):
"""Blocks until the system under test has cancelled the RPC."""
raise NotImplementedError()
@abc.abstractmethod
def terminate(self, response, trailing_metadata, code, details):
"""Terminates the RPC.
Args:
response: The response for the RPC.
trailing_metadata: The RPC's trailing metadata.
code: The RPC's status code.
details: The RPC's status details.
"""
raise NotImplementedError()
class StreamStreamChannelRpc(six.with_metaclass(abc.ABCMeta)):
"""Fixture for a stream-stream RPC invoked by a system under test.
Enables users to "play server" for the RPC.
"""
@abc.abstractmethod
def send_initial_metadata(self, initial_metadata):
"""Sends the RPC's initial metadata to the system under test.
Args:
initial_metadata: The RPC's initial metadata to be "sent" to the
system under test.
"""
raise NotImplementedError()
@abc.abstractmethod
def take_request(self):
"""Draws one of the requests added to the RPC by the system under test.
This method blocks until the system under test has added to the RPC
the request to be returned.
Successive calls to this method return requests in the same order in
which the system under test added them to the RPC.
Returns:
A request message added to the RPC by the system under test.
"""
raise NotImplementedError()
@abc.abstractmethod
def send_response(self, response):
"""Sends a response to the system under test.
Args:
response: A response messages to be "sent" to the system under test.
"""
raise NotImplementedError()
@abc.abstractmethod
def requests_closed(self):
"""Blocks until the system under test has closed the request stream."""
raise NotImplementedError()
@abc.abstractmethod
def cancelled(self):
"""Blocks until the system under test has cancelled the RPC."""
raise NotImplementedError()
@abc.abstractmethod
def terminate(self, trailing_metadata, code, details):
"""Terminates the RPC.
Args:
trailing_metadata: The RPC's trailing metadata.
code: The RPC's status code.
details: The RPC's status details.
"""
raise NotImplementedError()
class Channel(six.with_metaclass(abc.ABCMeta), grpc.Channel):
"""A grpc.Channel double with which to test a system that invokes RPCs."""
@abc.abstractmethod
def take_unary_unary(self, method_descriptor):
"""Draws an RPC currently being made by the system under test.
If the given descriptor does not identify any RPC currently being made
by the system under test, this method blocks until the system under
test invokes such an RPC.
Args:
method_descriptor: A descriptor.MethodDescriptor describing a
unary-unary RPC method.
Returns:
A (invocation_metadata, request, unary_unary_channel_rpc) tuple of
the RPC's invocation metadata, its request, and a
UnaryUnaryChannelRpc with which to "play server" for the RPC.
"""
raise NotImplementedError()
@abc.abstractmethod
def take_unary_stream(self, method_descriptor):
"""Draws an RPC currently being made by the system under test.
If the given descriptor does not identify any RPC currently being made
by the system under test, this method blocks until the system under
test invokes such an RPC.
Args:
method_descriptor: A descriptor.MethodDescriptor describing a
unary-stream RPC method.
Returns:
A (invocation_metadata, request, unary_stream_channel_rpc) tuple of
the RPC's invocation metadata, its request, and a
UnaryStreamChannelRpc with which to "play server" for the RPC.
"""
raise NotImplementedError()
@abc.abstractmethod
def take_stream_unary(self, method_descriptor):
"""Draws an RPC currently being made by the system under test.
If the given descriptor does not identify any RPC currently being made
by the system under test, this method blocks until the system under
test invokes such an RPC.
Args:
method_descriptor: A descriptor.MethodDescriptor describing a
stream-unary RPC method.
Returns:
A (invocation_metadata, stream_unary_channel_rpc) tuple of the RPC's
invocation metadata and a StreamUnaryChannelRpc with which to "play
server" for the RPC.
"""
raise NotImplementedError()
@abc.abstractmethod
def take_stream_stream(self, method_descriptor):
"""Draws an RPC currently being made by the system under test.
If the given descriptor does not identify any RPC currently being made
by the system under test, this method blocks until the system under
test invokes such an RPC.
Args:
method_descriptor: A descriptor.MethodDescriptor describing a
stream-stream RPC method.
Returns:
A (invocation_metadata, stream_stream_channel_rpc) tuple of the RPC's
invocation metadata and a StreamStreamChannelRpc with which to
"play server" for the RPC.
"""
raise NotImplementedError()
class Time(six.with_metaclass(abc.ABCMeta)): class Time(six.with_metaclass(abc.ABCMeta)):
"""A simulation of time. """A simulation of time.
@ -117,3 +390,19 @@ def strict_fake_time(now):
""" """
from grpc_testing import _time from grpc_testing import _time
return _time.StrictFakeTime(now) return _time.StrictFakeTime(now)
def channel(service_descriptors, time):
"""Creates a Channel for use in tests of a gRPC Python-using system.
Args:
service_descriptors: An iterable of descriptor.ServiceDescriptors
describing the RPCs that will be made on the returned Channel by the
system under test.
time: A Time to be used for tests.
Returns:
A Channel for use in tests.
"""
from grpc_testing import _channel
return _channel.testing_channel(service_descriptors, time)

@ -0,0 +1,23 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from grpc_testing._channel import _channel
from grpc_testing._channel import _channel_state
# descriptors is reserved for later use.
# pylint: disable=unused-argument
def testing_channel(descriptors, time):
return _channel.TestingChannel(time, _channel_state.State())
# pylint: enable=unused-argument

@ -0,0 +1,62 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import grpc_testing
from grpc_testing._channel import _channel_rpc
from grpc_testing._channel import _multi_callable
# All serializer and deserializer parameters are not (yet) used by this
# test infrastructure.
# pylint: disable=unused-argument
class TestingChannel(grpc_testing.Channel):
def __init__(self, time, state):
self._time = time
self._state = state
def subscribe(self, callback, try_to_connect=False):
raise NotImplementedError()
def unsubscribe(self, callback):
raise NotImplementedError()
def unary_unary(
self, method, request_serializer=None, response_deserializer=None):
return _multi_callable.UnaryUnary(method, self._state)
def unary_stream(
self, method, request_serializer=None, response_deserializer=None):
return _multi_callable.UnaryStream(method, self._state)
def stream_unary(
self, method, request_serializer=None, response_deserializer=None):
return _multi_callable.StreamUnary(method, self._state)
def stream_stream(
self, method, request_serializer=None, response_deserializer=None):
return _multi_callable.StreamStream(method, self._state)
def take_unary_unary(self, method_descriptor):
return _channel_rpc.unary_unary(self._state, method_descriptor)
def take_unary_stream(self, method_descriptor):
return _channel_rpc.unary_stream(self._state, method_descriptor)
def take_stream_unary(self, method_descriptor):
return _channel_rpc.stream_unary(self._state, method_descriptor)
def take_stream_stream(self, method_descriptor):
return _channel_rpc.stream_stream(self._state, method_descriptor)
# pylint: enable=unused-argument

@ -0,0 +1,119 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import grpc_testing
class _UnaryUnary(grpc_testing.UnaryUnaryChannelRpc):
def __init__(self, rpc_state):
self._rpc_state = rpc_state
def send_initial_metadata(self, initial_metadata):
self._rpc_state.send_initial_metadata(initial_metadata)
def cancelled(self):
self._rpc_state.cancelled()
def terminate(self, response, trailing_metadata, code, details):
self._rpc_state.terminate_with_response(
response, trailing_metadata, code, details)
class _UnaryStream(grpc_testing.UnaryStreamChannelRpc):
def __init__(self, rpc_state):
self._rpc_state = rpc_state
def send_initial_metadata(self, initial_metadata):
self._rpc_state.send_initial_metadata(initial_metadata)
def send_response(self, response):
self._rpc_state.send_response(response)
def cancelled(self):
self._rpc_state.cancelled()
def terminate(self, trailing_metadata, code, details):
self._rpc_state.terminate(trailing_metadata, code, details)
class _StreamUnary(grpc_testing.StreamUnaryChannelRpc):
def __init__(self, rpc_state):
self._rpc_state = rpc_state
def send_initial_metadata(self, initial_metadata):
self._rpc_state.send_initial_metadata(initial_metadata)
def take_request(self):
return self._rpc_state.take_request()
def requests_closed(self):
return self._rpc_state.requests_closed()
def cancelled(self):
self._rpc_state.cancelled()
def terminate(self, response, trailing_metadata, code, details):
self._rpc_state.terminate_with_response(
response, trailing_metadata, code, details)
class _StreamStream(grpc_testing.StreamStreamChannelRpc):
def __init__(self, rpc_state):
self._rpc_state = rpc_state
def send_initial_metadata(self, initial_metadata):
self._rpc_state.send_initial_metadata(initial_metadata)
def take_request(self):
return self._rpc_state.take_request()
def send_response(self, response):
self._rpc_state.send_response(response)
def requests_closed(self):
return self._rpc_state.requests_closed()
def cancelled(self):
self._rpc_state.cancelled()
def terminate(self, trailing_metadata, code, details):
self._rpc_state.terminate(trailing_metadata, code, details)
def unary_unary(channel_state, method_descriptor):
rpc_state = channel_state.take_rpc_state(method_descriptor)
invocation_metadata, request = (
rpc_state.take_invocation_metadata_and_request())
return invocation_metadata, request, _UnaryUnary(rpc_state)
def unary_stream(channel_state, method_descriptor):
rpc_state = channel_state.take_rpc_state(method_descriptor)
invocation_metadata, request = (
rpc_state.take_invocation_metadata_and_request())
return invocation_metadata, request, _UnaryStream(rpc_state)
def stream_unary(channel_state, method_descriptor):
rpc_state = channel_state.take_rpc_state(method_descriptor)
return rpc_state.take_invocation_metadata(), _StreamUnary(rpc_state)
def stream_stream(channel_state, method_descriptor):
rpc_state = channel_state.take_rpc_state(method_descriptor)
return rpc_state.take_invocation_metadata(), _StreamStream(rpc_state)

@ -0,0 +1,48 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import collections
import threading
from grpc_testing import _common
from grpc_testing._channel import _rpc_state
class State(_common.ChannelHandler):
def __init__(self):
self._condition = threading.Condition()
self._rpc_states = collections.defaultdict(list)
def invoke_rpc(
self, method_full_rpc_name, invocation_metadata, requests,
requests_closed, timeout):
rpc_state = _rpc_state.State(
invocation_metadata, requests, requests_closed)
with self._condition:
self._rpc_states[method_full_rpc_name].append(rpc_state)
self._condition.notify_all()
return rpc_state
def take_rpc_state(self, method_descriptor):
method_full_rpc_name = '/{}/{}'.format(
method_descriptor.containing_service.full_name,
method_descriptor.name)
with self._condition:
while True:
method_rpc_states = self._rpc_states[method_full_rpc_name]
if method_rpc_states:
return method_rpc_states.pop(0)
else:
self._condition.wait()

@ -0,0 +1,322 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import threading
import grpc
_NOT_YET_OBSERVED = object()
def _cancel(handler):
return handler.cancel(grpc.StatusCode.CANCELLED, 'Locally cancelled!')
def _is_active(handler):
return handler.is_active()
def _time_remaining(unused_handler):
raise NotImplementedError()
def _add_callback(handler, callback):
return handler.add_callback(callback)
def _initial_metadata(handler):
return handler.initial_metadata()
def _trailing_metadata(handler):
trailing_metadata, unused_code, unused_details = handler.termination()
return trailing_metadata
def _code(handler):
unused_trailing_metadata, code, unused_details = handler.termination()
return code
def _details(handler):
unused_trailing_metadata, unused_code, details = handler.termination()
return details
class _Call(grpc.Call):
def __init__(self, handler):
self._handler = handler
def cancel(self):
_cancel(self._handler)
def is_active(self):
return _is_active(self._handler)
def time_remaining(self):
return _time_remaining(self._handler)
def add_callback(self, callback):
return _add_callback(self._handler, callback)
def initial_metadata(self):
return _initial_metadata(self._handler)
def trailing_metadata(self):
return _trailing_metadata(self._handler)
def code(self):
return _code(self._handler)
def details(self):
return _details(self._handler)
class _RpcErrorCall(grpc.RpcError, grpc.Call):
def __init__(self, handler):
self._handler = handler
def cancel(self):
_cancel(self._handler)
def is_active(self):
return _is_active(self._handler)
def time_remaining(self):
return _time_remaining(self._handler)
def add_callback(self, callback):
return _add_callback(self._handler, callback)
def initial_metadata(self):
return _initial_metadata(self._handler)
def trailing_metadata(self):
return _trailing_metadata(self._handler)
def code(self):
return _code(self._handler)
def details(self):
return _details(self._handler)
def _next(handler):
read = handler.take_response()
if read.code is None:
return read.response
elif read.code is grpc.StatusCode.OK:
raise StopIteration()
else:
raise _RpcErrorCall(handler)
class _HandlerExtras(object):
def __init__(self):
self.condition = threading.Condition()
self.unary_response = _NOT_YET_OBSERVED
self.cancelled = False
def _with_extras_cancel(handler, extras):
with extras.condition:
if handler.cancel(grpc.StatusCode.CANCELLED, 'Locally cancelled!'):
extras.cancelled = True
return True
else:
return False
def _extras_without_cancelled(extras):
with extras.condition:
return extras.cancelled
def _running(handler):
return handler.is_active()
def _done(handler):
return not handler.is_active()
def _with_extras_unary_response(handler, extras):
with extras.condition:
if extras.unary_response is _NOT_YET_OBSERVED:
read = handler.take_response()
if read.code is None:
extras.unary_response = read.response
return read.response
else:
raise _RpcErrorCall(handler)
else:
return extras.unary_response
def _exception(unused_handler):
raise NotImplementedError('TODO!')
def _traceback(unused_handler):
raise NotImplementedError('TODO!')
def _add_done_callback(handler, callback, future):
adapted_callback = lambda: callback(future)
if not handler.add_callback(adapted_callback):
callback(future)
class _FutureCall(grpc.Future, grpc.Call):
def __init__(self, handler, extras):
self._handler = handler
self._extras = extras
def cancel(self):
return _with_extras_cancel(self._handler, self._extras)
def cancelled(self):
return _extras_without_cancelled(self._extras)
def running(self):
return _running(self._handler)
def done(self):
return _done(self._handler)
def result(self):
return _with_extras_unary_response(self._handler, self._extras)
def exception(self):
return _exception(self._handler)
def traceback(self):
return _traceback(self._handler)
def add_done_callback(self, fn):
_add_done_callback(self._handler, fn, self)
def is_active(self):
return _is_active(self._handler)
def time_remaining(self):
return _time_remaining(self._handler)
def add_callback(self, callback):
return _add_callback(self._handler, callback)
def initial_metadata(self):
return _initial_metadata(self._handler)
def trailing_metadata(self):
return _trailing_metadata(self._handler)
def code(self):
return _code(self._handler)
def details(self):
return _details(self._handler)
def consume_requests(request_iterator, handler):
def _consume():
while True:
try:
request = next(request_iterator)
added = handler.add_request(request)
if not added:
break
except StopIteration:
handler.close_requests()
break
except Exception: # pylint: disable=broad-except
details = 'Exception iterating requests!'
logging.exception(details)
handler.cancel(grpc.StatusCode.UNKNOWN, details)
consumption = threading.Thread(target=_consume)
consumption.start()
def blocking_unary_response(handler):
read = handler.take_response()
if read.code is None:
unused_trailing_metadata, code, unused_details = handler.termination()
if code is grpc.StatusCode.OK:
return read.response
else:
raise _RpcErrorCall(handler)
else:
raise _RpcErrorCall(handler)
def blocking_unary_response_with_call(handler):
read = handler.take_response()
if read.code is None:
unused_trailing_metadata, code, unused_details = handler.termination()
if code is grpc.StatusCode.OK:
return read.response, _Call(handler)
else:
raise _RpcErrorCall(handler)
else:
raise _RpcErrorCall(handler)
def future_call(handler):
return _FutureCall(handler, _HandlerExtras())
class ResponseIteratorCall(grpc.Call):
def __init__(self, handler):
self._handler = handler
def __iter__(self):
return self
def __next__(self):
return _next(self._handler)
def next(self):
return _next(self._handler)
def cancel(self):
_cancel(self._handler)
def is_active(self):
return _is_active(self._handler)
def time_remaining(self):
return _time_remaining(self._handler)
def add_callback(self, callback):
return _add_callback(self._handler, callback)
def initial_metadata(self):
return _initial_metadata(self._handler)
def trailing_metadata(self):
return _trailing_metadata(self._handler)
def code(self):
return _code(self._handler)
def details(self):
return _details(self._handler)

@ -0,0 +1,115 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import grpc
from grpc_testing import _common
from grpc_testing._channel import _invocation
# All per-call credentials parameters are unused by this test infrastructure.
# pylint: disable=unused-argument
class UnaryUnary(grpc.UnaryUnaryMultiCallable):
def __init__(self, method_full_rpc_name, channel_handler):
self._method_full_rpc_name = method_full_rpc_name
self._channel_handler = channel_handler
def __call__(self, request, timeout=None, metadata=None, credentials=None):
rpc_handler = self._channel_handler.invoke_rpc(
self._method_full_rpc_name, _common.fuss_with_metadata(metadata),
[request], True, timeout)
return _invocation.blocking_unary_response(rpc_handler)
def with_call(self, request, timeout=None, metadata=None, credentials=None):
rpc_handler = self._channel_handler.invoke_rpc(
self._method_full_rpc_name, _common.fuss_with_metadata(metadata),
[request], True, timeout)
return _invocation.blocking_unary_response_with_call(rpc_handler)
def future(self, request, timeout=None, metadata=None, credentials=None):
rpc_handler = self._channel_handler.invoke_rpc(
self._method_full_rpc_name, _common.fuss_with_metadata(metadata),
[request], True, timeout)
return _invocation.future_call(rpc_handler)
class UnaryStream(grpc.StreamStreamMultiCallable):
def __init__(self, method_full_rpc_name, channel_handler):
self._method_full_rpc_name = method_full_rpc_name
self._channel_handler = channel_handler
def __call__(self, request, timeout=None, metadata=None, credentials=None):
rpc_handler = self._channel_handler.invoke_rpc(
self._method_full_rpc_name,
_common.fuss_with_metadata(metadata), [request], True, timeout)
return _invocation.ResponseIteratorCall(rpc_handler)
class StreamUnary(grpc.StreamUnaryMultiCallable):
def __init__(self, method_full_rpc_name, channel_handler):
self._method_full_rpc_name = method_full_rpc_name
self._channel_handler = channel_handler
def __call__(self,
request_iterator,
timeout=None,
metadata=None,
credentials=None):
rpc_handler = self._channel_handler.invoke_rpc(
self._method_full_rpc_name,
_common.fuss_with_metadata(metadata), [], False, timeout)
_invocation.consume_requests(request_iterator, rpc_handler)
return _invocation.blocking_unary_response(rpc_handler)
def with_call(self,
request_iterator,
timeout=None,
metadata=None,
credentials=None):
rpc_handler = self._channel_handler.invoke_rpc(
self._method_full_rpc_name,
_common.fuss_with_metadata(metadata), [], False, timeout)
_invocation.consume_requests(request_iterator, rpc_handler)
return _invocation.blocking_unary_response_with_call(rpc_handler)
def future(self,
request_iterator,
timeout=None,
metadata=None,
credentials=None):
rpc_handler = self._channel_handler.invoke_rpc(
self._method_full_rpc_name,
_common.fuss_with_metadata(metadata), [], False, timeout)
_invocation.consume_requests(request_iterator, rpc_handler)
return _invocation.future_call(rpc_handler)
class StreamStream(grpc.StreamStreamMultiCallable):
def __init__(self, method_full_rpc_name, channel_handler):
self._method_full_rpc_name = method_full_rpc_name
self._channel_handler = channel_handler
def __call__(self,
request_iterator,
timeout=None,
metadata=None,
credentials=None):
rpc_handler = self._channel_handler.invoke_rpc(
self._method_full_rpc_name,
_common.fuss_with_metadata(metadata), [], False, timeout)
_invocation.consume_requests(request_iterator, rpc_handler)
return _invocation.ResponseIteratorCall(rpc_handler)
# pylint: enable=unused-argument

@ -0,0 +1,193 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import threading
import grpc
from grpc_testing import _common
class State(_common.ChannelRpcHandler):
def __init__(self, invocation_metadata, requests, requests_closed):
self._condition = threading.Condition()
self._invocation_metadata = invocation_metadata
self._requests = requests
self._requests_closed = requests_closed
self._initial_metadata = None
self._responses = []
self._trailing_metadata = None
self._code = None
self._details = None
def initial_metadata(self):
with self._condition:
while True:
if self._initial_metadata is None:
if self._code is None:
self._condition.wait()
else:
return _common.FUSSED_EMPTY_METADATA
else:
return self._initial_metadata
def add_request(self, request):
with self._condition:
if self._code is None and not self._requests_closed:
self._requests.append(request)
self._condition.notify_all()
return True
else:
return False
def close_requests(self):
with self._condition:
if self._code is None and not self._requests_closed:
self._requests_closed = True
self._condition.notify_all()
def take_response(self):
with self._condition:
while True:
if self._code is grpc.StatusCode.OK:
if self._responses:
response = self._responses.pop(0)
return _common.ChannelRpcRead(
response, None, None, None)
else:
return _common.ChannelRpcRead(
None, self._trailing_metadata,
grpc.StatusCode.OK, self._details)
elif self._code is None:
if self._responses:
response = self._responses.pop(0)
return _common.ChannelRpcRead(
response, None, None, None)
else:
self._condition.wait()
else:
return _common.ChannelRpcRead(
None, self._trailing_metadata, self._code,
self._details)
def termination(self):
with self._condition:
while True:
if self._code is None:
self._condition.wait()
else:
return self._trailing_metadata, self._code, self._details
def cancel(self, code, details):
with self._condition:
if self._code is None:
if self._initial_metadata is None:
self._initial_metadata = _common.FUSSED_EMPTY_METADATA
self._trailing_metadata = _common.FUSSED_EMPTY_METADATA
self._code = code
self._details = details
self._condition.notify_all()
return True
else:
return False
def take_invocation_metadata(self):
with self._condition:
if self._invocation_metadata is None:
raise ValueError('Expected invocation metadata!')
else:
invocation_metadata = self._invocation_metadata
self._invocation_metadata = None
return invocation_metadata
def take_invocation_metadata_and_request(self):
with self._condition:
if self._invocation_metadata is None:
raise ValueError('Expected invocation metadata!')
elif not self._requests:
raise ValueError('Expected at least one request!')
else:
invocation_metadata = self._invocation_metadata
self._invocation_metadata = None
return invocation_metadata, self._requests.pop(0)
def send_initial_metadata(self, initial_metadata):
with self._condition:
self._initial_metadata = _common.fuss_with_metadata(
initial_metadata)
self._condition.notify_all()
def take_request(self):
with self._condition:
while True:
if self._requests:
return self._requests.pop(0)
else:
self._condition.wait()
def requests_closed(self):
with self._condition:
while True:
if self._requests_closed:
return
else:
self._condition.wait()
def send_response(self, response):
with self._condition:
if self._code is None:
self._responses.append(response)
self._condition.notify_all()
def terminate_with_response(
self, response, trailing_metadata, code, details):
with self._condition:
if self._initial_metadata is None:
self._initial_metadata = _common.FUSSED_EMPTY_METADATA
self._responses.append(response)
self._trailing_metadata = _common.fuss_with_metadata(
trailing_metadata)
self._code = code
self._details = details
self._condition.notify_all()
def terminate(self, trailing_metadata, code, details):
with self._condition:
if self._initial_metadata is None:
self._initial_metadata = _common.FUSSED_EMPTY_METADATA
self._trailing_metadata = _common.fuss_with_metadata(
trailing_metadata)
self._code = code
self._details = details
self._condition.notify_all()
def cancelled(self):
with self._condition:
while True:
if self._code is grpc.StatusCode.CANCELLED:
return
elif self._code is None:
self._condition.wait()
else:
raise ValueError(
'Status code unexpectedly {}!'.format(self._code))
def is_active(self):
raise NotImplementedError()
def time_remaining(self):
raise NotImplementedError()
def add_callback(self, callback):
raise NotImplementedError()

@ -0,0 +1,92 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Common interfaces and implementation."""
import abc
import collections
import six
def _fuss(tuplified_metadata):
return tuplified_metadata + (
(
'grpc.metadata_added_by_runtime',
'gRPC is allowed to add metadata in transmission and does so.',
),
)
FUSSED_EMPTY_METADATA = _fuss(())
def fuss_with_metadata(metadata):
if metadata is None:
return FUSSED_EMPTY_METADATA
else:
return _fuss(tuple(metadata))
class ChannelRpcRead(
collections.namedtuple(
'ChannelRpcRead',
('response', 'trailing_metadata', 'code', 'details',))):
pass
class ChannelRpcHandler(six.with_metaclass(abc.ABCMeta)):
@abc.abstractmethod
def initial_metadata(self):
raise NotImplementedError()
@abc.abstractmethod
def add_request(self, request):
raise NotImplementedError()
@abc.abstractmethod
def close_requests(self):
raise NotImplementedError()
@abc.abstractmethod
def take_response(self):
raise NotImplementedError()
@abc.abstractmethod
def cancel(self, code, details):
raise NotImplementedError()
@abc.abstractmethod
def termination(self):
raise NotImplementedError()
@abc.abstractmethod
def is_active(self):
raise NotImplementedError()
@abc.abstractmethod
def time_remaining(self):
raise NotImplementedError()
@abc.abstractmethod
def add_callback(self, callback):
raise NotImplementedError()
class ChannelHandler(six.with_metaclass(abc.ABCMeta)):
@abc.abstractmethod
def invoke_rpc(
self, method_full_rpc_name, invocation_metadata, requests,
requests_closed, timeout):
raise NotImplementedError()

@ -68,6 +68,10 @@ PACKAGE_DATA = {
'tests.protoc_plugin.protos.invocation_testing.split_services': [ 'tests.protoc_plugin.protos.invocation_testing.split_services': [
'services.proto', 'services.proto',
], ],
'tests.testing.proto': [
'requests.proto',
'services.proto',
],
'tests.unit': [ 'tests.unit': [
'credentials/ca.pem', 'credentials/ca.pem',
'credentials/server1.key', 'credentials/server1.key',

@ -0,0 +1,36 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""An example gRPC Python-using application's common code elements."""
from tests.testing.proto import requests_pb2
from tests.testing.proto import services_pb2
SERVICE_NAME = 'tests_of_grpc_testing.FirstService'
UNARY_UNARY_METHOD_NAME = 'UnUn'
UNARY_STREAM_METHOD_NAME = 'UnStre'
STREAM_UNARY_METHOD_NAME = 'StreUn'
STREAM_STREAM_METHOD_NAME = 'StreStre'
UNARY_UNARY_REQUEST = requests_pb2.Up(first_up_field=2)
ERRONEOUS_UNARY_UNARY_REQUEST = requests_pb2.Up(first_up_field=3)
UNARY_UNARY_RESPONSE = services_pb2.Down(first_down_field=5)
ERRONEOUS_UNARY_UNARY_RESPONSE = services_pb2.Down(first_down_field=7)
UNARY_STREAM_REQUEST = requests_pb2.Charm(first_charm_field=11)
STREAM_UNARY_REQUEST = requests_pb2.Charm(first_charm_field=13)
STREAM_UNARY_RESPONSE = services_pb2.Strange(first_strange_field=17)
STREAM_STREAM_REQUEST = requests_pb2.Top(first_top_field=19)
STREAM_STREAM_RESPONSE = services_pb2.Bottom(first_bottom_field=23)
TWO_STREAM_STREAM_RESPONSES = (STREAM_STREAM_RESPONSE,) * 2
INFINITE_REQUEST_STREAM_TIMEOUT = 0.2

@ -0,0 +1,33 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import grpc_testing
from tests.testing.proto import requests_pb2
from tests.testing.proto import services_pb2
# TODO(https://github.com/grpc/grpc/issues/11657): Eliminate this entirely.
# TODO(https://github.com/google/protobuf/issues/3452): Eliminate this if/else.
if services_pb2.DESCRIPTOR.services_by_name.get('FirstService') is None:
FIRST_SERVICE = 'Fix protobuf issue 3452!'
FIRST_SERVICE_UNUN = 'Fix protobuf issue 3452!'
FIRST_SERVICE_UNSTRE = 'Fix protobuf issue 3452!'
FIRST_SERVICE_STREUN = 'Fix protobuf issue 3452!'
FIRST_SERVICE_STRESTRE = 'Fix protobuf issue 3452!'
else:
FIRST_SERVICE = services_pb2.DESCRIPTOR.services_by_name['FirstService']
FIRST_SERVICE_UNUN = FIRST_SERVICE.methods_by_name['UnUn']
FIRST_SERVICE_UNSTRE = FIRST_SERVICE.methods_by_name['UnStre']
FIRST_SERVICE_STREUN = FIRST_SERVICE.methods_by_name['StreUn']
FIRST_SERVICE_STRESTRE = FIRST_SERVICE.methods_by_name['StreStre']

@ -0,0 +1,260 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""An example gRPC Python-using client-side application."""
import collections
import enum
import threading
import time
import grpc
from tests.unit.framework.common import test_constants
from tests.testing.proto import requests_pb2
from tests.testing.proto import services_pb2
from tests.testing.proto import services_pb2_grpc
from tests.testing import _application_common
@enum.unique
class Scenario(enum.Enum):
UNARY_UNARY = 'unary unary'
UNARY_STREAM = 'unary stream'
STREAM_UNARY = 'stream unary'
STREAM_STREAM = 'stream stream'
CONCURRENT_STREAM_UNARY = 'concurrent stream unary'
CONCURRENT_STREAM_STREAM = 'concurrent stream stream'
CANCEL_UNARY_UNARY = 'cancel unary unary'
CANCEL_UNARY_STREAM = 'cancel unary stream'
INFINITE_REQUEST_STREAM = 'infinite request stream'
class Outcome(collections.namedtuple('Outcome', ('kind', 'code', 'details'))):
"""Outcome of a client application scenario.
Attributes:
kind: A Kind value describing the overall kind of scenario execution.
code: A grpc.StatusCode value. Only valid if kind is Kind.RPC_ERROR.
details: A status details string. Only valid if kind is Kind.RPC_ERROR.
"""
@enum.unique
class Kind(enum.Enum):
SATISFACTORY = 'satisfactory'
UNSATISFACTORY = 'unsatisfactory'
RPC_ERROR = 'rpc error'
_SATISFACTORY_OUTCOME = Outcome(Outcome.Kind.SATISFACTORY, None, None)
_UNSATISFACTORY_OUTCOME = Outcome(Outcome.Kind.UNSATISFACTORY, None, None)
class _Pipe(object):
def __init__(self):
self._condition = threading.Condition()
self._values = []
self._open = True
def __iter__(self):
return self
def _next(self):
with self._condition:
while True:
if self._values:
return self._values.pop(0)
elif not self._open:
raise StopIteration()
else:
self._condition.wait()
def __next__(self): # (Python 3 Iterator Protocol)
return self._next()
def next(self): # (Python 2 Iterator Protocol)
return self._next()
def add(self, value):
with self._condition:
self._values.append(value)
self._condition.notify_all()
def close(self):
with self._condition:
self._open = False
self._condition.notify_all()
def _run_unary_unary(stub):
response = stub.UnUn(_application_common.UNARY_UNARY_REQUEST)
if _application_common.UNARY_UNARY_RESPONSE == response:
return _SATISFACTORY_OUTCOME
else:
return _UNSATISFACTORY_OUTCOME
def _run_unary_stream(stub):
response_iterator = stub.UnStre(_application_common.UNARY_STREAM_REQUEST)
try:
next(response_iterator)
except StopIteration:
return _SATISFACTORY_OUTCOME
else:
return _UNSATISFACTORY_OUTCOME
def _run_stream_unary(stub):
response, call = stub.StreUn.with_call(
iter((_application_common.STREAM_UNARY_REQUEST,) * 3))
if (_application_common.STREAM_UNARY_RESPONSE == response and
call.code() is grpc.StatusCode.OK):
return _SATISFACTORY_OUTCOME
else:
return _UNSATISFACTORY_OUTCOME
def _run_stream_stream(stub):
request_pipe = _Pipe()
response_iterator = stub.StreStre(iter(request_pipe))
request_pipe.add(_application_common.STREAM_STREAM_REQUEST)
first_responses = next(response_iterator), next(response_iterator),
request_pipe.add(_application_common.STREAM_STREAM_REQUEST)
second_responses = next(response_iterator), next(response_iterator),
request_pipe.close()
try:
next(response_iterator)
except StopIteration:
unexpected_extra_response = False
else:
unexpected_extra_response = True
if (first_responses == _application_common.TWO_STREAM_STREAM_RESPONSES and
second_responses == _application_common.TWO_STREAM_STREAM_RESPONSES
and not unexpected_extra_response):
return _SATISFACTORY_OUTCOME
else:
return _UNSATISFACTORY_OUTCOME
def _run_concurrent_stream_unary(stub):
future_calls = tuple(
stub.StreUn.future(
iter((_application_common.STREAM_UNARY_REQUEST,) * 3))
for _ in range(test_constants.THREAD_CONCURRENCY))
for future_call in future_calls:
if future_call.code() is grpc.StatusCode.OK:
response = future_call.result()
if _application_common.STREAM_UNARY_RESPONSE != response:
return _UNSATISFACTORY_OUTCOME
else:
return _UNSATISFACTORY_OUTCOME
else:
return _SATISFACTORY_OUTCOME
def _run_concurrent_stream_stream(stub):
condition = threading.Condition()
outcomes = [None] * test_constants.RPC_CONCURRENCY
def run_stream_stream(index):
outcome = _run_stream_stream(stub)
with condition:
outcomes[index] = outcome
condition.notify()
for index in range(test_constants.RPC_CONCURRENCY):
thread = threading.Thread(target=run_stream_stream, args=(index,))
thread.start()
with condition:
while True:
if all(outcomes):
for outcome in outcomes:
if outcome.kind is not Outcome.Kind.SATISFACTORY:
return _UNSATISFACTORY_OUTCOME
else:
return _SATISFACTORY_OUTCOME
else:
condition.wait()
def _run_cancel_unary_unary(stub):
response_future_call = stub.UnUn.future(
_application_common.UNARY_UNARY_REQUEST)
initial_metadata = response_future_call.initial_metadata()
cancelled = response_future_call.cancel()
if initial_metadata is not None and cancelled:
return _SATISFACTORY_OUTCOME
else:
return _UNSATISFACTORY_OUTCOME
def _run_infinite_request_stream(stub):
def infinite_request_iterator():
while True:
yield _application_common.STREAM_UNARY_REQUEST
response_future_call = stub.StreUn.future(
infinite_request_iterator(),
timeout=_application_common.INFINITE_REQUEST_STREAM_TIMEOUT)
if response_future_call.code() is grpc.StatusCode.DEADLINE_EXCEEDED:
return _SATISFACTORY_OUTCOME
else:
return _UNSATISFACTORY_OUTCOME
def run(scenario, channel):
stub = services_pb2_grpc.FirstServiceStub(channel)
try:
if scenario is Scenario.UNARY_UNARY:
return _run_unary_unary(stub)
elif scenario is Scenario.UNARY_STREAM:
return _run_unary_stream(stub)
elif scenario is Scenario.STREAM_UNARY:
return _run_stream_unary(stub)
elif scenario is Scenario.STREAM_STREAM:
return _run_stream_stream(stub)
elif scenario is Scenario.CONCURRENT_STREAM_UNARY:
return _run_concurrent_stream_unary(stub)
elif scenario is Scenario.CONCURRENT_STREAM_STREAM:
return _run_concurrent_stream_stream(stub)
elif scenario is Scenario.CANCEL_UNARY_UNARY:
return _run_cancel_unary_unary(stub)
elif scenario is Scenario.INFINITE_REQUEST_STREAM:
return _run_infinite_request_stream(stub)
except grpc.RpcError as rpc_error:
return Outcome(Outcome.Kind.RPC_ERROR,
rpc_error.code(), rpc_error.details())
_IMPLEMENTATIONS = {
Scenario.UNARY_UNARY: _run_unary_unary,
Scenario.UNARY_STREAM: _run_unary_stream,
Scenario.STREAM_UNARY: _run_stream_unary,
Scenario.STREAM_STREAM: _run_stream_stream,
Scenario.CONCURRENT_STREAM_UNARY: _run_concurrent_stream_unary,
Scenario.CONCURRENT_STREAM_STREAM: _run_concurrent_stream_stream,
Scenario.CANCEL_UNARY_UNARY: _run_cancel_unary_unary,
Scenario.INFINITE_REQUEST_STREAM: _run_infinite_request_stream,
}
def run(scenario, channel):
stub = services_pb2_grpc.FirstServiceStub(channel)
try:
return _IMPLEMENTATIONS[scenario](stub)
except grpc.RpcError as rpc_error:
return Outcome(Outcome.Kind.RPC_ERROR,
rpc_error.code(), rpc_error.details())

@ -0,0 +1,306 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from concurrent import futures
import time
import unittest
import grpc
from grpc.framework.foundation import logging_pool
from tests.unit.framework.common import test_constants
import grpc_testing
from tests.testing import _application_common
from tests.testing import _application_testing_common
from tests.testing import _client_application
from tests.testing.proto import requests_pb2
from tests.testing.proto import services_pb2
# TODO(https://github.com/google/protobuf/issues/3452): Drop this skip.
@unittest.skipIf(
services_pb2.DESCRIPTOR.services_by_name.get('FirstService') is None,
'Fix protobuf issue 3452!')
class ClientTest(unittest.TestCase):
def setUp(self):
# In this test the client-side application under test executes in
# a separate thread while we retain use of the test thread to "play
# server".
self._client_execution_thread_pool = logging_pool.pool(1)
self._fake_time = grpc_testing.strict_fake_time(time.time())
self._real_time = grpc_testing.strict_real_time()
self._fake_time_channel = grpc_testing.channel(
services_pb2.DESCRIPTOR.services_by_name.values(), self._fake_time)
self._real_time_channel = grpc_testing.channel(
services_pb2.DESCRIPTOR.services_by_name.values(), self._real_time)
def tearDown(self):
self._client_execution_thread_pool.shutdown(wait=True)
def test_successful_unary_unary(self):
application_future = self._client_execution_thread_pool.submit(
_client_application.run, _client_application.Scenario.UNARY_UNARY,
self._real_time_channel)
invocation_metadata, request, rpc = (
self._real_time_channel.take_unary_unary(
_application_testing_common.FIRST_SERVICE_UNUN))
rpc.send_initial_metadata(())
rpc.terminate(_application_common.UNARY_UNARY_RESPONSE, (),
grpc.StatusCode.OK, '')
application_return_value = application_future.result()
self.assertEqual(_application_common.UNARY_UNARY_REQUEST, request)
self.assertIs(application_return_value.kind,
_client_application.Outcome.Kind.SATISFACTORY)
def test_successful_unary_stream(self):
application_future = self._client_execution_thread_pool.submit(
_client_application.run, _client_application.Scenario.UNARY_STREAM,
self._fake_time_channel)
invocation_metadata, request, rpc = (
self._fake_time_channel.take_unary_stream(
_application_testing_common.FIRST_SERVICE_UNSTRE))
rpc.send_initial_metadata(())
rpc.terminate((), grpc.StatusCode.OK, '')
application_return_value = application_future.result()
self.assertEqual(_application_common.UNARY_STREAM_REQUEST, request)
self.assertIs(application_return_value.kind,
_client_application.Outcome.Kind.SATISFACTORY)
def test_successful_stream_unary(self):
application_future = self._client_execution_thread_pool.submit(
_client_application.run, _client_application.Scenario.STREAM_UNARY,
self._real_time_channel)
invocation_metadata, rpc = self._real_time_channel.take_stream_unary(
_application_testing_common.FIRST_SERVICE_STREUN)
rpc.send_initial_metadata(())
first_request = rpc.take_request()
second_request = rpc.take_request()
third_request = rpc.take_request()
rpc.requests_closed()
rpc.terminate(_application_common.STREAM_UNARY_RESPONSE, (),
grpc.StatusCode.OK, '')
application_return_value = application_future.result()
self.assertEqual(_application_common.STREAM_UNARY_REQUEST,
first_request)
self.assertEqual(_application_common.STREAM_UNARY_REQUEST,
second_request)
self.assertEqual(_application_common.STREAM_UNARY_REQUEST,
third_request)
self.assertIs(application_return_value.kind,
_client_application.Outcome.Kind.SATISFACTORY)
def test_successful_stream_stream(self):
application_future = self._client_execution_thread_pool.submit(
_client_application.run, _client_application.Scenario.STREAM_STREAM,
self._fake_time_channel)
invocation_metadata, rpc = self._fake_time_channel.take_stream_stream(
_application_testing_common.FIRST_SERVICE_STRESTRE)
first_request = rpc.take_request()
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
second_request = rpc.take_request()
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.requests_closed()
rpc.terminate((), grpc.StatusCode.OK, '')
application_return_value = application_future.result()
self.assertEqual(_application_common.STREAM_STREAM_REQUEST,
first_request)
self.assertEqual(_application_common.STREAM_STREAM_REQUEST,
second_request)
self.assertIs(application_return_value.kind,
_client_application.Outcome.Kind.SATISFACTORY)
def test_concurrent_stream_stream(self):
application_future = self._client_execution_thread_pool.submit(
_client_application.run,
_client_application.Scenario.CONCURRENT_STREAM_STREAM,
self._real_time_channel)
rpcs = []
for _ in range(test_constants.RPC_CONCURRENCY):
invocation_metadata, rpc = (
self._real_time_channel.take_stream_stream(
_application_testing_common.FIRST_SERVICE_STRESTRE))
rpcs.append(rpc)
requests = {}
for rpc in rpcs:
requests[rpc] = [rpc.take_request()]
for rpc in rpcs:
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
for rpc in rpcs:
requests[rpc].append(rpc.take_request())
for rpc in rpcs:
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
for rpc in rpcs:
rpc.requests_closed()
for rpc in rpcs:
rpc.terminate((), grpc.StatusCode.OK, '')
application_return_value = application_future.result()
for requests_of_one_rpc in requests.values():
for request in requests_of_one_rpc:
self.assertEqual(_application_common.STREAM_STREAM_REQUEST,
request)
self.assertIs(application_return_value.kind,
_client_application.Outcome.Kind.SATISFACTORY)
def test_cancelled_unary_unary(self):
application_future = self._client_execution_thread_pool.submit(
_client_application.run,
_client_application.Scenario.CANCEL_UNARY_UNARY,
self._fake_time_channel)
invocation_metadata, request, rpc = (
self._fake_time_channel.take_unary_unary(
_application_testing_common.FIRST_SERVICE_UNUN))
rpc.send_initial_metadata(())
rpc.cancelled()
application_return_value = application_future.result()
self.assertEqual(_application_common.UNARY_UNARY_REQUEST, request)
self.assertIs(application_return_value.kind,
_client_application.Outcome.Kind.SATISFACTORY)
def test_status_stream_unary(self):
application_future = self._client_execution_thread_pool.submit(
_client_application.run,
_client_application.Scenario.CONCURRENT_STREAM_UNARY,
self._fake_time_channel)
rpcs = tuple(
self._fake_time_channel.take_stream_unary(
_application_testing_common.FIRST_SERVICE_STREUN)[1]
for _ in range(test_constants.THREAD_CONCURRENCY))
for rpc in rpcs:
rpc.take_request()
rpc.take_request()
rpc.take_request()
rpc.requests_closed()
rpc.send_initial_metadata((
('my_metadata_key', 'My Metadata Value!',),))
for rpc in rpcs[:-1]:
rpc.terminate(_application_common.STREAM_UNARY_RESPONSE, (),
grpc.StatusCode.OK, '')
rpcs[-1].terminate(_application_common.STREAM_UNARY_RESPONSE, (),
grpc.StatusCode.RESOURCE_EXHAUSTED,
'nope; not able to handle all those RPCs!')
application_return_value = application_future.result()
self.assertIs(application_return_value.kind,
_client_application.Outcome.Kind.UNSATISFACTORY)
def test_status_stream_stream(self):
code = grpc.StatusCode.DEADLINE_EXCEEDED
details = 'test deadline exceeded!'
application_future = self._client_execution_thread_pool.submit(
_client_application.run, _client_application.Scenario.STREAM_STREAM,
self._real_time_channel)
invocation_metadata, rpc = self._real_time_channel.take_stream_stream(
_application_testing_common.FIRST_SERVICE_STRESTRE)
first_request = rpc.take_request()
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
second_request = rpc.take_request()
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.requests_closed()
rpc.terminate((), code, details)
application_return_value = application_future.result()
self.assertEqual(_application_common.STREAM_STREAM_REQUEST,
first_request)
self.assertEqual(_application_common.STREAM_STREAM_REQUEST,
second_request)
self.assertIs(application_return_value.kind,
_client_application.Outcome.Kind.RPC_ERROR)
self.assertIs(application_return_value.code, code)
self.assertEqual(application_return_value.details, details)
def test_misbehaving_server_unary_unary(self):
application_future = self._client_execution_thread_pool.submit(
_client_application.run, _client_application.Scenario.UNARY_UNARY,
self._fake_time_channel)
invocation_metadata, request, rpc = (
self._fake_time_channel.take_unary_unary(
_application_testing_common.FIRST_SERVICE_UNUN))
rpc.send_initial_metadata(())
rpc.terminate(_application_common.ERRONEOUS_UNARY_UNARY_RESPONSE, (),
grpc.StatusCode.OK, '')
application_return_value = application_future.result()
self.assertEqual(_application_common.UNARY_UNARY_REQUEST, request)
self.assertIs(application_return_value.kind,
_client_application.Outcome.Kind.UNSATISFACTORY)
def test_misbehaving_server_stream_stream(self):
application_future = self._client_execution_thread_pool.submit(
_client_application.run, _client_application.Scenario.STREAM_STREAM,
self._real_time_channel)
invocation_metadata, rpc = self._real_time_channel.take_stream_stream(
_application_testing_common.FIRST_SERVICE_STRESTRE)
first_request = rpc.take_request()
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
second_request = rpc.take_request()
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE)
rpc.requests_closed()
rpc.terminate((), grpc.StatusCode.OK, '')
application_return_value = application_future.result()
self.assertEqual(_application_common.STREAM_STREAM_REQUEST,
first_request)
self.assertEqual(_application_common.STREAM_STREAM_REQUEST,
second_request)
self.assertIs(application_return_value.kind,
_client_application.Outcome.Kind.UNSATISFACTORY)
def test_infinite_request_stream_real_time(self):
application_future = self._client_execution_thread_pool.submit(
_client_application.run,
_client_application.Scenario.INFINITE_REQUEST_STREAM,
self._real_time_channel)
invocation_metadata, rpc = self._real_time_channel.take_stream_unary(
_application_testing_common.FIRST_SERVICE_STREUN)
rpc.send_initial_metadata(())
first_request = rpc.take_request()
second_request = rpc.take_request()
third_request = rpc.take_request()
self._real_time.sleep_for(
_application_common.INFINITE_REQUEST_STREAM_TIMEOUT)
rpc.terminate(_application_common.STREAM_UNARY_RESPONSE, (),
grpc.StatusCode.DEADLINE_EXCEEDED, '')
application_return_value = application_future.result()
self.assertEqual(_application_common.STREAM_UNARY_REQUEST,
first_request)
self.assertEqual(_application_common.STREAM_UNARY_REQUEST,
second_request)
self.assertEqual(_application_common.STREAM_UNARY_REQUEST,
third_request)
self.assertIs(application_return_value.kind,
_client_application.Outcome.Kind.SATISFACTORY)
if __name__ == '__main__':
unittest.main(verbosity=2)

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

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

@ -0,0 +1,42 @@
// Copyright 2017 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
import "tests/testing/proto/requests.proto";
package tests_of_grpc_testing;
message Down {
int32 first_down_field = 1;
}
message Strange {
int32 first_strange_field = 1;
}
message Bottom {
int32 first_bottom_field = 1;
}
service FirstService {
rpc UnUn(Up) returns (Down);
rpc UnStre(Charm) returns (stream Strange);
rpc StreUn(stream Charm) returns (Strange);
rpc StreStre(stream Top) returns (stream Bottom);
}
service SecondService {
rpc UnStre(Strange) returns (stream Charm);
}

@ -9,6 +9,7 @@
"protoc_plugin._split_definitions_test.SplitSeparateTest", "protoc_plugin._split_definitions_test.SplitSeparateTest",
"protoc_plugin.beta_python_plugin_test.PythonPluginTest", "protoc_plugin.beta_python_plugin_test.PythonPluginTest",
"reflection._reflection_servicer_test.ReflectionServicerTest", "reflection._reflection_servicer_test.ReflectionServicerTest",
"testing._client_test.ClientTest",
"testing._time_test.StrictFakeTimeTest", "testing._time_test.StrictFakeTimeTest",
"testing._time_test.StrictRealTimeTest", "testing._time_test.StrictRealTimeTest",
"unit._api_test.AllTest", "unit._api_test.AllTest",

@ -42,12 +42,9 @@
s.header_dir = name s.header_dir = name
src_dir = 'src/objective-c/GRPCClient' src_dir = 'src/objective-c/GRPCClient'
s.source_files = "#{src_dir}/*.{h,m}", "#{src_dir}/**/*.{h,m}"
s.private_header_files = "#{src_dir}/private/*.h"
s.header_mappings_dir = "#{src_dir}"
s.dependency 'gRPC-Core', version
s.dependency 'gRPC-RxLibrary', version s.dependency 'gRPC-RxLibrary', version
s.default_subspec = 'Main'
# Certificates, to be able to establish TLS connections: # Certificates, to be able to establish TLS connections:
s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] } s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] }
@ -56,4 +53,22 @@
# This is needed by all pods that depend on gRPC-RxLibrary: # This is needed by all pods that depend on gRPC-RxLibrary:
'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES', 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
} }
s.subspec 'Main' do |ss|
ss.header_mappings_dir = "#{src_dir}"
ss.source_files = "#{src_dir}/*.{h,m}", "#{src_dir}/**/*.{h,m}"
ss.exclude_files = "#{src_dir}/GRPCCall+GID.{h,m}"
ss.private_header_files = "#{src_dir}/private/*.h"
ss.dependency 'gRPC-Core', version
end
s.subspec 'GID' do |ss|
ss.header_mappings_dir = "#{src_dir}"
ss.source_files = "#{src_dir}/GRPCCall+GID.{h,m}"
ss.dependency 'Google/SignIn'
end
end end

@ -60,7 +60,8 @@ static void my_resolve_address(grpc_exec_ctx *exec_ctx, const char *addr,
static grpc_ares_request *my_dns_lookup_ares( static grpc_ares_request *my_dns_lookup_ares(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr, grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr,
const char *default_port, grpc_pollset_set *interested_parties, const char *default_port, grpc_pollset_set *interested_parties,
grpc_closure *on_done, grpc_lb_addresses **lb_addrs, bool check_grpclb) { grpc_closure *on_done, grpc_lb_addresses **lb_addrs, bool check_grpclb,
char **service_config_json) {
gpr_mu_lock(&g_mu); gpr_mu_lock(&g_mu);
GPR_ASSERT(0 == strcmp("test", addr)); GPR_ASSERT(0 == strcmp("test", addr));
grpc_error *error = GRPC_ERROR_NONE; grpc_error *error = GRPC_ERROR_NONE;

@ -416,7 +416,8 @@ void my_resolve_address(grpc_exec_ctx *exec_ctx, const char *addr,
grpc_ares_request *my_dns_lookup_ares( grpc_ares_request *my_dns_lookup_ares(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr, grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr,
const char *default_port, grpc_pollset_set *interested_parties, const char *default_port, grpc_pollset_set *interested_parties,
grpc_closure *on_done, grpc_lb_addresses **lb_addrs, bool check_grpclb) { grpc_closure *on_done, grpc_lb_addresses **lb_addrs, bool check_grpclb,
char **service_config_json) {
addr_req *r = gpr_malloc(sizeof(*r)); addr_req *r = gpr_malloc(sizeof(*r));
r->addr = gpr_strdup(addr); r->addr = gpr_strdup(addr);
r->on_done = on_done; r->on_done = on_done;

@ -48,7 +48,8 @@ static void (*iomgr_resolve_address)(grpc_exec_ctx *exec_ctx, const char *addr,
static grpc_ares_request *(*iomgr_dns_lookup_ares)( static grpc_ares_request *(*iomgr_dns_lookup_ares)(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr, grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr,
const char *default_port, grpc_pollset_set *interested_parties, const char *default_port, grpc_pollset_set *interested_parties,
grpc_closure *on_done, grpc_lb_addresses **addresses, bool check_grpclb); grpc_closure *on_done, grpc_lb_addresses **addresses, bool check_grpclb,
char **service_config_json);
static void set_resolve_port(int port) { static void set_resolve_port(int port) {
gpr_mu_lock(&g_mu); gpr_mu_lock(&g_mu);
@ -90,11 +91,12 @@ static void my_resolve_address(grpc_exec_ctx *exec_ctx, const char *addr,
static grpc_ares_request *my_dns_lookup_ares( static grpc_ares_request *my_dns_lookup_ares(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr, grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr,
const char *default_port, grpc_pollset_set *interested_parties, const char *default_port, grpc_pollset_set *interested_parties,
grpc_closure *on_done, grpc_lb_addresses **lb_addrs, bool check_grpclb) { grpc_closure *on_done, grpc_lb_addresses **lb_addrs, bool check_grpclb,
char **service_config_json) {
if (0 != strcmp(addr, "test")) { if (0 != strcmp(addr, "test")) {
return iomgr_dns_lookup_ares(exec_ctx, dns_server, addr, default_port, return iomgr_dns_lookup_ares(exec_ctx, dns_server, addr, default_port,
interested_parties, on_done, lb_addrs, interested_parties, on_done, lb_addrs,
check_grpclb); check_grpclb, service_config_json);
} }
grpc_error *error = GRPC_ERROR_NONE; grpc_error *error = GRPC_ERROR_NONE;

@ -73,6 +73,21 @@ static void test_alarm(void) {
GPR_ASSERT(ev.success == 0); GPR_ASSERT(ev.success == 0);
grpc_alarm_destroy(alarm); grpc_alarm_destroy(alarm);
} }
{
/* alarm_destroy before cq_next */
grpc_event ev;
void *tag = create_test_tag();
grpc_alarm *alarm =
grpc_alarm_create(cc, grpc_timeout_seconds_to_deadline(2), tag);
grpc_alarm_destroy(alarm);
ev = grpc_completion_queue_next(cc, grpc_timeout_seconds_to_deadline(1),
NULL);
GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
GPR_ASSERT(ev.tag == tag);
GPR_ASSERT(ev.success == 0);
}
shutdown_and_destroy(cc); shutdown_and_destroy(cc);
} }

@ -38,7 +38,7 @@ grpc_cc_library(
) )
grpc_cc_library( grpc_cc_library(
name = "grpc_test_util", name = "grpc_test_util_base",
srcs = [ srcs = [
"debugger_macros.c", "debugger_macros.c",
"grpc_profiler.c", "grpc_profiler.c",
@ -68,10 +68,32 @@ grpc_cc_library(
language = "C", language = "C",
deps = [ deps = [
":gpr_test_util", ":gpr_test_util",
"//:grpc_common",
],
)
grpc_cc_library(
name = "grpc_test_util",
srcs = [],
hdrs = [],
language = "C",
deps = [
":grpc_test_util_base",
"//:grpc", "//:grpc",
], ],
) )
grpc_cc_library(
name = "grpc_test_util_unsecure",
srcs = [],
hdrs = [],
language = "C",
deps = [
":grpc_test_util_base",
"//:grpc_unsecure",
],
)
grpc_cc_library( grpc_cc_library(
name = "one_corpus_entry_fuzzer", name = "one_corpus_entry_fuzzer",
srcs = ["one_corpus_entry_fuzzer.c"], srcs = ["one_corpus_entry_fuzzer.c"],

@ -79,7 +79,7 @@ static void chose_port(int port) {
chosen_ports[num_chosen_ports - 1] = port; chosen_ports[num_chosen_ports - 1] = port;
} }
int grpc_pick_unused_port(void) { static int grpc_pick_unused_port_impl(void) {
int port = grpc_pick_port_using_server(); int port = grpc_pick_port_using_server();
if (port != 0) { if (port != 0) {
chose_port(port); chose_port(port);
@ -88,7 +88,7 @@ int grpc_pick_unused_port(void) {
return port; return port;
} }
int grpc_pick_unused_port_or_die(void) { static int grpc_pick_unused_port_or_die_impl(void) {
int port = grpc_pick_unused_port(); int port = grpc_pick_unused_port();
if (port == 0) { if (port == 0) {
fprintf(stderr, fprintf(stderr,
@ -101,6 +101,31 @@ int grpc_pick_unused_port_or_die(void) {
return port; return port;
} }
void grpc_recycle_unused_port(int port) { GPR_ASSERT(free_chosen_port(port)); } static void grpc_recycle_unused_port_impl(int port) {
GPR_ASSERT(free_chosen_port(port));
}
static grpc_pick_port_functions g_pick_port_functions = {
grpc_pick_unused_port_impl, grpc_pick_unused_port_or_die_impl,
grpc_recycle_unused_port_impl};
int grpc_pick_unused_port(void) {
return g_pick_port_functions.pick_unused_port_fn();
}
int grpc_pick_unused_port_or_die(void) {
return g_pick_port_functions.pick_unused_port_or_die_fn();
}
void grpc_recycle_unused_port(int port) {
g_pick_port_functions.recycle_unused_port_fn(port);
}
void grpc_set_pick_port_functions(grpc_pick_port_functions functions) {
GPR_ASSERT(functions.pick_unused_port_fn != NULL);
GPR_ASSERT(functions.pick_unused_port_or_die_fn != NULL);
GPR_ASSERT(functions.recycle_unused_port_fn != NULL);
g_pick_port_functions = functions;
}
#endif /* GRPC_TEST_PICK_PORT */ #endif /* GRPC_TEST_PICK_PORT */

@ -23,6 +23,12 @@
extern "C" { extern "C" {
#endif #endif
typedef struct grpc_pick_port_functions {
int (*pick_unused_port_fn)(void);
int (*pick_unused_port_or_die_fn)(void);
void (*recycle_unused_port_fn)(int port);
} grpc_pick_port_functions;
/* pick a port number that is currently unused by either tcp or udp. return /* pick a port number that is currently unused by either tcp or udp. return
0 on failure. */ 0 on failure. */
int grpc_pick_unused_port(void); int grpc_pick_unused_port(void);
@ -36,6 +42,9 @@ int grpc_pick_unused_port_or_die(void);
* ports back to the server if it is going to allocate a large number. */ * ports back to the server if it is going to allocate a large number. */
void grpc_recycle_unused_port(int port); void grpc_recycle_unused_port(int port);
/** Request the family of pick_port functions in \a functions be used. */
void grpc_set_pick_port_functions(grpc_pick_port_functions functions);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

@ -27,7 +27,7 @@ grpc_cc_test(
name = "alarm_cpp_test", name = "alarm_cpp_test",
srcs = ["alarm_cpp_test.cc"], srcs = ["alarm_cpp_test.cc"],
deps = [ deps = [
"//:grpc++", "//:grpc++_unsecure",
"//test/core/util:gpr_test_util", "//test/core/util:gpr_test_util",
], ],
external_deps = [ external_deps = [

@ -215,7 +215,8 @@ class BalancerServiceImpl : public BalancerService {
{ {
std::unique_lock<std::mutex> lock(mu_); std::unique_lock<std::mutex> lock(mu_);
if (shutdown_) goto done; if (shutdown_) goto done;
serverlist_cond_.wait(lock); serverlist_cond_.wait(lock, [this] { return serverlist_ready_; });
serverlist_ready_ = false;
} }
if (client_load_reporting_interval_seconds_ > 0) { if (client_load_reporting_interval_seconds_ > 0) {
@ -242,6 +243,7 @@ class BalancerServiceImpl : public BalancerService {
.drop_token_counts[drop_token_count.load_balance_token()] += .drop_token_counts[drop_token_count.load_balance_token()] +=
drop_token_count.num_calls(); drop_token_count.num_calls();
} }
load_report_ready_ = true;
load_report_cond_.notify_one(); load_report_cond_.notify_one();
} }
done: done:
@ -285,12 +287,14 @@ class BalancerServiceImpl : public BalancerService {
const ClientStats& WaitForLoadReport() { const ClientStats& WaitForLoadReport() {
std::unique_lock<std::mutex> lock(mu_); std::unique_lock<std::mutex> lock(mu_);
load_report_cond_.wait(lock); load_report_cond_.wait(lock, [this] { return load_report_ready_; });
load_report_ready_ = false;
return client_stats_; return client_stats_;
} }
void NotifyDoneWithServerlists() { void NotifyDoneWithServerlists() {
std::lock_guard<std::mutex> lock(mu_); std::lock_guard<std::mutex> lock(mu_);
serverlist_ready_ = true;
serverlist_cond_.notify_one(); serverlist_cond_.notify_one();
} }
@ -313,7 +317,9 @@ class BalancerServiceImpl : public BalancerService {
std::vector<ResponseDelayPair> responses_and_delays_; std::vector<ResponseDelayPair> responses_and_delays_;
std::mutex mu_; std::mutex mu_;
std::condition_variable load_report_cond_; std::condition_variable load_report_cond_;
bool load_report_ready_ = false;
std::condition_variable serverlist_cond_; std::condition_variable serverlist_cond_;
bool serverlist_ready_ = false;
ClientStats client_stats_; ClientStats client_stats_;
bool shutdown_; bool shutdown_;
}; };

@ -40,9 +40,9 @@ grpc_cc_library(
"helpers.h", "helpers.h",
], ],
deps = [ deps = [
"//:grpc++", "//:grpc++_unsecure",
"//src/proto/grpc/testing:echo_proto", "//src/proto/grpc/testing:echo_proto",
"//test/core/util:grpc_test_util", "//test/core/util:grpc_test_util_unsecure",
], ],
external_deps = [ external_deps = [
"benchmark", "benchmark",

@ -120,7 +120,7 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
BenchmarkService::Stub* stub_; BenchmarkService::Stub* stub_;
CompletionQueue* cq_; CompletionQueue* cq_;
std::unique_ptr<Alarm> alarm_; std::unique_ptr<Alarm> alarm_;
RequestType req_; const RequestType& req_;
ResponseType response_; ResponseType response_;
enum State { INVALID, READY, RESP_DONE }; enum State { INVALID, READY, RESP_DONE };
State next_state_; State next_state_;
@ -415,7 +415,7 @@ class ClientRpcContextStreamingPingPongImpl : public ClientRpcContext {
BenchmarkService::Stub* stub_; BenchmarkService::Stub* stub_;
CompletionQueue* cq_; CompletionQueue* cq_;
std::unique_ptr<Alarm> alarm_; std::unique_ptr<Alarm> alarm_;
RequestType req_; const RequestType& req_;
ResponseType response_; ResponseType response_;
enum State { enum State {
INVALID, INVALID,
@ -554,7 +554,7 @@ class ClientRpcContextStreamingFromClientImpl : public ClientRpcContext {
BenchmarkService::Stub* stub_; BenchmarkService::Stub* stub_;
CompletionQueue* cq_; CompletionQueue* cq_;
std::unique_ptr<Alarm> alarm_; std::unique_ptr<Alarm> alarm_;
RequestType req_; const RequestType& req_;
ResponseType response_; ResponseType response_;
enum State { enum State {
INVALID, INVALID,
@ -672,7 +672,7 @@ class ClientRpcContextStreamingFromServerImpl : public ClientRpcContext {
BenchmarkService::Stub* stub_; BenchmarkService::Stub* stub_;
CompletionQueue* cq_; CompletionQueue* cq_;
std::unique_ptr<Alarm> alarm_; std::unique_ptr<Alarm> alarm_;
RequestType req_; const RequestType& req_;
ResponseType response_; ResponseType response_;
enum State { INVALID, STREAM_IDLE, READ_DONE }; enum State { INVALID, STREAM_IDLE, READ_DONE };
State next_state_; State next_state_;

@ -20,9 +20,9 @@ grpc_cc_test(
name = "server_builder_test", name = "server_builder_test",
srcs = ["server_builder_test.cc"], srcs = ["server_builder_test.cc"],
deps = [ deps = [
"//:grpc++", "//:grpc++_unsecure",
"//src/proto/grpc/testing:echo_proto", "//src/proto/grpc/testing:echo_proto",
"//test/core/util:grpc_test_util", "//test/core/util:grpc_test_util_unsecure",
], ],
external_deps = [ external_deps = [
"gtest", "gtest",
@ -33,9 +33,9 @@ grpc_cc_test(
name = "server_request_call_test", name = "server_request_call_test",
srcs = ["server_request_call_test.cc"], srcs = ["server_request_call_test.cc"],
deps = [ deps = [
"//:grpc++", "//:grpc++_unsecure",
"//src/proto/grpc/testing:echo_proto", "//src/proto/grpc/testing:echo_proto",
"//test/core/util:grpc_test_util", "//test/core/util:grpc_test_util_unsecure",
], ],
external_deps = [ external_deps = [
"gtest", "gtest",

@ -24,16 +24,6 @@ package(
], ],
) )
# The following builds a shared-object to confirm that grpc++_unsecure
# builds properly. Build-only is sufficient here
grpc_cc_binary(
name = "testso.so",
srcs = [],
linkshared = 1,
linkopts = ['-Wl,--no-undefined'],
deps = ["//:grpc++_unsecure"],
)
grpc_cc_library( grpc_cc_library(
name = "test_config", name = "test_config",
srcs = [ srcs = [
@ -64,26 +54,43 @@ grpc_cc_library(
], ],
) )
GRPCXX_TESTUTIL_SRCS = [
"byte_buffer_proto_helper.cc",
"string_ref_helper.cc",
"subprocess.cc",
]
GRPCXX_TESTUTIL_HDRS = [
"byte_buffer_proto_helper.h",
"string_ref_helper.h",
"subprocess.h",
]
grpc_cc_library( grpc_cc_library(
name = "test_util", name = "test_util",
srcs = [ srcs = GRPCXX_TESTUTIL_SRCS + [
"byte_buffer_proto_helper.cc",
"create_test_channel.cc", "create_test_channel.cc",
"string_ref_helper.cc",
"subprocess.cc",
"test_credentials_provider.cc", "test_credentials_provider.cc",
], ],
hdrs = [ hdrs = GRPCXX_TESTUTIL_HDRS + [
"byte_buffer_proto_helper.h",
"create_test_channel.h", "create_test_channel.h",
"string_ref_helper.h",
"subprocess.h",
"test_credentials_provider.h", "test_credentials_provider.h",
], ],
deps = [ deps = [
"//:grpc++", "//:grpc++",
"//test/core/end2end:ssl_test_data", "//test/core/end2end:ssl_test_data",
"//test/core/util:gpr_test_util", ],
external_deps = [
"protobuf",
],
)
grpc_cc_library(
name = "test_util_unsecure",
srcs = GRPCXX_TESTUTIL_SRCS,
hdrs = GRPCXX_TESTUTIL_HDRS,
deps = [
"//:grpc++_unsecure",
], ],
external_deps = [ external_deps = [
"protobuf", "protobuf",

@ -63,6 +63,42 @@ TEST_F(SliceTest, StaticBuf) {
CheckSlice(spp, kContent); CheckSlice(spp, kContent);
} }
TEST_F(SliceTest, SliceNew) {
char* x = new char[strlen(kContent) + 1];
strcpy(x, kContent);
Slice spp(x, strlen(x), [](void* p) { delete[] reinterpret_cast<char*>(p); });
CheckSlice(spp, kContent);
}
TEST_F(SliceTest, SliceNewDoNothing) {
Slice spp(const_cast<char*>(kContent), strlen(kContent), [](void* p) {});
CheckSlice(spp, kContent);
}
TEST_F(SliceTest, SliceNewWithUserData) {
struct stest {
char* x;
int y;
};
auto* t = new stest;
t->x = new char[strlen(kContent) + 1];
strcpy(t->x, kContent);
Slice spp(t->x, strlen(t->x),
[](void* p) {
auto* t = reinterpret_cast<stest*>(p);
delete[] t->x;
delete t;
},
t);
CheckSlice(spp, kContent);
}
TEST_F(SliceTest, SliceNewLen) {
Slice spp(const_cast<char*>(kContent), strlen(kContent),
[](void* p, size_t l) { EXPECT_EQ(l, strlen(kContent)); });
CheckSlice(spp, kContent);
}
TEST_F(SliceTest, Steal) { TEST_F(SliceTest, Steal) {
grpc_slice s = grpc_slice_from_copied_string(kContent); grpc_slice s = grpc_slice_from_copied_string(kContent);
Slice spp(s, Slice::STEAL_REF); Slice spp(s, Slice::STEAL_REF);

@ -959,6 +959,7 @@ src/core/lib/iomgr/ev_poll_posix.h \
src/core/lib/iomgr/ev_posix.h \ src/core/lib/iomgr/ev_posix.h \
src/core/lib/iomgr/exec_ctx.h \ src/core/lib/iomgr/exec_ctx.h \
src/core/lib/iomgr/executor.h \ src/core/lib/iomgr/executor.h \
src/core/lib/iomgr/gethostname.h \
src/core/lib/iomgr/iocp_windows.h \ src/core/lib/iomgr/iocp_windows.h \
src/core/lib/iomgr/iomgr.h \ src/core/lib/iomgr/iomgr.h \
src/core/lib/iomgr/iomgr_internal.h \ src/core/lib/iomgr/iomgr_internal.h \
@ -1033,6 +1034,7 @@ src/core/lib/support/string_windows.h \
src/core/lib/support/thd_internal.h \ src/core/lib/support/thd_internal.h \
src/core/lib/support/time_precise.h \ src/core/lib/support/time_precise.h \
src/core/lib/support/tmpfile.h \ src/core/lib/support/tmpfile.h \
src/core/lib/surface/alarm_internal.h \
src/core/lib/surface/api_trace.h \ src/core/lib/surface/api_trace.h \
src/core/lib/surface/call.h \ src/core/lib/surface/call.h \
src/core/lib/surface/call_test_only.h \ src/core/lib/surface/call_test_only.h \

@ -1116,6 +1116,10 @@ src/core/lib/iomgr/exec_ctx.c \
src/core/lib/iomgr/exec_ctx.h \ src/core/lib/iomgr/exec_ctx.h \
src/core/lib/iomgr/executor.c \ src/core/lib/iomgr/executor.c \
src/core/lib/iomgr/executor.h \ src/core/lib/iomgr/executor.h \
src/core/lib/iomgr/gethostname.h \
src/core/lib/iomgr/gethostname_fallback.c \
src/core/lib/iomgr/gethostname_host_name_max.c \
src/core/lib/iomgr/gethostname_sysconf.c \
src/core/lib/iomgr/iocp_windows.c \ src/core/lib/iomgr/iocp_windows.c \
src/core/lib/iomgr/iocp_windows.h \ src/core/lib/iomgr/iocp_windows.h \
src/core/lib/iomgr/iomgr.c \ src/core/lib/iomgr/iomgr.c \
@ -1342,6 +1346,7 @@ src/core/lib/support/tmpfile_windows.c \
src/core/lib/support/wrap_memcpy.c \ src/core/lib/support/wrap_memcpy.c \
src/core/lib/surface/README.md \ src/core/lib/surface/README.md \
src/core/lib/surface/alarm.c \ src/core/lib/surface/alarm.c \
src/core/lib/surface/alarm_internal.h \
src/core/lib/surface/api_trace.c \ src/core/lib/surface/api_trace.c \
src/core/lib/surface/api_trace.h \ src/core/lib/surface/api_trace.h \
src/core/lib/surface/byte_buffer.c \ src/core/lib/surface/byte_buffer.c \

@ -0,0 +1,38 @@
#!/bin/bash
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Source this rc script to prepare the environment for MacOS interop
# builds. This rc script must be used in the root directory of gRPC
# and is expected to be used before prepare_build_macos_rc
export CONFIG=opt
# Move gRPC repo to directory that Docker for Mac has drive access to
mkdir /Users/kbuilder/workspace
cp -R ./ /Users/kbuilder/workspace/grpc
cd /Users/kbuilder/workspace/grpc
# Needed for identifying Docker image sha1
brew install md5sha1sum
# Set up gRPC-Go and gRPC-Java to test
git clone --recursive https://github.com/grpc/grpc-go ./../grpc-go
git clone --recursive https://github.com/grpc/grpc-java ./../grpc-java
# Set up Docker for Mac
docker-machine create -d virtualbox --virtualbox-share-folder "/Users/kbuilder/workspace:" default
docker-machine env default
eval $(docker-machine env default)

@ -31,7 +31,6 @@ ulimit -a
# pip does not install google-api-python-client properly, so use easy_install # pip does not install google-api-python-client properly, so use easy_install
sudo easy_install --upgrade google-api-python-client sudo easy_install --upgrade google-api-python-client
export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json
gcloud auth activate-service-account --key-file=$GOOGLE_APPLICATION_CREDENTIALS
# required to build protobuf # required to build protobuf
brew install gflags brew install gflags

@ -15,9 +15,8 @@
# Config file for the internal CI (in protobuf text format) # Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository. # Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh" build_file: "grpc/tools/internal_ci/linux/grpc_build_submodule_at_head.sh"
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json" timeout_mins: 180
timeout_mins: 240
action { action {
define_artifacts { define_artifacts {
regex: "**/*sponge_log.xml" regex: "**/*sponge_log.xml"
@ -25,7 +24,8 @@ action {
} }
} }
# Tiny hack: misusing an already whitelisted env var to pass submodule name
env_vars { env_vars {
key: "RUN_TESTS_FLAGS" key: "RUN_TESTS_FLAGS"
value: "-f basictests macos --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results" value: "boringssl"
} }

@ -0,0 +1,31 @@
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/linux/grpc_build_submodule_at_head.sh"
timeout_mins: 180
action {
define_artifacts {
regex: "**/*sponge_log.xml"
regex: "github/grpc/reports/**"
}
}
# Tiny hack: misusing an already whitelisted env var to pass submodule name
env_vars {
key: "RUN_TESTS_FLAGS"
value: "protobuf"
}

@ -0,0 +1,30 @@
#!/bin/bash
# Copyright 2017 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Build portability tests with an updated submodule
set -ex
# change to grpc repo root
cd $(dirname $0)/../../..
source tools/internal_ci/helper_scripts/prepare_build_linux_rc
# Update submodule and commit it so changes are passed to Docker
(cd third_party/$RUN_TESTS_FLAGS && git pull origin master)
git -c user.name='foo' -c user.email='foo@google.com' commit -a -m 'Update submodule'
tools/run_tests/run_tests_matrix.py -f linux --internal_ci --build_only

@ -18,6 +18,7 @@ set -ex
# change to grpc repo root # change to grpc repo root
cd $(dirname $0)/../../.. cd $(dirname $0)/../../..
source tools/internal_ci/helper_scripts/prepare_build_interop_rc source tools/internal_ci/helper_scripts/prepare_build_macos_interop_rc
source tools/internal_ci/helper_scripts/prepare_build_macos_rc
tools/run_tests/run_interop_tests.py -l objc -s all --use_docker -t -j 1 tools/run_tests/run_interop_tests.py -l objc -s all --use_docker -t -j 1

@ -1,93 +0,0 @@
#!/bin/bash
# Copyright 2015 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This script is invoked by run_interop_tests.py to build the docker image
# for interop testing. You should never need to call this script on your own.
set -x
# Params:
# INTEROP_IMAGE - Name of tag of the final interop image
# INTEROP_IMAGE_REPOSITORY_TAG - Optional. If set, the created image will be tagged using
# the command: 'docker tag $INTEROP_IMAGE $INTEROP_IMAGE_REPOSITORY_TAG'
# BASE_NAME - Base name used to locate the base Dockerfile and build script
# BUILD_TYPE - The 'CONFIG' variable passed to the 'make' command (example:
# asan, tsan. Default value: opt).
# TTY_FLAG - optional -t flag to make docker allocate tty
# BUILD_INTEROP_DOCKER_EXTRA_ARGS - optional args to be passed to the
# docker run command
cd `dirname $0`/../../..
GRPC_ROOT=`pwd`
MOUNT_ARGS="-v $GRPC_ROOT:/var/local/jenkins/grpc:ro"
GRPC_JAVA_ROOT=`cd ../grpc-java && pwd`
if [ "$GRPC_JAVA_ROOT" != "" ]
then
MOUNT_ARGS+=" -v $GRPC_JAVA_ROOT:/var/local/jenkins/grpc-java:ro"
else
echo "WARNING: grpc-java not found, it won't be mounted to the docker container."
fi
GRPC_GO_ROOT=`cd ../grpc-go && pwd`
if [ "$GRPC_GO_ROOT" != "" ]
then
MOUNT_ARGS+=" -v $GRPC_GO_ROOT:/var/local/jenkins/grpc-go:ro"
else
echo "WARNING: grpc-go not found, it won't be mounted to the docker container."
fi
mkdir -p /tmp/ccache
# Mount service account dir if available.
# If service_directory does not contain the service account JSON file,
# some of the tests will fail.
if [ -e $HOME/service_account ]
then
MOUNT_ARGS+=" -v $HOME/service_account:/var/local/jenkins/service_account:ro"
fi
# Use image name based on Dockerfile checksum
BASE_IMAGE=${BASE_NAME}_base:`sha1sum tools/dockerfile/stress_test/$BASE_NAME/Dockerfile | cut -f1 -d\ `
# Make sure base docker image has been built. Should be instantaneous if so.
docker build -t $BASE_IMAGE --force-rm=true tools/dockerfile/stress_test/$BASE_NAME || exit $?
# Create a local branch so the child Docker script won't complain
git branch -f jenkins-docker
CONTAINER_NAME="build_${BASE_NAME}_$(uuidgen)"
# Prepare image for interop tests, commit it on success.
(docker run \
-e CCACHE_DIR=/tmp/ccache \
-e THIS_IS_REALLY_NEEDED='see https://github.com/docker/docker/issues/14203 for why docker is awful' \
-e BUILD_TYPE=${BUILD_TYPE:=opt} \
-i $TTY_FLAG \
$MOUNT_ARGS \
$BUILD_INTEROP_DOCKER_EXTRA_ARGS \
-v /tmp/ccache:/tmp/ccache \
--name=$CONTAINER_NAME \
$BASE_IMAGE \
bash -l /var/local/jenkins/grpc/tools/dockerfile/stress_test/$BASE_NAME/build_interop_stress.sh \
&& docker commit $CONTAINER_NAME $INTEROP_IMAGE \
&& ( if [ -n "$INTEROP_IMAGE_REPOSITORY_TAG" ]; then docker tag $INTEROP_IMAGE $INTEROP_IMAGE_REPOSITORY_TAG ; fi ) \
&& echo "Successfully built image $INTEROP_IMAGE")
EXITCODE=$?
# remove intermediate container, possibly killing it first
docker rm -f $CONTAINER_NAME
exit $EXITCODE

@ -7833,6 +7833,9 @@
"src/core/lib/iomgr/ev_windows.c", "src/core/lib/iomgr/ev_windows.c",
"src/core/lib/iomgr/exec_ctx.c", "src/core/lib/iomgr/exec_ctx.c",
"src/core/lib/iomgr/executor.c", "src/core/lib/iomgr/executor.c",
"src/core/lib/iomgr/gethostname_fallback.c",
"src/core/lib/iomgr/gethostname_host_name_max.c",
"src/core/lib/iomgr/gethostname_sysconf.c",
"src/core/lib/iomgr/iocp_windows.c", "src/core/lib/iomgr/iocp_windows.c",
"src/core/lib/iomgr/iomgr.c", "src/core/lib/iomgr/iomgr.c",
"src/core/lib/iomgr/iomgr_posix.c", "src/core/lib/iomgr/iomgr_posix.c",
@ -7979,6 +7982,7 @@
"src/core/lib/iomgr/ev_posix.h", "src/core/lib/iomgr/ev_posix.h",
"src/core/lib/iomgr/exec_ctx.h", "src/core/lib/iomgr/exec_ctx.h",
"src/core/lib/iomgr/executor.h", "src/core/lib/iomgr/executor.h",
"src/core/lib/iomgr/gethostname.h",
"src/core/lib/iomgr/iocp_windows.h", "src/core/lib/iomgr/iocp_windows.h",
"src/core/lib/iomgr/iomgr.h", "src/core/lib/iomgr/iomgr.h",
"src/core/lib/iomgr/iomgr_internal.h", "src/core/lib/iomgr/iomgr_internal.h",
@ -8035,6 +8039,7 @@
"src/core/lib/slice/slice_hash_table.h", "src/core/lib/slice/slice_hash_table.h",
"src/core/lib/slice/slice_internal.h", "src/core/lib/slice/slice_internal.h",
"src/core/lib/slice/slice_string_helpers.h", "src/core/lib/slice/slice_string_helpers.h",
"src/core/lib/surface/alarm_internal.h",
"src/core/lib/surface/api_trace.h", "src/core/lib/surface/api_trace.h",
"src/core/lib/surface/call.h", "src/core/lib/surface/call.h",
"src/core/lib/surface/call_test_only.h", "src/core/lib/surface/call_test_only.h",
@ -8107,6 +8112,7 @@
"src/core/lib/iomgr/ev_posix.h", "src/core/lib/iomgr/ev_posix.h",
"src/core/lib/iomgr/exec_ctx.h", "src/core/lib/iomgr/exec_ctx.h",
"src/core/lib/iomgr/executor.h", "src/core/lib/iomgr/executor.h",
"src/core/lib/iomgr/gethostname.h",
"src/core/lib/iomgr/iocp_windows.h", "src/core/lib/iomgr/iocp_windows.h",
"src/core/lib/iomgr/iomgr.h", "src/core/lib/iomgr/iomgr.h",
"src/core/lib/iomgr/iomgr_internal.h", "src/core/lib/iomgr/iomgr_internal.h",
@ -8163,6 +8169,7 @@
"src/core/lib/slice/slice_hash_table.h", "src/core/lib/slice/slice_hash_table.h",
"src/core/lib/slice/slice_internal.h", "src/core/lib/slice/slice_internal.h",
"src/core/lib/slice/slice_string_helpers.h", "src/core/lib/slice/slice_string_helpers.h",
"src/core/lib/surface/alarm_internal.h",
"src/core/lib/surface/api_trace.h", "src/core/lib/surface/api_trace.h",
"src/core/lib/surface/call.h", "src/core/lib/surface/call.h",
"src/core/lib/surface/call_test_only.h", "src/core/lib/surface/call_test_only.h",

@ -74,19 +74,21 @@ def get_flaky_tests(limit=None):
bq = big_query_utils.create_big_query() bq = big_query_utils.create_big_query()
query = """ query = """
SELECT SELECT
test_name, filtered_test_name,
SUM(result != 'PASSED' FROM (
AND result != 'SKIPPED') AS count_failed, SELECT
FROM REGEXP_REPLACE(test_name, r'/\d+', '') AS filtered_test_name,
[grpc-testing:jenkins_test_results.aggregate_results] result
WHERE FROM
timestamp >= DATE_ADD(CURRENT_DATE(), -1, "WEEK") [grpc-testing:jenkins_test_results.aggregate_results]
AND NOT REGEXP_MATCH(job_name, '.*portability.*') WHERE
GROUP BY timestamp >= DATE_ADD(CURRENT_DATE(), -1, "WEEK")
test_name AND NOT REGEXP_MATCH(job_name, '.*portability.*') )
HAVING GROUP BY
count_failed > 0""" filtered_test_name
HAVING
SUM(result != 'PASSED' AND result != 'SKIPPED') > 0"""
if limit: if limit:
query += " limit {}".format(limit) query += " limit {}".format(limit)
query_job = big_query_utils.sync_query_job(bq, 'grpc-testing', query) query_job = big_query_utils.sync_query_job(bq, 'grpc-testing', query)

@ -450,6 +450,7 @@
<ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_posix.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_posix.h" />
<ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\exec_ctx.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\exec_ctx.h" />
<ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.h" />
<ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname.h" />
<ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.h" />
<ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr.h" />
<ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr_internal.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr_internal.h" />
@ -506,6 +507,7 @@
<ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.h" />
<ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h" />
<ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h" />
<ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\alarm_internal.h" />
<ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.h" />
<ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\call.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\call.h" />
<ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\call_test_only.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\call_test_only.h" />

@ -701,6 +701,9 @@
<ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.h"> <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.h">
<Filter>src\core\lib\iomgr</Filter> <Filter>src\core\lib\iomgr</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname.h">
<Filter>src\core\lib\iomgr</Filter>
</ClInclude>
<ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.h"> <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.h">
<Filter>src\core\lib\iomgr</Filter> <Filter>src\core\lib\iomgr</Filter>
</ClInclude> </ClInclude>
@ -869,6 +872,9 @@
<ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h"> <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h">
<Filter>src\core\lib\slice</Filter> <Filter>src\core\lib\slice</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\alarm_internal.h">
<Filter>src\core\lib\surface</Filter>
</ClInclude>
<ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.h"> <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.h">
<Filter>src\core\lib\surface</Filter> <Filter>src\core\lib\surface</Filter>
</ClInclude> </ClInclude>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save