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
# work for now? Try with a later pylint?
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
# elements.
missing-docstring,

@ -593,6 +593,9 @@ grpc_cc_library(
"src/core/lib/iomgr/ev_windows.c",
"src/core/lib/iomgr/exec_ctx.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/iomgr.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/exec_ctx.h",
"src/core/lib/iomgr/executor.h",
"src/core/lib/iomgr/gethostname.h",
"src/core/lib/iomgr/iocp_windows.h",
"src/core/lib/iomgr/iomgr.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_internal.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/call.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/exec_ctx.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/iomgr.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/exec_ctx.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/iomgr.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/exec_ctx.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/iomgr.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/exec_ctx.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/iomgr.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/exec_ctx.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/iomgr.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/exec_ctx.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/iomgr.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/exec_ctx.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/iomgr.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/exec_ctx.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/iomgr.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/exec_ctx.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/iomgr.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/exec_ctx.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/iomgr.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/exec_ctx.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/iomgr.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/exec_ctx.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/iomgr.c \
src/core/lib/iomgr/iomgr_posix.c \

@ -687,6 +687,9 @@
'src/core/lib/iomgr/ev_windows.c',
'src/core/lib/iomgr/exec_ctx.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/iomgr.c',
'src/core/lib/iomgr/iomgr_posix.c',

@ -214,6 +214,9 @@ filegroups:
- src/core/lib/iomgr/ev_windows.c
- src/core/lib/iomgr/exec_ctx.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/iomgr.c
- src/core/lib/iomgr/iomgr_posix.c
@ -359,6 +362,7 @@ filegroups:
- src/core/lib/iomgr/ev_posix.h
- src/core/lib/iomgr/exec_ctx.h
- src/core/lib/iomgr/executor.h
- src/core/lib/iomgr/gethostname.h
- src/core/lib/iomgr/iocp_windows.h
- src/core/lib/iomgr/iomgr.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_internal.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/call.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/exec_ctx.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/iomgr.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\\exec_ctx.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\\iomgr.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
accomplished by invoking `CONFIG=dbg make <target>`
- alarm_refcount - refcounting traces for grpc_alarm structure
- metadata - tracks creation and mutation of metadata
- closure - tracks closure creation, scheduling, and completion
- 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.
> * 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
## 1. Introduction

@ -345,6 +345,7 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/ev_posix.h',
'src/core/lib/iomgr/exec_ctx.h',
'src/core/lib/iomgr/executor.h',
'src/core/lib/iomgr/gethostname.h',
'src/core/lib/iomgr/iocp_windows.h',
'src/core/lib/iomgr/iomgr.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_internal.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/call.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/exec_ctx.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/iomgr.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/exec_ctx.h',
'src/core/lib/iomgr/executor.h',
'src/core/lib/iomgr/gethostname.h',
'src/core/lib/iomgr/iocp_windows.h',
'src/core/lib/iomgr/iomgr.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_internal.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/call.h',
'src/core/lib/surface/call_test_only.h',

@ -40,12 +40,9 @@ Pod::Spec.new do |s|
s.header_dir = name
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.default_subspec = 'Main'
# Certificates, to be able to establish TLS connections:
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:
'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

@ -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/exec_ctx.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/iomgr.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_internal.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/call.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/exec_ctx.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/iomgr.c )
s.files += %w( src/core/lib/iomgr/iomgr_posix.c )

@ -349,6 +349,28 @@ class CallOpRecvMessage {
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 {
public:
CallOpGenericRecvMessage()
@ -356,9 +378,11 @@ class CallOpGenericRecvMessage {
template <class R>
void RecvMessage(R* message) {
deserialize_ = [message](grpc_byte_buffer* buf) -> Status {
return SerializationTraits<R>::Deserialize(buf, message);
};
// Use an explicit base class pointer to avoid resolution error in the
// 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.
@ -381,7 +405,7 @@ class CallOpGenericRecvMessage {
if (recv_buf_) {
if (*status) {
got_message = true;
*status = deserialize_(recv_buf_).ok();
*status = deserialize_->Deserialize(recv_buf_).ok();
} else {
got_message = false;
g_core_codegen_interface->grpc_byte_buffer_destroy(recv_buf_);
@ -392,12 +416,11 @@ class CallOpGenericRecvMessage {
*status = false;
}
}
deserialize_ = DeserializeFunc();
deserialize_.reset();
}
private:
typedef std::function<Status(grpc_byte_buffer*)> DeserializeFunc;
DeserializeFunc deserialize_;
std::unique_ptr<CallOpGenericRecvMessageHelper::DeserializeFunc> deserialize_;
grpc_byte_buffer* recv_buf_;
bool allow_not_getting_message_;
};

@ -67,6 +67,20 @@ class Slice final {
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.
size_t size() const { return GRPC_SLICE_LENGTH(slice_); }

@ -258,8 +258,12 @@ typedef struct {
#define GRPC_ARG_RESOURCE_QUOTA "grpc.resource_quota"
/** If non-zero, expand wildcard addresses to a list of local addresses. */
#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"
/** 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. */
#define GRPC_ARG_LB_POLICY_NAME "grpc.lb_policy_name"
/** 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/exec_ctx.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/iomgr.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_internal.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/call.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/exec_ctx.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/iomgr.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>
#if GRPC_ARES == 1 && !defined(GRPC_UV)
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
@ -31,11 +34,14 @@
#include "src/core/ext/filters/client_channel/resolver_registry.h"
#include "src/core/lib/channel/channel_args.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/timer.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/support/backoff.h"
#include "src/core/lib/support/env.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_INITIAL_CONNECT_BACKOFF_SECONDS 1
@ -54,6 +60,8 @@ typedef struct {
char *default_port;
/** 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 */
grpc_pollset_set *interested_parties;
@ -85,6 +93,8 @@ typedef struct {
/** currently resolving addresses */
grpc_lb_addresses *lb_addresses;
/** currently resolving service config */
char *service_config_json;
} ares_dns_resolver;
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");
}
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,
grpc_error *error) {
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->pending_request = NULL;
if (r->lb_addresses != NULL) {
grpc_arg new_arg = grpc_lb_addresses_create_channel_arg(r->lb_addresses);
result = grpc_channel_args_copy_and_add(r->channel_args, &new_arg, 1);
static const char *args_to_remove[2];
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);
} else {
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);
r->resolving = true;
r->lb_addresses = NULL;
r->service_config_json = NULL;
r->pending_request = grpc_dns_lookup_ares(
exec_ctx, r->dns_server, r->name_to_resolve, r->default_port,
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,
@ -256,6 +371,10 @@ static grpc_resolver *dns_ares_create(grpc_exec_ctx *exec_ctx,
r->name_to_resolve = gpr_strdup(path);
r->default_port = gpr_strdup(default_port);
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();
if (args->pollset_set != NULL) {
grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties,

@ -54,6 +54,8 @@ struct grpc_ares_request {
grpc_closure *on_done;
/** the pointer to receive the resolved addresses */
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 */
grpc_ares_ev_driver *ev_driver;
/** 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);
}
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(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
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;
/* TODO(zyc): Enable tracing after #9603 is checked in */
/* 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);
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);
r->ev_driver = ev_driver;
r->on_done = on_done;
r->lb_addrs_out = addrs;
r->service_config_json_out = service_config_json;
r->success = false;
r->error = GRPC_ERROR_NONE;
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;
if (grpc_parse_ipv4_hostport(dns_server, &addr, false /* log_errors */)) {
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.udp_port = grpc_sockaddr_get_port(&addr);
} else if (grpc_parse_ipv6_hostport(dns_server, &addr,
false /* log_errors */)) {
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.udp_port = grpc_sockaddr_get_port(&addr);
} else {
@ -342,8 +407,6 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl(
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);
if (grpc_ipv6_loopback_available()) {
grpc_ares_hostbyname_request *hr = create_hostbyname_request(
@ -362,6 +425,10 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl(
r);
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. */
grpc_ares_ev_driver_start(exec_ctx, r->ev_driver);
grpc_ares_request_unref(exec_ctx, r);
@ -379,8 +446,8 @@ error_cleanup:
grpc_ares_request *(*grpc_dns_lookup_ares)(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
const char *default_port, grpc_pollset_set *interested_parties,
grpc_closure *on_done, grpc_lb_addresses **addrs,
bool check_grpclb) = grpc_dns_lookup_ares_impl;
grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb,
char **service_config_json) = grpc_dns_lookup_ares_impl;
void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) {
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_dns_lookup_ares(exec_ctx, NULL /* dns_server */, name, default_port,
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)(

@ -27,29 +27,30 @@
typedef struct grpc_ares_request grpc_ares_request;
/* Asynchronously resolve addr. Use \a default_port if a port isn't designated
in addr, otherwise use the port in addr. grpc_ares_init() must be called at
least once before this 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 held by the caller. */
/* Asynchronously resolve \a name. Use \a default_port if a port isn't
designated in \a name, otherwise use the 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 scheduled with \a exec_ctx,
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,
const char *addr,
const char *name,
const char *default_port,
grpc_pollset_set *interested_parties,
grpc_closure *on_done,
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
\a default_port if a port isn't designated in \a addr, otherwise it uses the
port in \a addr. grpc_ares_init() must be called at least once before this
\a default_port if a port isn't designated in \a name, otherwise it uses the
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
scheduled with \a exec_ctx, it must not try to acquire locks that are being
held by the caller. */
scheduled with \a exec_ctx, so it must not try to acquire locks that are
being held by the caller. */
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,
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 */
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(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
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;
}
grpc_ares_request *(*grpc_dns_lookup_ares)(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name,
const char *default_port, grpc_pollset_set *interested_parties,
grpc_closure *on_done, grpc_lb_addresses **addrs,
bool check_grpclb) = grpc_dns_lookup_ares_impl;
grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb,
char **service_config_json) = grpc_dns_lookup_ares_impl;
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
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,
size_t *p_write_buffer_size, uint32_t flags) {
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);
/* append actual data */
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) {
size_t write_buffer_size;
create_grpc_frame(&write_slice_buffer, &stream_state->ws.write_buffer,
&write_buffer_size,
create_grpc_frame(exec_ctx, &write_slice_buffer,
&stream_state->ws.write_buffer, &write_buffer_size,
stream_op->payload->send_message.send_message->flags);
CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, %p)", s->cbs,
stream_state->ws.write_buffer);
stream_state->state_callback_received[OP_SEND_MESSAGE] = false;
bidirectional_stream_write(s->cbs, stream_state->ws.write_buffer,
(int)write_buffer_size, false);
grpc_slice_buffer_destroy_internal(exec_ctx, &write_slice_buffer);
if (t->use_packet_coalescing) {
if (!stream_op->send_trailing_metadata) {
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 {
stream_state->rs.remaining_bytes = 0;
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_stream_init(&stream_state->rs.sbs,
&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,
(size_t)stream_state->rs.length_field);
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_add(&stream_state->rs.read_slice_buffer,
read_data_slice);
@ -1369,6 +1378,8 @@ static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
grpc_closure *then_schedule_closure) {
stream_obj *s = (stream_obj *)gs;
null_and_maybe_free_read_buffer(s);
/* Clean up read_slice_buffer in case there is unread data. */
grpc_slice_buffer_destroy_internal(exec_ctx, &s->state.rs.read_slice_buffer);
GRPC_ERROR_UNREF(s->state.cancel_error);
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; }
/* Might be called multiple times */
static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) {
/* if 'releasing_fd' is true, it means that we are going to detach the internal
* 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,
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_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,
grpc_closure *on_done, int *release_fd,
bool already_closed, const char *reason) {
grpc_error *error = GRPC_ERROR_NONE;
bool is_release_fd = (release_fd != NULL);
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
descriptor fd->fd (but we still own the grpc_fd structure). */
if (release_fd != NULL) {
if (is_release_fd) {
*release_fd = fd->fd;
} else if (!already_closed) {
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_UNIX_SOCKET 1
#define GRPC_LINUX_MULTIPOLL_WITH_EPOLL 1
#define GRPC_POSIX_HOST_NAME_MAX 1
#define GRPC_POSIX_SOCKET 1
#define GRPC_POSIX_SOCKETADDR 1
#define GRPC_POSIX_WAKEUP_FD 1
@ -93,6 +94,7 @@
#define GRPC_POSIX_SOCKET 1
#define GRPC_POSIX_SOCKETADDR 1
#define GRPC_POSIX_SOCKETUTILS 1
#define GRPC_POSIX_SYSCONF 1
#define GRPC_POSIX_WAKEUP_FD 1
#define GRPC_TIMER_USE_GENERIC 1
#elif defined(GPR_FREEBSD)
@ -125,4 +127,11 @@
#error Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GPR_CUSTOM_SOCKET
#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 */

@ -39,7 +39,7 @@
#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;
/* get max listen queue size on linux */

@ -15,6 +15,7 @@
* limitations under the License.
*
*/
#include "src/core/lib/surface/alarm_internal.h"
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
@ -22,7 +23,13 @@
#include "src/core/lib/iomgr/timer.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 {
gpr_refcount refs;
grpc_timer alarm;
grpc_closure on_alarm;
grpc_cq_completion completion;
@ -32,13 +39,58 @@ struct grpc_alarm {
void *tag;
};
static void do_nothing_end_completion(grpc_exec_ctx *exec_ctx, void *arg,
grpc_cq_completion *c) {}
static void alarm_ref(grpc_alarm *alarm) { gpr_ref(&alarm->refs); }
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) {
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,
@ -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_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");
alarm->cq = cq;
alarm->tag = tag;
@ -67,9 +127,6 @@ void grpc_alarm_cancel(grpc_alarm *alarm) {
}
void grpc_alarm_destroy(grpc_alarm *alarm) {
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_alarm_cancel(alarm);
GRPC_CQ_INTERNAL_UNREF(&exec_ctx, alarm->cq, "alarm");
gpr_free(alarm);
grpc_exec_ctx_finish(&exec_ctx);
GRPC_ALARM_UNREF(alarm, "alarm_destroy");
}

@ -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/profiling/timers.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/call.h"
#include "src/core/lib/surface/channel_init.h"
@ -135,6 +136,7 @@ void grpc_init(void) {
grpc_register_tracer(&grpc_call_error_trace);
#ifndef NDEBUG
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_closure);
grpc_register_tracer(&grpc_trace_error_refcount);

@ -17,6 +17,7 @@
*/
#include <grpc++/support/slice.h>
#include <grpc/slice.h>
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(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

@ -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"
/**
* 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. */
@interface GRPCCall (OAuth2)
@ -33,4 +40,12 @@
/** Returns the value (if any) of the "www-authenticate" response header (the challenge header). */
@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

@ -16,6 +16,8 @@
*
*/
#import <objc/runtime.h>
#import "GRPCCall+OAuth2.h"
static NSString * const kAuthorizationHeader = @"authorization";
@ -23,6 +25,7 @@ static NSString * const kBearerPrefix = @"Bearer ";
static NSString * const kChallengeHeader = @"www-authenticate";
@implementation GRPCCall (OAuth2)
@dynamic tokenProvider;
- (NSString *)oauth2AccessToken {
NSString *headerValue = self.requestHeaders[kAuthorizationHeader];
@ -45,4 +48,12 @@ static NSString * const kChallengeHeader = @"www-authenticate";
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

@ -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
* 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

@ -18,6 +18,8 @@
#import "GRPCCall.h"
#import "GRPCCall+OAuth2.h"
#include <grpc/grpc.h>
#include <grpc/support/time.h>
#import <RxLibrary/GRXConcurrentWriteable.h>
@ -40,10 +42,14 @@ NSString * const kGRPCHeadersKey = @"io.grpc.HeadersKey";
NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
static NSMutableDictionary *callFlags;
static NSString * const kAuthorizationHeader = @"authorization";
static NSString * const kBearerPrefix = @"Bearer ";
@interface GRPCCall () <GRXWriteable>
// Make them read-write.
@property(atomic, strong) NSDictionary *responseHeaders;
@property(atomic, strong) NSDictionary *responseTrailers;
@property(atomic) BOOL isWaitingForToken;
@end
// 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 {
@synchronized(self) {
if (_state == GRXWriterStateFinished) {
return;
}
_state = GRXWriterStateFinished;
}
@ -211,7 +214,11 @@ static NSMutableDictionary *callFlags;
[self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
code:GRPCErrorCodeCancelled
userInfo:@{NSLocalizedDescriptionKey: @"Canceled by app"}]];
[self cancelCall];
if (!self.isWaitingForToken) {
[self cancelCall];
} else {
self.isWaitingForToken = NO;
}
}
- (void)dealloc {
@ -410,22 +417,13 @@ static NSMutableDictionary *callFlags;
#pragma mark GRXWriter implementation
- (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;
- (void)startCallWithWriteable:(id<GRXWriteable>)writeable {
_responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable
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?");
[self sendHeaders:_requestHeaders];
@ -437,20 +435,49 @@ static NSMutableDictionary *callFlags;
// TODO(jcanizales): Check this on init.
[NSException raise:NSInvalidArgumentException format:@"host of %@ is nil", _host];
}
__weak typeof(self) weakSelf = self;
_connectivityMonitor = [GRPCConnectivityMonitor monitorWithHost:host];
__weak typeof(self) weakSelf = self;
void (^handler)() = ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
code:GRPCErrorCodeUnavailable
userInfo:@{ NSLocalizedDescriptionKey : @"Connectivity lost." }]];
}
[strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
code:GRPCErrorCodeUnavailable
userInfo:@{ NSLocalizedDescriptionKey : @"Connectivity lost." }]];
};
[_connectivityMonitor handleLossWithHandler:handler
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 {
@synchronized(self) {
// 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 {
[self checkCallIsNotStarted];
CheckIsNonNilASCII(@"Header name", key);
key = key.lowercaseString;
CheckKeyValuePairIsValid(key, obj);

@ -214,10 +214,12 @@ PHP_METHOD(Call, __construct) {
return;
}
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,
"Call cannot be constructed from a closed Channel",
1 TSRMLS_CC);
gpr_mu_unlock(&channel->wrapper->mu);
return;
}
add_property_zval(getThis(), "channel", channel_obj);
@ -226,13 +228,15 @@ PHP_METHOD(Call, __construct) {
grpc_slice host_slice = host_override != NULL ?
grpc_slice_from_copied_string(host_override) : grpc_empty_slice();
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,
host_override != NULL ? &host_slice : NULL,
deadline->wrapped, NULL);
grpc_slice_unref(method_slice);
grpc_slice_unref(host_slice);
call->owned = true;
gpr_mu_unlock(&channel->wrapper->mu);
}
/**

@ -109,8 +109,8 @@ PHP_METHOD(CallCredentials, createFromPlugin) {
zend_fcall_info *fci;
zend_fcall_info_cache *fci_cache;
fci = (zend_fcall_info *)emalloc(sizeof(zend_fcall_info));
fci_cache = (zend_fcall_info_cache *)emalloc(sizeof(zend_fcall_info_cache));
fci = (zend_fcall_info *)malloc(sizeof(zend_fcall_info));
fci_cache = (zend_fcall_info_cache *)malloc(sizeof(zend_fcall_info_cache));
memset(fci, 0, sizeof(zend_fcall_info));
memset(fci_cache, 0, sizeof(zend_fcall_info_cache));
@ -123,7 +123,7 @@ PHP_METHOD(CallCredentials, createFromPlugin) {
}
plugin_state *state;
state = (plugin_state *)emalloc(sizeof(plugin_state));
state = (plugin_state *)malloc(sizeof(plugin_state));
memset(state, 0, sizeof(plugin_state));
/* 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 */
void plugin_destroy_state(void *ptr) {
plugin_state *state = (plugin_state *)ptr;
efree(state->fci);
efree(state->fci_cache);
free(state->fci);
free(state->fci_cache);
#if PHP_MAJOR_VERSION < 7
PHP_GRPC_FREE_STD_ZVAL(state->fci->params);
PHP_GRPC_FREE_STD_ZVAL(state->fci->retval);
#endif
efree(state);
free(state);
}
ZEND_BEGIN_ARG_INFO_EX(arginfo_createComposite, 0, 0, 2)

@ -25,6 +25,13 @@
#include <php.h>
#include <php_ini.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 "php_grpc.h"
@ -44,11 +51,25 @@ zend_class_entry *grpc_ce_channel;
#if PHP_MAJOR_VERSION >= 7
static zend_object_handlers channel_ce_handlers;
#endif
static gpr_mu global_persistent_list_mu;
int le_plink;
/* Frees and destroys an instance of wrapped_grpc_channel */
PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_channel)
if (p->wrapped != NULL) {
grpc_channel_destroy(p->wrapped);
if (p->wrapper != NULL) {
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()
@ -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);
}
void php_grpc_read_args_array(zval *args_array,
grpc_channel_args *args TSRMLS_DC) {
int php_grpc_read_args_array(zval *args_array,
grpc_channel_args *args TSRMLS_DC) {
HashTable *array_hash;
int args_index;
array_hash = Z_ARRVAL_P(args_array);
if (!array_hash) {
zend_throw_exception(spl_ce_InvalidArgumentException,
"array_hash is NULL", 1 TSRMLS_CC);
return;
return FAILURE;
}
args->num_args = zend_hash_num_elements(array_hash);
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) {
zend_throw_exception(spl_ce_InvalidArgumentException,
"args keys must be strings", 1 TSRMLS_CC);
return;
return FAILURE;
}
args->args[args_index].key = key;
switch (Z_TYPE_P(data)) {
@ -99,16 +120,78 @@ void php_grpc_read_args_array(zval *args_array,
default:
zend_throw_exception(spl_ce_InvalidArgumentException,
"args values must be int or string", 1 TSRMLS_CC);
return;
return FAILURE;
}
args_index++;
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
* "credentials" key mapping to a ChannelCredentials object, a secure channel
* will be created with those credentials.
* Construct an instance of the Channel class.
*
* 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 array $args_array The arguments to pass to the Channel
*/
@ -121,6 +204,9 @@ PHP_METHOD(Channel, __construct) {
grpc_channel_args args;
HashTable *array_hash;
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 */
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);
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) {
creds = NULL;
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_read_args_array(args_array, &args TSRMLS_CC);
if (creds == NULL) {
channel->wrapped = grpc_insecure_channel_create(target, &args, NULL);
if (php_grpc_zend_hash_find(array_hash, "force_new", sizeof("force_new"),
(void **)&force_new_obj) == SUCCESS) {
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 {
channel->wrapped =
grpc_secure_channel_create(creds->wrapped, target, &args, NULL);
// Found a previously stored channel in the persistent list
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) {
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) {
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;
/* "|b" == 1 optional bool */
@ -179,10 +350,18 @@ PHP_METHOD(Channel, getConnectivityState) {
== FAILURE) {
zend_throw_exception(spl_ce_InvalidArgumentException,
"getConnectivityState expects a bool", 1 TSRMLS_CC);
gpr_mu_unlock(&channel->wrapper->mu);
return;
}
RETURN_LONG(grpc_channel_check_connectivity_state(channel->wrapped,
(int)try_to_connect));
int state = grpc_channel_check_connectivity_state(channel->wrapper->wrapped,
(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) {
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;
zval *deadline_obj;
/* "lO" == 1 long 1 object */
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,
"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;
}
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,
deadline->wrapped, completion_queue,
NULL);
grpc_event event =
grpc_completion_queue_pluck(completion_queue, NULL,
gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
grpc_completion_queue_pluck(completion_queue, NULL,
gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
gpr_mu_unlock(&channel->wrapper->mu);
RETURN_BOOL(event.success);
}
@ -222,10 +413,48 @@ PHP_METHOD(Channel, watchConnectivityState) {
*/
PHP_METHOD(Channel, close) {
wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
if (channel->wrapped != NULL) {
grpc_channel_destroy(channel->wrapped);
channel->wrapped = NULL;
gpr_mu_lock(&channel->wrapper->mu);
if (channel->wrapper->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)
@ -262,10 +491,13 @@ static zend_function_entry channel_methods[] = {
PHP_FE_END
};
void grpc_init_channel(TSRMLS_D) {
GRPC_STARTUP_FUNCTION(channel) {
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "Grpc\\Channel", channel_methods);
ce.create_object = create_wrapped_grpc_channel;
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);
return SUCCESS;
}

@ -33,9 +33,18 @@
/* Class entry for the PHP Channel class */
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 */
PHP_GRPC_WRAP_OBJECT_START(wrapped_grpc_channel)
grpc_channel *wrapped;
grpc_channel_wrapper *wrapper;
PHP_GRPC_WRAP_OBJECT_END(wrapped_grpc_channel)
#if PHP_MAJOR_VERSION < 7
@ -57,10 +66,20 @@ static inline wrapped_grpc_channel
#endif /* PHP_MAJOR_VERSION */
/* 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 */
void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args
TSRMLS_DC);
int php_grpc_read_args_array(zval *args_array, grpc_channel_args *args
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_ */

@ -26,7 +26,9 @@
#include <php.h>
#include <php_ini.h>
#include <ext/standard/info.h>
#include <ext/standard/sha1.h>
#include <ext/spl/spl_exceptions.h>
#include "channel.h"
#include "php_grpc.h"
#include <zend_exceptions.h>
@ -69,14 +71,17 @@ php_grpc_zend_object create_wrapped_grpc_channel_credentials(
channel_credentials_ce_handlers);
}
zval *grpc_php_wrap_channel_credentials(grpc_channel_credentials
*wrapped TSRMLS_DC) {
zval *grpc_php_wrap_channel_credentials(grpc_channel_credentials *wrapped,
char *hashstr,
zend_bool has_call_creds TSRMLS_DC) {
zval *credentials_object;
PHP_GRPC_MAKE_STD_ZVAL(credentials_object);
object_init_ex(credentials_object, grpc_ce_channel_credentials);
wrapped_grpc_channel_credentials *credentials =
Z_WRAPPED_GRPC_CHANNEL_CREDS_P(credentials_object);
credentials->wrapped = wrapped;
credentials->hashstr = hashstr;
credentials->has_call_creds = has_call_creds;
return credentials_object;
}
@ -106,7 +111,8 @@ PHP_METHOD(ChannelCredentials, setDefaultRootsPem) {
*/
PHP_METHOD(ChannelCredentials, createDefault) {
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);
}
@ -140,10 +146,24 @@ PHP_METHOD(ChannelCredentials, createSsl) {
"createSsl expects 3 optional strings", 1 TSRMLS_CC);
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(
pem_root_certs,
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);
}
@ -172,7 +192,9 @@ PHP_METHOD(ChannelCredentials, createComposite) {
grpc_channel_credentials *creds =
grpc_composite_channel_credentials_create(cred1->wrapped, cred2->wrapped,
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);
}

@ -38,6 +38,8 @@ extern zend_class_entry *grpc_ce_channel_credentials;
* with a PHP object */
PHP_GRPC_WRAP_OBJECT_START(wrapped_grpc_channel_credentials)
grpc_channel_credentials *wrapped;
char *hashstr;
zend_bool has_call_creds;
PHP_GRPC_WRAP_OBJECT_END(wrapped_grpc_channel_credentials)
#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_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)
@ -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) {
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

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

@ -74,4 +74,8 @@ ZEND_END_MODULE_GLOBALS(grpc)
#define GRPC_G(v) (grpc_globals.v)
#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 */

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

@ -25,17 +25,15 @@ class ChannelTest extends PHPUnit_Framework_TestCase
public function tearDown()
{
unset($this->channel);
if (!empty($this->channel)) {
$this->channel->close();
}
}
public function testInsecureCredentials()
{
$this->channel = new Grpc\Channel(
'localhost:0',
[
'credentials' => Grpc\ChannelCredentials::createInsecure(),
]
);
$this->channel = new Grpc\Channel('localhost:0',
['credentials' => Grpc\ChannelCredentials::createInsecure()]);
$this->assertSame('Grpc\Channel', get_class($this->channel));
}
@ -111,7 +109,7 @@ class ChannelTest extends PHPUnit_Framework_TestCase
*/
public function testInvalidConstructorWith()
{
$this->channel = new Grpc\Channel('localhost', 'invalid');
$this->channel = new Grpc\Channel('localhost:0', 'invalid');
$this->assertNull($this->channel);
}
@ -120,12 +118,8 @@ class ChannelTest extends PHPUnit_Framework_TestCase
*/
public function testInvalidCredentials()
{
$this->channel = new Grpc\Channel(
'localhost:0',
[
'credentials' => new Grpc\Timeval(100),
]
);
$this->channel = new Grpc\Channel('localhost:0',
['credentials' => new Grpc\Timeval(100)]);
}
/**
@ -133,12 +127,8 @@ class ChannelTest extends PHPUnit_Framework_TestCase
*/
public function testInvalidOptionsArray()
{
$this->channel = new Grpc\Channel(
'localhost:0',
[
'abc' => [],
]
);
$this->channel = new Grpc\Channel('localhost:0',
['abc' => []]);
}
/**
@ -170,4 +160,431 @@ class ChannelTest extends PHPUnit_Framework_TestCase
['credentials' => Grpc\ChannelCredentials::createInsecure()]);
$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()
{
unset($this->channel);
unset($this->server);
$this->channel->close();
}
public function testSimpleRequestBody()
@ -516,7 +515,7 @@ class EndToEndTest extends PHPUnit_Framework_TestCase
$this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(500000); // should timeout
$delta = new Grpc\Timeval(50000); // should timeout
$deadline = $now->add($delta);
$this->assertFalse($this->channel->watchConnectivityState(
@ -545,7 +544,7 @@ class EndToEndTest extends PHPUnit_Framework_TestCase
$this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(100000);
$delta = new Grpc\Timeval(50000);
$deadline = $now->add($delta);
$this->assertFalse($this->channel->watchConnectivityState(

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

@ -92,6 +92,9 @@ CORE_SOURCE_FILES = [
'src/core/lib/iomgr/ev_windows.c',
'src/core/lib/iomgr/exec_ctx.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/iomgr.c',
'src/core/lib/iomgr/iomgr_posix.c',

@ -15,11 +15,284 @@
import abc
from google.protobuf import descriptor
import six
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)):
"""A simulation of time.
@ -117,3 +390,19 @@ def strict_fake_time(now):
"""
from grpc_testing import _time
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': [
'services.proto',
],
'tests.testing.proto': [
'requests.proto',
'services.proto',
],
'tests.unit': [
'credentials/ca.pem',
'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.beta_python_plugin_test.PythonPluginTest",
"reflection._reflection_servicer_test.ReflectionServicerTest",
"testing._client_test.ClientTest",
"testing._time_test.StrictFakeTimeTest",
"testing._time_test.StrictRealTimeTest",
"unit._api_test.AllTest",

@ -42,12 +42,9 @@
s.header_dir = name
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.default_subspec = 'Main'
# Certificates, to be able to establish TLS connections:
s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] }
@ -56,4 +53,22 @@
# This is needed by all pods that depend on gRPC-RxLibrary:
'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

@ -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(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr,
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_ASSERT(0 == strcmp("test", addr));
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_exec_ctx *exec_ctx, const char *dns_server, const char *addr,
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));
r->addr = gpr_strdup(addr);
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)(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr,
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) {
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(
grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr,
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")) {
return iomgr_dns_lookup_ares(exec_ctx, dns_server, addr, default_port,
interested_parties, on_done, lb_addrs,
check_grpclb);
check_grpclb, service_config_json);
}
grpc_error *error = GRPC_ERROR_NONE;

@ -73,6 +73,21 @@ static void test_alarm(void) {
GPR_ASSERT(ev.success == 0);
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);
}

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

@ -79,7 +79,7 @@ static void chose_port(int 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();
if (port != 0) {
chose_port(port);
@ -88,7 +88,7 @@ int grpc_pick_unused_port(void) {
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();
if (port == 0) {
fprintf(stderr,
@ -101,6 +101,31 @@ int grpc_pick_unused_port_or_die(void) {
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 */

@ -23,6 +23,12 @@
extern "C" {
#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
0 on failure. */
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. */
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
}
#endif

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

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

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

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

@ -20,9 +20,9 @@ grpc_cc_test(
name = "server_builder_test",
srcs = ["server_builder_test.cc"],
deps = [
"//:grpc++",
"//:grpc++_unsecure",
"//src/proto/grpc/testing:echo_proto",
"//test/core/util:grpc_test_util",
"//test/core/util:grpc_test_util_unsecure",
],
external_deps = [
"gtest",
@ -33,9 +33,9 @@ grpc_cc_test(
name = "server_request_call_test",
srcs = ["server_request_call_test.cc"],
deps = [
"//:grpc++",
"//:grpc++_unsecure",
"//src/proto/grpc/testing:echo_proto",
"//test/core/util:grpc_test_util",
"//test/core/util:grpc_test_util_unsecure",
],
external_deps = [
"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(
name = "test_config",
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(
name = "test_util",
srcs = [
"byte_buffer_proto_helper.cc",
srcs = GRPCXX_TESTUTIL_SRCS + [
"create_test_channel.cc",
"string_ref_helper.cc",
"subprocess.cc",
"test_credentials_provider.cc",
],
hdrs = [
"byte_buffer_proto_helper.h",
hdrs = GRPCXX_TESTUTIL_HDRS + [
"create_test_channel.h",
"string_ref_helper.h",
"subprocess.h",
"test_credentials_provider.h",
],
deps = [
"//:grpc++",
"//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 = [
"protobuf",

@ -63,6 +63,42 @@ TEST_F(SliceTest, StaticBuf) {
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) {
grpc_slice s = grpc_slice_from_copied_string(kContent);
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/exec_ctx.h \
src/core/lib/iomgr/executor.h \
src/core/lib/iomgr/gethostname.h \
src/core/lib/iomgr/iocp_windows.h \
src/core/lib/iomgr/iomgr.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/time_precise.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/call.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/executor.c \
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.h \
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/surface/README.md \
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.h \
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
sudo easy_install --upgrade google-api-python-client
export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json
gcloud auth activate-service-account --key-file=$GOOGLE_APPLICATION_CREDENTIALS
# required to build protobuf
brew install gflags

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

@ -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/exec_ctx.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/iomgr.c",
"src/core/lib/iomgr/iomgr_posix.c",
@ -7979,6 +7982,7 @@
"src/core/lib/iomgr/ev_posix.h",
"src/core/lib/iomgr/exec_ctx.h",
"src/core/lib/iomgr/executor.h",
"src/core/lib/iomgr/gethostname.h",
"src/core/lib/iomgr/iocp_windows.h",
"src/core/lib/iomgr/iomgr.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_internal.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/call.h",
"src/core/lib/surface/call_test_only.h",
@ -8107,6 +8112,7 @@
"src/core/lib/iomgr/ev_posix.h",
"src/core/lib/iomgr/exec_ctx.h",
"src/core/lib/iomgr/executor.h",
"src/core/lib/iomgr/gethostname.h",
"src/core/lib/iomgr/iocp_windows.h",
"src/core/lib/iomgr/iomgr.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_internal.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/call.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()
query = """
SELECT
test_name,
SUM(result != 'PASSED'
AND result != 'SKIPPED') AS count_failed,
FROM
[grpc-testing:jenkins_test_results.aggregate_results]
WHERE
timestamp >= DATE_ADD(CURRENT_DATE(), -1, "WEEK")
AND NOT REGEXP_MATCH(job_name, '.*portability.*')
GROUP BY
test_name
HAVING
count_failed > 0"""
SELECT
filtered_test_name,
FROM (
SELECT
REGEXP_REPLACE(test_name, r'/\d+', '') AS filtered_test_name,
result
FROM
[grpc-testing:jenkins_test_results.aggregate_results]
WHERE
timestamp >= DATE_ADD(CURRENT_DATE(), -1, "WEEK")
AND NOT REGEXP_MATCH(job_name, '.*portability.*') )
GROUP BY
filtered_test_name
HAVING
SUM(result != 'PASSED' AND result != 'SKIPPED') > 0"""
if limit:
query += " limit {}".format(limit)
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\exec_ctx.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\iomgr.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_internal.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\call.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">
<Filter>src\core\lib\iomgr</Filter>
</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">
<Filter>src\core\lib\iomgr</Filter>
</ClInclude>
@ -869,6 +872,9 @@
<ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h">
<Filter>src\core\lib\slice</Filter>
</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">
<Filter>src\core\lib\surface</Filter>
</ClInclude>

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

Loading…
Cancel
Save