diff --git a/.pylintrc b/.pylintrc index 05b4e685fb0549a5722edf75781f530b4ea78426..453b45aab52879c68506e214d54fb8846222df4b 100644 --- a/.pylintrc +++ b/.pylintrc @@ -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, diff --git a/BUILD b/BUILD index 67ba39263e6d23df81b7f730f04f01fd0ffb2ece..19d019bc51dcdfb077105d50ec6b83e412524134 100644 --- a/BUILD +++ b/BUILD @@ -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", diff --git a/CMakeLists.txt b/CMakeLists.txt index 534b906a2501d3d7082038b998b3abd284d3e0c1..128f44c277d597e3eff969135a4fdfbab75edb53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/Makefile b/Makefile index 8fd84869ccf515d8bc138a054153d6426d426f10..0d5dad2a4b12a65a21f769f12cc1a35c3d63e280 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/binding.gyp b/binding.gyp index eadc86ce240487e082557ea0eeec8d63119d6ff9..226e745844c3ba89810c742f83490b23d9804cd8 100644 --- a/binding.gyp +++ b/binding.gyp @@ -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', diff --git a/build.yaml b/build.yaml index 13d7074bd6aee03b24a589ac1bc13aa85e8adc2b..0c326fee028cfeb036ce1fff7354e3831a1f88ab 100644 --- a/build.yaml +++ b/build.yaml @@ -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 diff --git a/config.m4 b/config.m4 index 4f0bce6e1e2ca879129a8e965ffdbacdbd7a2604..066339423c219f07c9a9140ba3bcddcb6c9d232c 100644 --- a/config.m4 +++ b/config.m4 @@ -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 \ diff --git a/config.w32 b/config.w32 index 0f059ea149c84cbc7f9de192ddc7f7a2dfe0c778..12db12cae8728a8d0819a6398b13be0c061d54c4 100644 --- a/config.w32 +++ b/config.w32 @@ -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 " + diff --git a/doc/environment_variables.md b/doc/environment_variables.md index 036824d573fd3096e27f8d144f97b5f8ccf5b335..a2cde92736152a882e2f902b9285964ad0685baa 100644 --- a/doc/environment_variables.md +++ b/doc/environment_variables.md @@ -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 diff --git a/doc/epoll-polling-engine.md b/doc/epoll-polling-engine.md index ab7030a211d464aadd89e674706872f87c35ffc0..1f5d855743ad94b95874443ea8d6d35af3004bcb 100644 --- a/doc/epoll-polling-engine.md +++ b/doc/epoll-polling-engine.md @@ -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 diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 84dd391b756ddb36232f751cfaa18ba6a62cd82d..0815e83467b67517a7b723422db2a8cea6a29e8b 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -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', diff --git a/gRPC.podspec b/gRPC.podspec index 4c83ccc7a0fe3dcd26d028d6d36e83e87674a169..22ecdcf66f1f01234589c71aa57e57ea44f2e590 100644 --- a/gRPC.podspec +++ b/gRPC.podspec @@ -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 diff --git a/grpc.gemspec b/grpc.gemspec index 2dec2e342cc72b61cc53bbda87c05399dd57c3a4..089d6d32143940b15452e9761551dec7b4d737d2 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -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 ) diff --git a/include/grpc++/impl/codegen/call.h b/include/grpc++/impl/codegen/call.h index f6eefb9f7ff7b748cab6689b29a501aa0dc50ff8..33d8f4ce65e05602a6ce70833cff55d412f67afe 100644 --- a/include/grpc++/impl/codegen/call.h +++ b/include/grpc++/impl/codegen/call.h @@ -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_; }; diff --git a/include/grpc++/support/slice.h b/include/grpc++/support/slice.h index 0b4ba7cecb4f780b931905c09ae56595dd101d9b..bbf97f280ef7dbba4958c033e4ca5c39a4e92a9e 100644 --- a/include/grpc++/support/slice.h +++ b/include/grpc++/support/slice.h @@ -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_); } diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h index bb3c90e303e8800d8fbdd000de61e9e1650c9574..2b2036b24f035a2f1a610d9647e6a18702bf40ac 100644 --- a/include/grpc/impl/codegen/grpc_types.h +++ b/include/grpc/impl/codegen/grpc_types.h @@ -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. */ diff --git a/package.xml b/package.xml index 0ebb5035948e8390fa053789063dd0102b8e5f26..09f549832d4a074435bb19a55a0d1e8ab8cd1502 100644 --- a/package.xml +++ b/package.xml @@ -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" /> diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c index 04a7852323d54579004cf03b779a7011638f7b71..f1480bb1ae4a29cec9d6d327c65c14b8fe013b8a 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c @@ -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, diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c index 6ec3790a5fec9d3d6f915ab54e861a89341a0e41..e65723a63be87f5c9419e0ad7f12222878c54377 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c @@ -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)( diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h index 5d2d6c993bd3ff8cdb16ebb6ea54f3682600dbc3..108333047d313efe70db925ac73a357efd73835c 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h @@ -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, diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c index b67636a3e443e17d8c2e239decd29ca22c7aae1e..f2587c4520fc6b42c0a7d7962e10016cc6667da5 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c @@ -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) {} diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.c b/src/core/ext/transport/cronet/transport/cronet_transport.c index 776e0765fe375eb88af14fa114a4df5b3bc8222d..abb558982bceb4cea8d5bdaa902d920da17308db 100644 --- a/src/core/ext/transport/cronet/transport/cronet_transport.c +++ b/src/core/ext/transport/cronet/transport/cronet_transport.c @@ -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); } diff --git a/src/core/lib/iomgr/ev_epoll1_linux.c b/src/core/lib/iomgr/ev_epoll1_linux.c index dc48d73df9045047588dcc8b6bebd6361dbe4c5e..90e0ce36cd729d56ef26b4dbdc69057be3103482 100644 --- a/src/core/lib/iomgr/ev_epoll1_linux.c +++ b/src/core/lib/iomgr/ev_epoll1_linux.c @@ -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); diff --git a/src/core/lib/iomgr/gethostname.h b/src/core/lib/iomgr/gethostname.h new file mode 100644 index 0000000000000000000000000000000000000000..9c6b9d8d420bae574e3b1f96919f08e49d76322e --- /dev/null +++ b/src/core/lib/iomgr/gethostname.h @@ -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 */ diff --git a/src/core/lib/iomgr/gethostname_fallback.c b/src/core/lib/iomgr/gethostname_fallback.c new file mode 100644 index 0000000000000000000000000000000000000000..6229461568b508eb7c73c2dfe725bf86f553587f --- /dev/null +++ b/src/core/lib/iomgr/gethostname_fallback.c @@ -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 diff --git a/src/core/lib/iomgr/gethostname_host_name_max.c b/src/core/lib/iomgr/gethostname_host_name_max.c new file mode 100644 index 0000000000000000000000000000000000000000..4d0511412ec4b12f7e993d9a61e89c01bd52d8aa --- /dev/null +++ b/src/core/lib/iomgr/gethostname_host_name_max.c @@ -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 diff --git a/src/core/lib/iomgr/gethostname_sysconf.c b/src/core/lib/iomgr/gethostname_sysconf.c new file mode 100644 index 0000000000000000000000000000000000000000..51bac5d69dd8e51c92c5d0e06700340e10ab2376 --- /dev/null +++ b/src/core/lib/iomgr/gethostname_sysconf.c @@ -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 diff --git a/src/core/lib/iomgr/port.h b/src/core/lib/iomgr/port.h index c12058f890b118d08fe5767f75f793ded0488deb..42033d0ba4b9d99691bb798e6c35e3af3accf1ec 100644 --- a/src/core/lib/iomgr/port.h +++ b/src/core/lib/iomgr/port.h @@ -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 */ diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_common.c b/src/core/lib/iomgr/tcp_server_utils_posix_common.c index dbb43186bde45a33b65e59531014bb6b8a1e420a..ad535bc43ea8cff488989952ecf26a903e4d2fdd 100644 --- a/src/core/lib/iomgr/tcp_server_utils_posix_common.c +++ b/src/core/lib/iomgr/tcp_server_utils_posix_common.c @@ -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 */ diff --git a/src/core/lib/surface/alarm.c b/src/core/lib/surface/alarm.c index 55934964f3a9c2ffdf6d432191cb740465235258..7d60b1de17e9f1d256f610ab526ea18ff2e7bd94 100644 --- a/src/core/lib/surface/alarm.c +++ b/src/core/lib/surface/alarm.c @@ -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"); } diff --git a/src/core/lib/surface/alarm_internal.h b/src/core/lib/surface/alarm_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..7f2126c5c915b124c4f40e5787560ebac23af241 --- /dev/null +++ b/src/core/lib/surface/alarm_internal.h @@ -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 */ diff --git a/src/core/lib/surface/init.c b/src/core/lib/surface/init.c index db111e597f6a634a70193e6f76c34754a2f7b649..d199ac060e62116c40cc38165daa9df4c9c106f6 100644 --- a/src/core/lib/surface/init.c +++ b/src/core/lib/surface/init.c @@ -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); diff --git a/src/cpp/util/slice_cc.cc b/src/cpp/util/slice_cc.cc index 56e0328b949bb55a1823ad1d25896af5867fc3aa..486d0cdf0ec05abccf7c5c717b0b56e4338631e1 100644 --- a/src/cpp/util/slice_cc.cc +++ b/src/cpp/util/slice_cc.cc @@ -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 diff --git a/src/objective-c/GRPCClient/GRPCCall+GID.h b/src/objective-c/GRPCClient/GRPCCall+GID.h new file mode 100644 index 0000000000000000000000000000000000000000..3ee732e79ea3e3a62e63caa07d925ec4c8458674 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCall+GID.h @@ -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 diff --git a/src/objective-c/GRPCClient/GRPCCall+GID.m b/src/objective-c/GRPCClient/GRPCCall+GID.m new file mode 100644 index 0000000000000000000000000000000000000000..030737147b58255109950d07d9634b11c45fa076 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCall+GID.m @@ -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 diff --git a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h index 65465e9523621ed03f50b2cf44451b159469b000..adb1042aa0ebbbce8a48c729819c973e8e0c587d 100644 --- a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h +++ b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h @@ -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 diff --git a/src/objective-c/GRPCClient/GRPCCall+OAuth2.m b/src/objective-c/GRPCClient/GRPCCall+OAuth2.m index eaa7465087019c6f1f2cd2d6ff59524a250dd120..8451ebe870e34c1d8d2a0704c7d091e7cb86d4d4 100644 --- a/src/objective-c/GRPCClient/GRPCCall+OAuth2.m +++ b/src/objective-c/GRPCClient/GRPCCall+OAuth2.m @@ -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 diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index 178a446c8b406b783aaf416ad826bcd329da2771..4d90cfd3847924146d6f4053c6eab8994e3b3bf4 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -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 diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index 872362419eb3a450ad8a3a45557dd5a8aeee75a2..436c19e354a609e183d71222a659da87e93b04d1 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -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. diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m index 7640a64d6db08d3f2a2f336a2565abf0f7b25c3d..5de1d8fff5f51dea9e9ef8458e890746a07e33ff 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -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); diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c index 2f67e5cee79760302bceab76d96917f5c5a1f065..c4997f720d1921d54f0481058976208771da5e8c 100644 --- a/src/php/ext/grpc/call.c +++ b/src/php/ext/grpc/call.c @@ -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); } /** diff --git a/src/php/ext/grpc/call_credentials.c b/src/php/ext/grpc/call_credentials.c index a990206c08602a1ea503f28273013ba4ae5007ca..f46091d70988659f0efd8725c4995c8a308bd2ea 100644 --- a/src/php/ext/grpc/call_credentials.c +++ b/src/php/ext/grpc/call_credentials.c @@ -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) diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c index 6c432d2818744a8c6d230db9920f730ec65b7bbf..f1187e8722f227c610844becefe91cf75f9a6161 100644 --- a/src/php/ext/grpc/channel.c +++ b/src/php/ext/grpc/channel.c @@ -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; } diff --git a/src/php/ext/grpc/channel.h b/src/php/ext/grpc/channel.h index 45c97441350e0121ebebeeaacafb4d5f253c5d7b..69adc4782c5189dd7c1399eb7f50230fdee70728 100755 --- a/src/php/ext/grpc/channel.h +++ b/src/php/ext/grpc/channel.h @@ -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_ */ diff --git a/src/php/ext/grpc/channel_credentials.c b/src/php/ext/grpc/channel_credentials.c index 40629c8b0002f4877c58a197a218a88aabc7f879..19e1cefb6f59290ab8a492671d82420e426524eb 100644 --- a/src/php/ext/grpc/channel_credentials.c +++ b/src/php/ext/grpc/channel_credentials.c @@ -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); } diff --git a/src/php/ext/grpc/channel_credentials.h b/src/php/ext/grpc/channel_credentials.h index 28c7f2c1d3882a8c165238f80df028ae895e4796..357d732642f4f0ece785ef8d8c373c3b62367c75 100755 --- a/src/php/ext/grpc/channel_credentials.h +++ b/src/php/ext/grpc/channel_credentials.h @@ -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 diff --git a/src/php/ext/grpc/php7_wrapper.h b/src/php/ext/grpc/php7_wrapper.h index d4b4c262a7eaadf009c8e7c41dc0039a01dd0d30..96091f9dadb2e63719a4548c00bec59d518cf0e8 100644 --- a/src/php/ext/grpc/php7_wrapper.h +++ b/src/php/ext/grpc/php7_wrapper.h @@ -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 diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c index 281b9e6aba35288061ce5e0c2dc205702b280152..a96daf7d9b858373b602891f5fd3973ab8de52be 100644 --- a/src/php/ext/grpc/php_grpc.c +++ b/src/php/ext/grpc/php_grpc.c @@ -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); diff --git a/src/php/ext/grpc/php_grpc.h b/src/php/ext/grpc/php_grpc.h index ed846fdba4b308f62c311e6cea5b3f0ef8864023..32329178dc4ee3b92d308fbf50ac2cce76068834 100644 --- a/src/php/ext/grpc/php_grpc.h +++ b/src/php/ext/grpc/php_grpc.h @@ -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 */ diff --git a/src/php/tests/unit_tests/CallTest.php b/src/php/tests/unit_tests/CallTest.php index 3270e73f8210cbc00e451ef0eea8037443a065fe..c5e1890a98bb2f2c01f6e5239091c20d321815a0 100644 --- a/src/php/tests/unit_tests/CallTest.php +++ b/src/php/tests/unit_tests/CallTest.php @@ -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() diff --git a/src/php/tests/unit_tests/ChannelTest.php b/src/php/tests/unit_tests/ChannelTest.php index 34e6185031a65471dc4b9ecfdec80f2d4fee6a88..400df0fb66da21e69313e144ea1345f47fdb8d80 100644 --- a/src/php/tests/unit_tests/ChannelTest.php +++ b/src/php/tests/unit_tests/ChannelTest.php @@ -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(); + } } diff --git a/src/php/tests/unit_tests/EndToEndTest.php b/src/php/tests/unit_tests/EndToEndTest.php index 43d54d9ee60e82df05df2b964ac11e8912b3b5ee..b54f1d87c9f6aa93c4e69a4b78d37eaf870461ee 100644 --- a/src/php/tests/unit_tests/EndToEndTest.php +++ b/src/php/tests/unit_tests/EndToEndTest.php @@ -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( diff --git a/src/php/tests/unit_tests/SecureEndToEndTest.php b/src/php/tests/unit_tests/SecureEndToEndTest.php index 0fecbfb3dd3ab2ee92c34b95c62b967500ab1640..dff4e878ea1dea3534fc95eba67a8e88d3668603 100644 --- a/src/php/tests/unit_tests/SecureEndToEndTest.php +++ b/src/php/tests/unit_tests/SecureEndToEndTest.php @@ -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() diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 50818dce4c742e17651db95e38627a6560ac0eb0..bf831d063bfb475dfef1272f5f5bc52a3f001b05 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -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', diff --git a/src/python/grpcio_testing/grpc_testing/__init__.py b/src/python/grpcio_testing/grpc_testing/__init__.py index c5a17f457a481416c89cbb9cff45e4478ed3b085..14e25f09e239d2cdad77e0b6076075c8a324f2b3 100644 --- a/src/python/grpcio_testing/grpc_testing/__init__.py +++ b/src/python/grpcio_testing/grpc_testing/__init__.py @@ -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) diff --git a/src/python/grpcio_testing/grpc_testing/_channel/__init__.py b/src/python/grpcio_testing/grpc_testing/_channel/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8011975d0aa99463d4b9d44180c15da62af965c7 --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_channel/__init__.py @@ -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 diff --git a/src/python/grpcio_testing/grpc_testing/_channel/_channel.py b/src/python/grpcio_testing/grpc_testing/_channel/_channel.py new file mode 100644 index 0000000000000000000000000000000000000000..fbd064db88582e4c1db5c844b997f9a257a1e093 --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_channel/_channel.py @@ -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 diff --git a/src/python/grpcio_testing/grpc_testing/_channel/_channel_rpc.py b/src/python/grpcio_testing/grpc_testing/_channel/_channel_rpc.py new file mode 100644 index 0000000000000000000000000000000000000000..762b6a035bf6004ae845fca202c2c49200e2e9ab --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_channel/_channel_rpc.py @@ -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) diff --git a/src/python/grpcio_testing/grpc_testing/_channel/_channel_state.py b/src/python/grpcio_testing/grpc_testing/_channel/_channel_state.py new file mode 100644 index 0000000000000000000000000000000000000000..569c41d79d7a32e4524bfc104753c93e0b10e7c7 --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_channel/_channel_state.py @@ -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() diff --git a/src/python/grpcio_testing/grpc_testing/_channel/_invocation.py b/src/python/grpcio_testing/grpc_testing/_channel/_invocation.py new file mode 100644 index 0000000000000000000000000000000000000000..ebce652eeb0a23a4043e8773395df26b6b2ccdd4 --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_channel/_invocation.py @@ -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) diff --git a/src/python/grpcio_testing/grpc_testing/_channel/_multi_callable.py b/src/python/grpcio_testing/grpc_testing/_channel/_multi_callable.py new file mode 100644 index 0000000000000000000000000000000000000000..fe69257f5be6b1982d5e8520a13920d3ae16a799 --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_channel/_multi_callable.py @@ -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 diff --git a/src/python/grpcio_testing/grpc_testing/_channel/_rpc_state.py b/src/python/grpcio_testing/grpc_testing/_channel/_rpc_state.py new file mode 100644 index 0000000000000000000000000000000000000000..e1fa49a2a800884ddbab4a3e8bacad451f962400 --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_channel/_rpc_state.py @@ -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() diff --git a/src/python/grpcio_testing/grpc_testing/_common.py b/src/python/grpcio_testing/grpc_testing/_common.py new file mode 100644 index 0000000000000000000000000000000000000000..cb4a7f5fa2f83856d191e9e310a8c5207a4b1bb4 --- /dev/null +++ b/src/python/grpcio_testing/grpc_testing/_common.py @@ -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() diff --git a/src/python/grpcio_tests/setup.py b/src/python/grpcio_tests/setup.py index adc909ccdc758aa5194c6e9b87dfe2e9d7e827f5..debe14c40e5b19548f2cbb425863f2e533f48578 100644 --- a/src/python/grpcio_tests/setup.py +++ b/src/python/grpcio_tests/setup.py @@ -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', diff --git a/src/python/grpcio_tests/tests/testing/_application_common.py b/src/python/grpcio_tests/tests/testing/_application_common.py new file mode 100644 index 0000000000000000000000000000000000000000..4e98879607a54832398d61b8e5cf85809c646016 --- /dev/null +++ b/src/python/grpcio_tests/tests/testing/_application_common.py @@ -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 diff --git a/src/python/grpcio_tests/tests/testing/_application_testing_common.py b/src/python/grpcio_tests/tests/testing/_application_testing_common.py new file mode 100644 index 0000000000000000000000000000000000000000..9c9e485a7830cf7ffb19bf027c940b50774cfa1e --- /dev/null +++ b/src/python/grpcio_tests/tests/testing/_application_testing_common.py @@ -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'] diff --git a/src/python/grpcio_tests/tests/testing/_client_application.py b/src/python/grpcio_tests/tests/testing/_client_application.py new file mode 100644 index 0000000000000000000000000000000000000000..aff32fb4dc5ae9fe58598e31389015084d102dff --- /dev/null +++ b/src/python/grpcio_tests/tests/testing/_client_application.py @@ -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()) diff --git a/src/python/grpcio_tests/tests/testing/_client_test.py b/src/python/grpcio_tests/tests/testing/_client_test.py new file mode 100644 index 0000000000000000000000000000000000000000..172f386d7b13445ce8c93d18fb0394fcd12777d1 --- /dev/null +++ b/src/python/grpcio_tests/tests/testing/_client_test.py @@ -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) diff --git a/src/python/grpcio_tests/tests/testing/proto/__init__.py b/src/python/grpcio_tests/tests/testing/proto/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1e120359cf9513d6291cf27d9d6c2aa21cbc21d5 --- /dev/null +++ b/src/python/grpcio_tests/tests/testing/proto/__init__.py @@ -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. diff --git a/src/python/grpcio_tests/tests/testing/proto/requests.proto b/src/python/grpcio_tests/tests/testing/proto/requests.proto new file mode 100644 index 0000000000000000000000000000000000000000..54a60bff869b08cd34e766bb9346465c73b0f4a7 --- /dev/null +++ b/src/python/grpcio_tests/tests/testing/proto/requests.proto @@ -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; +} diff --git a/src/python/grpcio_tests/tests/testing/proto/services.proto b/src/python/grpcio_tests/tests/testing/proto/services.proto new file mode 100644 index 0000000000000000000000000000000000000000..cb15c0d1ce3215917aca53a980797e32f5571179 --- /dev/null +++ b/src/python/grpcio_tests/tests/testing/proto/services.proto @@ -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); +} diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json index f86eeb76c70f0a57462a320508e2ff49976d55ec..c10719b86f16174ecf8f50e38e61fa1bfb3a444e 100644 --- a/src/python/grpcio_tests/tests/tests.json +++ b/src/python/grpcio_tests/tests/tests.json @@ -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", diff --git a/templates/gRPC.podspec.template b/templates/gRPC.podspec.template index 62a6d37c3c70b2b31f3bebe0f3c2d747212401cd..5c92f9f9c42b41f57f6e6896d461d9de67176e3c 100644 --- a/templates/gRPC.podspec.template +++ b/templates/gRPC.podspec.template @@ -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 diff --git a/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c b/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c index 6e3d69c2653182a5010a995b5c8ea69d11783f1e..364e1809633aea9b01e72d59bd04aa50157577ba 100644 --- a/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c +++ b/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c @@ -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; diff --git a/test/core/end2end/fuzzers/api_fuzzer.c b/test/core/end2end/fuzzers/api_fuzzer.c index 01fa4f748f5d84cfefed00f6960d69ca07b49198..1228c9fe9ae48207964ecd05995d3f5752e1b6e4 100644 --- a/test/core/end2end/fuzzers/api_fuzzer.c +++ b/test/core/end2end/fuzzers/api_fuzzer.c @@ -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; diff --git a/test/core/end2end/goaway_server_test.c b/test/core/end2end/goaway_server_test.c index bf90e2525d432933613e1a66c0f6232e062f61f2..c3aca132495983bc967ab6fb047b926361f84fbe 100644 --- a/test/core/end2end/goaway_server_test.c +++ b/test/core/end2end/goaway_server_test.c @@ -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; diff --git a/test/core/surface/alarm_test.c b/test/core/surface/alarm_test.c index baaa05928e0df0811c51a0167061b9ededdd7ed4..6971d920745c63b916fcdd9ea4fbd7497cd223fe 100644 --- a/test/core/surface/alarm_test.c +++ b/test/core/surface/alarm_test.c @@ -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); } diff --git a/test/core/util/BUILD b/test/core/util/BUILD index 9d899bc7bebea1b930470f267cd400690dac925d..1268c2cd103407c679e4a893233457fcf221b48b 100644 --- a/test/core/util/BUILD +++ b/test/core/util/BUILD @@ -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"], diff --git a/test/core/util/port.c b/test/core/util/port.c index f430c543bd11411ad41f7255821af26fe59a34a3..b1fc722858ea19a4d2d416842baaf272a1885d65 100644 --- a/test/core/util/port.c +++ b/test/core/util/port.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 */ diff --git a/test/core/util/port.h b/test/core/util/port.h index 154e8f830c6079d87377f993cac29729dcf714b8..602099dea6d4b5f75003e802abcfa1cac9a6afd3 100644 --- a/test/core/util/port.h +++ b/test/core/util/port.h @@ -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 diff --git a/test/cpp/common/BUILD b/test/cpp/common/BUILD index bd1173322a7464a469924c5ec30b3c5c4e9a46fa..be9c279f13e24d3585e396cfe48f2529cdf0ece7 100644 --- a/test/cpp/common/BUILD +++ b/test/cpp/common/BUILD @@ -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 = [ diff --git a/test/cpp/end2end/grpclb_end2end_test.cc b/test/cpp/end2end/grpclb_end2end_test.cc index 4fef53550695cef91d60f36b2d9a2fee1e391dcc..b5cff664f636c248ad06690b52a1305ad959fe0d 100644 --- a/test/cpp/end2end/grpclb_end2end_test.cc +++ b/test/cpp/end2end/grpclb_end2end_test.cc @@ -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_; }; diff --git a/test/cpp/microbenchmarks/BUILD b/test/cpp/microbenchmarks/BUILD index 5e1bcee89ff2bde20f43dedabf82302ff270f459..442da38426d5e10f5119e598246ce63d77672d2b 100644 --- a/test/cpp/microbenchmarks/BUILD +++ b/test/cpp/microbenchmarks/BUILD @@ -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", diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc index f7dda0f7589fb81a077e15fae50c768274d3f6cb..265f174cc5c7d853c0a7502ec3c552c6d04090f8 100644 --- a/test/cpp/qps/client_async.cc +++ b/test/cpp/qps/client_async.cc @@ -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_; diff --git a/test/cpp/server/BUILD b/test/cpp/server/BUILD index 512241e3504f5b189a8a9bd93fbc2f09e0df27de..3f63be2aa3304b3f80fa876568c6cdcd831e4cf5 100644 --- a/test/cpp/server/BUILD +++ b/test/cpp/server/BUILD @@ -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", diff --git a/test/cpp/util/BUILD b/test/cpp/util/BUILD index c9b0d6c1df7fc42f9df66e6f4be8e16c32892937..fbdec0569898ba6196f05de36fd38bd37a28fcea 100644 --- a/test/cpp/util/BUILD +++ b/test/cpp/util/BUILD @@ -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", diff --git a/test/cpp/util/slice_test.cc b/test/cpp/util/slice_test.cc index 9e3329fab02fc739c4b2b3575abb3052979ca0dc..8a8962d7ee07f70a1e5e725d89f39aed40686209 100644 --- a/test/cpp/util/slice_test.cc +++ b/test/cpp/util/slice_test.cc @@ -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); diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index 335243114932ce9f9b96845923acaf4e3b04eb1b..7290f20c86bbd2583112ab118796d9de0af6ce8e 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -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 \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 2c8ee4b43de3132f29210fe623bfe8362b8a6fe0..4901fc92182542aafcd098eb422019f7a63a2584 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -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 \ diff --git a/tools/internal_ci/helper_scripts/prepare_build_macos_interop_rc b/tools/internal_ci/helper_scripts/prepare_build_macos_interop_rc new file mode 100644 index 0000000000000000000000000000000000000000..f467ac006af39494a10ee6c8b0c4255fcff2afb5 --- /dev/null +++ b/tools/internal_ci/helper_scripts/prepare_build_macos_interop_rc @@ -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) + diff --git a/tools/internal_ci/helper_scripts/prepare_build_macos_rc b/tools/internal_ci/helper_scripts/prepare_build_macos_rc index 00105d483831646701d270fbe29e9d9565d696fe..b48040f5e26d5f0ae21b7fe9a957e0124fc30ba8 100644 --- a/tools/internal_ci/helper_scripts/prepare_build_macos_rc +++ b/tools/internal_ci/helper_scripts/prepare_build_macos_rc @@ -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 diff --git a/tools/internal_ci/macos/grpc_basictests.cfg b/tools/internal_ci/linux/grpc_build_boringssl_at_head.cfg similarity index 74% rename from tools/internal_ci/macos/grpc_basictests.cfg rename to tools/internal_ci/linux/grpc_build_boringssl_at_head.cfg index 3faba2fc36ce9c0369f518e8c689f3f5a78c1e79..11c211fd2b438ec8ef3d9188289ecc02468b7c33 100644 --- a/tools/internal_ci/macos/grpc_basictests.cfg +++ b/tools/internal_ci/linux/grpc_build_boringssl_at_head.cfg @@ -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" } diff --git a/tools/internal_ci/linux/grpc_build_protobuf_at_head.cfg b/tools/internal_ci/linux/grpc_build_protobuf_at_head.cfg new file mode 100644 index 0000000000000000000000000000000000000000..2f08e15e6389e9871721205d7eff09a4f851a097 --- /dev/null +++ b/tools/internal_ci/linux/grpc_build_protobuf_at_head.cfg @@ -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" +} diff --git a/tools/internal_ci/linux/grpc_build_submodule_at_head.sh b/tools/internal_ci/linux/grpc_build_submodule_at_head.sh new file mode 100755 index 0000000000000000000000000000000000000000..8189e6876b0382becb4f3d3bf60d6d73a0bf5ffc --- /dev/null +++ b/tools/internal_ci/linux/grpc_build_submodule_at_head.sh @@ -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 + diff --git a/tools/internal_ci/macos/grpc_interop.sh b/tools/internal_ci/macos/grpc_interop.sh index 07601a67e4ae2b2027f2c3031693946b9eddd72d..b03401bd962f6be675b65bbd617b57e349d15caf 100755 --- a/tools/internal_ci/macos/grpc_interop.sh +++ b/tools/internal_ci/macos/grpc_interop.sh @@ -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 diff --git a/tools/run_tests/dockerize/build_interop_stress_image.sh b/tools/run_tests/dockerize/build_interop_stress_image.sh deleted file mode 100755 index acb566f5d54840fea001cba2cad72aebb77a7add..0000000000000000000000000000000000000000 --- a/tools/run_tests/dockerize/build_interop_stress_image.sh +++ /dev/null @@ -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 diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index 933ea89100528607bf9f1189377615812f205690..c1d68d273f55eef7c9edf5062b6bf90f4e7f5a7d 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -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", diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index 106c7e0699d6c22c70c281af98b95fe4290ff1fe..19186bea124aacf64310a7701e76ea9ab25913af 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -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) diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj b/vsprojects/vcxproj/grpc++/grpc++.vcxproj index 625c62adcd6bf2ce6dae3fb7087931bb118d886e..416adb4efdd4bc817f9b1c1b2dc3e4e526093f4d 100644 --- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj +++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj @@ -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" /> diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters index ff9913a362ad3bfb7dfe587696ef6d866fed41ef..0e33351e05551e987d81c82b22ac28a44c15bfdf 100644 --- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters +++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters @@ -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> diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj index baeb6e35b2d0244ec73b33235671a093d8c6d5a6..8ea35c18daa64a51b98ad655ce862a967b35d256 100644 --- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj +++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj @@ -444,6 +444,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" /> @@ -500,6 +501,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" /> diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters index 5820ce041419a26c0c1bd5633dee69657081a25c..edc3b81121a3d5edfc1fb26c5346b5461e97d375 100644 --- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters +++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters @@ -668,6 +668,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> @@ -836,6 +839,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> diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj index 35f7da7bf6dcddb9a1dea927dfc15bbe2be78802..6eab48c62b4bbde90cbd93734e8b12f35cfc3b12 100644 --- a/vsprojects/vcxproj/grpc/grpc.vcxproj +++ b/vsprojects/vcxproj/grpc/grpc.vcxproj @@ -402,6 +402,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" /> @@ -458,6 +459,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" /> @@ -583,6 +585,12 @@ </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.c"> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_fallback.c"> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_host_name_max.c"> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_sysconf.c"> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.c"> </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr.c"> diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters index 4c89d5cc36c642fcc26db28f0c27a054b252ca08..eaae1c6693ad28e5f9c9cb322953196916df65f1 100644 --- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters +++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters @@ -94,6 +94,15 @@ <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.c"> <Filter>src\core\lib\iomgr</Filter> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_fallback.c"> + <Filter>src\core\lib\iomgr</Filter> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_host_name_max.c"> + <Filter>src\core\lib\iomgr</Filter> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_sysconf.c"> + <Filter>src\core\lib\iomgr</Filter> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.c"> <Filter>src\core\lib\iomgr</Filter> </ClCompile> @@ -1169,6 +1178,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> @@ -1337,6 +1349,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> diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj index fa31dbe3e19fa400779bcc0aaa38ff7ebd3fde94..1766e2445d7d973bafa3c1eb1eec0ae907065653 100644 --- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj +++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj @@ -329,6 +329,12 @@ </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.c"> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_fallback.c"> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_host_name_max.c"> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_sysconf.c"> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.c"> </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr.c"> diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters index fc9f64a6146be322eeb215ae547fc99821c5683c..8516a07196d19e42ab1c84082d9adc99b3f30835 100644 --- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters +++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters @@ -151,6 +151,15 @@ <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.c"> <Filter>src\core\lib\iomgr</Filter> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_fallback.c"> + <Filter>src\core\lib\iomgr</Filter> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_host_name_max.c"> + <Filter>src\core\lib\iomgr</Filter> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_sysconf.c"> + <Filter>src\core\lib\iomgr</Filter> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.c"> <Filter>src\core\lib\iomgr</Filter> </ClCompile> diff --git a/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj b/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj index 457dbd57c74601dd7f643cdcc09cebcc5b6b69af..c92f0d123783a8e3d4c13ec56025356281ea6e4a 100644 --- a/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj +++ b/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj @@ -317,6 +317,12 @@ </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.c"> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_fallback.c"> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_host_name_max.c"> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_sysconf.c"> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.c"> </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr.c"> diff --git a/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj.filters index 49e886de3def3e1b685cab43a454a650f23fcaee..df34bdba88d77e3be8dc00fc6a2891a7d33ba5ce 100644 --- a/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj.filters +++ b/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj.filters @@ -136,6 +136,15 @@ <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.c"> <Filter>src\core\lib\iomgr</Filter> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_fallback.c"> + <Filter>src\core\lib\iomgr</Filter> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_host_name_max.c"> + <Filter>src\core\lib\iomgr</Filter> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_sysconf.c"> + <Filter>src\core\lib\iomgr</Filter> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.c"> <Filter>src\core\lib\iomgr</Filter> </ClCompile> diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj index c0ce8f0a4be6df43d8d1f075765ada6b7fdf915c..945ca2f3d72b2e58bf3b21887f5b1ef9b3af9438 100644 --- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj +++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj @@ -365,6 +365,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" /> @@ -421,6 +422,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" /> @@ -548,6 +550,12 @@ </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.c"> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_fallback.c"> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_host_name_max.c"> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_sysconf.c"> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.c"> </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr.c"> diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters index 2d2a8200e0a94ea088b155276d13c6525555d1de..deb3433168d440a2cdd75690c33382c907358370 100644 --- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters +++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters @@ -97,6 +97,15 @@ <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.c"> <Filter>src\core\lib\iomgr</Filter> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_fallback.c"> + <Filter>src\core\lib\iomgr</Filter> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_host_name_max.c"> + <Filter>src\core\lib\iomgr</Filter> + </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\gethostname_sysconf.c"> + <Filter>src\core\lib\iomgr</Filter> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.c"> <Filter>src\core\lib\iomgr</Filter> </ClCompile> @@ -992,6 +1001,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> @@ -1160,6 +1172,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>