aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Craig Tiller <ctiller@google.com>2015-07-16 08:45:15 -0700
committerGravatar Craig Tiller <ctiller@google.com>2015-07-16 08:45:15 -0700
commite0981dfa05e49acdd9fc5e8abb83a91d84d1a104 (patch)
tree09086d0cbdb4e130b6c66c837eed27d4f8c30bbc
parentb0c13ad7698e577298f199007cc2d7a0a3049d1c (diff)
parentf101af1ab4c553ff2a8cd8f72e8ca156ef188b86 (diff)
Merge github.com:grpc/grpc into i-want-to-wait-free
-rw-r--r--.gitignore2
-rw-r--r--Makefile32
-rw-r--r--doc/connection-backoff.md57
-rw-r--r--include/grpc++/impl/call.h141
-rw-r--r--include/grpc++/server_context.h6
-rw-r--r--include/grpc++/stream.h26
-rw-r--r--include/grpc/support/port_platform.h2
-rw-r--r--src/compiler/objective_c_generator.cc76
-rw-r--r--src/core/httpcli/httpcli.c1
-rw-r--r--src/core/iomgr/socket_windows.c30
-rw-r--r--src/core/iomgr/tcp_server_windows.c80
-rw-r--r--src/core/security/secure_transport_setup.c29
-rw-r--r--src/core/security/secure_transport_setup.h2
-rw-r--r--src/core/security/server_secure_chttp2.c47
-rw-r--r--src/core/support/stack_lockfree.c11
-rw-r--r--src/core/support/stack_lockfree.h10
-rw-r--r--src/core/surface/secure_channel_create.c1
-rw-r--r--src/core/transport/chttp2/frame_data.c9
-rw-r--r--src/cpp/server/async_server_context.cc99
-rw-r--r--src/cpp/server/server_context.cc7
-rw-r--r--src/csharp/Grpc.Auth/Grpc.Auth.csproj4
-rw-r--r--src/csharp/Grpc.Auth/Grpc.Auth.nuspec6
-rw-r--r--src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs1
-rw-r--r--src/csharp/Grpc.Core.Tests/ClientServerTest.cs6
-rw-r--r--src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj5
-rw-r--r--src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs17
-rw-r--r--src/csharp/Grpc.Core.Tests/PInvokeTest.cs12
-rw-r--r--src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs1
-rw-r--r--src/csharp/Grpc.Core.Tests/ServerTest.cs3
-rw-r--r--src/csharp/Grpc.Core/Calls.cs16
-rw-r--r--src/csharp/Grpc.Core/Channel.cs30
-rw-r--r--src/csharp/Grpc.Core/Grpc.Core.csproj2
-rw-r--r--src/csharp/Grpc.Core/Grpc.Core.nuspec6
-rw-r--r--src/csharp/Grpc.Core/GrpcEnvironment.cs110
-rw-r--r--src/csharp/Grpc.Core/Internal/AsyncCall.cs9
-rw-r--r--src/csharp/Grpc.Core/Internal/AsyncCallServer.cs9
-rw-r--r--src/csharp/Grpc.Core/Internal/CallSafeHandle.cs30
-rw-r--r--src/csharp/Grpc.Core/Internal/CompletionRegistry.cs12
-rw-r--r--src/csharp/Grpc.Core/Internal/DebugStats.cs35
-rw-r--r--src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs8
-rw-r--r--src/csharp/Grpc.Core/Internal/ServerCallHandler.cs26
-rw-r--r--src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs14
-rw-r--r--src/csharp/Grpc.Core/Properties/AssemblyInfo.cs1
-rw-r--r--src/csharp/Grpc.Core/Server.cs17
-rw-r--r--src/csharp/Grpc.Core/Version.cs6
-rw-r--r--src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj7
-rw-r--r--src/csharp/Grpc.Examples.MathClient/MathClient.cs2
-rw-r--r--src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs1
-rw-r--r--src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj3
-rw-r--r--src/csharp/Grpc.Examples.MathServer/MathServer.cs2
-rw-r--r--src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs1
-rw-r--r--src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj3
-rw-r--r--src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs3
-rw-r--r--src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs1
-rw-r--r--src/csharp/Grpc.Examples/Grpc.Examples.csproj3
-rw-r--r--src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs1
-rw-r--r--src/csharp/Grpc.HealthCheck.Tests/.gitignore2
-rw-r--r--src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj82
-rw-r--r--src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs97
-rw-r--r--src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs107
-rw-r--r--src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs11
-rw-r--r--src/csharp/Grpc.HealthCheck.Tests/packages.config5
-rw-r--r--src/csharp/Grpc.HealthCheck/.gitignore2
-rw-r--r--src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj79
-rw-r--r--src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec28
-rw-r--r--src/csharp/Grpc.HealthCheck/Health.cs687
-rw-r--r--src/csharp/Grpc.HealthCheck/HealthGrpc.cs78
-rw-r--r--src/csharp/Grpc.HealthCheck/HealthServiceImpl.cs132
-rw-r--r--src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs11
-rw-r--r--src/csharp/Grpc.HealthCheck/packages.config5
-rw-r--r--src/csharp/Grpc.HealthCheck/proto/health.proto52
-rw-r--r--src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj3
-rw-r--r--src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs1
-rw-r--r--src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj3
-rw-r--r--src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs1
-rw-r--r--src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj3
-rw-r--r--src/csharp/Grpc.IntegrationTesting/InteropClient.cs3
-rw-r--r--src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs3
-rw-r--r--src/csharp/Grpc.IntegrationTesting/InteropServer.cs2
-rw-r--r--src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs1
-rw-r--r--src/csharp/Grpc.Tools.nuspec4
-rw-r--r--src/csharp/Grpc.nuspec6
-rw-r--r--src/csharp/Grpc.sln65
-rw-r--r--src/csharp/build_packages.bat15
-rwxr-xr-xsrc/csharp/generate_proto_csharp.sh4
-rw-r--r--src/node/src/server.js6
-rwxr-xr-xsrc/php/bin/determine_extension_dir.sh10
-rwxr-xr-xsrc/php/bin/interop_client.sh2
-rwxr-xr-xsrc/php/bin/run_gen_code_test.sh6
-rwxr-xr-xsrc/php/bin/run_tests.sh2
-rw-r--r--src/python/src/grpc/_links/__init__.py30
-rw-r--r--src/python/src/grpc/_links/_lonely_invocation_link_test.py88
-rw-r--r--src/python/src/grpc/_links/_proto_scenarios.py261
-rw-r--r--src/python/src/grpc/_links/_transmission_test.py226
-rw-r--r--src/python/src/grpc/_links/invocation.py363
-rw-r--r--src/python/src/grpc/_links/service.py402
-rw-r--r--src/python/src/grpc/framework/common/test_constants.py37
-rw-r--r--src/python/src/grpc/framework/common/test_control.py87
-rw-r--r--src/python/src/grpc/framework/common/test_coverage.py116
-rw-r--r--src/python/src/grpc/framework/foundation/relay.py175
-rw-r--r--src/python/src/grpc/framework/interfaces/__init__.py30
-rw-r--r--src/python/src/grpc/framework/interfaces/links/__init__.py30
-rw-r--r--src/python/src/grpc/framework/interfaces/links/links.py124
-rw-r--r--src/python/src/grpc/framework/interfaces/links/test_cases.py332
-rw-r--r--src/python/src/grpc/framework/interfaces/links/test_utilities.py66
-rw-r--r--src/python/src/grpc/framework/interfaces/links/utilities.py44
-rw-r--r--src/python/src/setup.py4
-rwxr-xr-xsrc/ruby/grpc.gemspec2
-rw-r--r--src/ruby/lib/grpc/generic/rpc_server.rb10
-rw-r--r--templates/Makefile.template56
-rw-r--r--test/core/support/stack_lockfree_test.c30
-rw-r--r--test/core/util/test_config.c38
-rw-r--r--test/cpp/end2end/end2end_test.cc8
-rw-r--r--test/cpp/end2end/mock_test.cc4
-rwxr-xr-xtools/buildgen/generate_projects.py5
-rwxr-xr-xtools/jenkins/docker_run_jenkins.sh2
-rwxr-xr-xtools/jenkins/run_jenkins.sh11
-rwxr-xr-xtools/run_tests/jobset.py3
-rwxr-xr-xtools/run_tests/python_tests.json12
119 files changed, 4604 insertions, 591 deletions
diff --git a/.gitignore b/.gitignore
index a6b34fd4f3..89bd1003f7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,5 @@ coverage
# vim temp files
.*.swp
+# Makefile's cache
+cache.mk
diff --git a/Makefile b/Makefile
index 695f0008f3..dedfc2a4d5 100644
--- a/Makefile
+++ b/Makefile
@@ -339,10 +339,10 @@ CACHE_MK =
HAS_PKG_CONFIG ?= $(shell command -v $(PKG_CONFIG) >/dev/null 2>&1 && echo true || echo false)
ifeq ($(HAS_PKG_CONFIG), true)
-CACHE_MK += HAS_PKG_CONFIG = true\n
+CACHE_MK += HAS_PKG_CONFIG = true,
endif
-PC_TEMPLATE = prefix=$(prefix)\nexec_prefix=\$${prefix}\nincludedir=\$${prefix}/include\nlibdir=\$${exec_prefix}/lib\n\nName: $(PC_NAME)\nDescription: $(PC_DESCRIPTION)\nVersion: $(VERSION)\nCflags: -I\$${includedir} $(PC_CFLAGS)\nRequires.private: $(PC_REQUIRES_PRIVATE)\nLibs: -L\$${libdir} $(PC_LIB)\nLibs.private: $(PC_LIBS_PRIVATE)
+PC_TEMPLATE = prefix=$(prefix),exec_prefix=\$${prefix},includedir=\$${prefix}/include,libdir=\$${exec_prefix}/lib,,Name: $(PC_NAME),Description: $(PC_DESCRIPTION),Version: $(VERSION),Cflags: -I\$${includedir} $(PC_CFLAGS),Requires.private: $(PC_REQUIRES_PRIVATE),Libs: -L\$${libdir} $(PC_LIB),Libs.private: $(PC_LIBS_PRIVATE)
# gpr .pc file
PC_NAME = gRPC Portable Runtime
@@ -417,7 +417,7 @@ HAS_SYSTEM_PERFTOOLS ?= $(shell $(PERFTOOLS_CHECK_CMD) 2> /dev/null && echo true
ifeq ($(HAS_SYSTEM_PERFTOOLS),true)
DEFINES += GRPC_HAVE_PERFTOOLS
LIBS += profiler
-CACHE_MK += HAS_SYSTEM_PERFTOOLS = true\n
+CACHE_MK += HAS_SYSTEM_PERFTOOLS = true,
endif
endif
@@ -426,20 +426,20 @@ ifndef REQUIRE_CUSTOM_LIBRARIES_$(CONFIG)
HAS_SYSTEM_OPENSSL_ALPN ?= $(shell $(OPENSSL_ALPN_CHECK_CMD) 2> /dev/null && echo true || echo false)
ifeq ($(HAS_SYSTEM_OPENSSL_ALPN),true)
HAS_SYSTEM_OPENSSL_NPN = true
-CACHE_MK += HAS_SYSTEM_OPENSSL_ALPN = true\n
+CACHE_MK += HAS_SYSTEM_OPENSSL_ALPN = true,
else
HAS_SYSTEM_OPENSSL_NPN ?= $(shell $(OPENSSL_NPN_CHECK_CMD) 2> /dev/null && echo true || echo false)
endif
ifeq ($(HAS_SYSTEM_OPENSSL_NPN),true)
-CACHE_MK += HAS_SYSTEM_OPENSSL_NPN = true\n
+CACHE_MK += HAS_SYSTEM_OPENSSL_NPN = true,
endif
HAS_SYSTEM_ZLIB ?= $(shell $(ZLIB_CHECK_CMD) 2> /dev/null && echo true || echo false)
ifeq ($(HAS_SYSTEM_ZLIB),true)
-CACHE_MK += HAS_SYSTEM_ZLIB = true\n
+CACHE_MK += HAS_SYSTEM_ZLIB = true,
endif
HAS_SYSTEM_PROTOBUF ?= $(HAS_SYSTEM_PROTOBUF_VERIFY)
ifeq ($(HAS_SYSTEM_PROTOBUF),true)
-CACHE_MK += HAS_SYSTEM_PROTOBUF = true\n
+CACHE_MK += HAS_SYSTEM_PROTOBUF = true,
endif
else
# override system libraries if the config requires a custom compiled library
@@ -451,10 +451,10 @@ endif
HAS_PROTOC ?= $(shell $(PROTOC_CHECK_CMD) 2> /dev/null && echo true || echo false)
ifeq ($(HAS_PROTOC),true)
-CACHE_MK += HAS_PROTOC = true\n
+CACHE_MK += HAS_PROTOC = true,
HAS_VALID_PROTOC ?= $(shell $(PROTOC_CHECK_VERSION_CMD) 2> /dev/null && echo true || echo false)
ifeq ($(HAS_VALID_PROTOC),true)
-CACHE_MK += HAS_VALID_PROTOC = true\n
+CACHE_MK += HAS_VALID_PROTOC = true,
endif
else
HAS_VALID_PROTOC = false
@@ -475,7 +475,7 @@ endif
endif
ifeq ($(HAS_SYSTEMTAP),true)
-CACHE_MK += HAS_SYSTEMTAP = true\n
+CACHE_MK += HAS_SYSTEMTAP = true,
endif
# Note that for testing purposes, one can do:
@@ -2674,32 +2674,32 @@ endif
cache.mk::
$(E) "[MAKE] Generating $@"
- $(Q) echo -e "$(CACHE_MK)" >$@
+ $(Q) echo "$(CACHE_MK)" | tr , '\n' >$@
$(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc:
$(E) "[MAKE] Generating $@"
$(Q) mkdir -p $(@D)
- $(Q) echo -e "$(GPR_PC_FILE)" >$@
+ $(Q) echo "$(GPR_PC_FILE)" | tr , '\n' >$@
$(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc:
$(E) "[MAKE] Generating $@"
$(Q) mkdir -p $(@D)
- $(Q) echo -e "$(GRPC_PC_FILE)" >$@
+ $(Q) echo "$(GRPC_PC_FILE)" | tr , '\n' >$@
$(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc:
$(E) "[MAKE] Generating $@"
$(Q) mkdir -p $(@D)
- $(Q) echo -e "$(GRPC_UNSECURE_PC_FILE)" >$@
+ $(Q) echo "$(GRPC_UNSECURE_PC_FILE)" | tr , '\n' >$@
$(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc:
$(E) "[MAKE] Generating $@"
$(Q) mkdir -p $(@D)
- $(Q) echo -e "$(GRPCXX_PC_FILE)" >$@
+ $(Q) echo "$(GRPCXX_PC_FILE)" | tr , '\n' >$@
$(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc:
$(E) "[MAKE] Generating $@"
$(Q) mkdir -p $(@D)
- $(Q) echo -e "$(GRPCXX_UNSECURE_PC_FILE)" >$@
+ $(Q) echo "$(GRPCXX_UNSECURE_PC_FILE)" | tr , '\n' >$@
ifeq ($(NO_PROTOC),true)
$(GENDIR)/examples/pubsub/empty.pb.cc: protoc_dep_error
diff --git a/doc/connection-backoff.md b/doc/connection-backoff.md
index 47b71f927b..7094e737c5 100644
--- a/doc/connection-backoff.md
+++ b/doc/connection-backoff.md
@@ -8,58 +8,39 @@ requests) and instead do some form of exponential backoff.
We have several parameters:
1. INITIAL_BACKOFF (how long to wait after the first failure before retrying)
2. MULTIPLIER (factor with which to multiply backoff after a failed retry)
- 3. MAX_BACKOFF (Upper bound on backoff)
- 4. MIN_CONNECTION_TIMEOUT
+ 3. MAX_BACKOFF (upper bound on backoff)
+ 4. MIN_CONNECT_TIMEOUT (minimum time we're willing to give a connection to
+ complete)
## Proposed Backoff Algorithm
Exponentially back off the start time of connection attempts up to a limit of
-MAX_BACKOFF.
+MAX_BACKOFF, with jitter.
```
ConnectWithBackoff()
current_backoff = INITIAL_BACKOFF
current_deadline = now() + INITIAL_BACKOFF
- while (TryConnect(Max(current_deadline, MIN_CONNECT_TIMEOUT))
+ while (TryConnect(Max(current_deadline, now() + MIN_CONNECT_TIMEOUT))
!= SUCCESS)
SleepUntil(current_deadline)
current_backoff = Min(current_backoff * MULTIPLIER, MAX_BACKOFF)
- current_deadline = now() + current_backoff
-```
-
-## Historical Algorithm in Stubby
-
-Exponentially increase up to a limit of MAX_BACKOFF the intervals between
-connection attempts. This is what stubby 2 uses, and is equivalent if
-TryConnect() fails instantly.
+ current_deadline = now() + current_backoff +
+ UniformRandom(-JITTER * current_backoff, JITTER * current_backoff)
```
-LegacyConnectWithBackoff()
- current_backoff = INITIAL_BACKOFF
- while (TryConnect(MIN_CONNECT_TIMEOUT) != SUCCESS)
- SleepFor(current_backoff)
- current_backoff = Min(current_backoff * MULTIPLIER, MAX_BACKOFF)
-```
-
-The grpc C implementation currently uses this approach with an initial backoff
-of 1 second, multiplier of 2, and maximum backoff of 120 seconds. (This will
-change)
-Stubby, or at least rpc2, uses exactly this algorithm with an initial backoff
-of 1 second, multiplier of 1.2, and a maximum backoff of 120 seconds.
+With specific parameters of
+MIN_CONNECT_TIMEOUT = 20 seconds
+INITIAL_BACKOFF = 1 second
+MULTIPLIER = 1.6
+MAX_BACKOFF = 120 seconds
+JITTER = 0.2
-## Use Cases to Consider
+Implementations with pressing concerns (such as minimizing the number of wakeups
+on a mobile phone) may wish to use a different algorithm, and in particular
+different jitter logic.
-* Client tries to connect to a server which is down for multiple hours, eg for
- maintenance
-* Client tries to connect to a server which is overloaded
-* User is bringing up both a client and a server at the same time
- * In particular, we would like to avoid a large unnecessary delay if the
- client connects to a server which is about to come up
-* Client/server are misconfigured such that connection attempts always fail
- * We want to make sure these don’t put too much load on the server by
- default.
-* Server is overloaded and wants to transiently make clients back off
-* Application has out of band reason to believe a server is back
- * We should consider an out of band mechanism for the client to hint that
- we should short circuit the backoff.
+Alternate implementations must ensure that connection backoffs started at the
+same time disperse, and must not attempt connections substantially more often
+than the above algorithm.
diff --git a/include/grpc++/impl/call.h b/include/grpc++/impl/call.h
index 64fa5d6efb..1fa4490779 100644
--- a/include/grpc++/impl/call.h
+++ b/include/grpc++/impl/call.h
@@ -60,6 +60,93 @@ void FillMetadataMap(grpc_metadata_array* arr,
grpc_metadata* FillMetadataArray(
const std::multimap<grpc::string, grpc::string>& metadata);
+/// Per-message write options.
+class WriteOptions {
+ public:
+ WriteOptions() : flags_(0) {}
+ WriteOptions(const WriteOptions& other) : flags_(other.flags_) {}
+
+ /// Clear all flags.
+ inline void Clear() {
+ flags_ = 0;
+ }
+
+ /// Returns raw flags bitset.
+ inline gpr_uint32 flags() const {
+ return flags_;
+ }
+
+ /// Sets flag for the disabling of compression for the next message write.
+ ///
+ /// \sa GRPC_WRITE_NO_COMPRESS
+ inline WriteOptions& set_no_compression() {
+ SetBit(GRPC_WRITE_NO_COMPRESS);
+ return *this;
+ }
+
+ /// Clears flag for the disabling of compression for the next message write.
+ ///
+ /// \sa GRPC_WRITE_NO_COMPRESS
+ inline WriteOptions& clear_no_compression() {
+ ClearBit(GRPC_WRITE_NO_COMPRESS);
+ return *this;
+ }
+
+ /// Get value for the flag indicating whether compression for the next
+ /// message write is forcefully disabled.
+ ///
+ /// \sa GRPC_WRITE_NO_COMPRESS
+ inline bool get_no_compression() const {
+ return GetBit(GRPC_WRITE_NO_COMPRESS);
+ }
+
+ /// Sets flag indicating that the write may be buffered and need not go out on
+ /// the wire immediately.
+ ///
+ /// \sa GRPC_WRITE_BUFFER_HINT
+ inline WriteOptions& set_buffer_hint() {
+ SetBit(GRPC_WRITE_BUFFER_HINT);
+ return *this;
+ }
+
+ /// Clears flag indicating that the write may be buffered and need not go out
+ /// on the wire immediately.
+ ///
+ /// \sa GRPC_WRITE_BUFFER_HINT
+ inline WriteOptions& clear_buffer_hint() {
+ ClearBit(GRPC_WRITE_BUFFER_HINT);
+ return *this;
+ }
+
+ /// Get value for the flag indicating that the write may be buffered and need
+ /// not go out on the wire immediately.
+ ///
+ /// \sa GRPC_WRITE_BUFFER_HINT
+ inline bool get_buffer_hint() const {
+ return GetBit(GRPC_WRITE_BUFFER_HINT);
+ }
+
+ WriteOptions& operator=(const WriteOptions& rhs) {
+ flags_ = rhs.flags_;
+ return *this;
+ }
+
+ private:
+ void SetBit(const gpr_int32 mask) {
+ flags_ |= mask;
+ }
+
+ void ClearBit(const gpr_int32 mask) {
+ flags_ &= ~mask;
+ }
+
+ bool GetBit(const gpr_int32 mask) const {
+ return flags_ & mask;
+ }
+
+ gpr_uint32 flags_;
+};
+
/// Default argument for CallOpSet. I is unused by the class, but can be
/// used for generating multiple names for the same thing.
template <int I>
@@ -104,6 +191,12 @@ class CallOpSendMessage {
public:
CallOpSendMessage() : send_buf_(nullptr), own_buf_(false) {}
+ /// Send \a message using \a options for the write. The \a options are cleared
+ /// after use.
+ template <class M>
+ Status SendMessage(const M& message,
+ const WriteOptions& options) GRPC_MUST_USE_RESULT;
+
template <class M>
Status SendMessage(const M& message) GRPC_MUST_USE_RESULT;
@@ -112,8 +205,10 @@ class CallOpSendMessage {
if (send_buf_ == nullptr) return;
grpc_op* op = &ops[(*nops)++];
op->op = GRPC_OP_SEND_MESSAGE;
- op->flags = 0;
+ op->flags = write_options_.flags();
op->data.send_message = send_buf_;
+ // Flags are per-message: clear them after use.
+ write_options_.Clear();
}
void FinishOp(bool* status, int max_message_size) {
if (own_buf_) grpc_byte_buffer_destroy(send_buf_);
@@ -122,14 +217,22 @@ class CallOpSendMessage {
private:
grpc_byte_buffer* send_buf_;
+ WriteOptions write_options_;
bool own_buf_;
};
template <class M>
-Status CallOpSendMessage::SendMessage(const M& message) {
+Status CallOpSendMessage::SendMessage(const M& message,
+ const WriteOptions& options) {
+ write_options_ = options;
return SerializationTraits<M>::Serialize(message, &send_buf_, &own_buf_);
}
+template <class M>
+Status CallOpSendMessage::SendMessage(const M& message) {
+ return SendMessage(message, WriteOptions());
+}
+
template <class R>
class CallOpRecvMessage {
public:
@@ -172,17 +275,34 @@ class CallOpRecvMessage {
grpc_byte_buffer* recv_buf_;
};
+namespace CallOpGenericRecvMessageHelper {
+class DeserializeFunc {
+ public:
+ virtual Status Deserialize(grpc_byte_buffer* buf, int max_message_size) = 0;
+};
+
+template <class R>
+class DeserializeFuncType GRPC_FINAL : public DeserializeFunc {
+ public:
+ DeserializeFuncType(R* message) : message_(message) {}
+ Status Deserialize(grpc_byte_buffer* buf,
+ int max_message_size) GRPC_OVERRIDE {
+ return SerializationTraits<R>::Deserialize(buf, message_, max_message_size);
+ }
+
+ private:
+ R* message_; // Not a managed pointer because management is external to this
+};
+} // namespace CallOpGenericRecvMessageHelper
+
class CallOpGenericRecvMessage {
public:
CallOpGenericRecvMessage() : got_message(false) {}
template <class R>
void RecvMessage(R* message) {
- deserialize_ = [message](grpc_byte_buffer* buf,
- int max_message_size) -> Status {
- return SerializationTraits<R>::Deserialize(buf, message,
- max_message_size);
- };
+ deserialize_.reset(
+ new CallOpGenericRecvMessageHelper::DeserializeFuncType<R>(message));
}
bool got_message;
@@ -201,7 +321,7 @@ class CallOpGenericRecvMessage {
if (recv_buf_) {
if (*status) {
got_message = true;
- *status = deserialize_(recv_buf_, max_message_size).ok();
+ *status = deserialize_->Deserialize(recv_buf_, max_message_size).ok();
} else {
got_message = false;
grpc_byte_buffer_destroy(recv_buf_);
@@ -210,12 +330,11 @@ class CallOpGenericRecvMessage {
got_message = false;
*status = false;
}
- deserialize_ = DeserializeFunc();
+ deserialize_.reset();
}
private:
- typedef std::function<Status(grpc_byte_buffer*, int)> DeserializeFunc;
- DeserializeFunc deserialize_;
+ std::unique_ptr<CallOpGenericRecvMessageHelper::DeserializeFunc> deserialize_;
grpc_byte_buffer* recv_buf_;
};
diff --git a/include/grpc++/server_context.h b/include/grpc++/server_context.h
index a4ee986df1..2123d03291 100644
--- a/include/grpc++/server_context.h
+++ b/include/grpc++/server_context.h
@@ -99,9 +99,7 @@ class ServerContext {
return client_metadata_;
}
- std::shared_ptr<const AuthContext> auth_context() const {
- return auth_context_;
- }
+ std::shared_ptr<const AuthContext> auth_context() const;
private:
friend class ::grpc::Server;
@@ -147,7 +145,7 @@ class ServerContext {
grpc_call* call_;
CompletionQueue* cq_;
bool sent_initial_metadata_;
- std::shared_ptr<const AuthContext> auth_context_;
+ mutable std::shared_ptr<const AuthContext> auth_context_;
std::multimap<grpc::string, grpc::string> client_metadata_;
std::multimap<grpc::string, grpc::string> initial_metadata_;
std::multimap<grpc::string, grpc::string> trailing_metadata_;
diff --git a/include/grpc++/stream.h b/include/grpc++/stream.h
index dd5e52d6d3..3903f2ec06 100644
--- a/include/grpc++/stream.h
+++ b/include/grpc++/stream.h
@@ -79,7 +79,11 @@ class WriterInterface {
// Blocking write msg to the stream. Returns true on success.
// Returns false when the stream has been closed.
- virtual bool Write(const W& msg) = 0;
+ virtual bool Write(const W& msg, const WriteOptions& options) = 0;
+
+ inline bool Write(const W& msg) {
+ return Write(msg, WriteOptions());
+ }
};
template <class R>
@@ -168,9 +172,10 @@ class ClientWriter : public ClientWriterInterface<W> {
cq_.Pluck(&ops);
}
- bool Write(const W& msg) GRPC_OVERRIDE {
+ using WriterInterface<W>::Write;
+ bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE {
CallOpSet<CallOpSendMessage> ops;
- if (!ops.SendMessage(msg).ok()) {
+ if (!ops.SendMessage(msg, options).ok()) {
return false;
}
call_.PerformOps(&ops);
@@ -246,9 +251,10 @@ class ClientReaderWriter GRPC_FINAL : public ClientReaderWriterInterface<W, R> {
return cq_.Pluck(&ops) && ops.got_message;
}
- bool Write(const W& msg) GRPC_OVERRIDE {
+ using WriterInterface<W>::Write;
+ bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE {
CallOpSet<CallOpSendMessage> ops;
- if (!ops.SendMessage(msg).ok()) return false;
+ if (!ops.SendMessage(msg, options).ok()) return false;
call_.PerformOps(&ops);
return cq_.Pluck(&ops);
}
@@ -317,9 +323,10 @@ class ServerWriter GRPC_FINAL : public WriterInterface<W> {
call_->cq()->Pluck(&ops);
}
- bool Write(const W& msg) GRPC_OVERRIDE {
+ using WriterInterface<W>::Write;
+ bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE {
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> ops;
- if (!ops.SendMessage(msg).ok()) {
+ if (!ops.SendMessage(msg, options).ok()) {
return false;
}
if (!ctx_->sent_initial_metadata_) {
@@ -359,9 +366,10 @@ class ServerReaderWriter GRPC_FINAL : public WriterInterface<W>,
return call_->cq()->Pluck(&ops) && ops.got_message;
}
- bool Write(const W& msg) GRPC_OVERRIDE {
+ using WriterInterface<W>::Write;
+ bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE {
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> ops;
- if (!ops.SendMessage(msg).ok()) {
+ if (!ops.SendMessage(msg, options).ok()) {
return false;
}
if (!ctx_->sent_initial_metadata_) {
diff --git a/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h
index d3cfee113d..a5d1b62702 100644
--- a/include/grpc/support/port_platform.h
+++ b/include/grpc/support/port_platform.h
@@ -82,6 +82,7 @@
#define GPR_WIN32_ATOMIC 1
#define GPR_MSVC_TLS 1
#endif
+#define GPR_WINDOWS_CRASH_HANDLER 1
#elif defined(_WIN32) || defined(WIN32)
#define GPR_ARCH_32 1
#define GPR_WIN32 1
@@ -94,6 +95,7 @@
#define GPR_WIN32_ATOMIC 1
#define GPR_MSVC_TLS 1
#endif
+#define GPR_WINDOWS_CRASH_HANDLER 1
#elif defined(ANDROID) || defined(__ANDROID__)
#define GPR_ANDROID 1
#define GPR_ARCH_32 1
diff --git a/src/compiler/objective_c_generator.cc b/src/compiler/objective_c_generator.cc
index 79a84b4a7a..2a74a3b340 100644
--- a/src/compiler/objective_c_generator.cc
+++ b/src/compiler/objective_c_generator.cc
@@ -57,13 +57,12 @@ void PrintProtoRpcDeclarationAsPragma(Printer *printer,
vars["server_stream"] = method->server_streaming() ? "stream " : "";
printer->Print(vars,
- "#pragma mark $method_name$($client_stream$$request_type$)"
- " returns ($server_stream$$response_type$)\n\n");
+ "#pragma mark $method_name$($client_stream$$request_type$)"
+ " returns ($server_stream$$response_type$)\n\n");
}
-void PrintMethodSignature(Printer *printer,
- const MethodDescriptor *method,
- const map<string, string>& vars) {
+void PrintMethodSignature(Printer *printer, const MethodDescriptor *method,
+ const map<string, string> &vars) {
// TODO(jcanizales): Print method comments.
printer->Print(vars, "- ($return_type$)$method_name$With");
@@ -75,16 +74,17 @@ void PrintMethodSignature(Printer *printer,
// TODO(jcanizales): Put this on a new line and align colons.
if (method->server_streaming()) {
- printer->Print(vars, " eventHandler:(void(^)(BOOL done, "
- "$response_class$ *response, NSError *error))eventHandler");
+ printer->Print(vars,
+ " eventHandler:(void(^)(BOOL done, "
+ "$response_class$ *response, NSError *error))eventHandler");
} else {
- printer->Print(vars, " handler:(void(^)($response_class$ *response, "
- "NSError *error))handler");
+ printer->Print(vars,
+ " handler:(void(^)($response_class$ *response, "
+ "NSError *error))handler");
}
}
-void PrintSimpleSignature(Printer *printer,
- const MethodDescriptor *method,
+void PrintSimpleSignature(Printer *printer, const MethodDescriptor *method,
map<string, string> vars) {
vars["method_name"] =
grpc_generator::LowercaseFirstLetter(vars["method_name"]);
@@ -92,8 +92,7 @@ void PrintSimpleSignature(Printer *printer,
PrintMethodSignature(printer, method, vars);
}
-void PrintAdvancedSignature(Printer *printer,
- const MethodDescriptor *method,
+void PrintAdvancedSignature(Printer *printer, const MethodDescriptor *method,
map<string, string> vars) {
vars["method_name"] = "RPCTo" + vars["method_name"];
vars["return_type"] = "ProtoRPC *";
@@ -101,15 +100,16 @@ void PrintAdvancedSignature(Printer *printer,
}
inline map<string, string> GetMethodVars(const MethodDescriptor *method) {
- return {{ "method_name", method->name() },
- { "request_type", method->input_type()->name() },
- { "response_type", method->output_type()->name() },
- { "request_class", ClassName(method->input_type()) },
- { "response_class", ClassName(method->output_type()) }};
+ map<string, string> res;
+ res["method_name"] = method->name();
+ res["request_type"] = method->input_type()->name();
+ res["response_type"] = method->output_type()->name();
+ res["request_class"] = ClassName(method->input_type());
+ res["response_class"] = ClassName(method->output_type());
+ return res;
}
-void PrintMethodDeclarations(Printer *printer,
- const MethodDescriptor *method) {
+void PrintMethodDeclarations(Printer *printer, const MethodDescriptor *method) {
map<string, string> vars = GetMethodVars(method);
PrintProtoRpcDeclarationAsPragma(printer, method, vars);
@@ -120,8 +120,7 @@ void PrintMethodDeclarations(Printer *printer,
printer->Print(";\n\n\n");
}
-void PrintSimpleImplementation(Printer *printer,
- const MethodDescriptor *method,
+void PrintSimpleImplementation(Printer *printer, const MethodDescriptor *method,
map<string, string> vars) {
printer->Print("{\n");
printer->Print(vars, " [[self RPCTo$method_name$With");
@@ -178,7 +177,7 @@ void PrintMethodImplementations(Printer *printer,
PrintAdvancedImplementation(printer, method, vars);
}
-} // namespace
+} // namespace
string GetHeader(const ServiceDescriptor *service) {
string output;
@@ -186,7 +185,7 @@ string GetHeader(const ServiceDescriptor *service) {
// Scope the output stream so it closes and finalizes output to the string.
grpc::protobuf::io::StringOutputStream output_stream(&output);
Printer printer(&output_stream, '$');
-
+
printer.Print("@protocol GRXWriteable;\n");
printer.Print("@protocol GRXWriter;\n\n");
@@ -199,12 +198,15 @@ string GetHeader(const ServiceDescriptor *service) {
}
printer.Print("@end\n\n");
- printer.Print("// Basic service implementation, over gRPC, that only does"
+ printer.Print(
+ "// Basic service implementation, over gRPC, that only does"
" marshalling and parsing.\n");
- printer.Print(vars, "@interface $service_class$ :"
- " ProtoService<$service_class$>\n");
- printer.Print("- (instancetype)initWithHost:(NSString *)host"
- " NS_DESIGNATED_INITIALIZER;\n");
+ printer.Print(vars,
+ "@interface $service_class$ :"
+ " ProtoService<$service_class$>\n");
+ printer.Print(
+ "- (instancetype)initWithHost:(NSString *)host"
+ " NS_DESIGNATED_INITIALIZER;\n");
printer.Print("@end\n");
}
return output;
@@ -222,18 +224,20 @@ string GetSource(const ServiceDescriptor *service) {
{"package", service->file()->package()}};
printer.Print(vars,
- "static NSString *const kPackageName = @\"$package$\";\n");
- printer.Print(vars,
- "static NSString *const kServiceName = @\"$service_name$\";\n\n");
+ "static NSString *const kPackageName = @\"$package$\";\n");
+ printer.Print(
+ vars, "static NSString *const kServiceName = @\"$service_name$\";\n\n");
printer.Print(vars, "@implementation $service_class$\n\n");
-
+
printer.Print("// Designated initializer\n");
printer.Print("- (instancetype)initWithHost:(NSString *)host {\n");
- printer.Print(" return (self = [super initWithHost:host"
+ printer.Print(
+ " return (self = [super initWithHost:host"
" packageName:kPackageName serviceName:kServiceName]);\n");
printer.Print("}\n\n");
- printer.Print("// Override superclass initializer to disallow different"
+ printer.Print(
+ "// Override superclass initializer to disallow different"
" package and service names.\n");
printer.Print("- (instancetype)initWithHost:(NSString *)host\n");
printer.Print(" packageName:(NSString *)packageName\n");
@@ -250,4 +254,4 @@ string GetSource(const ServiceDescriptor *service) {
return output;
}
-} // namespace grpc_objective_c_generator
+} // namespace grpc_objective_c_generator
diff --git a/src/core/httpcli/httpcli.c b/src/core/httpcli/httpcli.c
index 3f5557e08e..65997d5f44 100644
--- a/src/core/httpcli/httpcli.c
+++ b/src/core/httpcli/httpcli.c
@@ -165,6 +165,7 @@ static void start_write(internal_request *req) {
static void on_secure_transport_setup_done(void *rp,
grpc_security_status status,
+ grpc_endpoint *wrapped_endpoint,
grpc_endpoint *secure_endpoint) {
internal_request *req = rp;
if (status != GRPC_SECURITY_OK) {
diff --git a/src/core/iomgr/socket_windows.c b/src/core/iomgr/socket_windows.c
index 897408ded2..f6ddfff0ad 100644
--- a/src/core/iomgr/socket_windows.c
+++ b/src/core/iomgr/socket_windows.c
@@ -37,6 +37,7 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
#include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/iomgr_internal.h"
@@ -61,22 +62,27 @@ grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name) {
operations to abort them. We need to do that this way because of the
various callsites of that function, which happens to be in various
mutex hold states, and that'd be unsafe to call them directly. */
-int grpc_winsocket_shutdown(grpc_winsocket *socket) {
+int grpc_winsocket_shutdown(grpc_winsocket *winsocket) {
int callbacks_set = 0;
- gpr_mu_lock(&socket->state_mu);
- if (socket->read_info.cb) {
+ SOCKET socket;
+ gpr_mu_lock(&winsocket->state_mu);
+ socket = winsocket->socket;
+ if (winsocket->read_info.cb) {
callbacks_set++;
- grpc_iomgr_closure_init(&socket->shutdown_closure, socket->read_info.cb,
- socket->read_info.opaque);
- grpc_iomgr_add_delayed_callback(&socket->shutdown_closure, 0);
+ grpc_iomgr_closure_init(&winsocket->shutdown_closure,
+ winsocket->read_info.cb,
+ winsocket->read_info.opaque);
+ grpc_iomgr_add_delayed_callback(&winsocket->shutdown_closure, 0);
}
- if (socket->write_info.cb) {
+ if (winsocket->write_info.cb) {
callbacks_set++;
- grpc_iomgr_closure_init(&socket->shutdown_closure, socket->write_info.cb,
- socket->write_info.opaque);
- grpc_iomgr_add_delayed_callback(&socket->shutdown_closure, 0);
+ grpc_iomgr_closure_init(&winsocket->shutdown_closure,
+ winsocket->write_info.cb,
+ winsocket->write_info.opaque);
+ grpc_iomgr_add_delayed_callback(&winsocket->shutdown_closure, 0);
}
- gpr_mu_unlock(&socket->state_mu);
+ gpr_mu_unlock(&winsocket->state_mu);
+ closesocket(socket);
return callbacks_set;
}
@@ -87,14 +93,12 @@ int grpc_winsocket_shutdown(grpc_winsocket *socket) {
an "idle" socket which is neither trying to read or write, we'd start leaking
both memory and sockets. */
void grpc_winsocket_orphan(grpc_winsocket *winsocket) {
- SOCKET socket = winsocket->socket;
grpc_iomgr_unregister_object(&winsocket->iomgr_object);
if (winsocket->read_info.outstanding || winsocket->write_info.outstanding) {
grpc_iocp_socket_orphan(winsocket);
} else {
grpc_winsocket_destroy(winsocket);
}
- closesocket(socket);
}
void grpc_winsocket_destroy(grpc_winsocket *winsocket) {
diff --git a/src/core/iomgr/tcp_server_windows.c b/src/core/iomgr/tcp_server_windows.c
index d70968de88..e6e1d1499e 100644
--- a/src/core/iomgr/tcp_server_windows.c
+++ b/src/core/iomgr/tcp_server_windows.c
@@ -108,9 +108,10 @@ void grpc_tcp_server_destroy(grpc_tcp_server *s,
size_t i;
gpr_mu_lock(&s->mu);
/* First, shutdown all fd's. This will queue abortion calls for all
- of the pending accepts. */
+ of the pending accepts due to the normal operation mechanism. */
for (i = 0; i < s->nports; i++) {
server_port *sp = &s->ports[i];
+ sp->shutting_down = 1;
grpc_winsocket_shutdown(sp->socket);
}
/* This happens asynchronously. Wait while that happens. */
@@ -242,63 +243,52 @@ static void on_accept(void *arg, int from_iocp) {
SOCKET sock = sp->new_socket;
grpc_winsocket_callback_info *info = &sp->socket->read_info;
grpc_endpoint *ep = NULL;
-
- /* The shutdown sequence is done in two parts. This is the second
- part here, acknowledging the IOCP notification, and doing nothing
- else, especially not queuing a new accept. */
- if (sp->shutting_down) {
- GPR_ASSERT(from_iocp);
- sp->shutting_down = 0;
- sp->socket->read_info.outstanding = 0;
- gpr_mu_lock(&sp->server->mu);
- if (0 == --sp->server->active_ports) {
- gpr_cv_broadcast(&sp->server->cv);
- }
- gpr_mu_unlock(&sp->server->mu);
- return;
- }
-
- if (from_iocp) {
- /* The IOCP notified us of a completed operation. Let's grab the results,
- and act accordingly. */
- DWORD transfered_bytes = 0;
- DWORD flags;
- BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
- &transfered_bytes, FALSE, &flags);
- if (!wsa_success) {
+ DWORD transfered_bytes;
+ DWORD flags;
+ BOOL wsa_success;
+
+ /* The general mechanism for shutting down is to queue abortion calls. While
+ this is necessary in the read/write case, it's useless for the accept
+ case. Let's do nothing. */
+ if (!from_iocp) return;
+
+ /* The IOCP notified us of a completed operation. Let's grab the results,
+ and act accordingly. */
+ transfered_bytes = 0;
+ wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
+ &transfered_bytes, FALSE, &flags);
+ if (!wsa_success) {
+ if (sp->shutting_down) {
+ /* During the shutdown case, we ARE expecting an error. So that's swell,
+ and we can wake up the shutdown thread. */
+ sp->shutting_down = 0;
+ sp->socket->read_info.outstanding = 0;
+ gpr_mu_lock(&sp->server->mu);
+ if (0 == --sp->server->active_ports) {
+ gpr_cv_broadcast(&sp->server->cv);
+ }
+ gpr_mu_unlock(&sp->server->mu);
+ return;
+ } else {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message);
gpr_free(utf8_message);
closesocket(sock);
- } else {
- /* TODO(ctiller): add sockaddr address to label */
- ep = grpc_tcp_create(grpc_winsocket_create(sock, "server"));
}
} else {
- /* If we're not notified from the IOCP, it means we are asked to shutdown.
- This will initiate that shutdown. Calling closesocket will trigger an
- IOCP notification, that will call this function a second time, from
- the IOCP thread. Of course, this only works if the socket was, in fact,
- listening. If that's not the case, we'd wait indefinitely. That's a bit
- of a degenerate case, but it can happen if you create a server, but
- don't start it. So let's support that by recursing once. */
- sp->shutting_down = 1;
- sp->new_socket = INVALID_SOCKET;
- if (sock != INVALID_SOCKET) {
- closesocket(sock);
- } else {
- on_accept(sp, 1);
+ if (!sp->shutting_down) {
+ /* TODO(ctiller): add sockaddr address to label */
+ ep = grpc_tcp_create(grpc_winsocket_create(sock, "server"));
}
- return;
}
/* The only time we should call our callback, is where we successfully
managed to accept a connection, and created an endpoint. */
if (ep) sp->server->cb(sp->server->cb_arg, ep);
/* As we were notified from the IOCP of one and exactly one accept,
- the former socked we created has now either been destroy or assigned
- to the new connection. We need to create a new one for the next
- connection. */
+ the former socked we created has now either been destroy or assigned
+ to the new connection. We need to create a new one for the next
+ connection. */
start_accept(sp);
}
diff --git a/src/core/security/secure_transport_setup.c b/src/core/security/secure_transport_setup.c
index 731b382f09..0c3572b53c 100644
--- a/src/core/security/secure_transport_setup.c
+++ b/src/core/security/secure_transport_setup.c
@@ -47,7 +47,8 @@ typedef struct {
tsi_handshaker *handshaker;
unsigned char *handshake_buffer;
size_t handshake_buffer_size;
- grpc_endpoint *endpoint;
+ grpc_endpoint *wrapped_endpoint;
+ grpc_endpoint *secure_endpoint;
gpr_slice_buffer left_overs;
grpc_secure_transport_setup_done_cb cb;
void *user_data;
@@ -63,13 +64,16 @@ static void on_handshake_data_sent_to_peer(void *setup,
static void secure_transport_setup_done(grpc_secure_transport_setup *s,
int is_success) {
if (is_success) {
- s->cb(s->user_data, GRPC_SECURITY_OK, s->endpoint);
+ s->cb(s->user_data, GRPC_SECURITY_OK, s->wrapped_endpoint,
+ s->secure_endpoint);
} else {
- if (s->endpoint != NULL) {
- grpc_endpoint_shutdown(s->endpoint);
- grpc_endpoint_destroy(s->endpoint);
+ if (s->secure_endpoint != NULL) {
+ grpc_endpoint_shutdown(s->secure_endpoint);
+ grpc_endpoint_destroy(s->secure_endpoint);
+ } else {
+ grpc_endpoint_destroy(s->wrapped_endpoint);
}
- s->cb(s->user_data, GRPC_SECURITY_ERROR, NULL);
+ s->cb(s->user_data, GRPC_SECURITY_ERROR, s->wrapped_endpoint, NULL);
}
if (s->handshaker != NULL) tsi_handshaker_destroy(s->handshaker);
if (s->handshake_buffer != NULL) gpr_free(s->handshake_buffer);
@@ -95,8 +99,9 @@ static void on_peer_checked(void *user_data, grpc_security_status status) {
secure_transport_setup_done(s, 0);
return;
}
- s->endpoint = grpc_secure_endpoint_create(
- protector, s->endpoint, s->left_overs.slices, s->left_overs.count);
+ s->secure_endpoint =
+ grpc_secure_endpoint_create(protector, s->wrapped_endpoint,
+ s->left_overs.slices, s->left_overs.count);
secure_transport_setup_done(s, 1);
return;
}
@@ -152,7 +157,7 @@ static void send_handshake_bytes_to_peer(grpc_secure_transport_setup *s) {
gpr_slice_from_copied_buffer((const char *)s->handshake_buffer, offset);
/* TODO(klempner,jboeuf): This should probably use the client setup
deadline */
- write_status = grpc_endpoint_write(s->endpoint, &to_send, 1,
+ write_status = grpc_endpoint_write(s->wrapped_endpoint, &to_send, 1,
on_handshake_data_sent_to_peer, s);
if (write_status == GRPC_ENDPOINT_WRITE_ERROR) {
gpr_log(GPR_ERROR, "Could not send handshake data to peer.");
@@ -198,7 +203,7 @@ static void on_handshake_data_received_from_peer(
if (result == TSI_INCOMPLETE_DATA) {
/* TODO(klempner,jboeuf): This should probably use the client setup
deadline */
- grpc_endpoint_notify_on_read(s->endpoint,
+ grpc_endpoint_notify_on_read(s->wrapped_endpoint,
on_handshake_data_received_from_peer, setup);
cleanup_slices(slices, nslices);
return;
@@ -256,7 +261,7 @@ static void on_handshake_data_sent_to_peer(void *setup,
if (tsi_handshaker_is_in_progress(s->handshaker)) {
/* TODO(klempner,jboeuf): This should probably use the client setup
deadline */
- grpc_endpoint_notify_on_read(s->endpoint,
+ grpc_endpoint_notify_on_read(s->wrapped_endpoint,
on_handshake_data_received_from_peer, setup);
} else {
check_peer(s);
@@ -280,7 +285,7 @@ void grpc_setup_secure_transport(grpc_security_connector *connector,
GRPC_SECURITY_CONNECTOR_REF(connector, "secure_transport_setup");
s->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE;
s->handshake_buffer = gpr_malloc(s->handshake_buffer_size);
- s->endpoint = nonsecure_endpoint;
+ s->wrapped_endpoint = nonsecure_endpoint;
s->user_data = user_data;
s->cb = cb;
gpr_slice_buffer_init(&s->left_overs);
diff --git a/src/core/security/secure_transport_setup.h b/src/core/security/secure_transport_setup.h
index 58701c461d..29025f5236 100644
--- a/src/core/security/secure_transport_setup.h
+++ b/src/core/security/secure_transport_setup.h
@@ -42,7 +42,7 @@
/* Ownership of the secure_endpoint is transfered. */
typedef void (*grpc_secure_transport_setup_done_cb)(
void *user_data, grpc_security_status status,
- grpc_endpoint *secure_endpoint);
+ grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint);
/* Calls the callback upon completion. */
void grpc_setup_secure_transport(grpc_security_connector *connector,
diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c
index 8a7ada07af..3717b8989f 100644
--- a/src/core/security/server_secure_chttp2.c
+++ b/src/core/security/server_secure_chttp2.c
@@ -51,10 +51,16 @@
#include <grpc/support/sync.h>
#include <grpc/support/useful.h>
+typedef struct tcp_endpoint_list {
+ grpc_endpoint *tcp_endpoint;
+ struct tcp_endpoint_list *next;
+} tcp_endpoint_list;
+
typedef struct grpc_server_secure_state {
grpc_server *server;
grpc_tcp_server *tcp;
grpc_security_connector *sc;
+ tcp_endpoint_list *handshaking_tcp_endpoints;
int is_shutdown;
gpr_mu mu;
gpr_refcount refcount;
@@ -88,14 +94,37 @@ static void setup_transport(void *statep, grpc_transport *transport,
grpc_channel_args_destroy(args_copy);
}
+static int remove_tcp_from_list_locked(grpc_server_secure_state *state,
+ grpc_endpoint *tcp) {
+ tcp_endpoint_list *node = state->handshaking_tcp_endpoints;
+ tcp_endpoint_list *tmp = NULL;
+ if (node && node->tcp_endpoint == tcp) {
+ state->handshaking_tcp_endpoints = state->handshaking_tcp_endpoints->next;
+ gpr_free(node);
+ return 0;
+ }
+ while (node) {
+ if (node->next->tcp_endpoint == tcp) {
+ tmp = node->next;
+ node->next = node->next->next;
+ gpr_free(tmp);
+ return 0;
+ }
+ node = node->next;
+ }
+ return -1;
+}
+
static void on_secure_transport_setup_done(void *statep,
grpc_security_status status,
+ grpc_endpoint *wrapped_endpoint,
grpc_endpoint *secure_endpoint) {
grpc_server_secure_state *state = statep;
grpc_transport *transport;
grpc_mdctx *mdctx;
if (status == GRPC_SECURITY_OK) {
gpr_mu_lock(&state->mu);
+ remove_tcp_from_list_locked(state, wrapped_endpoint);
if (!state->is_shutdown) {
mdctx = grpc_mdctx_create();
transport = grpc_create_chttp2_transport(
@@ -110,6 +139,9 @@ static void on_secure_transport_setup_done(void *statep,
}
gpr_mu_unlock(&state->mu);
} else {
+ gpr_mu_lock(&state->mu);
+ remove_tcp_from_list_locked(state, wrapped_endpoint);
+ gpr_mu_unlock(&state->mu);
gpr_log(GPR_ERROR, "Secure transport failed with error %d", status);
}
state_unref(state);
@@ -117,7 +149,14 @@ static void on_secure_transport_setup_done(void *statep,
static void on_accept(void *statep, grpc_endpoint *tcp) {
grpc_server_secure_state *state = statep;
+ tcp_endpoint_list *node;
state_ref(state);
+ node = gpr_malloc(sizeof(tcp_endpoint_list));
+ node->tcp_endpoint = tcp;
+ gpr_mu_lock(&state->mu);
+ node->next = state->handshaking_tcp_endpoints;
+ state->handshaking_tcp_endpoints = node;
+ gpr_mu_unlock(&state->mu);
grpc_setup_secure_transport(state->sc, tcp, on_secure_transport_setup_done,
state);
}
@@ -132,6 +171,13 @@ static void start(grpc_server *server, void *statep, grpc_pollset **pollsets,
static void destroy_done(void *statep) {
grpc_server_secure_state *state = statep;
grpc_server_listener_destroy_done(state->server);
+ gpr_mu_lock(&state->mu);
+ while (state->handshaking_tcp_endpoints != NULL) {
+ grpc_endpoint_shutdown(state->handshaking_tcp_endpoints->tcp_endpoint);
+ remove_tcp_from_list_locked(state,
+ state->handshaking_tcp_endpoints->tcp_endpoint);
+ }
+ gpr_mu_unlock(&state->mu);
state_unref(state);
}
@@ -209,6 +255,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
state->server = server;
state->tcp = tcp;
state->sc = sc;
+ state->handshaking_tcp_endpoints = NULL;
state->is_shutdown = 0;
gpr_mu_init(&state->mu);
gpr_ref_init(&state->refcount, 1);
diff --git a/src/core/support/stack_lockfree.c b/src/core/support/stack_lockfree.c
index 0093b33a2e..2844330379 100644
--- a/src/core/support/stack_lockfree.c
+++ b/src/core/support/stack_lockfree.c
@@ -65,7 +65,9 @@ typedef union lockfree_node {
} lockfree_node;
#define ENTRY_ALIGNMENT_BITS 3 /* make sure that entries aligned to 8-bytes */
-#define INVALID_ENTRY_INDEX ((1<<16)-1) /* reserve this entry as invalid */
+#define INVALID_ENTRY_INDEX \
+ ((1 << 16) - 1) /* reserve this entry as invalid \
+ */
struct gpr_stack_lockfree {
lockfree_node *entries;
@@ -109,8 +111,7 @@ int gpr_stack_lockfree_push(gpr_stack_lockfree *stack, int entry) {
head.atm = gpr_atm_no_barrier_load(&(stack->head.atm));
/* Point to it */
stack->entries[entry].contents.index = head.contents.index;
- } while (!gpr_atm_rel_cas(&(stack->head.atm),
- head.atm, newhead.atm));
+ } while (!gpr_atm_rel_cas(&(stack->head.atm), head.atm, newhead.atm));
/* Use rel_cas above to make sure that entry index is set properly */
return head.contents.index == INVALID_ENTRY_INDEX;
}
@@ -126,8 +127,6 @@ int gpr_stack_lockfree_pop(gpr_stack_lockfree *stack) {
newhead.atm =
gpr_atm_no_barrier_load(&(stack->entries[head.contents.index].atm));
- } while (!gpr_atm_no_barrier_cas(&(stack->head.atm),
- head.atm,
- newhead.atm));
+ } while (!gpr_atm_no_barrier_cas(&(stack->head.atm), head.atm, newhead.atm));
return head.contents.index;
}
diff --git a/src/core/support/stack_lockfree.h b/src/core/support/stack_lockfree.h
index 4da3572deb..eec960fbb0 100644
--- a/src/core/support/stack_lockfree.h
+++ b/src/core/support/stack_lockfree.h
@@ -38,14 +38,14 @@ typedef struct gpr_stack_lockfree gpr_stack_lockfree;
/* This stack must specify the maximum number of entries to track.
The current implementation only allows up to 65534 entries */
-gpr_stack_lockfree *gpr_stack_lockfree_create(int entries);
-void gpr_stack_lockfree_destroy(gpr_stack_lockfree *);
+gpr_stack_lockfree* gpr_stack_lockfree_create(int entries);
+void gpr_stack_lockfree_destroy(gpr_stack_lockfree* stack);
/* Pass in a valid entry number for the next stack entry */
/* Returns 1 if this is the first element on the stack, 0 otherwise */
-int gpr_stack_lockfree_push(gpr_stack_lockfree *, int entry);
+int gpr_stack_lockfree_push(gpr_stack_lockfree*, int entry);
/* Returns -1 on empty or the actual entry number */
-int gpr_stack_lockfree_pop(gpr_stack_lockfree *);
+int gpr_stack_lockfree_pop(gpr_stack_lockfree* stack);
-#endif /* GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H */
+#endif /* GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H */
diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c
index 34ee3f8400..f3c7d8397b 100644
--- a/src/core/surface/secure_channel_create.c
+++ b/src/core/surface/secure_channel_create.c
@@ -75,6 +75,7 @@ static void connector_unref(grpc_connector *con) {
static void on_secure_transport_setup_done(void *arg,
grpc_security_status status,
+ grpc_endpoint *wrapped_endpoint,
grpc_endpoint *secure_endpoint) {
connector *c = arg;
grpc_iomgr_closure *notify;
diff --git a/src/core/transport/chttp2/frame_data.c b/src/core/transport/chttp2/frame_data.c
index 0ad62a9999..7e3980159e 100644
--- a/src/core/transport/chttp2/frame_data.c
+++ b/src/core/transport/chttp2/frame_data.c
@@ -89,12 +89,9 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
fh_0:
case GRPC_CHTTP2_DATA_FH_0:
p->frame_type = *cur;
- if (++cur == end) {
- p->state = GRPC_CHTTP2_DATA_FH_1;
- return GRPC_CHTTP2_PARSE_OK;
- }
switch (p->frame_type) {
case 0:
+ /* noop */
break;
case 1:
gpr_log(GPR_ERROR, "Compressed GRPC frames not yet supported");
@@ -103,6 +100,10 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
gpr_log(GPR_ERROR, "Bad GRPC frame type 0x%02x", p->frame_type);
return GRPC_CHTTP2_STREAM_ERROR;
}
+ if (++cur == end) {
+ p->state = GRPC_CHTTP2_DATA_FH_1;
+ return GRPC_CHTTP2_PARSE_OK;
+ }
/* fallthrough */
case GRPC_CHTTP2_DATA_FH_1:
p->frame_size = ((gpr_uint32)*cur) << 24;
diff --git a/src/cpp/server/async_server_context.cc b/src/cpp/server/async_server_context.cc
deleted file mode 100644
index e1f29452a4..0000000000
--- a/src/cpp/server/async_server_context.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <grpc++/async_server_context.h>
-
-#include <grpc/grpc.h>
-#include <grpc/support/log.h>
-#include "src/cpp/proto/proto_utils.h"
-#include <grpc++/config.h>
-#include <grpc++/status.h>
-
-namespace grpc {
-
-AsyncServerContext::AsyncServerContext(
- grpc_call* call, const grpc::string& method, const grpc::string& host,
- system_clock::time_point absolute_deadline)
- : method_(method),
- host_(host),
- absolute_deadline_(absolute_deadline),
- request_(nullptr),
- call_(call) {}
-
-AsyncServerContext::~AsyncServerContext() { grpc_call_destroy(call_); }
-
-void AsyncServerContext::Accept(grpc_completion_queue* cq) {
- GPR_ASSERT(grpc_call_server_accept_old(call_, cq, this) == GRPC_CALL_OK);
- GPR_ASSERT(grpc_call_server_end_initial_metadata_old(
- call_, GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
-}
-
-bool AsyncServerContext::StartRead(grpc::protobuf::Message* request) {
- GPR_ASSERT(request);
- request_ = request;
- grpc_call_error err = grpc_call_start_read_old(call_, this);
- return err == GRPC_CALL_OK;
-}
-
-bool AsyncServerContext::StartWrite(const grpc::protobuf::Message& response,
- int flags) {
- grpc_byte_buffer* buffer = nullptr;
- GRPC_TIMER_MARK(SER_PROTO_BEGIN, call_->call());
- if (!SerializeProto(response, &buffer)) {
- return false;
- }
- GRPC_TIMER_MARK(SER_PROTO_END, call_->call());
- grpc_call_error err = grpc_call_start_write_old(call_, buffer, this, flags);
- grpc_byte_buffer_destroy(buffer);
- return err == GRPC_CALL_OK;
-}
-
-bool AsyncServerContext::StartWriteStatus(const Status& status) {
- grpc_call_error err = grpc_call_start_write_status_old(
- call_, static_cast<grpc_status_code>(status.code()),
- status.details().empty() ? nullptr
- : const_cast<char*>(status.details().c_str()),
- this);
- return err == GRPC_CALL_OK;
-}
-
-bool AsyncServerContext::ParseRead(grpc_byte_buffer* read_buffer) {
- GPR_ASSERT(request_);
- GRPC_TIMER_MARK(DESER_PROTO_BEGIN, call_->call());
- bool success = DeserializeProto(read_buffer, request_);
- GRPC_TIMER_MARK(DESER_PROTO_END, call_->call());
- request_ = nullptr;
- return success;
-}
-
-} // namespace grpc
diff --git a/src/cpp/server/server_context.cc b/src/cpp/server/server_context.cc
index 1bb3a8bcc4..0be77138d1 100644
--- a/src/cpp/server/server_context.cc
+++ b/src/cpp/server/server_context.cc
@@ -153,4 +153,11 @@ void ServerContext::set_call(grpc_call* call) {
auth_context_ = CreateAuthContext(call);
}
+std::shared_ptr<const AuthContext> ServerContext::auth_context() const {
+ if (auth_context_.get() == nullptr) {
+ auth_context_ = CreateAuthContext(call_);
+ }
+ return auth_context_;
+}
+
} // namespace grpc
diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.csproj b/src/csharp/Grpc.Auth/Grpc.Auth.csproj
index e6abbbfdf0..fdec2e7bd7 100644
--- a/src/csharp/Grpc.Auth/Grpc.Auth.csproj
+++ b/src/csharp/Grpc.Auth/Grpc.Auth.csproj
@@ -68,6 +68,9 @@
<Reference Include="System.Net.Http.WebRequest" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Grpc.Core\Version.cs">
+ <Link>Version.cs</Link>
+ </Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="GoogleCredential.cs" />
<Compile Include="OAuth2InterceptorFactory.cs" />
@@ -81,6 +84,7 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
+ <None Include="Grpc.Auth.nuspec" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.nuspec b/src/csharp/Grpc.Auth/Grpc.Auth.nuspec
index 978b04d70b..1262bdbdab 100644
--- a/src/csharp/Grpc.Auth/Grpc.Auth.nuspec
+++ b/src/csharp/Grpc.Auth/Grpc.Auth.nuspec
@@ -5,19 +5,19 @@
<title>gRPC C# Auth</title>
<summary>Auth library for C# implementation of gRPC - an RPC library and framework</summary>
<description>Auth library for C# implementation of gRPC - an RPC library and framework. See project site for more info.</description>
- <version>0.6.0</version>
+ <version>$version$</version>
<authors>Google Inc.</authors>
<owners>grpc-packages</owners>
<licenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/grpc/grpc</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
- <releaseNotes>Release 0.6.0 of gRPC C#</releaseNotes>
+ <releaseNotes>Release $version$ of gRPC C#</releaseNotes>
<copyright>Copyright 2015, Google Inc.</copyright>
<tags>gRPC RPC Protocol HTTP/2 Auth OAuth2</tags>
<dependencies>
<dependency id="BouncyCastle" version="1.7.0" />
<dependency id="Google.Apis.Auth" version="1.9.1" />
- <dependency id="Grpc.Core" version="0.6.0" />
+ <dependency id="Grpc.Core" version="$version$" />
</dependencies>
</metadata>
<files>
diff --git a/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs
index 2cdf643597..70cb32d5b2 100644
--- a/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs
@@ -9,6 +9,5 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("0.6.*")]
[assembly: InternalsVisibleTo("Grpc.Auth.Tests")]
diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
index 21f94d3cf5..e797dd82f2 100644
--- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
+++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
@@ -73,12 +73,6 @@ namespace Grpc.Core.Tests
Server server;
Channel channel;
- [TestFixtureSetUp]
- public void InitClass()
- {
- GrpcEnvironment.Initialize();
- }
-
[SetUp]
public void Init()
{
diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
index 92e28b7d74..927954c448 100644
--- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
+++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
@@ -37,6 +37,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Grpc.Core\Version.cs">
+ <Link>Version.cs</Link>
+ </Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ClientServerTest.cs" />
<Compile Include="ServerTest.cs" />
@@ -63,4 +66,4 @@
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup />
-</Project>
+</Project> \ No newline at end of file
diff --git a/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs b/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs
index 6a132a5b22..9ae12776f3 100644
--- a/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs
+++ b/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs
@@ -43,16 +43,17 @@ namespace Grpc.Core.Tests
[Test]
public void InitializeAndShutdownGrpcEnvironment()
{
- GrpcEnvironment.Initialize();
- Assert.IsNotNull(GrpcEnvironment.ThreadPool.CompletionQueue);
+ var env = GrpcEnvironment.GetInstance();
+ Assert.IsNotNull(env.CompletionQueue);
GrpcEnvironment.Shutdown();
}
[Test]
public void SubsequentInvocations()
{
- GrpcEnvironment.Initialize();
- GrpcEnvironment.Initialize();
+ var env1 = GrpcEnvironment.GetInstance();
+ var env2 = GrpcEnvironment.GetInstance();
+ Assert.IsTrue(object.ReferenceEquals(env1, env2));
GrpcEnvironment.Shutdown();
GrpcEnvironment.Shutdown();
}
@@ -60,15 +61,13 @@ namespace Grpc.Core.Tests
[Test]
public void InitializeAfterShutdown()
{
- GrpcEnvironment.Initialize();
- var tp1 = GrpcEnvironment.ThreadPool;
+ var env1 = GrpcEnvironment.GetInstance();
GrpcEnvironment.Shutdown();
- GrpcEnvironment.Initialize();
- var tp2 = GrpcEnvironment.ThreadPool;
+ var env2 = GrpcEnvironment.GetInstance();
GrpcEnvironment.Shutdown();
- Assert.IsFalse(object.ReferenceEquals(tp1, tp2));
+ Assert.IsFalse(object.ReferenceEquals(env1, env2));
}
}
}
diff --git a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs
index 8b3c910251..714c2f7494 100644
--- a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs
+++ b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs
@@ -53,18 +53,6 @@ namespace Grpc.Core.Tests
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpcsharp_test_nop(IntPtr ptr);
- [TestFixtureSetUp]
- public void Init()
- {
- GrpcEnvironment.Initialize();
- }
-
- [TestFixtureTearDown]
- public void Cleanup()
- {
- GrpcEnvironment.Shutdown();
- }
-
/// <summary>
/// (~1.26us .NET Windows)
/// </summary>
diff --git a/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs
index d5fffb8b18..c2e5e81e91 100644
--- a/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs
@@ -9,4 +9,3 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("0.6.*")]
diff --git a/src/csharp/Grpc.Core.Tests/ServerTest.cs b/src/csharp/Grpc.Core.Tests/ServerTest.cs
index 02c773c9cc..1119aa370e 100644
--- a/src/csharp/Grpc.Core.Tests/ServerTest.cs
+++ b/src/csharp/Grpc.Core.Tests/ServerTest.cs
@@ -44,13 +44,10 @@ namespace Grpc.Core.Tests
[Test]
public void StartAndShutdownServer()
{
- GrpcEnvironment.Initialize();
-
Server server = new Server();
server.AddListeningPort("localhost", Server.PickUnusedPort);
server.Start();
server.ShutdownAsync().Wait();
-
GrpcEnvironment.Shutdown();
}
}
diff --git a/src/csharp/Grpc.Core/Calls.cs b/src/csharp/Grpc.Core/Calls.cs
index 9f8baac684..750282258f 100644
--- a/src/csharp/Grpc.Core/Calls.cs
+++ b/src/csharp/Grpc.Core/Calls.cs
@@ -58,7 +58,7 @@ namespace Grpc.Core
where TResponse : class
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
- asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name);
+ asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name);
var asyncResult = asyncCall.UnaryCallAsync(req, call.Headers);
RegisterCancellationCallback(asyncCall, token);
return await asyncResult;
@@ -69,7 +69,7 @@ namespace Grpc.Core
where TResponse : class
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
- asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name);
+ asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name);
asyncCall.StartServerStreamingCall(req, call.Headers);
RegisterCancellationCallback(asyncCall, token);
var responseStream = new ClientResponseStream<TRequest, TResponse>(asyncCall);
@@ -81,7 +81,7 @@ namespace Grpc.Core
where TResponse : class
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
- asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name);
+ asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name);
var resultTask = asyncCall.ClientStreamingCallAsync(call.Headers);
RegisterCancellationCallback(asyncCall, token);
var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall);
@@ -93,7 +93,7 @@ namespace Grpc.Core
where TResponse : class
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
- asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name);
+ asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name);
asyncCall.StartDuplexStreamingCall(call.Headers);
RegisterCancellationCallback(asyncCall, token);
var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall);
@@ -108,13 +108,5 @@ namespace Grpc.Core
token.Register(() => asyncCall.Cancel());
}
}
-
- /// <summary>
- /// Gets shared completion queue used for async calls.
- /// </summary>
- private static CompletionQueueSafeHandle GetCompletionQueue()
- {
- return GrpcEnvironment.ThreadPool.CompletionQueue;
- }
}
}
diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs
index d6bfbb7bc4..5baf260003 100644
--- a/src/csharp/Grpc.Core/Channel.cs
+++ b/src/csharp/Grpc.Core/Channel.cs
@@ -42,8 +42,10 @@ namespace Grpc.Core
/// </summary>
public class Channel : IDisposable
{
+ readonly GrpcEnvironment environment;
readonly ChannelSafeHandle handle;
readonly string target;
+ bool disposed;
/// <summary>
/// Creates a channel that connects to a specific host.
@@ -54,6 +56,7 @@ namespace Grpc.Core
/// <param name="options">Channel options.</param>
public Channel(string host, Credentials credentials = null, IEnumerable<ChannelOption> options = null)
{
+ this.environment = GrpcEnvironment.GetInstance();
using (ChannelArgsSafeHandle nativeChannelArgs = ChannelOptions.CreateChannelArgs(options))
{
if (credentials != null)
@@ -105,10 +108,35 @@ namespace Grpc.Core
}
}
+ internal CompletionQueueSafeHandle CompletionQueue
+ {
+ get
+ {
+ return this.environment.CompletionQueue;
+ }
+ }
+
+ internal CompletionRegistry CompletionRegistry
+ {
+ get
+ {
+ return this.environment.CompletionRegistry;
+ }
+ }
+
+ internal GrpcEnvironment Environment
+ {
+ get
+ {
+ return this.environment;
+ }
+ }
+
protected virtual void Dispose(bool disposing)
{
- if (handle != null && !handle.IsInvalid)
+ if (disposing && handle != null && !disposed)
{
+ disposed = true;
handle.Dispose();
}
}
diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj
index 0e67da3245..cde42c3b7e 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.csproj
+++ b/src/csharp/Grpc.Core/Grpc.Core.csproj
@@ -49,6 +49,7 @@
<Compile Include="IAsyncStreamWriter.cs" />
<Compile Include="IAsyncStreamReader.cs" />
<Compile Include="Internal\GrpcLog.cs" />
+ <Compile Include="Version.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RpcException.cs" />
<Compile Include="Calls.cs" />
@@ -104,6 +105,7 @@
<Compile Include="ChannelOptions.cs" />
</ItemGroup>
<ItemGroup>
+ <None Include="Grpc.Core.nuspec" />
<None Include="packages.config" />
</ItemGroup>
<Choose>
diff --git a/src/csharp/Grpc.Core/Grpc.Core.nuspec b/src/csharp/Grpc.Core/Grpc.Core.nuspec
index 457983532a..5ace6dcf89 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.nuspec
+++ b/src/csharp/Grpc.Core/Grpc.Core.nuspec
@@ -5,19 +5,19 @@
<title>gRPC C# Core</title>
<summary>Core C# implementation of gRPC - an RPC library and framework</summary>
<description>Core C# implementation of gRPC - an RPC library and framework. See project site for more info.</description>
- <version>0.6.0</version>
+ <version>$version$</version>
<authors>Google Inc.</authors>
<owners>grpc-packages</owners>
<licenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/grpc/grpc</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
- <releaseNotes>Release 0.6.0 of gRPC C#</releaseNotes>
+ <releaseNotes>Release $version$ of gRPC C#</releaseNotes>
<copyright>Copyright 2015, Google Inc.</copyright>
<tags>gRPC RPC Protocol HTTP/2</tags>
<dependencies>
<dependency id="System.Collections.Immutable" version="1.1.36" />
<dependency id="Ix-Async" version="1.2.3" />
- <dependency id="grpc.native.csharp_ext" version="0.10.0" />
+ <dependency id="grpc.native.csharp_ext" version="$GrpcNativeCsharpExtVersion$" />
</dependencies>
</metadata>
<files>
diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs
index 30ff289714..47d1651aab 100644
--- a/src/csharp/Grpc.Core/GrpcEnvironment.cs
+++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs
@@ -33,7 +33,9 @@
using System;
using System.Runtime.InteropServices;
+using System.Threading.Tasks;
using Grpc.Core.Internal;
+using Grpc.Core.Utils;
namespace Grpc.Core
{
@@ -51,20 +53,18 @@ namespace Grpc.Core
static extern void grpcsharp_shutdown();
static object staticLock = new object();
- static volatile GrpcEnvironment instance;
+ static GrpcEnvironment instance;
readonly GrpcThreadPool threadPool;
readonly CompletionRegistry completionRegistry;
+ readonly DebugStats debugStats = new DebugStats();
bool isClosed;
/// <summary>
- /// Makes sure GRPC environment is initialized. Subsequent invocations don't have any
- /// effect unless you call Shutdown first.
- /// Although normal use cases assume you will call this just once in your application's
- /// lifetime (and call Shutdown once you're done), for the sake of easier testing it's
- /// allowed to initialize the environment again after it has been successfully shutdown.
+ /// Returns an instance of initialized gRPC environment.
+ /// Subsequent invocations return the same instance unless Shutdown has been called first.
/// </summary>
- public static void Initialize()
+ internal static GrpcEnvironment GetInstance()
{
lock (staticLock)
{
@@ -72,12 +72,13 @@ namespace Grpc.Core
{
instance = new GrpcEnvironment();
}
+ return instance;
}
}
/// <summary>
- /// Shuts down the GRPC environment if it was initialized before.
- /// Repeated invocations have no effect.
+ /// Shuts down the gRPC environment if it was initialized before.
+ /// Blocks until the environment has been fully shutdown.
/// </summary>
public static void Shutdown()
{
@@ -87,50 +88,55 @@ namespace Grpc.Core
{
instance.Close();
instance = null;
-
- CheckDebugStats();
}
}
}
- internal static GrpcThreadPool ThreadPool
+ /// <summary>
+ /// Creates gRPC environment.
+ /// </summary>
+ private GrpcEnvironment()
+ {
+ GrpcLog.RedirectNativeLogs(Console.Error);
+ grpcsharp_init();
+ completionRegistry = new CompletionRegistry(this);
+ threadPool = new GrpcThreadPool(this, THREAD_POOL_SIZE);
+ threadPool.Start();
+ // TODO: use proper logging here
+ Console.WriteLine("GRPC initialized.");
+ }
+
+ /// <summary>
+ /// Gets the completion registry used by this gRPC environment.
+ /// </summary>
+ internal CompletionRegistry CompletionRegistry
{
get
{
- var inst = instance;
- if (inst == null)
- {
- throw new InvalidOperationException("GRPC environment not initialized");
- }
- return inst.threadPool;
+ return this.completionRegistry;
}
}
- internal static CompletionRegistry CompletionRegistry
+ /// <summary>
+ /// Gets the completion queue used by this gRPC environment.
+ /// </summary>
+ internal CompletionQueueSafeHandle CompletionQueue
{
get
{
- var inst = instance;
- if (inst == null)
- {
- throw new InvalidOperationException("GRPC environment not initialized");
- }
- return inst.completionRegistry;
+ return this.threadPool.CompletionQueue;
}
}
/// <summary>
- /// Creates gRPC environment.
+ /// Gets the completion queue used by this gRPC environment.
/// </summary>
- private GrpcEnvironment()
+ internal DebugStats DebugStats
{
- GrpcLog.RedirectNativeLogs(Console.Error);
- grpcsharp_init();
- completionRegistry = new CompletionRegistry();
- threadPool = new GrpcThreadPool(THREAD_POOL_SIZE);
- threadPool.Start();
- // TODO: use proper logging here
- Console.WriteLine("GRPC initialized.");
+ get
+ {
+ return this.debugStats;
+ }
}
/// <summary>
@@ -146,32 +152,28 @@ namespace Grpc.Core
grpcsharp_shutdown();
isClosed = true;
+ debugStats.CheckOK();
+
// TODO: use proper logging here
Console.WriteLine("GRPC shutdown.");
}
- private static void CheckDebugStats()
+ /// <summary>
+ /// Shuts down this environment asynchronously.
+ /// </summary>
+ private Task CloseAsync()
{
- var remainingClientCalls = DebugStats.ActiveClientCalls.Count;
- if (remainingClientCalls != 0)
- {
- DebugWarning(string.Format("Detected {0} client calls that weren't disposed properly.", remainingClientCalls));
- }
- var remainingServerCalls = DebugStats.ActiveServerCalls.Count;
- if (remainingServerCalls != 0)
- {
- DebugWarning(string.Format("Detected {0} server calls that weren't disposed properly.", remainingServerCalls));
- }
- var pendingBatchCompletions = DebugStats.PendingBatchCompletions.Count;
- if (pendingBatchCompletions != 0)
+ return Task.Run(() =>
{
- DebugWarning(string.Format("Detected {0} pending batch completions.", pendingBatchCompletions));
- }
- }
-
- private static void DebugWarning(string message)
- {
- throw new Exception("Shutdown check: " + message);
+ try
+ {
+ Close();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Error occured while shutting down GrpcEnvironment: " + e);
+ }
+ });
}
}
}
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index d350f45da6..24b75d1668 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -47,6 +47,8 @@ namespace Grpc.Core.Internal
/// </summary>
internal class AsyncCall<TRequest, TResponse> : AsyncCallBase<TRequest, TResponse>
{
+ Channel channel;
+
// Completion of a pending unary response if not null.
TaskCompletionSource<TResponse> unaryResponseTcs;
@@ -61,8 +63,9 @@ namespace Grpc.Core.Internal
public void Initialize(Channel channel, CompletionQueueSafeHandle cq, string methodName)
{
- var call = CallSafeHandle.Create(channel.Handle, cq, methodName, channel.Target, Timespec.InfFuture);
- DebugStats.ActiveClientCalls.Increment();
+ this.channel = channel;
+ var call = CallSafeHandle.Create(channel.Handle, channel.CompletionRegistry, cq, methodName, channel.Target, Timespec.InfFuture);
+ channel.Environment.DebugStats.ActiveClientCalls.Increment();
InitializeInternal(call);
}
@@ -277,7 +280,7 @@ namespace Grpc.Core.Internal
protected override void OnReleaseResources()
{
- DebugStats.ActiveClientCalls.Decrement();
+ channel.Environment.DebugStats.ActiveClientCalls.Decrement();
}
/// <summary>
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
index 4f510ba40a..309067ea9d 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
@@ -48,14 +48,17 @@ namespace Grpc.Core.Internal
internal class AsyncCallServer<TRequest, TResponse> : AsyncCallBase<TResponse, TRequest>
{
readonly TaskCompletionSource<object> finishedServersideTcs = new TaskCompletionSource<object>();
+ readonly GrpcEnvironment environment;
- public AsyncCallServer(Func<TResponse, byte[]> serializer, Func<byte[], TRequest> deserializer) : base(serializer, deserializer)
+ public AsyncCallServer(Func<TResponse, byte[]> serializer, Func<byte[], TRequest> deserializer, GrpcEnvironment environment) : base(serializer, deserializer)
{
+ this.environment = Preconditions.CheckNotNull(environment);
}
public void Initialize(CallSafeHandle call)
{
- DebugStats.ActiveServerCalls.Increment();
+ call.SetCompletionRegistry(environment.CompletionRegistry);
+ environment.DebugStats.ActiveServerCalls.Increment();
InitializeInternal(call);
}
@@ -114,7 +117,7 @@ namespace Grpc.Core.Internal
protected override void OnReleaseResources()
{
- DebugStats.ActiveServerCalls.Decrement();
+ environment.DebugStats.ActiveServerCalls.Decrement();
}
/// <summary>
diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
index ef92b44402..3b246ac01b 100644
--- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
@@ -43,6 +43,7 @@ namespace Grpc.Core.Internal
internal class CallSafeHandle : SafeHandleZeroIsInvalid
{
const uint GRPC_WRITE_BUFFER_HINT = 1;
+ CompletionRegistry completionRegistry;
[DllImport("grpc_csharp_ext.dll")]
static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline);
@@ -97,15 +98,22 @@ namespace Grpc.Core.Internal
{
}
- public static CallSafeHandle Create(ChannelSafeHandle channel, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline)
+ public static CallSafeHandle Create(ChannelSafeHandle channel, CompletionRegistry registry, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline)
{
- return grpcsharp_channel_create_call(channel, cq, method, host, deadline);
+ var result = grpcsharp_channel_create_call(channel, cq, method, host, deadline);
+ result.SetCompletionRegistry(registry);
+ return result;
+ }
+
+ public void SetCompletionRegistry(CompletionRegistry completionRegistry)
+ {
+ this.completionRegistry = completionRegistry;
}
public void StartUnary(byte[] payload, BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
{
var ctx = BatchContextSafeHandle.Create();
- GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray)
.CheckOk();
}
@@ -119,56 +127,56 @@ namespace Grpc.Core.Internal
public void StartClientStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
{
var ctx = BatchContextSafeHandle.Create();
- GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_start_client_streaming(this, ctx, metadataArray).CheckOk();
}
public void StartServerStreaming(byte[] payload, BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
{
var ctx = BatchContextSafeHandle.Create();
- GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray).CheckOk();
}
public void StartDuplexStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
{
var ctx = BatchContextSafeHandle.Create();
- GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray).CheckOk();
}
public void StartSendMessage(byte[] payload, BatchCompletionDelegate callback)
{
var ctx = BatchContextSafeHandle.Create();
- GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length)).CheckOk();
}
public void StartSendCloseFromClient(BatchCompletionDelegate callback)
{
var ctx = BatchContextSafeHandle.Create();
- GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_send_close_from_client(this, ctx).CheckOk();
}
public void StartSendStatusFromServer(Status status, BatchCompletionDelegate callback)
{
var ctx = BatchContextSafeHandle.Create();
- GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, status.Detail).CheckOk();
}
public void StartReceiveMessage(BatchCompletionDelegate callback)
{
var ctx = BatchContextSafeHandle.Create();
- GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_recv_message(this, ctx).CheckOk();
}
public void StartServerSide(BatchCompletionDelegate callback)
{
var ctx = BatchContextSafeHandle.Create();
- GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_start_serverside(this, ctx).CheckOk();
}
diff --git a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
index 80f006ae50..f6d8aa0600 100644
--- a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
+++ b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
@@ -45,11 +45,17 @@ namespace Grpc.Core.Internal
internal class CompletionRegistry
{
- readonly ConcurrentDictionary<IntPtr, OpCompletionDelegate> dict = new ConcurrentDictionary<IntPtr, OpCompletionDelegate>();
+ readonly GrpcEnvironment environment;
+ readonly ConcurrentDictionary<IntPtr, OpCompletionDelegate> dict = new ConcurrentDictionary<IntPtr, OpCompletionDelegate>();
+
+ public CompletionRegistry(GrpcEnvironment environment)
+ {
+ this.environment = environment;
+ }
public void Register(IntPtr key, OpCompletionDelegate callback)
{
- DebugStats.PendingBatchCompletions.Increment();
+ environment.DebugStats.PendingBatchCompletions.Increment();
Preconditions.CheckState(dict.TryAdd(key, callback));
}
@@ -63,7 +69,7 @@ namespace Grpc.Core.Internal
{
OpCompletionDelegate value;
Preconditions.CheckState(dict.TryRemove(key, out value));
- DebugStats.PendingBatchCompletions.Decrement();
+ environment.DebugStats.PendingBatchCompletions.Decrement();
return value;
}
diff --git a/src/csharp/Grpc.Core/Internal/DebugStats.cs b/src/csharp/Grpc.Core/Internal/DebugStats.cs
index ef9d9afe11..8793450ff3 100644
--- a/src/csharp/Grpc.Core/Internal/DebugStats.cs
+++ b/src/csharp/Grpc.Core/Internal/DebugStats.cs
@@ -36,12 +36,39 @@ using System.Threading;
namespace Grpc.Core.Internal
{
- internal static class DebugStats
+ internal class DebugStats
{
- public static readonly AtomicCounter ActiveClientCalls = new AtomicCounter();
+ public readonly AtomicCounter ActiveClientCalls = new AtomicCounter();
- public static readonly AtomicCounter ActiveServerCalls = new AtomicCounter();
+ public readonly AtomicCounter ActiveServerCalls = new AtomicCounter();
- public static readonly AtomicCounter PendingBatchCompletions = new AtomicCounter();
+ public readonly AtomicCounter PendingBatchCompletions = new AtomicCounter();
+
+ /// <summary>
+ /// Checks the debug stats and take action for any inconsistency found.
+ /// </summary>
+ public void CheckOK()
+ {
+ var remainingClientCalls = ActiveClientCalls.Count;
+ if (remainingClientCalls != 0)
+ {
+ DebugWarning(string.Format("Detected {0} client calls that weren't disposed properly.", remainingClientCalls));
+ }
+ var remainingServerCalls = ActiveServerCalls.Count;
+ if (remainingServerCalls != 0)
+ {
+ DebugWarning(string.Format("Detected {0} server calls that weren't disposed properly.", remainingServerCalls));
+ }
+ var pendingBatchCompletions = PendingBatchCompletions.Count;
+ if (pendingBatchCompletions != 0)
+ {
+ DebugWarning(string.Format("Detected {0} pending batch completions.", pendingBatchCompletions));
+ }
+ }
+
+ private void DebugWarning(string message)
+ {
+ throw new Exception("Shutdown check: " + message);
+ }
}
}
diff --git a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
index 89b44a4e2b..b77e893044 100644
--- a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
+++ b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
@@ -45,14 +45,16 @@ namespace Grpc.Core.Internal
/// </summary>
internal class GrpcThreadPool
{
+ readonly GrpcEnvironment environment;
readonly object myLock = new object();
readonly List<Thread> threads = new List<Thread>();
readonly int poolSize;
CompletionQueueSafeHandle cq;
- public GrpcThreadPool(int poolSize)
+ public GrpcThreadPool(GrpcEnvironment environment, int poolSize)
{
+ this.environment = environment;
this.poolSize = poolSize;
}
@@ -80,7 +82,7 @@ namespace Grpc.Core.Internal
{
cq.Shutdown();
- Console.WriteLine("Waiting for GPRC threads to finish.");
+ Console.WriteLine("Waiting for GRPC threads to finish.");
foreach (var thread in threads)
{
thread.Join();
@@ -122,7 +124,7 @@ namespace Grpc.Core.Internal
IntPtr tag = ev.tag;
try
{
- var callback = GrpcEnvironment.CompletionRegistry.Extract(tag);
+ var callback = environment.CompletionRegistry.Extract(tag);
callback(success);
}
catch (Exception e)
diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
index c0e5bae13f..594e46b159 100644
--- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
@@ -42,7 +42,7 @@ namespace Grpc.Core.Internal
{
internal interface IServerCallHandler
{
- Task HandleCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq);
+ Task HandleCall(string methodName, CallSafeHandle call, GrpcEnvironment environment);
}
internal class UnaryServerCallHandler<TRequest, TResponse> : IServerCallHandler
@@ -58,11 +58,12 @@ namespace Grpc.Core.Internal
this.handler = handler;
}
- public async Task HandleCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq)
+ public async Task HandleCall(string methodName, CallSafeHandle call, GrpcEnvironment environment)
{
var asyncCall = new AsyncCallServer<TRequest, TResponse>(
method.ResponseMarshaller.Serializer,
- method.RequestMarshaller.Deserializer);
+ method.RequestMarshaller.Deserializer,
+ environment);
asyncCall.Initialize(call);
var finishedTask = asyncCall.ServerSideCallAsync();
@@ -110,11 +111,12 @@ namespace Grpc.Core.Internal
this.handler = handler;
}
- public async Task HandleCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq)
+ public async Task HandleCall(string methodName, CallSafeHandle call, GrpcEnvironment environment)
{
var asyncCall = new AsyncCallServer<TRequest, TResponse>(
method.ResponseMarshaller.Serializer,
- method.RequestMarshaller.Deserializer);
+ method.RequestMarshaller.Deserializer,
+ environment);
asyncCall.Initialize(call);
var finishedTask = asyncCall.ServerSideCallAsync();
@@ -163,11 +165,12 @@ namespace Grpc.Core.Internal
this.handler = handler;
}
- public async Task HandleCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq)
+ public async Task HandleCall(string methodName, CallSafeHandle call, GrpcEnvironment environment)
{
var asyncCall = new AsyncCallServer<TRequest, TResponse>(
method.ResponseMarshaller.Serializer,
- method.RequestMarshaller.Deserializer);
+ method.RequestMarshaller.Deserializer,
+ environment);
asyncCall.Initialize(call);
var finishedTask = asyncCall.ServerSideCallAsync();
@@ -219,11 +222,12 @@ namespace Grpc.Core.Internal
this.handler = handler;
}
- public async Task HandleCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq)
+ public async Task HandleCall(string methodName, CallSafeHandle call, GrpcEnvironment environment)
{
var asyncCall = new AsyncCallServer<TRequest, TResponse>(
method.ResponseMarshaller.Serializer,
- method.RequestMarshaller.Deserializer);
+ method.RequestMarshaller.Deserializer,
+ environment);
asyncCall.Initialize(call);
var finishedTask = asyncCall.ServerSideCallAsync();
@@ -255,11 +259,11 @@ namespace Grpc.Core.Internal
internal class NoSuchMethodCallHandler : IServerCallHandler
{
- public async Task HandleCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq)
+ public async Task HandleCall(string methodName, CallSafeHandle call, GrpcEnvironment environment)
{
// We don't care about the payload type here.
var asyncCall = new AsyncCallServer<byte[], byte[]>(
- (payload) => payload, (payload) => payload);
+ (payload) => payload, (payload) => payload, environment);
asyncCall.Initialize(call);
var finishedTask = asyncCall.ServerSideCallAsync();
diff --git a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
index 83dbb910aa..9e1170e6dd 100644
--- a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
@@ -91,19 +91,19 @@ namespace Grpc.Core.Internal
{
grpcsharp_server_start(this);
}
-
- public void ShutdownAndNotify(CompletionQueueSafeHandle cq, BatchCompletionDelegate callback)
+
+ public void ShutdownAndNotify(BatchCompletionDelegate callback, GrpcEnvironment environment)
{
var ctx = BatchContextSafeHandle.Create();
- GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
- grpcsharp_server_shutdown_and_notify_callback(this, cq, ctx);
+ environment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ grpcsharp_server_shutdown_and_notify_callback(this, environment.CompletionQueue, ctx);
}
- public void RequestCall(CompletionQueueSafeHandle cq, BatchCompletionDelegate callback)
+ public void RequestCall(BatchCompletionDelegate callback, GrpcEnvironment environment)
{
var ctx = BatchContextSafeHandle.Create();
- GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
- grpcsharp_server_request_call(this, cq, ctx).CheckOk();
+ environment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ grpcsharp_server_request_call(this, environment.CompletionQueue, ctx).CheckOk();
}
protected override bool ReleaseHandle()
diff --git a/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs
index c57eef65aa..2b3d7530f2 100644
--- a/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs
@@ -9,6 +9,5 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("0.6.*")]
[assembly: InternalsVisibleTo("Grpc.Core.Tests")]
diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs
index 8e818885d1..cbf77196cf 100644
--- a/src/csharp/Grpc.Core/Server.cs
+++ b/src/csharp/Grpc.Core/Server.cs
@@ -52,6 +52,7 @@ namespace Grpc.Core
/// </summary>
public const int PickUnusedPort = 0;
+ readonly GrpcEnvironment environment;
readonly ServerSafeHandle handle;
readonly object myLock = new object();
@@ -67,9 +68,10 @@ namespace Grpc.Core
/// <param name="options">Channel options.</param>
public Server(IEnumerable<ChannelOption> options = null)
{
+ this.environment = GrpcEnvironment.GetInstance();
using (var channelArgs = ChannelOptions.CreateChannelArgs(options))
{
- this.handle = ServerSafeHandle.NewServer(GetCompletionQueue(), channelArgs);
+ this.handle = ServerSafeHandle.NewServer(environment.CompletionQueue, channelArgs);
}
}
@@ -144,7 +146,7 @@ namespace Grpc.Core
shutdownRequested = true;
}
- handle.ShutdownAndNotify(GetCompletionQueue(), HandleServerShutdown);
+ handle.ShutdownAndNotify(HandleServerShutdown, environment);
await shutdownTcs.Task;
handle.Dispose();
}
@@ -173,7 +175,7 @@ namespace Grpc.Core
shutdownRequested = true;
}
- handle.ShutdownAndNotify(GetCompletionQueue(), HandleServerShutdown);
+ handle.ShutdownAndNotify(HandleServerShutdown, environment);
handle.CancelAllCalls();
await shutdownTcs.Task;
handle.Dispose();
@@ -208,7 +210,7 @@ namespace Grpc.Core
{
if (!shutdownRequested)
{
- handle.RequestCall(GetCompletionQueue(), HandleNewServerRpc);
+ handle.RequestCall(HandleNewServerRpc, environment);
}
}
}
@@ -225,7 +227,7 @@ namespace Grpc.Core
{
callHandler = new NoSuchMethodCallHandler();
}
- await callHandler.HandleCall(method, call, GetCompletionQueue());
+ await callHandler.HandleCall(method, call, environment);
}
catch (Exception e)
{
@@ -259,10 +261,5 @@ namespace Grpc.Core
{
shutdownTcs.SetResult(null);
}
-
- private static CompletionQueueSafeHandle GetCompletionQueue()
- {
- return GrpcEnvironment.ThreadPool.CompletionQueue;
- }
}
}
diff --git a/src/csharp/Grpc.Core/Version.cs b/src/csharp/Grpc.Core/Version.cs
new file mode 100644
index 0000000000..972f495bd7
--- /dev/null
+++ b/src/csharp/Grpc.Core/Version.cs
@@ -0,0 +1,6 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// The current version of gRPC C#.
+[assembly: AssemblyVersion("0.6.0.*")]
+
diff --git a/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj b/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj
index 19bb4347aa..5d5401593d 100644
--- a/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj
+++ b/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -35,6 +35,9 @@
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Grpc.Core\Version.cs">
+ <Link>Version.cs</Link>
+ </Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MathClient.cs" />
</ItemGroup>
@@ -49,4 +52,4 @@
<Name>Grpc.Examples</Name>
</ProjectReference>
</ItemGroup>
-</Project>
+</Project> \ No newline at end of file
diff --git a/src/csharp/Grpc.Examples.MathClient/MathClient.cs b/src/csharp/Grpc.Examples.MathClient/MathClient.cs
index 360fe928dd..b763721460 100644
--- a/src/csharp/Grpc.Examples.MathClient/MathClient.cs
+++ b/src/csharp/Grpc.Examples.MathClient/MathClient.cs
@@ -39,8 +39,6 @@ namespace math
{
public static void Main(string[] args)
{
- GrpcEnvironment.Initialize();
-
using (Channel channel = new Channel("127.0.0.1", 23456))
{
Math.IMathClient stub = new Math.MathClient(channel);
diff --git a/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs
index a57c540215..0fb0dbd510 100644
--- a/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs
@@ -9,4 +9,3 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("0.6.*")]
diff --git a/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj b/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj
index ba6586ee5e..677d87da20 100644
--- a/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj
+++ b/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj
@@ -35,6 +35,9 @@
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Grpc.Core\Version.cs">
+ <Link>Version.cs</Link>
+ </Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MathServer.cs" />
</ItemGroup>
diff --git a/src/csharp/Grpc.Examples.MathServer/MathServer.cs b/src/csharp/Grpc.Examples.MathServer/MathServer.cs
index d05e3f2808..f440985112 100644
--- a/src/csharp/Grpc.Examples.MathServer/MathServer.cs
+++ b/src/csharp/Grpc.Examples.MathServer/MathServer.cs
@@ -42,8 +42,6 @@ namespace math
{
string host = "0.0.0.0";
- GrpcEnvironment.Initialize();
-
Server server = new Server();
server.AddServiceDefinition(Math.BindService(new MathServiceImpl()));
int port = server.AddListeningPort(host, 23456);
diff --git a/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs
index 6c772cb45c..63035b6c63 100644
--- a/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs
@@ -9,4 +9,3 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("0.6.*")]
diff --git a/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj b/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj
index 6e84add42b..d59d7515d1 100644
--- a/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj
+++ b/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj
@@ -43,6 +43,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Grpc.Core\Version.cs">
+ <Link>Version.cs</Link>
+ </Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MathClientServerTests.cs" />
</ItemGroup>
diff --git a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
index aadd49f795..10dceb60aa 100644
--- a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
+++ b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
@@ -54,8 +54,6 @@ namespace math.Tests
[TestFixtureSetUp]
public void Init()
{
- GrpcEnvironment.Initialize();
-
server = new Server();
server.AddServiceDefinition(Math.BindService(new MathServiceImpl()));
int port = server.AddListeningPort(host, Server.PickUnusedPort);
@@ -75,7 +73,6 @@ namespace math.Tests
public void Cleanup()
{
channel.Dispose();
-
server.ShutdownAsync().Wait();
GrpcEnvironment.Shutdown();
}
diff --git a/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs
index 4acaeaa438..846afb4616 100644
--- a/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs
@@ -9,4 +9,3 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("0.6.*")]
diff --git a/src/csharp/Grpc.Examples/Grpc.Examples.csproj b/src/csharp/Grpc.Examples/Grpc.Examples.csproj
index 5ce490f403..eaf24a253c 100644
--- a/src/csharp/Grpc.Examples/Grpc.Examples.csproj
+++ b/src/csharp/Grpc.Examples/Grpc.Examples.csproj
@@ -40,6 +40,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Grpc.Core\Version.cs">
+ <Link>Version.cs</Link>
+ </Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Math.cs" />
<Compile Include="MathGrpc.cs" />
diff --git a/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs
index 60a7aaea13..92111389cb 100644
--- a/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs
@@ -9,4 +9,3 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("0.6.*")]
diff --git a/src/csharp/Grpc.HealthCheck.Tests/.gitignore b/src/csharp/Grpc.HealthCheck.Tests/.gitignore
new file mode 100644
index 0000000000..1746e3269e
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck.Tests/.gitignore
@@ -0,0 +1,2 @@
+bin
+obj
diff --git a/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj b/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj
new file mode 100644
index 0000000000..72e110302b
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Grpc.HealthCheck.Tests</RootNamespace>
+ <AssemblyName>Grpc.HealthCheck.Tests</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Google.ProtocolBuffers">
+ <HintPath>..\packages\Google.ProtocolBuffers.2.4.1.555\lib\net40\Google.ProtocolBuffers.dll</HintPath>
+ </Reference>
+ <Reference Include="Google.ProtocolBuffers.Serialization">
+ <HintPath>..\packages\Google.ProtocolBuffers.2.4.1.555\lib\net40\Google.ProtocolBuffers.Serialization.dll</HintPath>
+ </Reference>
+ <Reference Include="nunit.framework">
+ <HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="..\Grpc.Core\Version.cs">
+ <Link>Version.cs</Link>
+ </Compile>
+ <Compile Include="HealthServiceImplTest.cs" />
+ <Compile Include="HealthClientServerTest.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Grpc.Core\Grpc.Core.csproj">
+ <Project>{ccc4440e-49f7-4790-b0af-feabb0837ae7}</Project>
+ <Name>Grpc.Core</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Grpc.HealthCheck\Grpc.HealthCheck.csproj">
+ <Project>{aa5e328a-8835-49d7-98ed-c29f2b3049f0}</Project>
+ <Name>Grpc.HealthCheck</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs
new file mode 100644
index 0000000000..0ac1add8e4
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs
@@ -0,0 +1,97 @@
+#region Copyright notice and license
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Grpc.Core;
+using Grpc.Health.V1Alpha;
+using NUnit.Framework;
+
+namespace Grpc.HealthCheck.Tests
+{
+ /// <summary>
+ /// Health client talks to health server.
+ /// </summary>
+ public class HealthClientServerTest
+ {
+ const string Host = "localhost";
+ Server server;
+ Channel channel;
+ Grpc.Health.V1Alpha.Health.IHealthClient client;
+ Grpc.HealthCheck.HealthServiceImpl serviceImpl;
+
+ [TestFixtureSetUp]
+ public void Init()
+ {
+ serviceImpl = new HealthServiceImpl();
+
+ server = new Server();
+ server.AddServiceDefinition(Grpc.Health.V1Alpha.Health.BindService(serviceImpl));
+ int port = server.AddListeningPort(Host, Server.PickUnusedPort);
+ server.Start();
+ channel = new Channel(Host, port);
+
+ client = Grpc.Health.V1Alpha.Health.NewStub(channel);
+ }
+
+ [TestFixtureTearDown]
+ public void Cleanup()
+ {
+ channel.Dispose();
+
+ server.ShutdownAsync().Wait();
+ GrpcEnvironment.Shutdown();
+ }
+
+ [Test]
+ public void ServiceIsRunning()
+ {
+ serviceImpl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.SERVING);
+
+ var response = client.Check(HealthCheckRequest.CreateBuilder().SetHost("").SetService("").Build());
+ Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.SERVING, response.Status);
+ }
+
+ [Test]
+ public void ServiceDoesntExist()
+ {
+ // TODO(jtattermusch): currently, this returns wrong status code, because we don't enable sending arbitrary status code from
+ // server handlers yet.
+ Assert.Throws(typeof(RpcException), () => client.Check(HealthCheckRequest.CreateBuilder().SetHost("").SetService("nonexistent.service").Build()));
+ }
+
+ // TODO(jtattermusch): add test with timeout once timeouts are supported
+ }
+} \ No newline at end of file
diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs
new file mode 100644
index 0000000000..9b7c4f2140
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs
@@ -0,0 +1,107 @@
+#region Copyright notice and license
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Grpc.Core;
+using Grpc.Health.V1Alpha;
+using NUnit.Framework;
+
+namespace Grpc.HealthCheck.Tests
+{
+ /// <summary>
+ /// Tests for HealthCheckServiceImpl
+ /// </summary>
+ public class HealthServiceImplTest
+ {
+ [Test]
+ public void SetStatus()
+ {
+ var impl = new HealthServiceImpl();
+ impl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.SERVING);
+ Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.SERVING, GetStatusHelper(impl, "", ""));
+
+ impl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.NOT_SERVING);
+ Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.NOT_SERVING, GetStatusHelper(impl, "", ""));
+
+ impl.SetStatus("virtual-host", "", HealthCheckResponse.Types.ServingStatus.UNKNOWN);
+ Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.UNKNOWN, GetStatusHelper(impl, "virtual-host", ""));
+
+ impl.SetStatus("virtual-host", "grpc.test.TestService", HealthCheckResponse.Types.ServingStatus.SERVING);
+ Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.SERVING, GetStatusHelper(impl, "virtual-host", "grpc.test.TestService"));
+ }
+
+ [Test]
+ public void ClearStatus()
+ {
+ var impl = new HealthServiceImpl();
+ impl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.SERVING);
+ impl.SetStatus("virtual-host", "", HealthCheckResponse.Types.ServingStatus.UNKNOWN);
+
+ impl.ClearStatus("", "");
+
+ Assert.Throws(Is.TypeOf(typeof(RpcException)).And.Property("Status").Property("StatusCode").EqualTo(StatusCode.NotFound), () => GetStatusHelper(impl, "", ""));
+ Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.UNKNOWN, GetStatusHelper(impl, "virtual-host", ""));
+ }
+
+ [Test]
+ public void ClearAll()
+ {
+ var impl = new HealthServiceImpl();
+ impl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.SERVING);
+ impl.SetStatus("virtual-host", "", HealthCheckResponse.Types.ServingStatus.UNKNOWN);
+
+ impl.ClearAll();
+ Assert.Throws(typeof(RpcException), () => GetStatusHelper(impl, "", ""));
+ Assert.Throws(typeof(RpcException), () => GetStatusHelper(impl, "virtual-host", ""));
+ }
+
+ [Test]
+ public void NullsRejected()
+ {
+ var impl = new HealthServiceImpl();
+ Assert.Throws(typeof(NullReferenceException), () => impl.SetStatus(null, "", HealthCheckResponse.Types.ServingStatus.SERVING));
+ Assert.Throws(typeof(NullReferenceException), () => impl.SetStatus("", null, HealthCheckResponse.Types.ServingStatus.SERVING));
+
+ Assert.Throws(typeof(NullReferenceException), () => impl.ClearStatus(null, ""));
+ Assert.Throws(typeof(NullReferenceException), () => impl.ClearStatus("", null));
+ }
+
+ private static HealthCheckResponse.Types.ServingStatus GetStatusHelper(HealthServiceImpl impl, string host, string service)
+ {
+ return impl.Check(null, HealthCheckRequest.CreateBuilder().SetHost(host).SetService(service).Build()).Result.Status;
+ }
+ }
+}
diff --git a/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..d5660305be
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,11 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+[assembly: AssemblyTitle("Grpc.HealthCheck.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
diff --git a/src/csharp/Grpc.HealthCheck.Tests/packages.config b/src/csharp/Grpc.HealthCheck.Tests/packages.config
new file mode 100644
index 0000000000..050c4eaed6
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck.Tests/packages.config
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="Google.ProtocolBuffers" version="2.4.1.555" targetFramework="net45" />
+ <package id="NUnit" version="2.6.4" targetFramework="net45" />
+</packages> \ No newline at end of file
diff --git a/src/csharp/Grpc.HealthCheck/.gitignore b/src/csharp/Grpc.HealthCheck/.gitignore
new file mode 100644
index 0000000000..1746e3269e
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck/.gitignore
@@ -0,0 +1,2 @@
+bin
+obj
diff --git a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj
new file mode 100644
index 0000000000..4ebb6446dd
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{AA5E328A-8835-49D7-98ED-C29F2B3049F0}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Grpc.HealthCheck</RootNamespace>
+ <AssemblyName>Grpc.HealthCheck</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <DocumentationFile>bin\$(Configuration)\Grpc.HealthCheck.Xml</DocumentationFile>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Google.ProtocolBuffers">
+ <HintPath>..\packages\Google.ProtocolBuffers.2.4.1.555\lib\net40\Google.ProtocolBuffers.dll</HintPath>
+ </Reference>
+ <Reference Include="Google.ProtocolBuffers.Serialization">
+ <HintPath>..\packages\Google.ProtocolBuffers.2.4.1.555\lib\net40\Google.ProtocolBuffers.Serialization.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Interactive.Async">
+ <HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="..\Grpc.Core\Version.cs">
+ <Link>Version.cs</Link>
+ </Compile>
+ <Compile Include="HealthServiceImpl.cs" />
+ <Compile Include="Health.cs" />
+ <Compile Include="HealthGrpc.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Grpc.HealthCheck.nuspec" />
+ <None Include="packages.config" />
+ <None Include="proto\health.proto" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Grpc.Core\Grpc.Core.csproj">
+ <Project>{ccc4440e-49f7-4790-b0af-feabb0837ae7}</Project>
+ <Name>Grpc.Core</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec
new file mode 100644
index 0000000000..ca35b36805
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<package>
+ <metadata>
+ <id>Grpc.HealthCheck</id>
+ <title>gRPC C# Healthchecking</title>
+ <summary>Implementation of gRPC health service</summary>
+ <description>Example implementation of grpc.health.v1alpha service that can be used for health-checking.</description>
+ <version>$version$</version>
+ <authors>Google Inc.</authors>
+ <owners>grpc-packages</owners>
+ <licenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</licenseUrl>
+ <projectUrl>https://github.com/grpc/grpc</projectUrl>
+ <requireLicenseAcceptance>false</requireLicenseAcceptance>
+ <copyright>Copyright 2015, Google Inc.</copyright>
+ <tags>gRPC health check</tags>
+ <dependencies>
+ <dependency id="Google.ProtocolBuffers" version="2.4.1.555" />
+ <dependency id="Grpc.Core" version="$version$" />
+ <dependency id="Ix-Async" version="1.2.3" />
+ </dependencies>
+ </metadata>
+ <files>
+ <file src="bin/Release/Grpc.HealthCheck.dll" target="lib/net45" />
+ <file src="bin/Release/Grpc.HealthCheck.pdb" target="lib/net45" />
+ <file src="bin/Release/Grpc.HealthCheck.xml" target="lib/net45" />
+ <file src="**\*.cs" target="src" />
+ </files>
+</package>
diff --git a/src/csharp/Grpc.HealthCheck/Health.cs b/src/csharp/Grpc.HealthCheck/Health.cs
new file mode 100644
index 0000000000..361382d4bd
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck/Health.cs
@@ -0,0 +1,687 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: health.proto
+#pragma warning disable 1591, 0612, 3021
+#region Designer generated code
+
+using pb = global::Google.ProtocolBuffers;
+using pbc = global::Google.ProtocolBuffers.Collections;
+using pbd = global::Google.ProtocolBuffers.Descriptors;
+using scg = global::System.Collections.Generic;
+namespace Grpc.Health.V1Alpha {
+
+ namespace Proto {
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public static partial class Health {
+
+ #region Extension registration
+ public static void RegisterAllExtensions(pb::ExtensionRegistry registry) {
+ }
+ #endregion
+ #region Static variables
+ internal static pbd::MessageDescriptor internal__static_grpc_health_v1alpha_HealthCheckRequest__Descriptor;
+ internal static pb::FieldAccess.FieldAccessorTable<global::Grpc.Health.V1Alpha.HealthCheckRequest, global::Grpc.Health.V1Alpha.HealthCheckRequest.Builder> internal__static_grpc_health_v1alpha_HealthCheckRequest__FieldAccessorTable;
+ internal static pbd::MessageDescriptor internal__static_grpc_health_v1alpha_HealthCheckResponse__Descriptor;
+ internal static pb::FieldAccess.FieldAccessorTable<global::Grpc.Health.V1Alpha.HealthCheckResponse, global::Grpc.Health.V1Alpha.HealthCheckResponse.Builder> internal__static_grpc_health_v1alpha_HealthCheckResponse__FieldAccessorTable;
+ #endregion
+ #region Descriptor
+ public static pbd::FileDescriptor Descriptor {
+ get { return descriptor; }
+ }
+ private static pbd::FileDescriptor descriptor;
+
+ static Health() {
+ byte[] descriptorData = global::System.Convert.FromBase64String(
+ string.Concat(
+ "CgxoZWFsdGgucHJvdG8SE2dycGMuaGVhbHRoLnYxYWxwaGEiMwoSSGVhbHRo",
+ "Q2hlY2tSZXF1ZXN0EgwKBGhvc3QYASABKAkSDwoHc2VydmljZRgCIAEoCSKZ",
+ "AQoTSGVhbHRoQ2hlY2tSZXNwb25zZRJGCgZzdGF0dXMYASABKA4yNi5ncnBj",
+ "LmhlYWx0aC52MWFscGhhLkhlYWx0aENoZWNrUmVzcG9uc2UuU2VydmluZ1N0",
+ "YXR1cyI6Cg1TZXJ2aW5nU3RhdHVzEgsKB1VOS05PV04QABILCgdTRVJWSU5H",
+ "EAESDwoLTk9UX1NFUlZJTkcQAjJkCgZIZWFsdGgSWgoFQ2hlY2sSJy5ncnBj",
+ "LmhlYWx0aC52MWFscGhhLkhlYWx0aENoZWNrUmVxdWVzdBooLmdycGMuaGVh",
+ "bHRoLnYxYWxwaGEuSGVhbHRoQ2hlY2tSZXNwb25zZUIWqgITR3JwYy5IZWFs",
+ "dGguVjFBbHBoYQ=="));
+ pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
+ descriptor = root;
+ internal__static_grpc_health_v1alpha_HealthCheckRequest__Descriptor = Descriptor.MessageTypes[0];
+ internal__static_grpc_health_v1alpha_HealthCheckRequest__FieldAccessorTable =
+ new pb::FieldAccess.FieldAccessorTable<global::Grpc.Health.V1Alpha.HealthCheckRequest, global::Grpc.Health.V1Alpha.HealthCheckRequest.Builder>(internal__static_grpc_health_v1alpha_HealthCheckRequest__Descriptor,
+ new string[] { "Host", "Service", });
+ internal__static_grpc_health_v1alpha_HealthCheckResponse__Descriptor = Descriptor.MessageTypes[1];
+ internal__static_grpc_health_v1alpha_HealthCheckResponse__FieldAccessorTable =
+ new pb::FieldAccess.FieldAccessorTable<global::Grpc.Health.V1Alpha.HealthCheckResponse, global::Grpc.Health.V1Alpha.HealthCheckResponse.Builder>(internal__static_grpc_health_v1alpha_HealthCheckResponse__Descriptor,
+ new string[] { "Status", });
+ pb::ExtensionRegistry registry = pb::ExtensionRegistry.CreateInstance();
+ RegisterAllExtensions(registry);
+ return registry;
+ };
+ pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ new pbd::FileDescriptor[] {
+ }, assigner);
+ }
+ #endregion
+
+ }
+ }
+ #region Messages
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class HealthCheckRequest : pb::GeneratedMessage<HealthCheckRequest, HealthCheckRequest.Builder> {
+ private HealthCheckRequest() { }
+ private static readonly HealthCheckRequest defaultInstance = new HealthCheckRequest().MakeReadOnly();
+ private static readonly string[] _healthCheckRequestFieldNames = new string[] { "host", "service" };
+ private static readonly uint[] _healthCheckRequestFieldTags = new uint[] { 10, 18 };
+ public static HealthCheckRequest DefaultInstance {
+ get { return defaultInstance; }
+ }
+
+ public override HealthCheckRequest DefaultInstanceForType {
+ get { return DefaultInstance; }
+ }
+
+ protected override HealthCheckRequest ThisMessage {
+ get { return this; }
+ }
+
+ public static pbd::MessageDescriptor Descriptor {
+ get { return global::Grpc.Health.V1Alpha.Proto.Health.internal__static_grpc_health_v1alpha_HealthCheckRequest__Descriptor; }
+ }
+
+ protected override pb::FieldAccess.FieldAccessorTable<HealthCheckRequest, HealthCheckRequest.Builder> InternalFieldAccessors {
+ get { return global::Grpc.Health.V1Alpha.Proto.Health.internal__static_grpc_health_v1alpha_HealthCheckRequest__FieldAccessorTable; }
+ }
+
+ public const int HostFieldNumber = 1;
+ private bool hasHost;
+ private string host_ = "";
+ public bool HasHost {
+ get { return hasHost; }
+ }
+ public string Host {
+ get { return host_; }
+ }
+
+ public const int ServiceFieldNumber = 2;
+ private bool hasService;
+ private string service_ = "";
+ public bool HasService {
+ get { return hasService; }
+ }
+ public string Service {
+ get { return service_; }
+ }
+
+ public override bool IsInitialized {
+ get {
+ return true;
+ }
+ }
+
+ public override void WriteTo(pb::ICodedOutputStream output) {
+ CalcSerializedSize();
+ string[] field_names = _healthCheckRequestFieldNames;
+ if (hasHost) {
+ output.WriteString(1, field_names[0], Host);
+ }
+ if (hasService) {
+ output.WriteString(2, field_names[1], Service);
+ }
+ UnknownFields.WriteTo(output);
+ }
+
+ private int memoizedSerializedSize = -1;
+ public override int SerializedSize {
+ get {
+ int size = memoizedSerializedSize;
+ if (size != -1) return size;
+ return CalcSerializedSize();
+ }
+ }
+
+ private int CalcSerializedSize() {
+ int size = memoizedSerializedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (hasHost) {
+ size += pb::CodedOutputStream.ComputeStringSize(1, Host);
+ }
+ if (hasService) {
+ size += pb::CodedOutputStream.ComputeStringSize(2, Service);
+ }
+ size += UnknownFields.SerializedSize;
+ memoizedSerializedSize = size;
+ return size;
+ }
+ public static HealthCheckRequest ParseFrom(pb::ByteString data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static HealthCheckRequest ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static HealthCheckRequest ParseFrom(byte[] data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static HealthCheckRequest ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static HealthCheckRequest ParseFrom(global::System.IO.Stream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static HealthCheckRequest ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ public static HealthCheckRequest ParseDelimitedFrom(global::System.IO.Stream input) {
+ return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
+ }
+ public static HealthCheckRequest ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
+ }
+ public static HealthCheckRequest ParseFrom(pb::ICodedInputStream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static HealthCheckRequest ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ private HealthCheckRequest MakeReadOnly() {
+ return this;
+ }
+
+ public static Builder CreateBuilder() { return new Builder(); }
+ public override Builder ToBuilder() { return CreateBuilder(this); }
+ public override Builder CreateBuilderForType() { return new Builder(); }
+ public static Builder CreateBuilder(HealthCheckRequest prototype) {
+ return new Builder(prototype);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class Builder : pb::GeneratedBuilder<HealthCheckRequest, Builder> {
+ protected override Builder ThisBuilder {
+ get { return this; }
+ }
+ public Builder() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ }
+ internal Builder(HealthCheckRequest cloneFrom) {
+ result = cloneFrom;
+ resultIsReadOnly = true;
+ }
+
+ private bool resultIsReadOnly;
+ private HealthCheckRequest result;
+
+ private HealthCheckRequest PrepareBuilder() {
+ if (resultIsReadOnly) {
+ HealthCheckRequest original = result;
+ result = new HealthCheckRequest();
+ resultIsReadOnly = false;
+ MergeFrom(original);
+ }
+ return result;
+ }
+
+ public override bool IsInitialized {
+ get { return result.IsInitialized; }
+ }
+
+ protected override HealthCheckRequest MessageBeingBuilt {
+ get { return PrepareBuilder(); }
+ }
+
+ public override Builder Clear() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ return this;
+ }
+
+ public override Builder Clone() {
+ if (resultIsReadOnly) {
+ return new Builder(result);
+ } else {
+ return new Builder().MergeFrom(result);
+ }
+ }
+
+ public override pbd::MessageDescriptor DescriptorForType {
+ get { return global::Grpc.Health.V1Alpha.HealthCheckRequest.Descriptor; }
+ }
+
+ public override HealthCheckRequest DefaultInstanceForType {
+ get { return global::Grpc.Health.V1Alpha.HealthCheckRequest.DefaultInstance; }
+ }
+
+ public override HealthCheckRequest BuildPartial() {
+ if (resultIsReadOnly) {
+ return result;
+ }
+ resultIsReadOnly = true;
+ return result.MakeReadOnly();
+ }
+
+ public override Builder MergeFrom(pb::IMessage other) {
+ if (other is HealthCheckRequest) {
+ return MergeFrom((HealthCheckRequest) other);
+ } else {
+ base.MergeFrom(other);
+ return this;
+ }
+ }
+
+ public override Builder MergeFrom(HealthCheckRequest other) {
+ if (other == global::Grpc.Health.V1Alpha.HealthCheckRequest.DefaultInstance) return this;
+ PrepareBuilder();
+ if (other.HasHost) {
+ Host = other.Host;
+ }
+ if (other.HasService) {
+ Service = other.Service;
+ }
+ this.MergeUnknownFields(other.UnknownFields);
+ return this;
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input) {
+ return MergeFrom(input, pb::ExtensionRegistry.Empty);
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ PrepareBuilder();
+ pb::UnknownFieldSet.Builder unknownFields = null;
+ uint tag;
+ string field_name;
+ while (input.ReadTag(out tag, out field_name)) {
+ if(tag == 0 && field_name != null) {
+ int field_ordinal = global::System.Array.BinarySearch(_healthCheckRequestFieldNames, field_name, global::System.StringComparer.Ordinal);
+ if(field_ordinal >= 0)
+ tag = _healthCheckRequestFieldTags[field_ordinal];
+ else {
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ continue;
+ }
+ }
+ switch (tag) {
+ case 0: {
+ throw pb::InvalidProtocolBufferException.InvalidTag();
+ }
+ default: {
+ if (pb::WireFormat.IsEndGroupTag(tag)) {
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ break;
+ }
+ case 10: {
+ result.hasHost = input.ReadString(ref result.host_);
+ break;
+ }
+ case 18: {
+ result.hasService = input.ReadString(ref result.service_);
+ break;
+ }
+ }
+ }
+
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+
+
+ public bool HasHost {
+ get { return result.hasHost; }
+ }
+ public string Host {
+ get { return result.Host; }
+ set { SetHost(value); }
+ }
+ public Builder SetHost(string value) {
+ pb::ThrowHelper.ThrowIfNull(value, "value");
+ PrepareBuilder();
+ result.hasHost = true;
+ result.host_ = value;
+ return this;
+ }
+ public Builder ClearHost() {
+ PrepareBuilder();
+ result.hasHost = false;
+ result.host_ = "";
+ return this;
+ }
+
+ public bool HasService {
+ get { return result.hasService; }
+ }
+ public string Service {
+ get { return result.Service; }
+ set { SetService(value); }
+ }
+ public Builder SetService(string value) {
+ pb::ThrowHelper.ThrowIfNull(value, "value");
+ PrepareBuilder();
+ result.hasService = true;
+ result.service_ = value;
+ return this;
+ }
+ public Builder ClearService() {
+ PrepareBuilder();
+ result.hasService = false;
+ result.service_ = "";
+ return this;
+ }
+ }
+ static HealthCheckRequest() {
+ object.ReferenceEquals(global::Grpc.Health.V1Alpha.Proto.Health.Descriptor, null);
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class HealthCheckResponse : pb::GeneratedMessage<HealthCheckResponse, HealthCheckResponse.Builder> {
+ private HealthCheckResponse() { }
+ private static readonly HealthCheckResponse defaultInstance = new HealthCheckResponse().MakeReadOnly();
+ private static readonly string[] _healthCheckResponseFieldNames = new string[] { "status" };
+ private static readonly uint[] _healthCheckResponseFieldTags = new uint[] { 8 };
+ public static HealthCheckResponse DefaultInstance {
+ get { return defaultInstance; }
+ }
+
+ public override HealthCheckResponse DefaultInstanceForType {
+ get { return DefaultInstance; }
+ }
+
+ protected override HealthCheckResponse ThisMessage {
+ get { return this; }
+ }
+
+ public static pbd::MessageDescriptor Descriptor {
+ get { return global::Grpc.Health.V1Alpha.Proto.Health.internal__static_grpc_health_v1alpha_HealthCheckResponse__Descriptor; }
+ }
+
+ protected override pb::FieldAccess.FieldAccessorTable<HealthCheckResponse, HealthCheckResponse.Builder> InternalFieldAccessors {
+ get { return global::Grpc.Health.V1Alpha.Proto.Health.internal__static_grpc_health_v1alpha_HealthCheckResponse__FieldAccessorTable; }
+ }
+
+ #region Nested types
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public static partial class Types {
+ public enum ServingStatus {
+ UNKNOWN = 0,
+ SERVING = 1,
+ NOT_SERVING = 2,
+ }
+
+ }
+ #endregion
+
+ public const int StatusFieldNumber = 1;
+ private bool hasStatus;
+ private global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus status_ = global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus.UNKNOWN;
+ public bool HasStatus {
+ get { return hasStatus; }
+ }
+ public global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus Status {
+ get { return status_; }
+ }
+
+ public override bool IsInitialized {
+ get {
+ return true;
+ }
+ }
+
+ public override void WriteTo(pb::ICodedOutputStream output) {
+ CalcSerializedSize();
+ string[] field_names = _healthCheckResponseFieldNames;
+ if (hasStatus) {
+ output.WriteEnum(1, field_names[0], (int) Status, Status);
+ }
+ UnknownFields.WriteTo(output);
+ }
+
+ private int memoizedSerializedSize = -1;
+ public override int SerializedSize {
+ get {
+ int size = memoizedSerializedSize;
+ if (size != -1) return size;
+ return CalcSerializedSize();
+ }
+ }
+
+ private int CalcSerializedSize() {
+ int size = memoizedSerializedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (hasStatus) {
+ size += pb::CodedOutputStream.ComputeEnumSize(1, (int) Status);
+ }
+ size += UnknownFields.SerializedSize;
+ memoizedSerializedSize = size;
+ return size;
+ }
+ public static HealthCheckResponse ParseFrom(pb::ByteString data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static HealthCheckResponse ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static HealthCheckResponse ParseFrom(byte[] data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static HealthCheckResponse ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static HealthCheckResponse ParseFrom(global::System.IO.Stream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static HealthCheckResponse ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ public static HealthCheckResponse ParseDelimitedFrom(global::System.IO.Stream input) {
+ return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
+ }
+ public static HealthCheckResponse ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
+ }
+ public static HealthCheckResponse ParseFrom(pb::ICodedInputStream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static HealthCheckResponse ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ private HealthCheckResponse MakeReadOnly() {
+ return this;
+ }
+
+ public static Builder CreateBuilder() { return new Builder(); }
+ public override Builder ToBuilder() { return CreateBuilder(this); }
+ public override Builder CreateBuilderForType() { return new Builder(); }
+ public static Builder CreateBuilder(HealthCheckResponse prototype) {
+ return new Builder(prototype);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class Builder : pb::GeneratedBuilder<HealthCheckResponse, Builder> {
+ protected override Builder ThisBuilder {
+ get { return this; }
+ }
+ public Builder() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ }
+ internal Builder(HealthCheckResponse cloneFrom) {
+ result = cloneFrom;
+ resultIsReadOnly = true;
+ }
+
+ private bool resultIsReadOnly;
+ private HealthCheckResponse result;
+
+ private HealthCheckResponse PrepareBuilder() {
+ if (resultIsReadOnly) {
+ HealthCheckResponse original = result;
+ result = new HealthCheckResponse();
+ resultIsReadOnly = false;
+ MergeFrom(original);
+ }
+ return result;
+ }
+
+ public override bool IsInitialized {
+ get { return result.IsInitialized; }
+ }
+
+ protected override HealthCheckResponse MessageBeingBuilt {
+ get { return PrepareBuilder(); }
+ }
+
+ public override Builder Clear() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ return this;
+ }
+
+ public override Builder Clone() {
+ if (resultIsReadOnly) {
+ return new Builder(result);
+ } else {
+ return new Builder().MergeFrom(result);
+ }
+ }
+
+ public override pbd::MessageDescriptor DescriptorForType {
+ get { return global::Grpc.Health.V1Alpha.HealthCheckResponse.Descriptor; }
+ }
+
+ public override HealthCheckResponse DefaultInstanceForType {
+ get { return global::Grpc.Health.V1Alpha.HealthCheckResponse.DefaultInstance; }
+ }
+
+ public override HealthCheckResponse BuildPartial() {
+ if (resultIsReadOnly) {
+ return result;
+ }
+ resultIsReadOnly = true;
+ return result.MakeReadOnly();
+ }
+
+ public override Builder MergeFrom(pb::IMessage other) {
+ if (other is HealthCheckResponse) {
+ return MergeFrom((HealthCheckResponse) other);
+ } else {
+ base.MergeFrom(other);
+ return this;
+ }
+ }
+
+ public override Builder MergeFrom(HealthCheckResponse other) {
+ if (other == global::Grpc.Health.V1Alpha.HealthCheckResponse.DefaultInstance) return this;
+ PrepareBuilder();
+ if (other.HasStatus) {
+ Status = other.Status;
+ }
+ this.MergeUnknownFields(other.UnknownFields);
+ return this;
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input) {
+ return MergeFrom(input, pb::ExtensionRegistry.Empty);
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ PrepareBuilder();
+ pb::UnknownFieldSet.Builder unknownFields = null;
+ uint tag;
+ string field_name;
+ while (input.ReadTag(out tag, out field_name)) {
+ if(tag == 0 && field_name != null) {
+ int field_ordinal = global::System.Array.BinarySearch(_healthCheckResponseFieldNames, field_name, global::System.StringComparer.Ordinal);
+ if(field_ordinal >= 0)
+ tag = _healthCheckResponseFieldTags[field_ordinal];
+ else {
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ continue;
+ }
+ }
+ switch (tag) {
+ case 0: {
+ throw pb::InvalidProtocolBufferException.InvalidTag();
+ }
+ default: {
+ if (pb::WireFormat.IsEndGroupTag(tag)) {
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ break;
+ }
+ case 8: {
+ object unknown;
+ if(input.ReadEnum(ref result.status_, out unknown)) {
+ result.hasStatus = true;
+ } else if(unknown is int) {
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ unknownFields.MergeVarintField(1, (ulong)(int)unknown);
+ }
+ break;
+ }
+ }
+ }
+
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+
+
+ public bool HasStatus {
+ get { return result.hasStatus; }
+ }
+ public global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus Status {
+ get { return result.Status; }
+ set { SetStatus(value); }
+ }
+ public Builder SetStatus(global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus value) {
+ PrepareBuilder();
+ result.hasStatus = true;
+ result.status_ = value;
+ return this;
+ }
+ public Builder ClearStatus() {
+ PrepareBuilder();
+ result.hasStatus = false;
+ result.status_ = global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus.UNKNOWN;
+ return this;
+ }
+ }
+ static HealthCheckResponse() {
+ object.ReferenceEquals(global::Grpc.Health.V1Alpha.Proto.Health.Descriptor, null);
+ }
+ }
+
+ #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
new file mode 100644
index 0000000000..ed9fc4ed77
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
@@ -0,0 +1,78 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: health.proto
+#region Designer generated code
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core;
+
+namespace Grpc.Health.V1Alpha {
+ public static class Health
+ {
+ static readonly string __ServiceName = "grpc.health.v1alpha.Health";
+
+ static readonly Marshaller<global::Grpc.Health.V1Alpha.HealthCheckRequest> __Marshaller_HealthCheckRequest = Marshallers.Create((arg) => arg.ToByteArray(), global::Grpc.Health.V1Alpha.HealthCheckRequest.ParseFrom);
+ static readonly Marshaller<global::Grpc.Health.V1Alpha.HealthCheckResponse> __Marshaller_HealthCheckResponse = Marshallers.Create((arg) => arg.ToByteArray(), global::Grpc.Health.V1Alpha.HealthCheckResponse.ParseFrom);
+
+ static readonly Method<global::Grpc.Health.V1Alpha.HealthCheckRequest, global::Grpc.Health.V1Alpha.HealthCheckResponse> __Method_Check = new Method<global::Grpc.Health.V1Alpha.HealthCheckRequest, global::Grpc.Health.V1Alpha.HealthCheckResponse>(
+ MethodType.Unary,
+ "Check",
+ __Marshaller_HealthCheckRequest,
+ __Marshaller_HealthCheckResponse);
+
+ // client-side stub interface
+ public interface IHealthClient
+ {
+ global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CancellationToken token = default(CancellationToken));
+ Task<global::Grpc.Health.V1Alpha.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CancellationToken token = default(CancellationToken));
+ }
+
+ // server-side interface
+ public interface IHealth
+ {
+ Task<global::Grpc.Health.V1Alpha.HealthCheckResponse> Check(ServerCallContext context, global::Grpc.Health.V1Alpha.HealthCheckRequest request);
+ }
+
+ // client stub
+ public class HealthClient : AbstractStub<HealthClient, StubConfiguration>, IHealthClient
+ {
+ public HealthClient(Channel channel) : this(channel, StubConfiguration.Default)
+ {
+ }
+ public HealthClient(Channel channel, StubConfiguration config) : base(channel, config)
+ {
+ }
+ public global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CancellationToken token = default(CancellationToken))
+ {
+ var call = CreateCall(__ServiceName, __Method_Check);
+ return Calls.BlockingUnaryCall(call, request, token);
+ }
+ public Task<global::Grpc.Health.V1Alpha.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CancellationToken token = default(CancellationToken))
+ {
+ var call = CreateCall(__ServiceName, __Method_Check);
+ return Calls.AsyncUnaryCall(call, request, token);
+ }
+ }
+
+ // creates service definition that can be registered with a server
+ public static ServerServiceDefinition BindService(IHealth serviceImpl)
+ {
+ return ServerServiceDefinition.CreateBuilder(__ServiceName)
+ .AddMethod(__Method_Check, serviceImpl.Check).Build();
+ }
+
+ // creates a new client stub
+ public static IHealthClient NewStub(Channel channel)
+ {
+ return new HealthClient(channel);
+ }
+
+ // creates a new client stub
+ public static IHealthClient NewStub(Channel channel, StubConfiguration config)
+ {
+ return new HealthClient(channel, config);
+ }
+ }
+}
+#endregion
diff --git a/src/csharp/Grpc.HealthCheck/HealthServiceImpl.cs b/src/csharp/Grpc.HealthCheck/HealthServiceImpl.cs
new file mode 100644
index 0000000000..db3a2a0942
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck/HealthServiceImpl.cs
@@ -0,0 +1,132 @@
+#region Copyright notice and license
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Grpc.Core;
+using Grpc.Core.Utils;
+using Grpc.Health.V1Alpha;
+
+namespace Grpc.HealthCheck
+{
+ /// <summary>
+ /// Implementation of a simple Health service. Useful for health checking.
+ ///
+ /// Registering service with a server:
+ /// <code>
+ /// var serviceImpl = new HealthServiceImpl();
+ /// server = new Server();
+ /// server.AddServiceDefinition(Grpc.Health.V1Alpha.Health.BindService(serviceImpl));
+ /// </code>
+ /// </summary>
+ public class HealthServiceImpl : Grpc.Health.V1Alpha.Health.IHealth
+ {
+ private readonly object myLock = new object();
+ private readonly Dictionary<Key, HealthCheckResponse.Types.ServingStatus> statusMap =
+ new Dictionary<Key, HealthCheckResponse.Types.ServingStatus>();
+
+ /// <summary>
+ /// Sets the health status for given host and service.
+ /// </summary>
+ /// <param name="host">The host. Cannot be null.</param>
+ /// <param name="service">The service. Cannot be null.</param>
+ /// <param name="status">the health status</param>
+ public void SetStatus(string host, string service, HealthCheckResponse.Types.ServingStatus status)
+ {
+ lock (myLock)
+ {
+ statusMap[CreateKey(host, service)] = status;
+ }
+ }
+
+ /// <summary>
+ /// Clears health status for given host and service.
+ /// </summary>
+ /// <param name="host">The host. Cannot be null.</param>
+ /// <param name="service">The service. Cannot be null.</param>
+ public void ClearStatus(string host, string service)
+ {
+ lock (myLock)
+ {
+ statusMap.Remove(CreateKey(host, service));
+ }
+ }
+
+ /// <summary>
+ /// Clears statuses for all hosts and services.
+ /// </summary>
+ public void ClearAll()
+ {
+ lock (myLock)
+ {
+ statusMap.Clear();
+ }
+ }
+
+ public Task<HealthCheckResponse> Check(ServerCallContext context, HealthCheckRequest request)
+ {
+ lock (myLock)
+ {
+ var host = request.HasHost ? request.Host : "";
+ var service = request.HasService ? request.Service : "";
+
+ HealthCheckResponse.Types.ServingStatus status;
+ if (!statusMap.TryGetValue(CreateKey(host, service), out status))
+ {
+ // TODO(jtattermusch): returning specific status from server handler is not supported yet.
+ throw new RpcException(new Status(StatusCode.NotFound, ""));
+ }
+ return Task.FromResult(HealthCheckResponse.CreateBuilder().SetStatus(status).Build());
+ }
+ }
+
+ private static Key CreateKey(string host, string service)
+ {
+ return new Key(host, service);
+ }
+
+ private struct Key
+ {
+ public Key(string host, string service)
+ {
+ this.Host = Preconditions.CheckNotNull(host);
+ this.Service = Preconditions.CheckNotNull(service);
+ }
+
+ readonly string Host;
+ readonly string Service;
+ }
+ }
+}
diff --git a/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs b/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..41a54a98bc
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs
@@ -0,0 +1,11 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+[assembly: AssemblyTitle("Grpc.HealthCheck")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")] \ No newline at end of file
diff --git a/src/csharp/Grpc.HealthCheck/packages.config b/src/csharp/Grpc.HealthCheck/packages.config
new file mode 100644
index 0000000000..094a30981e
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck/packages.config
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="Google.ProtocolBuffers" version="2.4.1.555" targetFramework="net45" />
+ <package id="Ix-Async" version="1.2.3" targetFramework="net45" />
+</packages> \ No newline at end of file
diff --git a/src/csharp/Grpc.HealthCheck/proto/health.proto b/src/csharp/Grpc.HealthCheck/proto/health.proto
new file mode 100644
index 0000000000..08df7e104e
--- /dev/null
+++ b/src/csharp/Grpc.HealthCheck/proto/health.proto
@@ -0,0 +1,52 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// TODO(jtattermusch): switch to proto3 once C# supports that.
+syntax = "proto2";
+
+package grpc.health.v1alpha;
+option csharp_namespace = "Grpc.Health.V1Alpha";
+
+message HealthCheckRequest {
+ optional string host = 1;
+ optional string service = 2;
+}
+
+message HealthCheckResponse {
+ enum ServingStatus {
+ UNKNOWN = 0;
+ SERVING = 1;
+ NOT_SERVING = 2;
+ }
+ optional ServingStatus status = 1;
+}
+
+service Health {
+ rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
+} \ No newline at end of file
diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
index df05c535e2..328acb5b47 100644
--- a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
+++ b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
@@ -36,6 +36,9 @@
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Grpc.Core\Version.cs">
+ <Link>Version.cs</Link>
+ </Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs
index 9a389a1e30..f51f2796c4 100644
--- a/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs
@@ -9,4 +9,3 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("0.6.*")]
diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
index 235897c888..ae184c1dc7 100644
--- a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
+++ b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
@@ -36,6 +36,9 @@
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Grpc.Core\Version.cs">
+ <Link>Version.cs</Link>
+ </Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs
index ff31035d53..f68d9a3ddc 100644
--- a/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs
@@ -9,4 +9,3 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("0.6.*")]
diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
index a6d847ca65..af4a75a034 100644
--- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
+++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
@@ -75,6 +75,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Grpc.Core\Version.cs">
+ <Link>Version.cs</Link>
+ </Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Empty.cs" />
<Compile Include="Messages.cs" />
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
index f0be522bc6..bdcb2c505c 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
@@ -102,8 +102,6 @@ namespace Grpc.IntegrationTesting
private void Run()
{
- GrpcEnvironment.Initialize();
-
Credentials credentials = null;
if (options.useTls)
{
@@ -135,7 +133,6 @@ namespace Grpc.IntegrationTesting
TestService.ITestServiceClient client = new TestService.TestServiceClient(channel, stubConfig);
RunTestCase(options.testCase, client);
}
-
GrpcEnvironment.Shutdown();
}
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
index 1a733450c1..6c2da9d2ee 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
@@ -55,8 +55,6 @@ namespace Grpc.IntegrationTesting
[TestFixtureSetUp]
public void Init()
{
- GrpcEnvironment.Initialize();
-
server = new Server();
server.AddServiceDefinition(TestService.BindService(new TestServiceImpl()));
int port = server.AddListeningPort(host, Server.PickUnusedPort, TestCredentials.CreateTestServerCredentials());
@@ -74,7 +72,6 @@ namespace Grpc.IntegrationTesting
public void Cleanup()
{
channel.Dispose();
-
server.ShutdownAsync().Wait();
GrpcEnvironment.Shutdown();
}
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
index 87c3cbe1d4..9475e66c40 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
@@ -88,8 +88,6 @@ namespace Grpc.IntegrationTesting
private void Run()
{
- GrpcEnvironment.Initialize();
-
var server = new Server();
server.AddServiceDefinition(TestService.BindService(new TestServiceImpl()));
diff --git a/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs
index 7134b04892..1beb0bbb41 100644
--- a/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs
@@ -9,4 +9,3 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("0.6.*")]
diff --git a/src/csharp/Grpc.Tools.nuspec b/src/csharp/Grpc.Tools.nuspec
index 0f4fa79277..eabf5dc7db 100644
--- a/src/csharp/Grpc.Tools.nuspec
+++ b/src/csharp/Grpc.Tools.nuspec
@@ -5,13 +5,13 @@
<title>gRPC C# Tools</title>
<summary>Tools for C# implementation of gRPC - an RPC library and framework</summary>
<description>Precompiled Windows binaries for generating protocol buffer messages and gRPC client/server code</description>
- <version>0.6.0</version>
+ <version>$version$</version>
<authors>Google Inc.</authors>
<owners>grpc-packages</owners>
<licenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/grpc/grpc</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
- <releaseNotes>protoc.exe - protocol buffer compiler v3.0.0-alpha-3; grpc_csharp_plugin.exe - gRPC C# protoc plugin version 0.6.0</releaseNotes>
+ <releaseNotes>protoc.exe - protocol buffer compiler v3.0.0-alpha-3; grpc_csharp_plugin.exe - gRPC C# protoc plugin version $version$</releaseNotes>
<copyright>Copyright 2015, Google Inc.</copyright>
<tags>gRPC RPC Protocol HTTP/2</tags>
</metadata>
diff --git a/src/csharp/Grpc.nuspec b/src/csharp/Grpc.nuspec
index 70203a6203..7fbd861923 100644
--- a/src/csharp/Grpc.nuspec
+++ b/src/csharp/Grpc.nuspec
@@ -5,17 +5,17 @@
<title>gRPC C#</title>
<summary>C# implementation of gRPC - an RPC library and framework</summary>
<description>C# implementation of gRPC - an RPC library and framework. See project site for more info.</description>
- <version>0.6.0</version>
+ <version>$version$</version>
<authors>Google Inc.</authors>
<owners>grpc-packages</owners>
<licenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/grpc/grpc</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
- <releaseNotes>Release 0.6.0 of gRPC C#</releaseNotes>
+ <releaseNotes>Release $version$ of gRPC C#</releaseNotes>
<copyright>Copyright 2015, Google Inc.</copyright>
<tags>gRPC RPC Protocol HTTP/2</tags>
<dependencies>
- <dependency id="Grpc.Core" version="0.6.0" />
+ <dependency id="Grpc.Core" version="$version$" />
</dependencies>
</metadata>
<files/>
diff --git a/src/csharp/Grpc.sln b/src/csharp/Grpc.sln
index 978739f23a..705e4fb1c2 100644
--- a/src/csharp/Grpc.sln
+++ b/src/csharp/Grpc.sln
@@ -28,57 +28,68 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{B5B871
.nuget\packages.config = .nuget\packages.config
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.HealthCheck", "Grpc.HealthCheck\Grpc.HealthCheck.csproj", "{AA5E328A-8835-49D7-98ED-C29F2B3049F0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.HealthCheck.Tests", "Grpc.HealthCheck.Tests\Grpc.HealthCheck.Tests.csproj", "{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.ActiveCfg = Debug|Any CPU
- {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.Build.0 = Debug|Any CPU
- {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.ActiveCfg = Release|Any CPU
- {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.Build.0 = Release|Any CPU
- {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.ActiveCfg = Debug|Any CPU
- {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.Build.0 = Debug|Any CPU
- {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.ActiveCfg = Release|Any CPU
- {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.Build.0 = Release|Any CPU
- {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.ActiveCfg = Debug|Any CPU
- {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.Build.0 = Debug|Any CPU
- {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.ActiveCfg = Release|Any CPU
- {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.Build.0 = Release|Any CPU
{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|x86.ActiveCfg = Debug|Any CPU
{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|x86.Build.0 = Debug|Any CPU
{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|x86.ActiveCfg = Release|Any CPU
{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|x86.Build.0 = Release|Any CPU
- {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.ActiveCfg = Debug|x86
- {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.Build.0 = Debug|x86
- {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.ActiveCfg = Release|x86
- {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.Build.0 = Release|x86
- {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.ActiveCfg = Debug|x86
- {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.Build.0 = Debug|x86
- {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.ActiveCfg = Release|x86
- {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.Build.0 = Release|x86
{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|x86.ActiveCfg = Debug|x86
{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|x86.Build.0 = Debug|x86
{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|x86.ActiveCfg = Release|x86
{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|x86.Build.0 = Release|x86
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.ActiveCfg = Debug|x86
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.Build.0 = Debug|x86
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.ActiveCfg = Release|x86
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.Build.0 = Release|x86
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.Build.0 = Debug|Any CPU
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.ActiveCfg = Release|Any CPU
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.Build.0 = Release|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.Build.0 = Debug|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.ActiveCfg = Release|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.Build.0 = Release|Any CPU
{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|x86.ActiveCfg = Debug|x86
{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|x86.Build.0 = Debug|x86
{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|x86.ActiveCfg = Release|x86
{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|x86.Build.0 = Release|x86
- {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|x86.ActiveCfg = Debug|x86
- {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|x86.Build.0 = Debug|x86
- {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|x86.ActiveCfg = Release|x86
- {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|x86.Build.0 = Release|x86
+ {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|x86.Build.0 = Debug|Any CPU
+ {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|x86.ActiveCfg = Release|Any CPU
+ {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|x86.Build.0 = Release|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|x86.ActiveCfg = Debug|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|x86.Build.0 = Debug|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|x86.ActiveCfg = Release|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|x86.Build.0 = Release|Any CPU
+ {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|x86.ActiveCfg = Debug|x86
+ {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|x86.Build.0 = Debug|x86
+ {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|x86.ActiveCfg = Release|x86
+ {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|x86.Build.0 = Release|x86
+ {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.ActiveCfg = Debug|x86
+ {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.Build.0 = Debug|x86
+ {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.ActiveCfg = Release|x86
+ {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.Build.0 = Release|x86
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.Build.0 = Debug|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.ActiveCfg = Release|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.Build.0 = Release|Any CPU
+ {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|x86.Build.0 = Debug|Any CPU
+ {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|x86.ActiveCfg = Release|Any CPU
+ {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
- GlobalSection(MonoDevelopProperties) = preSolution
- StartupItem = Grpc.Examples\Grpc.Examples.csproj
- EndGlobalSection
EndGlobal
diff --git a/src/csharp/build_packages.bat b/src/csharp/build_packages.bat
index 3412129fb2..c3e5fe8817 100644
--- a/src/csharp/build_packages.bat
+++ b/src/csharp/build_packages.bat
@@ -1,5 +1,9 @@
@rem Builds gRPC NuGet packages
+@rem Current package versions
+set VERSION=0.6.0
+set CORE_VERSION=0.10.0
+
@rem Adjust the location of nuget.exe
set NUGET=C:\nuget\nuget.exe
@@ -10,11 +14,12 @@ endlocal
@call buildall.bat || goto :error
-%NUGET% pack ..\..\vsprojects\nuget_package\grpc.native.csharp_ext.nuspec || goto :error
-%NUGET% pack Grpc.Core\Grpc.Core.nuspec -Symbols || goto :error
-%NUGET% pack Grpc.Auth\Grpc.Auth.nuspec -Symbols || goto :error
-%NUGET% pack Grpc.Tools.nuspec || goto :error
-%NUGET% pack Grpc.nuspec || goto :error
+%NUGET% pack ..\..\vsprojects\nuget_package\grpc.native.csharp_ext.nuspec -Version %CORE_VERSION% || goto :error
+%NUGET% pack Grpc.Auth\Grpc.Auth.nuspec -Symbols -Version %VERSION% || goto :error
+%NUGET% pack Grpc.Core\Grpc.Core.nuspec -Symbols -Version %VERSION% -Properties GrpcNativeCsharpExtVersion=%CORE_VERSION% || goto :error
+%NUGET% pack Grpc.HealthCheck\Grpc.HealthCheck.nuspec -Symbols -Version %VERSION% || goto :error
+%NUGET% pack Grpc.Tools.nuspec -Version %VERSION% || goto :error
+%NUGET% pack Grpc.nuspec -Version %VERSION% || goto :error
goto :EOF
diff --git a/src/csharp/generate_proto_csharp.sh b/src/csharp/generate_proto_csharp.sh
index f980787bb7..6eb3887ea1 100755
--- a/src/csharp/generate_proto_csharp.sh
+++ b/src/csharp/generate_proto_csharp.sh
@@ -35,9 +35,13 @@ cd $(dirname $0)
PLUGIN=protoc-gen-grpc=../../bins/opt/grpc_csharp_plugin
EXAMPLES_DIR=Grpc.Examples
INTEROP_DIR=Grpc.IntegrationTesting
+HEALTHCHECK_DIR=Grpc.HealthCheck
protoc --plugin=$PLUGIN --grpc_out=$EXAMPLES_DIR \
-I $EXAMPLES_DIR/proto $EXAMPLES_DIR/proto/math.proto
protoc --plugin=$PLUGIN --grpc_out=$INTEROP_DIR \
-I $INTEROP_DIR/proto $INTEROP_DIR/proto/test.proto
+
+protoc --plugin=$PLUGIN --grpc_out=$HEALTHCHECK_DIR \
+ -I $HEALTHCHECK_DIR/proto $HEALTHCHECK_DIR/proto/health.proto
diff --git a/src/node/src/server.js b/src/node/src/server.js
index 9ac428f3ee..00be400e61 100644
--- a/src/node/src/server.js
+++ b/src/node/src/server.js
@@ -55,7 +55,7 @@ var EventEmitter = require('events').EventEmitter;
*/
function handleError(call, error) {
var status = {
- code: grpc.status.INTERNAL,
+ code: grpc.status.UNKNOWN,
details: 'Unknown Error',
metadata: {}
};
@@ -142,12 +142,12 @@ function setUpWritable(stream, serialize) {
stream.on('finish', sendStatus);
/**
* Set the pending status to a given error status. If the error does not have
- * code or details properties, the code will be set to grpc.status.INTERNAL
+ * code or details properties, the code will be set to grpc.status.UNKNOWN
* and the details will be set to 'Unknown Error'.
* @param {Error} err The error object
*/
function setStatus(err) {
- var code = grpc.status.INTERNAL;
+ var code = grpc.status.UNKNOWN;
var details = 'Unknown Error';
var metadata = {};
if (err.hasOwnProperty('message')) {
diff --git a/src/php/bin/determine_extension_dir.sh b/src/php/bin/determine_extension_dir.sh
index 6bbd934bf1..3c1fc297fa 100755
--- a/src/php/bin/determine_extension_dir.sh
+++ b/src/php/bin/determine_extension_dir.sh
@@ -27,12 +27,12 @@
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
set -e
default_extension_dir=$(php-config --extension-dir)
-if command -v brew >/dev/null && [ -d $(brew --prefix)/opt/grpc-php ]; then
- # homebrew and the grpc-php formula are installed
- extension_dir="-d extension_dir="$(brew --prefix)/opt/grpc-php
+if command -v brew > /dev/null && \
+ brew ls --versions | grep php5[56]-grpc > /dev/null; then
+ # the grpc php extension was installed by homebrew
+ :
elif [ ! -e $default_extension_dir/grpc.so ]; then
# the grpc extension is not found in the default PHP extension dir
# try the source modules directory
@@ -45,5 +45,5 @@ elif [ ! -e $default_extension_dir/grpc.so ]; then
for f in $default_extension_dir/*.so; do
ln -s $f $module_dir/$(basename $f) &> /dev/null || true
done
- extension_dir="-d extension_dir="$module_dir
+ extension_dir="-d extension_dir=${module_dir} -d extension=grpc.so"
fi
diff --git a/src/php/bin/interop_client.sh b/src/php/bin/interop_client.sh
index 42e075cbe8..17b888dd4e 100755
--- a/src/php/bin/interop_client.sh
+++ b/src/php/bin/interop_client.sh
@@ -31,5 +31,5 @@
set -e
cd $(dirname $0)
source ./determine_extension_dir.sh
-php $extension_dir -d extension=grpc.so \
+php $extension_dir \
../tests/interop/interop_client.php $@ 1>&2
diff --git a/src/php/bin/run_gen_code_test.sh b/src/php/bin/run_gen_code_test.sh
index 03a9101a45..6e56c7207c 100755
--- a/src/php/bin/run_gen_code_test.sh
+++ b/src/php/bin/run_gen_code_test.sh
@@ -31,8 +31,8 @@
set -e
cd $(dirname $0)
source ./determine_extension_dir.sh
-export GRPC_TEST_HOST=localhost:7071
-php $extension_dir -d extension=grpc.so $(which phpunit) -v --debug --strict \
+export GRPC_TEST_HOST=localhost:50051
+php $extension_dir $(which phpunit) -v --debug --strict \
../tests/generated_code/GeneratedCodeTest.php
-php $extension_dir -d extension=grpc.so $(which phpunit) -v --debug --strict \
+php $extension_dir $(which phpunit) -v --debug --strict \
../tests/generated_code/GeneratedCodeWithCallbackTest.php
diff --git a/src/php/bin/run_tests.sh b/src/php/bin/run_tests.sh
index 4c37285455..953f408ea8 100755
--- a/src/php/bin/run_tests.sh
+++ b/src/php/bin/run_tests.sh
@@ -33,5 +33,5 @@
set -e
cd $(dirname $0)
source ./determine_extension_dir.sh
-php $extension_dir -d extension=grpc.so $(which phpunit) -v --debug --strict \
+php $extension_dir $(which phpunit) -v --debug --strict \
../tests/unit_tests
diff --git a/src/python/src/grpc/_links/__init__.py b/src/python/src/grpc/_links/__init__.py
new file mode 100644
index 0000000000..7086519106
--- /dev/null
+++ b/src/python/src/grpc/_links/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
diff --git a/src/python/src/grpc/_links/_lonely_invocation_link_test.py b/src/python/src/grpc/_links/_lonely_invocation_link_test.py
new file mode 100644
index 0000000000..3d629f4387
--- /dev/null
+++ b/src/python/src/grpc/_links/_lonely_invocation_link_test.py
@@ -0,0 +1,88 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""A test of invocation-side code unconnected to an RPC server."""
+
+import unittest
+
+from grpc._adapter import _intermediary_low
+from grpc._links import invocation
+from grpc.framework.common import test_constants
+from grpc.framework.interfaces.links import links
+from grpc.framework.interfaces.links import test_cases
+from grpc.framework.interfaces.links import test_utilities
+
+_NULL_BEHAVIOR = lambda unused_argument: None
+
+
+class LonelyInvocationLinkTest(unittest.TestCase):
+
+ def testUpAndDown(self):
+ channel = _intermediary_low.Channel('nonexistent:54321', None)
+ invocation_link = invocation.invocation_link(channel, 'nonexistent', {}, {})
+
+ invocation_link.start()
+ invocation_link.stop()
+
+ def _test_lonely_invocation_with_termination(self, termination):
+ test_operation_id = object()
+ test_group = 'test package.Test Service'
+ test_method = 'test method'
+ invocation_link_mate = test_utilities.RecordingLink()
+
+ channel = _intermediary_low.Channel('nonexistent:54321', None)
+ invocation_link = invocation.invocation_link(
+ channel, 'nonexistent', {(test_group, test_method): _NULL_BEHAVIOR},
+ {(test_group, test_method): _NULL_BEHAVIOR})
+ invocation_link.join_link(invocation_link_mate)
+ invocation_link.start()
+
+ ticket = links.Ticket(
+ test_operation_id, 0, test_group, test_method,
+ links.Ticket.Subscription.FULL, test_constants.SHORT_TIMEOUT, 1, None,
+ None, None, None, None, termination)
+ invocation_link.accept_ticket(ticket)
+ invocation_link_mate.block_until_tickets_satisfy(test_cases.terminated)
+
+ invocation_link.stop()
+
+ self.assertIsNot(
+ invocation_link_mate.tickets()[-1].termination,
+ links.Ticket.Termination.COMPLETION)
+
+ def testLonelyInvocationLinkWithCommencementTicket(self):
+ self._test_lonely_invocation_with_termination(None)
+
+ def testLonelyInvocationLinkWithEntireTicket(self):
+ self._test_lonely_invocation_with_termination(
+ links.Ticket.Termination.COMPLETION)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/python/src/grpc/_links/_proto_scenarios.py b/src/python/src/grpc/_links/_proto_scenarios.py
new file mode 100644
index 0000000000..ccf3c29782
--- /dev/null
+++ b/src/python/src/grpc/_links/_proto_scenarios.py
@@ -0,0 +1,261 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Test scenarios using protocol buffers."""
+
+import abc
+import threading
+
+from grpc._junkdrawer import math_pb2
+
+
+class ProtoScenario(object):
+ """An RPC test scenario using protocol buffers."""
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def group_and_method(self):
+ """Access the test group and method.
+
+ Returns:
+ The test group and method as a pair.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def serialize_request(self, request):
+ """Serialize a request protocol buffer.
+
+ Args:
+ request: A request protocol buffer.
+
+ Returns:
+ The bytestring serialization of the given request protocol buffer.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def deserialize_request(self, request_bytestring):
+ """Deserialize a request protocol buffer.
+
+ Args:
+ request_bytestring: The bytestring serialization of a request protocol
+ buffer.
+
+ Returns:
+ The request protocol buffer deserialized from the given byte string.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def serialize_response(self, response):
+ """Serialize a response protocol buffer.
+
+ Args:
+ response: A response protocol buffer.
+
+ Returns:
+ The bytestring serialization of the given response protocol buffer.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def deserialize_response(self, response_bytestring):
+ """Deserialize a response protocol buffer.
+
+ Args:
+ response_bytestring: The bytestring serialization of a response protocol
+ buffer.
+
+ Returns:
+ The response protocol buffer deserialized from the given byte string.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def requests(self):
+ """Access the sequence of requests for this scenario.
+
+ Returns:
+ A sequence of request protocol buffers.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def response_for_request(self, request):
+ """Access the response for a particular request.
+
+ Args:
+ request: A request protocol buffer.
+
+ Returns:
+ The response protocol buffer appropriate for the given request.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def verify_requests(self, experimental_requests):
+ """Verify the requests transmitted through the system under test.
+
+ Args:
+ experimental_requests: The request protocol buffers transmitted through
+ the system under test.
+
+ Returns:
+ True if the requests satisfy this test scenario; False otherwise.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def verify_responses(self, experimental_responses):
+ """Verify the responses transmitted through the system under test.
+
+ Args:
+ experimental_responses: The response protocol buffers transmitted through
+ the system under test.
+
+ Returns:
+ True if the responses satisfy this test scenario; False otherwise.
+ """
+ raise NotImplementedError()
+
+
+class EmptyScenario(ProtoScenario):
+ """A scenario that transmits no protocol buffers in either direction."""
+
+ def group_and_method(self):
+ return 'math.Math', 'DivMany'
+
+ def serialize_request(self, request):
+ raise ValueError('This should not be necessary to call!')
+
+ def deserialize_request(self, request_bytestring):
+ raise ValueError('This should not be necessary to call!')
+
+ def serialize_response(self, response):
+ raise ValueError('This should not be necessary to call!')
+
+ def deserialize_response(self, response_bytestring):
+ raise ValueError('This should not be necessary to call!')
+
+ def requests(self):
+ return ()
+
+ def response_for_request(self, request):
+ raise ValueError('This should not be necessary to call!')
+
+ def verify_requests(self, experimental_requests):
+ return not experimental_requests
+
+ def verify_responses(self, experimental_responses):
+ return not experimental_responses
+
+
+class BidirectionallyUnaryScenario(ProtoScenario):
+ """A scenario that transmits no protocol buffers in either direction."""
+
+ _DIVIDEND = 59
+ _DIVISOR = 7
+ _QUOTIENT = 8
+ _REMAINDER = 3
+
+ _REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR)
+ _RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER)
+
+ def group_and_method(self):
+ return 'math.Math', 'Div'
+
+ def serialize_request(self, request):
+ return request.SerializeToString()
+
+ def deserialize_request(self, request_bytestring):
+ return math_pb2.DivArgs.FromString(request_bytestring)
+
+ def serialize_response(self, response):
+ return response.SerializeToString()
+
+ def deserialize_response(self, response_bytestring):
+ return math_pb2.DivReply.FromString(response_bytestring)
+
+ def requests(self):
+ return [self._REQUEST]
+
+ def response_for_request(self, request):
+ return self._RESPONSE
+
+ def verify_requests(self, experimental_requests):
+ return tuple(experimental_requests) == (self._REQUEST,)
+
+ def verify_responses(self, experimental_responses):
+ return tuple(experimental_responses) == (self._RESPONSE,)
+
+
+class BidirectionallyStreamingScenario(ProtoScenario):
+ """A scenario that transmits no protocol buffers in either direction."""
+
+ _STREAM_LENGTH = 200
+ _REQUESTS = tuple(
+ math_pb2.DivArgs(dividend=59 + index, divisor=7 + index)
+ for index in range(_STREAM_LENGTH))
+
+ def __init__(self):
+ self._lock = threading.Lock()
+ self._responses = []
+
+ def group_and_method(self):
+ return 'math.Math', 'DivMany'
+
+ def serialize_request(self, request):
+ return request.SerializeToString()
+
+ def deserialize_request(self, request_bytestring):
+ return math_pb2.DivArgs.FromString(request_bytestring)
+
+ def serialize_response(self, response):
+ return response.SerializeToString()
+
+ def deserialize_response(self, response_bytestring):
+ return math_pb2.DivReply.FromString(response_bytestring)
+
+ def requests(self):
+ return self._REQUESTS
+
+ def response_for_request(self, request):
+ quotient, remainder = divmod(request.dividend, request.divisor)
+ response = math_pb2.DivReply(quotient=quotient, remainder=remainder)
+ with self._lock:
+ self._responses.append(response)
+ return response
+
+ def verify_requests(self, experimental_requests):
+ return tuple(experimental_requests) == self._REQUESTS
+
+ def verify_responses(self, experimental_responses):
+ with self._lock:
+ return tuple(experimental_responses) == tuple(self._responses)
diff --git a/src/python/src/grpc/_links/_transmission_test.py b/src/python/src/grpc/_links/_transmission_test.py
new file mode 100644
index 0000000000..c5ef1edb25
--- /dev/null
+++ b/src/python/src/grpc/_links/_transmission_test.py
@@ -0,0 +1,226 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Tests transmission of tickets across gRPC-on-the-wire."""
+
+import unittest
+
+from grpc._adapter import _intermediary_low
+from grpc._links import _proto_scenarios
+from grpc._links import invocation
+from grpc._links import service
+from grpc.framework.common import test_constants
+from grpc.framework.interfaces.links import links
+from grpc.framework.interfaces.links import test_cases
+from grpc.framework.interfaces.links import test_utilities
+
+_IDENTITY = lambda x: x
+
+
+class TransmissionTest(test_cases.TransmissionTest, unittest.TestCase):
+
+ def create_transmitting_links(self):
+ service_link = service.service_link(
+ {self.group_and_method(): self.deserialize_request},
+ {self.group_and_method(): self.serialize_response})
+ port = service_link.add_port(0, None)
+ service_link.start()
+ channel = _intermediary_low.Channel('localhost:%d' % port, None)
+ invocation_link = invocation.invocation_link(
+ channel, 'localhost',
+ {self.group_and_method(): self.serialize_request},
+ {self.group_and_method(): self.deserialize_response})
+ invocation_link.start()
+ return invocation_link, service_link
+
+ def destroy_transmitting_links(self, invocation_side_link, service_side_link):
+ invocation_side_link.stop()
+ service_side_link.stop_gracefully()
+
+ def create_invocation_initial_metadata(self):
+ return (
+ ('first invocation initial metadata key', 'just a string value'),
+ ('second invocation initial metadata key', '0123456789'),
+ ('third invocation initial metadata key-bin', '\x00\x57' * 100),
+ )
+
+ def create_invocation_terminal_metadata(self):
+ return None
+
+ def create_service_initial_metadata(self):
+ return (
+ ('first service initial metadata key', 'just another string value'),
+ ('second service initial metadata key', '9876543210'),
+ ('third service initial metadata key-bin', '\x00\x59\x02' * 100),
+ )
+
+ def create_service_terminal_metadata(self):
+ return (
+ ('first service terminal metadata key', 'yet another string value'),
+ ('second service terminal metadata key', 'abcdefghij'),
+ ('third service terminal metadata key-bin', '\x00\x37' * 100),
+ )
+
+ def create_invocation_completion(self):
+ return None, None
+
+ def create_service_completion(self):
+ return _intermediary_low.Code.OK, 'An exuberant test "details" message!'
+
+ def assertMetadataEqual(self, original_metadata, transmitted_metadata):
+ self.assertSequenceEqual(original_metadata, transmitted_metadata)
+
+
+class RoundTripTest(unittest.TestCase):
+
+ def testZeroMessageRoundTrip(self):
+ test_operation_id = object()
+ test_group = 'test package.Test Group'
+ test_method = 'test method'
+ identity_transformation = {(test_group, test_method): _IDENTITY}
+ test_code = _intermediary_low.Code.OK
+ test_message = 'a test message'
+
+ service_link = service.service_link(
+ identity_transformation, identity_transformation)
+ service_mate = test_utilities.RecordingLink()
+ service_link.join_link(service_mate)
+ port = service_link.add_port(0, None)
+ service_link.start()
+ channel = _intermediary_low.Channel('localhost:%d' % port, None)
+ invocation_link = invocation.invocation_link(
+ channel, 'localhost', identity_transformation, identity_transformation)
+ invocation_mate = test_utilities.RecordingLink()
+ invocation_link.join_link(invocation_mate)
+ invocation_link.start()
+
+ invocation_ticket = links.Ticket(
+ test_operation_id, 0, test_group, test_method,
+ links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None,
+ None, None, None, None, links.Ticket.Termination.COMPLETION)
+ invocation_link.accept_ticket(invocation_ticket)
+ service_mate.block_until_tickets_satisfy(test_cases.terminated)
+
+ service_ticket = links.Ticket(
+ service_mate.tickets()[-1].operation_id, 0, None, None, None, None,
+ None, None, None, None, test_code, test_message,
+ links.Ticket.Termination.COMPLETION)
+ service_link.accept_ticket(service_ticket)
+ invocation_mate.block_until_tickets_satisfy(test_cases.terminated)
+
+ invocation_link.stop()
+ service_link.stop_gracefully()
+
+ self.assertIs(
+ service_mate.tickets()[-1].termination,
+ links.Ticket.Termination.COMPLETION)
+ self.assertIs(
+ invocation_mate.tickets()[-1].termination,
+ links.Ticket.Termination.COMPLETION)
+
+ def _perform_scenario_test(self, scenario):
+ test_operation_id = object()
+ test_group, test_method = scenario.group_and_method()
+ test_code = _intermediary_low.Code.OK
+ test_message = 'a scenario test message'
+
+ service_link = service.service_link(
+ {(test_group, test_method): scenario.deserialize_request},
+ {(test_group, test_method): scenario.serialize_response})
+ service_mate = test_utilities.RecordingLink()
+ service_link.join_link(service_mate)
+ port = service_link.add_port(0, None)
+ service_link.start()
+ channel = _intermediary_low.Channel('localhost:%d' % port, None)
+ invocation_link = invocation.invocation_link(
+ channel, 'localhost',
+ {(test_group, test_method): scenario.serialize_request},
+ {(test_group, test_method): scenario.deserialize_response})
+ invocation_mate = test_utilities.RecordingLink()
+ invocation_link.join_link(invocation_mate)
+ invocation_link.start()
+
+ invocation_ticket = links.Ticket(
+ test_operation_id, 0, test_group, test_method,
+ links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None,
+ None, None, None, None, None)
+ invocation_link.accept_ticket(invocation_ticket)
+ requests = scenario.requests()
+ for request_index, request in enumerate(requests):
+ request_ticket = links.Ticket(
+ test_operation_id, 1 + request_index, None, None, None, None, 1, None,
+ request, None, None, None, None)
+ invocation_link.accept_ticket(request_ticket)
+ service_mate.block_until_tickets_satisfy(
+ test_cases.at_least_n_payloads_received_predicate(1 + request_index))
+ response_ticket = links.Ticket(
+ service_mate.tickets()[0].operation_id, request_index, None, None,
+ None, None, 1, None, scenario.response_for_request(request), None,
+ None, None, None)
+ service_link.accept_ticket(response_ticket)
+ invocation_mate.block_until_tickets_satisfy(
+ test_cases.at_least_n_payloads_received_predicate(1 + request_index))
+ request_count = len(requests)
+ invocation_completion_ticket = links.Ticket(
+ test_operation_id, request_count + 1, None, None, None, None, None,
+ None, None, None, None, None, links.Ticket.Termination.COMPLETION)
+ invocation_link.accept_ticket(invocation_completion_ticket)
+ service_mate.block_until_tickets_satisfy(test_cases.terminated)
+ service_completion_ticket = links.Ticket(
+ service_mate.tickets()[0].operation_id, request_count, None, None, None,
+ None, None, None, None, None, test_code, test_message,
+ links.Ticket.Termination.COMPLETION)
+ service_link.accept_ticket(service_completion_ticket)
+ invocation_mate.block_until_tickets_satisfy(test_cases.terminated)
+
+ invocation_link.stop()
+ service_link.stop_gracefully()
+
+ observed_requests = tuple(
+ ticket.payload for ticket in service_mate.tickets()
+ if ticket.payload is not None)
+ observed_responses = tuple(
+ ticket.payload for ticket in invocation_mate.tickets()
+ if ticket.payload is not None)
+ self.assertTrue(scenario.verify_requests(observed_requests))
+ self.assertTrue(scenario.verify_responses(observed_responses))
+
+ def testEmptyScenario(self):
+ self._perform_scenario_test(_proto_scenarios.EmptyScenario())
+
+ def testBidirectionallyUnaryScenario(self):
+ self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario())
+
+ def testBidirectionallyStreamingScenario(self):
+ self._perform_scenario_test(
+ _proto_scenarios.BidirectionallyStreamingScenario())
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/src/python/src/grpc/_links/invocation.py b/src/python/src/grpc/_links/invocation.py
new file mode 100644
index 0000000000..0058ae91f8
--- /dev/null
+++ b/src/python/src/grpc/_links/invocation.py
@@ -0,0 +1,363 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""The RPC-invocation-side bridge between RPC Framework and GRPC-on-the-wire."""
+
+import abc
+import enum
+import logging
+import threading
+import time
+
+from grpc._adapter import _intermediary_low
+from grpc.framework.foundation import activated
+from grpc.framework.foundation import logging_pool
+from grpc.framework.foundation import relay
+from grpc.framework.interfaces.links import links
+
+
+@enum.unique
+class _Read(enum.Enum):
+ AWAITING_METADATA = 'awaiting metadata'
+ READING = 'reading'
+ AWAITING_ALLOWANCE = 'awaiting allowance'
+ CLOSED = 'closed'
+
+
+@enum.unique
+class _HighWrite(enum.Enum):
+ OPEN = 'open'
+ CLOSED = 'closed'
+
+
+@enum.unique
+class _LowWrite(enum.Enum):
+ OPEN = 'OPEN'
+ ACTIVE = 'ACTIVE'
+ CLOSED = 'CLOSED'
+
+
+class _RPCState(object):
+
+ def __init__(
+ self, call, request_serializer, response_deserializer, sequence_number,
+ read, allowance, high_write, low_write):
+ self.call = call
+ self.request_serializer = request_serializer
+ self.response_deserializer = response_deserializer
+ self.sequence_number = sequence_number
+ self.read = read
+ self.allowance = allowance
+ self.high_write = high_write
+ self.low_write = low_write
+
+
+class _Kernel(object):
+
+ def __init__(
+ self, channel, host, request_serializers, response_deserializers,
+ ticket_relay):
+ self._lock = threading.Lock()
+ self._channel = channel
+ self._host = host
+ self._request_serializers = request_serializers
+ self._response_deserializers = response_deserializers
+ self._relay = ticket_relay
+
+ self._completion_queue = None
+ self._rpc_states = None
+ self._pool = None
+
+ def _on_write_event(self, operation_id, unused_event, rpc_state):
+ if rpc_state.high_write is _HighWrite.CLOSED:
+ rpc_state.call.complete(operation_id)
+ rpc_state.low_write = _LowWrite.CLOSED
+ else:
+ ticket = links.Ticket(
+ operation_id, rpc_state.sequence_number, None, None, None, None, 1,
+ None, None, None, None, None, None)
+ rpc_state.sequence_number += 1
+ self._relay.add_value(ticket)
+ rpc_state.low_write = _LowWrite.OPEN
+
+ def _on_read_event(self, operation_id, event, rpc_state):
+ if event.bytes is None:
+ rpc_state.read = _Read.CLOSED
+ else:
+ if 0 < rpc_state.allowance:
+ rpc_state.allowance -= 1
+ rpc_state.call.read(operation_id)
+ else:
+ rpc_state.read = _Read.AWAITING_ALLOWANCE
+ ticket = links.Ticket(
+ operation_id, rpc_state.sequence_number, None, None, None, None, None,
+ None, rpc_state.response_deserializer(event.bytes), None, None, None,
+ None)
+ rpc_state.sequence_number += 1
+ self._relay.add_value(ticket)
+
+ def _on_metadata_event(self, operation_id, event, rpc_state):
+ rpc_state.allowance -= 1
+ rpc_state.call.read(operation_id)
+ rpc_state.read = _Read.READING
+ ticket = links.Ticket(
+ operation_id, rpc_state.sequence_number, None, None,
+ links.Ticket.Subscription.FULL, None, None, event.metadata, None, None,
+ None, None, None)
+ rpc_state.sequence_number += 1
+ self._relay.add_value(ticket)
+
+ def _on_finish_event(self, operation_id, event, rpc_state):
+ self._rpc_states.pop(operation_id, None)
+ if event.status.code is _intermediary_low.Code.OK:
+ termination = links.Ticket.Termination.COMPLETION
+ elif event.status.code is _intermediary_low.Code.CANCELLED:
+ termination = links.Ticket.Termination.CANCELLATION
+ elif event.status.code is _intermediary_low.Code.DEADLINE_EXCEEDED:
+ termination = links.Ticket.Termination.EXPIRATION
+ else:
+ termination = links.Ticket.Termination.TRANSMISSION_FAILURE
+ ticket = links.Ticket(
+ operation_id, rpc_state.sequence_number, None, None, None, None, None,
+ None, None, event.metadata, event.status.code, event.status.details,
+ termination)
+ rpc_state.sequence_number += 1
+ self._relay.add_value(ticket)
+
+ def _spin(self, completion_queue):
+ while True:
+ event = completion_queue.get(None)
+ if event.kind is _intermediary_low.Event.Kind.STOP:
+ return
+ operation_id = event.tag
+ with self._lock:
+ if self._completion_queue is None:
+ continue
+ rpc_state = self._rpc_states.get(operation_id)
+ if rpc_state is not None:
+ if event.kind is _intermediary_low.Event.Kind.WRITE_ACCEPTED:
+ self._on_write_event(operation_id, event, rpc_state)
+ elif event.kind is _intermediary_low.Event.Kind.METADATA_ACCEPTED:
+ self._on_metadata_event(operation_id, event, rpc_state)
+ elif event.kind is _intermediary_low.Event.Kind.READ_ACCEPTED:
+ self._on_read_event(operation_id, event, rpc_state)
+ elif event.kind is _intermediary_low.Event.Kind.FINISH:
+ self._on_finish_event(operation_id, event, rpc_state)
+ elif event.kind is _intermediary_low.Event.Kind.COMPLETE_ACCEPTED:
+ pass
+ else:
+ logging.error('Illegal RPC event! %s', (event,))
+
+ def _invoke(
+ self, operation_id, group, method, initial_metadata, payload, termination,
+ timeout, allowance):
+ """Invoke an RPC.
+
+ Args:
+ operation_id: Any object to be used as an operation ID for the RPC.
+ group: The group to which the RPC method belongs.
+ method: The RPC method name.
+ initial_metadata: The initial metadata object for the RPC.
+ payload: A payload object for the RPC or None if no payload was given at
+ invocation-time.
+ termination: A links.Ticket.Termination value or None indicated whether or
+ not more writes will follow from this side of the RPC.
+ timeout: A duration of time in seconds to allow for the RPC.
+ allowance: The number of payloads (beyond the free first one) that the
+ local ticket exchange mate has granted permission to be read.
+ """
+ if termination is links.Ticket.Termination.COMPLETION:
+ high_write = _HighWrite.CLOSED
+ elif termination is None:
+ high_write = _HighWrite.OPEN
+ else:
+ return
+
+ request_serializer = self._request_serializers.get((group, method))
+ response_deserializer = self._response_deserializers.get((group, method))
+ if request_serializer is None or response_deserializer is None:
+ cancellation_ticket = links.Ticket(
+ operation_id, 0, None, None, None, None, None, None, None, None, None,
+ None, links.Ticket.Termination.CANCELLATION)
+ self._relay.add_value(cancellation_ticket)
+ return
+
+ call = _intermediary_low.Call(
+ self._channel, self._completion_queue, '/%s/%s' % (group, method),
+ self._host, time.time() + timeout)
+ if initial_metadata is not None:
+ for metadata_key, metadata_value in initial_metadata:
+ call.add_metadata(metadata_key, metadata_value)
+ call.invoke(self._completion_queue, operation_id, operation_id)
+ if payload is None:
+ if high_write is _HighWrite.CLOSED:
+ call.complete(operation_id)
+ low_write = _LowWrite.CLOSED
+ else:
+ low_write = _LowWrite.OPEN
+ else:
+ call.write(request_serializer(payload), operation_id)
+ low_write = _LowWrite.ACTIVE
+ self._rpc_states[operation_id] = _RPCState(
+ call, request_serializer, response_deserializer, 0,
+ _Read.AWAITING_METADATA, 1 if allowance is None else (1 + allowance),
+ high_write, low_write)
+
+ def _advance(self, operation_id, rpc_state, payload, termination, allowance):
+ if payload is not None:
+ rpc_state.call.write(rpc_state.request_serializer(payload), operation_id)
+ rpc_state.low_write = _LowWrite.ACTIVE
+
+ if allowance is not None:
+ if rpc_state.read is _Read.AWAITING_ALLOWANCE:
+ rpc_state.allowance += allowance - 1
+ rpc_state.call.read(operation_id)
+ rpc_state.read = _Read.READING
+ else:
+ rpc_state.allowance += allowance
+
+ if termination is links.Ticket.Termination.COMPLETION:
+ rpc_state.high_write = _HighWrite.CLOSED
+ if rpc_state.low_write is _LowWrite.OPEN:
+ rpc_state.call.complete(operation_id)
+ rpc_state.low_write = _LowWrite.CLOSED
+ elif termination is not None:
+ rpc_state.call.cancel()
+
+ def add_ticket(self, ticket):
+ with self._lock:
+ if self._completion_queue is None:
+ return
+ if ticket.sequence_number == 0:
+ self._invoke(
+ ticket.operation_id, ticket.group, ticket.method,
+ ticket.initial_metadata, ticket.payload, ticket.termination,
+ ticket.timeout, ticket.allowance)
+ else:
+ rpc_state = self._rpc_states.get(ticket.operation_id)
+ if rpc_state is not None:
+ self._advance(
+ ticket.operation_id, rpc_state, ticket.payload,
+ ticket.termination, ticket.allowance)
+
+ def start(self):
+ """Starts this object.
+
+ This method must be called before attempting to exchange tickets with this
+ object.
+ """
+ with self._lock:
+ self._completion_queue = _intermediary_low.CompletionQueue()
+ self._rpc_states = {}
+ self._pool = logging_pool.pool(1)
+ self._pool.submit(self._spin, self._completion_queue)
+
+ def stop(self):
+ """Stops this object.
+
+ This method must be called for proper termination of this object, and no
+ attempts to exchange tickets with this object may be made after this method
+ has been called.
+ """
+ with self._lock:
+ self._completion_queue.stop()
+ self._completion_queue = None
+ pool = self._pool
+ self._pool = None
+ self._rpc_states = None
+ pool.shutdown(wait=True)
+
+
+class InvocationLink(links.Link, activated.Activated):
+ """A links.Link for use on the invocation-side of a gRPC connection.
+
+ Implementations of this interface are only valid for use when activated.
+ """
+ __metaclass__ = abc.ABCMeta
+
+
+class _InvocationLink(InvocationLink):
+
+ def __init__(
+ self, channel, host, request_serializers, response_deserializers):
+ self._relay = relay.relay(None)
+ self._kernel = _Kernel(
+ channel, host, request_serializers, response_deserializers, self._relay)
+
+ def _start(self):
+ self._relay.start()
+ self._kernel.start()
+ return self
+
+ def _stop(self):
+ self._kernel.stop()
+ self._relay.stop()
+
+ def accept_ticket(self, ticket):
+ """See links.Link.accept_ticket for specification."""
+ self._kernel.add_ticket(ticket)
+
+ def join_link(self, link):
+ """See links.Link.join_link for specification."""
+ self._relay.set_behavior(link.accept_ticket)
+
+ def __enter__(self):
+ """See activated.Activated.__enter__ for specification."""
+ return self._start()
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ """See activated.Activated.__exit__ for specification."""
+ self._stop()
+ return False
+
+ def start(self):
+ """See activated.Activated.start for specification."""
+ return self._start()
+
+ def stop(self):
+ """See activated.Activated.stop for specification."""
+ self._stop()
+
+
+def invocation_link(channel, host, request_serializers, response_deserializers):
+ """Creates an InvocationLink.
+
+ Args:
+ channel: A channel for use by the link.
+ host: The host to specify when invoking RPCs.
+ request_serializers: A dict from group-method pair to request object
+ serialization behavior.
+ response_deserializers: A dict from group-method pair to response object
+ deserialization behavior.
+
+ Returns:
+ An InvocationLink.
+ """
+ return _InvocationLink(
+ channel, host, request_serializers, response_deserializers)
diff --git a/src/python/src/grpc/_links/service.py b/src/python/src/grpc/_links/service.py
new file mode 100644
index 0000000000..7783e91824
--- /dev/null
+++ b/src/python/src/grpc/_links/service.py
@@ -0,0 +1,402 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""The RPC-service-side bridge between RPC Framework and GRPC-on-the-wire."""
+
+import abc
+import enum
+import logging
+import threading
+import time
+
+from grpc._adapter import _intermediary_low
+from grpc.framework.foundation import logging_pool
+from grpc.framework.foundation import relay
+from grpc.framework.interfaces.links import links
+
+
+@enum.unique
+class _Read(enum.Enum):
+ READING = 'reading'
+ AWAITING_ALLOWANCE = 'awaiting allowance'
+ CLOSED = 'closed'
+
+
+@enum.unique
+class _HighWrite(enum.Enum):
+ OPEN = 'open'
+ CLOSED = 'closed'
+
+
+@enum.unique
+class _LowWrite(enum.Enum):
+ """The possible categories of low-level write state."""
+
+ OPEN = 'OPEN'
+ ACTIVE = 'ACTIVE'
+ CLOSED = 'CLOSED'
+
+
+class _RPCState(object):
+
+ def __init__(
+ self, request_deserializer, response_serializer, sequence_number, read,
+ allowance, high_write, low_write, premetadataed, terminal_metadata, code,
+ message):
+ self.request_deserializer = request_deserializer
+ self.response_serializer = response_serializer
+ self.sequence_number = sequence_number
+ self.read = read
+ self.allowance = allowance
+ self.high_write = high_write
+ self.low_write = low_write
+ self.premetadataed = premetadataed
+ self.terminal_metadata = terminal_metadata
+ self.code = code
+ self.message = message
+
+
+def _metadatafy(call, metadata):
+ for metadata_key, metadata_value in metadata:
+ call.add_metadata(metadata_key, metadata_value)
+
+
+class _Kernel(object):
+
+ def __init__(self, request_deserializers, response_serializers, ticket_relay):
+ self._lock = threading.Lock()
+ self._request_deserializers = request_deserializers
+ self._response_serializers = response_serializers
+ self._relay = ticket_relay
+
+ self._completion_queue = None
+ self._server = None
+ self._rpc_states = {}
+ self._pool = None
+
+ def _on_service_acceptance_event(self, event, server):
+ server.service(None)
+
+ service_acceptance = event.service_acceptance
+ call = service_acceptance.call
+ call.accept(self._completion_queue, call)
+ try:
+ group, method = service_acceptance.method.split('/')[1:3]
+ except ValueError:
+ logging.info('Illegal path "%s"!', service_acceptance.method)
+ return
+ request_deserializer = self._request_deserializers.get((group, method))
+ response_serializer = self._response_serializers.get((group, method))
+ if request_deserializer is None or response_serializer is None:
+ # TODO(nathaniel): Terminate the RPC with code NOT_FOUND.
+ call.cancel()
+ return
+
+ call.read(call)
+ self._rpc_states[call] = _RPCState(
+ request_deserializer, response_serializer, 1, _Read.READING, 0,
+ _HighWrite.OPEN, _LowWrite.OPEN, False, None, None, None)
+ ticket = links.Ticket(
+ call, 0, group, method, links.Ticket.Subscription.FULL,
+ service_acceptance.deadline - time.time(), None, event.metadata, None,
+ None, None, None, None)
+ self._relay.add_value(ticket)
+
+ def _on_read_event(self, event):
+ call = event.tag
+ rpc_state = self._rpc_states.get(call, None)
+ if rpc_state is None:
+ return
+
+ if event.bytes is None:
+ rpc_state.read = _Read.CLOSED
+ payload = None
+ termination = links.Ticket.Termination.COMPLETION
+ else:
+ if 0 < rpc_state.allowance:
+ rpc_state.allowance -= 1
+ call.read(call)
+ else:
+ rpc_state.read = _Read.AWAITING_ALLOWANCE
+ payload = rpc_state.request_deserializer(event.bytes)
+ termination = None
+ ticket = links.Ticket(
+ call, rpc_state.sequence_number, None, None, None, None, None, None,
+ payload, None, None, None, termination)
+ rpc_state.sequence_number += 1
+ self._relay.add_value(ticket)
+
+ def _on_write_event(self, event):
+ call = event.tag
+ rpc_state = self._rpc_states.get(call, None)
+ if rpc_state is None:
+ return
+
+ if rpc_state.high_write is _HighWrite.CLOSED:
+ if rpc_state.terminal_metadata is not None:
+ _metadatafy(call, rpc_state.terminal_metadata)
+ call.status(
+ _intermediary_low.Status(rpc_state.code, rpc_state.message), call)
+ rpc_state.low_write = _LowWrite.CLOSED
+ else:
+ ticket = links.Ticket(
+ call, rpc_state.sequence_number, None, None, None, None, 1, None,
+ None, None, None, None, None)
+ rpc_state.sequence_number += 1
+ self._relay.add_value(ticket)
+ rpc_state.low_write = _LowWrite.OPEN
+
+ def _on_finish_event(self, event):
+ call = event.tag
+ rpc_state = self._rpc_states.pop(call, None)
+ if rpc_state is None:
+ return
+ code = event.status.code
+ if code is _intermediary_low.Code.OK:
+ return
+
+ if code is _intermediary_low.Code.CANCELLED:
+ termination = links.Ticket.Termination.CANCELLATION
+ elif code is _intermediary_low.Code.DEADLINE_EXCEEDED:
+ termination = links.Ticket.Termination.EXPIRATION
+ else:
+ termination = links.Ticket.Termination.TRANSMISSION_FAILURE
+ ticket = links.Ticket(
+ call, rpc_state.sequence_number, None, None, None, None, None, None,
+ None, None, None, None, termination)
+ rpc_state.sequence_number += 1
+ self._relay.add_value(ticket)
+
+ def _spin(self, completion_queue, server):
+ while True:
+ event = completion_queue.get(None)
+ if event.kind is _intermediary_low.Event.Kind.STOP:
+ return
+ with self._lock:
+ if self._server is None:
+ continue
+ elif event.kind is _intermediary_low.Event.Kind.SERVICE_ACCEPTED:
+ self._on_service_acceptance_event(event, server)
+ elif event.kind is _intermediary_low.Event.Kind.READ_ACCEPTED:
+ self._on_read_event(event)
+ elif event.kind is _intermediary_low.Event.Kind.WRITE_ACCEPTED:
+ self._on_write_event(event)
+ elif event.kind is _intermediary_low.Event.Kind.COMPLETE_ACCEPTED:
+ pass
+ elif event.kind is _intermediary_low.Event.Kind.FINISH:
+ self._on_finish_event(event)
+ else:
+ logging.error('Illegal event! %s', (event,))
+
+ def add_ticket(self, ticket):
+ with self._lock:
+ if self._server is None:
+ return
+ call = ticket.operation_id
+ rpc_state = self._rpc_states.get(call)
+ if rpc_state is None:
+ return
+
+ if ticket.initial_metadata is not None:
+ _metadatafy(call, ticket.initial_metadata)
+ call.premetadata()
+ rpc_state.premetadataed = True
+ elif not rpc_state.premetadataed:
+ if (ticket.terminal_metadata is not None or
+ ticket.payload is not None or
+ ticket.termination is links.Ticket.Termination.COMPLETION or
+ ticket.code is not None or
+ ticket.message is not None):
+ call.premetadata()
+ rpc_state.premetadataed = True
+
+ if ticket.allowance is not None:
+ if rpc_state.read is _Read.AWAITING_ALLOWANCE:
+ rpc_state.allowance += ticket.allowance - 1
+ call.read(call)
+ rpc_state.read = _Read.READING
+ else:
+ rpc_state.allowance += ticket.allowance
+
+ if ticket.payload is not None:
+ call.write(rpc_state.response_serializer(ticket.payload), call)
+ rpc_state.low_write = _LowWrite.ACTIVE
+
+ if ticket.terminal_metadata is not None:
+ rpc_state.terminal_metadata = ticket.terminal_metadata
+ if ticket.code is not None:
+ rpc_state.code = ticket.code
+ if ticket.message is not None:
+ rpc_state.message = ticket.message
+
+ if ticket.termination is links.Ticket.Termination.COMPLETION:
+ rpc_state.high_write = _HighWrite.CLOSED
+ if rpc_state.low_write is _LowWrite.OPEN:
+ if rpc_state.terminal_metadata is not None:
+ _metadatafy(call, rpc_state.terminal_metadata)
+ status = _intermediary_low.Status(
+ _intermediary_low.Code.OK
+ if rpc_state.code is None else rpc_state.code,
+ '' if rpc_state.message is None else rpc_state.message)
+ call.status(status, call)
+ rpc_state.low_write = _LowWrite.CLOSED
+ elif ticket.termination is not None:
+ call.cancel()
+ self._rpc_states.pop(call, None)
+
+ def add_port(self, port, server_credentials):
+ with self._lock:
+ address = '[::]:%d' % port
+ if self._server is None:
+ self._completion_queue = _intermediary_low.CompletionQueue()
+ self._server = _intermediary_low.Server(self._completion_queue)
+ if server_credentials is None:
+ return self._server.add_http2_addr(address)
+ else:
+ return self._server.add_secure_http2_addr(address, server_credentials)
+
+ def start(self):
+ with self._lock:
+ if self._server is None:
+ self._completion_queue = _intermediary_low.CompletionQueue()
+ self._server = _intermediary_low.Server(self._completion_queue)
+ self._pool = logging_pool.pool(1)
+ self._pool.submit(self._spin, self._completion_queue, self._server)
+ self._server.start()
+ self._server.service(None)
+
+ def graceful_stop(self):
+ with self._lock:
+ self._server.stop()
+ self._server = None
+ self._completion_queue.stop()
+ self._completion_queue = None
+ pool = self._pool
+ self._pool = None
+ self._rpc_states = None
+ pool.shutdown(wait=True)
+
+ def immediate_stop(self):
+ # TODO(nathaniel): Implementation.
+ raise NotImplementedError(
+ 'TODO(nathaniel): after merge of rewritten lower layers')
+
+
+class ServiceLink(links.Link):
+ """A links.Link for use on the service-side of a gRPC connection.
+
+ Implementations of this interface are only valid for use between calls to
+ their start method and one of their stop methods.
+ """
+
+ @abc.abstractmethod
+ def add_port(self, port, server_credentials):
+ """Adds a port on which to service RPCs after this link has been started.
+
+ Args:
+ port: The port on which to service RPCs, or zero to request that a port be
+ automatically selected and used.
+ server_credentials: A ServerCredentials object, or None for insecure
+ service.
+
+ Returns:
+ A port on which RPCs will be serviced after this link has been started.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def start(self):
+ """Starts this object.
+
+ This method must be called before attempting to use this Link in ticket
+ exchange.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def stop_gracefully(self):
+ """Stops this link.
+
+ New RPCs will be rejected as soon as this method is called, but ongoing RPCs
+ will be allowed to continue until they terminate. This method blocks until
+ all RPCs have terminated.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def stop_immediately(self):
+ """Stops this link.
+
+ All in-progress RPCs will be terminated immediately.
+ """
+ raise NotImplementedError()
+
+
+class _ServiceLink(ServiceLink):
+
+ def __init__(self, request_deserializers, response_serializers):
+ self._relay = relay.relay(None)
+ self._kernel = _Kernel(
+ request_deserializers, response_serializers, self._relay)
+
+ def accept_ticket(self, ticket):
+ self._kernel.add_ticket(ticket)
+
+ def join_link(self, link):
+ self._relay.set_behavior(link.accept_ticket)
+
+ def add_port(self, port, server_credentials):
+ return self._kernel.add_port(port, server_credentials)
+
+ def start(self):
+ self._relay.start()
+ return self._kernel.start()
+
+ def stop_gracefully(self):
+ self._kernel.graceful_stop()
+ self._relay.stop()
+
+ def stop_immediately(self):
+ self._kernel.immediate_stop()
+ self._relay.stop()
+
+
+def service_link(request_deserializers, response_serializers):
+ """Creates a ServiceLink.
+
+ Args:
+ request_deserializers: A dict from group-method pair to request object
+ deserialization behavior.
+ response_serializers: A dict from group-method pair to response ojbect
+ serialization behavior.
+
+ Returns:
+ A ServiceLink.
+ """
+ return _ServiceLink(request_deserializers, response_serializers)
diff --git a/src/python/src/grpc/framework/common/test_constants.py b/src/python/src/grpc/framework/common/test_constants.py
new file mode 100644
index 0000000000..237b8754ed
--- /dev/null
+++ b/src/python/src/grpc/framework/common/test_constants.py
@@ -0,0 +1,37 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Constants shared among tests throughout RPC Framework."""
+
+# Value for maximum duration in seconds of RPCs that may time out as part of a
+# test.
+SHORT_TIMEOUT = 4
+# Absurdly large value for maximum duration in seconds for should-not-time-out
+# RPCs made during tests.
+LONG_TIMEOUT = 3000
diff --git a/src/python/src/grpc/framework/common/test_control.py b/src/python/src/grpc/framework/common/test_control.py
new file mode 100644
index 0000000000..3960c4e649
--- /dev/null
+++ b/src/python/src/grpc/framework/common/test_control.py
@@ -0,0 +1,87 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Code for instructing systems under test to block or fail."""
+
+import abc
+import contextlib
+import threading
+
+
+class Control(object):
+ """An object that accepts program control from a system under test.
+
+ Systems under test passed a Control should call its control() method
+ frequently during execution. The control() method may block, raise an
+ exception, or do nothing, all according to the enclosing test's desire for
+ the system under test to simulate hanging, failing, or functioning.
+ """
+
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def control(self):
+ """Potentially does anything."""
+ raise NotImplementedError()
+
+
+class PauseFailControl(Control):
+ """A Control that can be used to pause or fail code under control."""
+
+ def __init__(self):
+ self._condition = threading.Condition()
+ self._paused = False
+ self._fail = False
+
+ def control(self):
+ with self._condition:
+ if self._fail:
+ raise ValueError()
+
+ while self._paused:
+ self._condition.wait()
+
+ @contextlib.contextmanager
+ def pause(self):
+ """Pauses code under control while controlling code is in context."""
+ with self._condition:
+ self._paused = True
+ yield
+ with self._condition:
+ self._paused = False
+ self._condition.notify_all()
+
+ @contextlib.contextmanager
+ def fail(self):
+ """Fails code under control while controlling code is in context."""
+ with self._condition:
+ self._fail = True
+ yield
+ with self._condition:
+ self._fail = False
diff --git a/src/python/src/grpc/framework/common/test_coverage.py b/src/python/src/grpc/framework/common/test_coverage.py
new file mode 100644
index 0000000000..a7ed3582c4
--- /dev/null
+++ b/src/python/src/grpc/framework/common/test_coverage.py
@@ -0,0 +1,116 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Governs coverage for tests of RPCs throughout RPC Framework."""
+
+import abc
+
+# This code is designed for use with the unittest module.
+# pylint: disable=invalid-name
+
+
+class Coverage(object):
+ """Specification of test coverage."""
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def testSuccessfulUnaryRequestUnaryResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testSuccessfulUnaryRequestStreamResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testSuccessfulStreamRequestUnaryResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testSuccessfulStreamRequestStreamResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testSequentialInvocations(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testParallelInvocations(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testWaitingForSomeButNotAllParallelInvocations(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testCancelledUnaryRequestUnaryResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testCancelledUnaryRequestStreamResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testCancelledStreamRequestUnaryResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testCancelledStreamRequestStreamResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testExpiredUnaryRequestUnaryResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testExpiredUnaryRequestStreamResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testExpiredStreamRequestUnaryResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testExpiredStreamRequestStreamResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testFailedUnaryRequestUnaryResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testFailedUnaryRequestStreamResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testFailedStreamRequestUnaryResponse(self):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def testFailedStreamRequestStreamResponse(self):
+ raise NotImplementedError()
diff --git a/src/python/src/grpc/framework/foundation/relay.py b/src/python/src/grpc/framework/foundation/relay.py
new file mode 100644
index 0000000000..9c23946552
--- /dev/null
+++ b/src/python/src/grpc/framework/foundation/relay.py
@@ -0,0 +1,175 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Implementations of in-order work deference."""
+
+import abc
+import enum
+import threading
+
+from grpc.framework.foundation import activated
+from grpc.framework.foundation import logging_pool
+
+_NULL_BEHAVIOR = lambda unused_value: None
+
+
+class Relay(object):
+ """Performs work submitted to it in another thread.
+
+ Performs work in the order in which work was submitted to it; otherwise there
+ would be no reason to use an implementation of this interface instead of a
+ thread pool.
+ """
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def add_value(self, value):
+ """Adds a value to be passed to the behavior registered with this Relay.
+
+ Args:
+ value: A value that will be passed to a call made in another thread to the
+ behavior registered with this Relay.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def set_behavior(self, behavior):
+ """Sets the behavior that this Relay should call when passed values.
+
+ Args:
+ behavior: The behavior that this Relay should call in another thread when
+ passed a value, or None to have passed values ignored.
+ """
+ raise NotImplementedError()
+
+
+class _PoolRelay(activated.Activated, Relay):
+
+ @enum.unique
+ class _State(enum.Enum):
+ INACTIVE = 'inactive'
+ IDLE = 'idle'
+ SPINNING = 'spinning'
+
+ def __init__(self, pool, behavior):
+ self._condition = threading.Condition()
+ self._pool = pool
+ self._own_pool = pool is None
+ self._state = _PoolRelay._State.INACTIVE
+ self._activated = False
+ self._spinning = False
+ self._values = []
+ self._behavior = _NULL_BEHAVIOR if behavior is None else behavior
+
+ def _spin(self, behavior, value):
+ while True:
+ behavior(value)
+ with self._condition:
+ if self._values:
+ value = self._values.pop(0)
+ behavior = self._behavior
+ else:
+ self._state = _PoolRelay._State.IDLE
+ self._condition.notify_all()
+ break
+
+ def add_value(self, value):
+ with self._condition:
+ if self._state is _PoolRelay._State.INACTIVE:
+ raise ValueError('add_value not valid on inactive Relay!')
+ elif self._state is _PoolRelay._State.IDLE:
+ self._pool.submit(self._spin, self._behavior, value)
+ self._state = _PoolRelay._State.SPINNING
+ else:
+ self._values.append(value)
+
+ def set_behavior(self, behavior):
+ with self._condition:
+ self._behavior = _NULL_BEHAVIOR if behavior is None else behavior
+
+ def _start(self):
+ with self._condition:
+ self._state = _PoolRelay._State.IDLE
+ if self._own_pool:
+ self._pool = logging_pool.pool(1)
+ return self
+
+ def _stop(self):
+ with self._condition:
+ while self._state is _PoolRelay._State.SPINNING:
+ self._condition.wait()
+ if self._own_pool:
+ self._pool.shutdown(wait=True)
+ self._state = _PoolRelay._State.INACTIVE
+
+ def __enter__(self):
+ return self._start()
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self._stop()
+ return False
+
+ def start(self):
+ return self._start()
+
+ def stop(self):
+ self._stop()
+
+
+def relay(behavior):
+ """Creates a Relay.
+
+ Args:
+ behavior: The behavior to be called by the created Relay, or None to have
+ passed values dropped until a different behavior is given to the returned
+ Relay later.
+
+ Returns:
+ An object that is both an activated.Activated and a Relay. The object is
+ only valid for use as a Relay when activated.
+ """
+ return _PoolRelay(None, behavior)
+
+
+def pool_relay(pool, behavior):
+ """Creates a Relay that uses a given thread pool.
+
+ This object will make use of at most one thread in the given pool.
+
+ Args:
+ pool: A futures.ThreadPoolExecutor for use by the created Relay.
+ behavior: The behavior to be called by the created Relay, or None to have
+ passed values dropped until a different behavior is given to the returned
+ Relay later.
+
+ Returns:
+ An object that is both an activated.Activated and a Relay. The object is
+ only valid for use as a Relay when activated.
+ """
+ return _PoolRelay(pool, behavior)
diff --git a/src/python/src/grpc/framework/interfaces/__init__.py b/src/python/src/grpc/framework/interfaces/__init__.py
new file mode 100644
index 0000000000..7086519106
--- /dev/null
+++ b/src/python/src/grpc/framework/interfaces/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
diff --git a/src/python/src/grpc/framework/interfaces/links/__init__.py b/src/python/src/grpc/framework/interfaces/links/__init__.py
new file mode 100644
index 0000000000..7086519106
--- /dev/null
+++ b/src/python/src/grpc/framework/interfaces/links/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
diff --git a/src/python/src/grpc/framework/interfaces/links/links.py b/src/python/src/grpc/framework/interfaces/links/links.py
new file mode 100644
index 0000000000..5ebbac8a6f
--- /dev/null
+++ b/src/python/src/grpc/framework/interfaces/links/links.py
@@ -0,0 +1,124 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""The low-level ticket-exchanging-links interface of RPC Framework."""
+
+import abc
+import collections
+import enum
+
+
+class Ticket(
+ collections.namedtuple(
+ 'Ticket',
+ ['operation_id', 'sequence_number', 'group', 'method', 'subscription',
+ 'timeout', 'allowance', 'initial_metadata', 'payload',
+ 'terminal_metadata', 'code', 'message', 'termination'])):
+ """A sum type for all values sent from a front to a back.
+
+ Attributes:
+ operation_id: A unique-with-respect-to-equality hashable object identifying
+ a particular operation.
+ sequence_number: A zero-indexed integer sequence number identifying the
+ ticket's place in the stream of tickets sent in one direction for the
+ particular operation.
+ group: The group to which the method of the operation belongs. Must be
+ present in the first ticket from invocation side to service side. Ignored
+ for all other tickets exchanged during the operation.
+ method: The name of an operation. Must be present in the first ticket from
+ invocation side to service side. Ignored for all other tickets exchanged
+ during the operation.
+ subscription: A Subscription value describing the interest one side has in
+ receiving information from the other side. Must be present in the first
+ ticket from either side. Ignored for all other tickets exchanged during
+ the operation.
+ timeout: A nonzero length of time (measured from the beginning of the
+ operation) to allow for the entire operation. Must be present in the first
+ ticket from invocation side to service side. Optional for all other
+ tickets exchanged during the operation. Receipt of a value from the other
+ side of the operation indicates the value in use by that side. Setting a
+ value on a later ticket allows either side to request time extensions (or
+ even time reductions!) on in-progress operations.
+ allowance: A positive integer granting permission for a number of payloads
+ to be transmitted to the communicating side of the operation, or None if
+ no additional allowance is being granted with this ticket.
+ initial_metadata: An optional metadata value communicated from one side to
+ the other at the beginning of the operation. May be non-None in at most
+ one ticket from each side. Any non-None value must appear no later than
+ the first payload value.
+ payload: A customer payload object. May be None.
+ terminal_metadata: A metadata value comminicated from one side to the other
+ at the end of the operation. May be non-None in the same ticket as
+ the code and message, but must be None for all earlier tickets.
+ code: A value communicated at operation completion. May be None.
+ message: A value communicated at operation completion. May be None.
+ termination: A Termination value describing the end of the operation, or
+ None if the operation has not yet terminated. If set, no further tickets
+ may be sent in the same direction.
+ """
+
+ @enum.unique
+ class Subscription(enum.Enum):
+ """Identifies the level of subscription of a side of an operation."""
+
+ NONE = 'none'
+ TERMINATION = 'termination'
+ FULL = 'full'
+
+ @enum.unique
+ class Termination(enum.Enum):
+ """Identifies the termination of an operation."""
+
+ COMPLETION = 'completion'
+ CANCELLATION = 'cancellation'
+ EXPIRATION = 'expiration'
+ LOCAL_SHUTDOWN = 'local shutdown'
+ RECEPTION_FAILURE = 'reception failure'
+ TRANSMISSION_FAILURE = 'transmission failure'
+ LOCAL_FAILURE = 'local failure'
+ REMOTE_FAILURE = 'remote failure'
+
+
+class Link(object):
+ """Accepts and emits tickets."""
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def accept_ticket(self, ticket):
+ """Accept a Ticket.
+
+ Args:
+ ticket: Any Ticket.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def join_link(self, link):
+ """Mates this object with a peer with which it will exchange tickets."""
+ raise NotImplementedError()
diff --git a/src/python/src/grpc/framework/interfaces/links/test_cases.py b/src/python/src/grpc/framework/interfaces/links/test_cases.py
new file mode 100644
index 0000000000..3ac212ebdf
--- /dev/null
+++ b/src/python/src/grpc/framework/interfaces/links/test_cases.py
@@ -0,0 +1,332 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Tests of the links interface of RPC Framework."""
+
+# unittest is referenced from specification in this module.
+import abc
+import unittest # pylint: disable=unused-import
+
+from grpc.framework.common import test_constants
+from grpc.framework.interfaces.links import links
+from grpc.framework.interfaces.links import test_utilities
+
+
+def at_least_n_payloads_received_predicate(n):
+ def predicate(ticket_sequence):
+ payload_count = 0
+ for ticket in ticket_sequence:
+ if ticket.payload is not None:
+ payload_count += 1
+ if n <= payload_count:
+ return True
+ else:
+ return False
+ return predicate
+
+
+def terminated(ticket_sequence):
+ return ticket_sequence and ticket_sequence[-1].termination is not None
+
+_TRANSMISSION_GROUP = 'test.Group'
+_TRANSMISSION_METHOD = 'TestMethod'
+
+
+class TransmissionTest(object):
+ """Tests ticket transmission between two connected links.
+
+ This class must be mixed into a unittest.TestCase that implements the abstract
+ methods it provides.
+ """
+ __metaclass__ = abc.ABCMeta
+
+ # This is a unittest.TestCase mix-in.
+ # pylint: disable=invalid-name
+
+ @abc.abstractmethod
+ def create_transmitting_links(self):
+ """Creates two connected links for use in this test.
+
+ Returns:
+ Two links.Links, the first of which will be used on the invocation side
+ of RPCs and the second of which will be used on the service side of
+ RPCs.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def destroy_transmitting_links(self, invocation_side_link, service_side_link):
+ """Destroys the two connected links created for this test.
+
+
+ Args:
+ invocation_side_link: The link used on the invocation side of RPCs in
+ this test.
+ service_side_link: The link used on the service side of RPCs in this
+ test.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def create_invocation_initial_metadata(self):
+ """Creates a value for use as invocation-side initial metadata.
+
+ Returns:
+ A metadata value appropriate for use as invocation-side initial metadata
+ or None if invocation-side initial metadata transmission is not
+ supported by the links under test.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def create_invocation_terminal_metadata(self):
+ """Creates a value for use as invocation-side terminal metadata.
+
+ Returns:
+ A metadata value appropriate for use as invocation-side terminal
+ metadata or None if invocation-side terminal metadata transmission is
+ not supported by the links under test.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def create_service_initial_metadata(self):
+ """Creates a value for use as service-side initial metadata.
+
+ Returns:
+ A metadata value appropriate for use as service-side initial metadata or
+ None if service-side initial metadata transmission is not supported by
+ the links under test.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def create_service_terminal_metadata(self):
+ """Creates a value for use as service-side terminal metadata.
+
+ Returns:
+ A metadata value appropriate for use as service-side terminal metadata or
+ None if service-side terminal metadata transmission is not supported by
+ the links under test.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def create_invocation_completion(self):
+ """Creates values for use as invocation-side code and message.
+
+ Returns:
+ An invocation-side code value and an invocation-side message value.
+ Either or both may be None if invocation-side code and/or
+ invocation-side message transmission is not supported by the links
+ under test.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def create_service_completion(self):
+ """Creates values for use as service-side code and message.
+
+ Returns:
+ A service-side code value and a service-side message value. Either or
+ both may be None if service-side code and/or service-side message
+ transmission is not supported by the links under test.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def assertMetadataEqual(self, original_metadata, transmitted_metadata):
+ """Asserts that two metadata objects are equal.
+
+ Args:
+ original_metadata: A metadata object used in this test.
+ transmitted_metadata: A metadata object obtained after transmission
+ through the system under test.
+
+ Raises:
+ AssertionError: if the two metadata objects are not equal.
+ """
+ raise NotImplementedError()
+
+ def group_and_method(self):
+ """Returns the group and method used in this test case.
+
+ Returns:
+ A pair of the group and method used in this test case.
+ """
+ return _TRANSMISSION_GROUP, _TRANSMISSION_METHOD
+
+ def serialize_request(self, request):
+ """Serializes a request value used in this test case.
+
+ Args:
+ request: A request value created by this test case.
+
+ Returns:
+ A bytestring that is the serialization of the given request.
+ """
+ return request
+
+ def deserialize_request(self, serialized_request):
+ """Deserializes a request value used in this test case.
+
+ Args:
+ serialized_request: A bytestring that is the serialization of some request
+ used in this test case.
+
+ Returns:
+ The request value encoded by the given bytestring.
+ """
+ return serialized_request
+
+ def serialize_response(self, response):
+ """Serializes a response value used in this test case.
+
+ Args:
+ response: A response value created by this test case.
+
+ Returns:
+ A bytestring that is the serialization of the given response.
+ """
+ return response
+
+ def deserialize_response(self, serialized_response):
+ """Deserializes a response value used in this test case.
+
+ Args:
+ serialized_response: A bytestring that is the serialization of some
+ response used in this test case.
+
+ Returns:
+ The response value encoded by the given bytestring.
+ """
+ return serialized_response
+
+ def _assert_is_valid_metadata_payload_sequence(
+ self, ticket_sequence, payloads, initial_metadata, terminal_metadata):
+ initial_metadata_seen = False
+ seen_payloads = []
+ terminal_metadata_seen = False
+
+ for ticket in ticket_sequence:
+ if ticket.initial_metadata is not None:
+ self.assertFalse(initial_metadata_seen)
+ self.assertFalse(seen_payloads)
+ self.assertFalse(terminal_metadata_seen)
+ self.assertMetadataEqual(initial_metadata, ticket.initial_metadata)
+ initial_metadata_seen = True
+
+ if ticket.payload is not None:
+ self.assertFalse(terminal_metadata_seen)
+ seen_payloads.append(ticket.payload)
+
+ if ticket.terminal_metadata is not None:
+ self.assertFalse(terminal_metadata_seen)
+ self.assertMetadataEqual(terminal_metadata, ticket.terminal_metadata)
+ terminal_metadata_seen = True
+ self.assertSequenceEqual(payloads, seen_payloads)
+
+ def _assert_is_valid_invocation_sequence(
+ self, ticket_sequence, group, method, payloads, initial_metadata,
+ terminal_metadata, termination):
+ self.assertLess(0, len(ticket_sequence))
+ self.assertEqual(group, ticket_sequence[0].group)
+ self.assertEqual(method, ticket_sequence[0].method)
+ self._assert_is_valid_metadata_payload_sequence(
+ ticket_sequence, payloads, initial_metadata, terminal_metadata)
+ self.assertIs(termination, ticket_sequence[-1].termination)
+
+ def _assert_is_valid_service_sequence(
+ self, ticket_sequence, payloads, initial_metadata, terminal_metadata,
+ code, message, termination):
+ self.assertLess(0, len(ticket_sequence))
+ self._assert_is_valid_metadata_payload_sequence(
+ ticket_sequence, payloads, initial_metadata, terminal_metadata)
+ self.assertEqual(code, ticket_sequence[-1].code)
+ self.assertEqual(message, ticket_sequence[-1].message)
+ self.assertIs(termination, ticket_sequence[-1].termination)
+
+ def setUp(self):
+ self._invocation_link, self._service_link = self.create_transmitting_links()
+ self._invocation_mate = test_utilities.RecordingLink()
+ self._service_mate = test_utilities.RecordingLink()
+ self._invocation_link.join_link(self._invocation_mate)
+ self._service_link.join_link(self._service_mate)
+
+ def tearDown(self):
+ self.destroy_transmitting_links(self._invocation_link, self._service_link)
+
+ def testSimplestRoundTrip(self):
+ """Tests transmission of one ticket in each direction."""
+ invocation_operation_id = object()
+ invocation_payload = b'\x07' * 1023
+ timeout = test_constants.LONG_TIMEOUT
+ invocation_initial_metadata = self.create_invocation_initial_metadata()
+ invocation_terminal_metadata = self.create_invocation_terminal_metadata()
+ invocation_code, invocation_message = self.create_invocation_completion()
+ service_payload = b'\x08' * 1025
+ service_initial_metadata = self.create_service_initial_metadata()
+ service_terminal_metadata = self.create_service_terminal_metadata()
+ service_code, service_message = self.create_service_completion()
+
+ original_invocation_ticket = links.Ticket(
+ invocation_operation_id, 0, _TRANSMISSION_GROUP, _TRANSMISSION_METHOD,
+ links.Ticket.Subscription.FULL, timeout, 0, invocation_initial_metadata,
+ invocation_payload, invocation_terminal_metadata, invocation_code,
+ invocation_message, links.Ticket.Termination.COMPLETION)
+ self._invocation_link.accept_ticket(original_invocation_ticket)
+
+ # TODO(nathaniel): This shouldn't be necessary. Detecting the end of the
+ # invocation-side ticket sequence shouldn't require granting allowance for
+ # another payload.
+ self._service_mate.block_until_tickets_satisfy(
+ at_least_n_payloads_received_predicate(1))
+ service_operation_id = self._service_mate.tickets()[0].operation_id
+ self._service_link.accept_ticket(
+ links.Ticket(
+ service_operation_id, 0, None, None, links.Ticket.Subscription.FULL,
+ None, 1, None, None, None, None, None, None))
+
+ self._service_mate.block_until_tickets_satisfy(terminated)
+ self._assert_is_valid_invocation_sequence(
+ self._service_mate.tickets(), _TRANSMISSION_GROUP, _TRANSMISSION_METHOD,
+ (invocation_payload,), invocation_initial_metadata,
+ invocation_terminal_metadata, links.Ticket.Termination.COMPLETION)
+
+ original_service_ticket = links.Ticket(
+ service_operation_id, 1, None, None, links.Ticket.Subscription.FULL,
+ timeout, 0, service_initial_metadata, service_payload,
+ service_terminal_metadata, service_code, service_message,
+ links.Ticket.Termination.COMPLETION)
+ self._service_link.accept_ticket(original_service_ticket)
+ self._invocation_mate.block_until_tickets_satisfy(terminated)
+ self._assert_is_valid_service_sequence(
+ self._invocation_mate.tickets(), (service_payload,),
+ service_initial_metadata, service_terminal_metadata, service_code,
+ service_message, links.Ticket.Termination.COMPLETION)
diff --git a/src/python/src/grpc/framework/interfaces/links/test_utilities.py b/src/python/src/grpc/framework/interfaces/links/test_utilities.py
new file mode 100644
index 0000000000..6c2e3346aa
--- /dev/null
+++ b/src/python/src/grpc/framework/interfaces/links/test_utilities.py
@@ -0,0 +1,66 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""State and behavior appropriate for use in tests."""
+
+import threading
+
+from grpc.framework.interfaces.links import links
+
+
+class RecordingLink(links.Link):
+ """A Link that records every ticket passed to it."""
+
+ def __init__(self):
+ self._condition = threading.Condition()
+ self._tickets = []
+
+ def accept_ticket(self, ticket):
+ with self._condition:
+ self._tickets.append(ticket)
+ self._condition.notify_all()
+
+ def join_link(self, link):
+ pass
+
+ def block_until_tickets_satisfy(self, predicate):
+ """Blocks until the received tickets satisfy the given predicate.
+
+ Args:
+ predicate: A callable that takes a sequence of tickets and returns a
+ boolean value.
+ """
+ with self._condition:
+ while not predicate(self._tickets):
+ self._condition.wait()
+
+ def tickets(self):
+ """Returns a copy of the list of all tickets received by this Link."""
+ with self._condition:
+ return tuple(self._tickets)
diff --git a/src/python/src/grpc/framework/interfaces/links/utilities.py b/src/python/src/grpc/framework/interfaces/links/utilities.py
new file mode 100644
index 0000000000..6e4fd76d93
--- /dev/null
+++ b/src/python/src/grpc/framework/interfaces/links/utilities.py
@@ -0,0 +1,44 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Utilities provided as part of the links interface."""
+
+from grpc.framework.interfaces.links import links
+
+
+class _NullLink(links.Link):
+ """A do-nothing links.Link."""
+
+ def accept_ticket(self, ticket):
+ pass
+
+ def join_link(self, link):
+ pass
+
+NULL_LINK = _NullLink()
diff --git a/src/python/src/setup.py b/src/python/src/setup.py
index b9764bdc07..a857ae98cc 100644
--- a/src/python/src/setup.py
+++ b/src/python/src/setup.py
@@ -76,6 +76,7 @@ _PACKAGES = (
'grpc',
'grpc._adapter',
'grpc._junkdrawer',
+ 'grpc._links',
'grpc.early_adopter',
'grpc.framework',
'grpc.framework.alpha',
@@ -84,12 +85,15 @@ _PACKAGES = (
'grpc.framework.face',
'grpc.framework.face.testing',
'grpc.framework.foundation',
+ 'grpc.framework.interfaces',
+ 'grpc.framework.interfaces.links',
)
_PACKAGE_DIRECTORIES = {
'grpc': 'grpc',
'grpc._adapter': 'grpc/_adapter',
'grpc._junkdrawer': 'grpc/_junkdrawer',
+ 'grpc._links': 'grpc/_links',
'grpc.early_adopter': 'grpc/early_adopter',
'grpc.framework': 'grpc/framework',
}
diff --git a/src/ruby/grpc.gemspec b/src/ruby/grpc.gemspec
index 319da9470a..dd4e27df51 100755
--- a/src/ruby/grpc.gemspec
+++ b/src/ruby/grpc.gemspec
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
s.license = 'BSD-3-Clause'
s.required_ruby_version = '>= 2.0.0'
- s.requirements << 'libgrpc ~> 0.9.1 needs to be installed'
+ s.requirements << 'libgrpc ~> 0.10.0 needs to be installed'
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- spec/*`.split("\n")
diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb
index a7e20d6b82..67bf35ce02 100644
--- a/src/ruby/lib/grpc/generic/rpc_server.rb
+++ b/src/ruby/lib/grpc/generic/rpc_server.rb
@@ -398,14 +398,14 @@ module GRPC
nil
end
- # Sends NOT_FOUND if the method can't be found
- def found?(an_rpc)
+ # Sends UNIMPLEMENTED if the method is not implemented by this server
+ def implemented?(an_rpc)
mth = an_rpc.method.to_sym
return an_rpc if rpc_descs.key?(mth)
- GRPC.logger.warn("NOT_FOUND: #{an_rpc}")
+ GRPC.logger.warn("UNIMPLEMENTED: #{an_rpc}")
noop = proc { |x| x }
c = ActiveCall.new(an_rpc.call, @cq, noop, noop, an_rpc.deadline)
- c.send_status(StatusCodes::NOT_FOUND, '')
+ c.send_status(StatusCodes::UNIMPLEMENTED, '')
nil
end
@@ -446,7 +446,7 @@ module GRPC
an_rpc.call.run_batch(@cq, handle_call_tag, INFINITE_FUTURE,
SEND_INITIAL_METADATA => connect_md)
return nil unless available?(an_rpc)
- return nil unless found?(an_rpc)
+ return nil unless implemented?(an_rpc)
# Create the ActiveCall
GRPC.logger.info("deadline is #{an_rpc.deadline}; (now=#{Time.now})")
diff --git a/templates/Makefile.template b/templates/Makefile.template
index 490c002f9a..044db4dbfe 100644
--- a/templates/Makefile.template
+++ b/templates/Makefile.template
@@ -353,20 +353,20 @@ CACHE_MK =
HAS_PKG_CONFIG ?= $(shell command -v $(PKG_CONFIG) >/dev/null 2>&1 && echo true || echo false)
ifeq ($(HAS_PKG_CONFIG), true)
-CACHE_MK += HAS_PKG_CONFIG = true\n
-endif
-
-PC_TEMPLATE = prefix=$(prefix)\n\
-exec_prefix=${'\$${prefix}'}\n\
-includedir=${'\$${prefix}'}/include\n\
-libdir=${'\$${exec_prefix}'}/lib\n\
-\n\
-Name: $(PC_NAME)\n\
-Description: $(PC_DESCRIPTION)\n\
-Version: $(VERSION)\n\
-Cflags: -I${'\$${includedir}'} $(PC_CFLAGS)\n\
-Requires.private: $(PC_REQUIRES_PRIVATE)\n\
-Libs: -L${'\$${libdir}'} $(PC_LIB)\n\
+CACHE_MK += HAS_PKG_CONFIG = true,
+endif
+
+PC_TEMPLATE = prefix=$(prefix),\
+exec_prefix=${'\$${prefix}'},\
+includedir=${'\$${prefix}'}/include,\
+libdir=${'\$${exec_prefix}'}/lib,\
+,\
+Name: $(PC_NAME),\
+Description: $(PC_DESCRIPTION),\
+Version: $(VERSION),\
+Cflags: -I${'\$${includedir}'} $(PC_CFLAGS),\
+Requires.private: $(PC_REQUIRES_PRIVATE),\
+Libs: -L${'\$${libdir}'} $(PC_LIB),\
Libs.private: $(PC_LIBS_PRIVATE)
# gpr .pc file
@@ -442,7 +442,7 @@ HAS_SYSTEM_PERFTOOLS ?= $(shell $(PERFTOOLS_CHECK_CMD) 2> /dev/null && echo true
ifeq ($(HAS_SYSTEM_PERFTOOLS),true)
DEFINES += GRPC_HAVE_PERFTOOLS
LIBS += profiler
-CACHE_MK += HAS_SYSTEM_PERFTOOLS = true\n
+CACHE_MK += HAS_SYSTEM_PERFTOOLS = true,
endif
endif
@@ -451,20 +451,20 @@ ifndef REQUIRE_CUSTOM_LIBRARIES_$(CONFIG)
HAS_SYSTEM_OPENSSL_ALPN ?= $(shell $(OPENSSL_ALPN_CHECK_CMD) 2> /dev/null && echo true || echo false)
ifeq ($(HAS_SYSTEM_OPENSSL_ALPN),true)
HAS_SYSTEM_OPENSSL_NPN = true
-CACHE_MK += HAS_SYSTEM_OPENSSL_ALPN = true\n
+CACHE_MK += HAS_SYSTEM_OPENSSL_ALPN = true,
else
HAS_SYSTEM_OPENSSL_NPN ?= $(shell $(OPENSSL_NPN_CHECK_CMD) 2> /dev/null && echo true || echo false)
endif
ifeq ($(HAS_SYSTEM_OPENSSL_NPN),true)
-CACHE_MK += HAS_SYSTEM_OPENSSL_NPN = true\n
+CACHE_MK += HAS_SYSTEM_OPENSSL_NPN = true,
endif
HAS_SYSTEM_ZLIB ?= $(shell $(ZLIB_CHECK_CMD) 2> /dev/null && echo true || echo false)
ifeq ($(HAS_SYSTEM_ZLIB),true)
-CACHE_MK += HAS_SYSTEM_ZLIB = true\n
+CACHE_MK += HAS_SYSTEM_ZLIB = true,
endif
HAS_SYSTEM_PROTOBUF ?= $(HAS_SYSTEM_PROTOBUF_VERIFY)
ifeq ($(HAS_SYSTEM_PROTOBUF),true)
-CACHE_MK += HAS_SYSTEM_PROTOBUF = true\n
+CACHE_MK += HAS_SYSTEM_PROTOBUF = true,
endif
else
# override system libraries if the config requires a custom compiled library
@@ -476,10 +476,10 @@ endif
HAS_PROTOC ?= $(shell $(PROTOC_CHECK_CMD) 2> /dev/null && echo true || echo false)
ifeq ($(HAS_PROTOC),true)
-CACHE_MK += HAS_PROTOC = true\n
+CACHE_MK += HAS_PROTOC = true,
HAS_VALID_PROTOC ?= $(shell $(PROTOC_CHECK_VERSION_CMD) 2> /dev/null && echo true || echo false)
ifeq ($(HAS_VALID_PROTOC),true)
-CACHE_MK += HAS_VALID_PROTOC = true\n
+CACHE_MK += HAS_VALID_PROTOC = true,
endif
else
HAS_VALID_PROTOC = false
@@ -500,7 +500,7 @@ endif
endif
ifeq ($(HAS_SYSTEMTAP),true)
-CACHE_MK += HAS_SYSTEMTAP = true\n
+CACHE_MK += HAS_SYSTEMTAP = true,
endif
# Note that for testing purposes, one can do:
@@ -1108,32 +1108,32 @@ endif
cache.mk::
$(E) "[MAKE] Generating $@"
- $(Q) echo -e "$(CACHE_MK)" >$@
+ $(Q) echo "$(CACHE_MK)" | tr , '\n' >$@
$(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc:
$(E) "[MAKE] Generating $@"
$(Q) mkdir -p $(@D)
- $(Q) echo -e "$(GPR_PC_FILE)" >$@
+ $(Q) echo "$(GPR_PC_FILE)" | tr , '\n' >$@
$(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc:
$(E) "[MAKE] Generating $@"
$(Q) mkdir -p $(@D)
- $(Q) echo -e "$(GRPC_PC_FILE)" >$@
+ $(Q) echo "$(GRPC_PC_FILE)" | tr , '\n' >$@
$(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc:
$(E) "[MAKE] Generating $@"
$(Q) mkdir -p $(@D)
- $(Q) echo -e "$(GRPC_UNSECURE_PC_FILE)" >$@
+ $(Q) echo "$(GRPC_UNSECURE_PC_FILE)" | tr , '\n' >$@
$(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc:
$(E) "[MAKE] Generating $@"
$(Q) mkdir -p $(@D)
- $(Q) echo -e "$(GRPCXX_PC_FILE)" >$@
+ $(Q) echo "$(GRPCXX_PC_FILE)" | tr , '\n' >$@
$(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc:
$(E) "[MAKE] Generating $@"
$(Q) mkdir -p $(@D)
- $(Q) echo -e "$(GRPCXX_UNSECURE_PC_FILE)" >$@
+ $(Q) echo "$(GRPCXX_UNSECURE_PC_FILE)" | tr , '\n' >$@
% for p in protos:
ifeq ($(NO_PROTOC),true)
diff --git a/test/core/support/stack_lockfree_test.c b/test/core/support/stack_lockfree_test.c
index 6141f9811d..02ec3154d5 100644
--- a/test/core/support/stack_lockfree_test.c
+++ b/test/core/support/stack_lockfree_test.c
@@ -59,13 +59,13 @@ static void test_serial_sized(int size) {
GPR_ASSERT(gpr_stack_lockfree_pop(stack) == -1);
/* Now add repeatedly more items and check them */
- for (i=1; i<size; i*=2) {
+ for (i = 1; i < size; i *= 2) {
int j;
- for (j=0; j<=i; j++) {
- GPR_ASSERT(gpr_stack_lockfree_push(stack, j) == (j==0));
+ for (j = 0; j <= i; j++) {
+ GPR_ASSERT(gpr_stack_lockfree_push(stack, j) == (j == 0));
}
- for (j=0; j<=i; j++) {
- GPR_ASSERT(gpr_stack_lockfree_pop(stack) == i-j);
+ for (j = 0; j <= i; j++) {
+ GPR_ASSERT(gpr_stack_lockfree_pop(stack) == i - j);
}
GPR_ASSERT(gpr_stack_lockfree_pop(stack) == -1);
}
@@ -75,7 +75,7 @@ static void test_serial_sized(int size) {
static void test_serial() {
int i;
- for (i=128; i<MAX_STACK_SIZE; i*=2) {
+ for (i = 128; i < MAX_STACK_SIZE; i *= 2) {
test_serial_sized(i);
}
test_serial_sized(MAX_STACK_SIZE);
@@ -94,9 +94,9 @@ static void test_mt_body(void *v) {
int lo, hi;
int i;
int res;
- lo = arg->rank*arg->stack_size/arg->nthreads;
- hi = (arg->rank+1)*arg->stack_size/arg->nthreads;
- for (i=lo; i<hi; i++) {
+ lo = arg->rank * arg->stack_size / arg->nthreads;
+ hi = (arg->rank + 1) * arg->stack_size / arg->nthreads;
+ for (i = lo; i < hi; i++) {
gpr_stack_lockfree_push(arg->stack, i);
if ((res = gpr_stack_lockfree_pop(arg->stack)) != -1) {
arg->sum += res;
@@ -116,7 +116,7 @@ static void test_mt_sized(int size, int nth) {
gpr_thd_options options = gpr_thd_options_default();
stack = gpr_stack_lockfree_create(size);
- for (i=0; i<nth; i++) {
+ for (i = 0; i < nth; i++) {
args[i].stack = stack;
args[i].stack_size = size;
args[i].nthreads = nth;
@@ -132,17 +132,17 @@ static void test_mt_sized(int size, int nth) {
gpr_thd_join(thds[i]);
sum = sum + args[i].sum;
}
- GPR_ASSERT((unsigned)sum == ((unsigned)size*(size-1))/2);
+ GPR_ASSERT((unsigned)sum == ((unsigned)size * (size - 1)) / 2);
gpr_stack_lockfree_destroy(stack);
}
static void test_mt() {
int size, nth;
- for (nth=1; nth < MAX_THREADS; nth++) {
- for (size=128; size < MAX_STACK_SIZE; size*=2) {
- test_mt_sized(size,nth);
+ for (nth = 1; nth < MAX_THREADS; nth++) {
+ for (size = 128; size < MAX_STACK_SIZE; size *= 2) {
+ test_mt_sized(size, nth);
}
- test_mt_sized(MAX_STACK_SIZE,nth);
+ test_mt_sized(MAX_STACK_SIZE, nth);
}
}
diff --git a/test/core/util/test_config.c b/test/core/util/test_config.c
index 20ab67ec15..225658f5e2 100644
--- a/test/core/util/test_config.c
+++ b/test/core/util/test_config.c
@@ -48,7 +48,45 @@ static int seed(void) { return getpid(); }
static int seed(void) { return _getpid(); }
#endif
+#if GPR_WINDOWS_CRASH_HANDLER
+LONG crash_handler(struct _EXCEPTION_POINTERS* ex_info) {
+ gpr_log(GPR_DEBUG, "Exception handler called, dumping information");
+ while (ex_info->ExceptionRecord) {
+ DWORD code = ex_info->ExceptionRecord->ExceptionCode;
+ DWORD flgs = ex_info->ExceptionRecord->ExceptionFlags;
+ PVOID addr = ex_info->ExceptionRecord->ExceptionAddress;
+ gpr_log("code: %x - flags: %d - address: %p", code, flgs, addr);
+ ex_info->ExceptionRecord = ex_info->ExceptionRecord->ExceptionRecord;
+ }
+ if (IsDebuggerPresent()) {
+ __debugbreak();
+ } else {
+ _exit(1);
+ }
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+void abort_handler(int sig) {
+ gpr_log(GPR_DEBUG, "Abort handler called.");
+ if (IsDebuggerPresent()) {
+ __debugbreak();
+ } else {
+ _exit(1);
+ }
+}
+
+static void install_crash_handler() {
+ SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) crash_handler);
+ _set_abort_behavior(0, _WRITE_ABORT_MSG);
+ _set_abort_behavior(0, _CALL_REPORTFAULT);
+ signal(SIGABRT, abort_handler);
+}
+#else
+static void install_crash_handler() { }
+#endif
+
void grpc_test_init(int argc, char **argv) {
+ install_crash_handler();
gpr_log(GPR_DEBUG, "test slowdown: machine=%f build=%f total=%f",
(double)GRPC_TEST_SLOWDOWN_MACHINE_FACTOR,
(double)GRPC_TEST_SLOWDOWN_BUILD_FACTOR,
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index f0d9f75214..207dad5282 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -98,8 +98,9 @@ void CheckAuthContext(T* context) {
class TestServiceImpl : public ::grpc::cpp::test::util::TestService::Service {
public:
- TestServiceImpl() : signal_client_(false), host_(nullptr) {}
- explicit TestServiceImpl(const grpc::string& host) : signal_client_(false), host_(new grpc::string(host)) {}
+ TestServiceImpl() : signal_client_(false), host_() {}
+ explicit TestServiceImpl(const grpc::string& host)
+ : signal_client_(false), host_(new grpc::string(host)) {}
Status Echo(ServerContext* context, const EchoRequest* request,
EchoResponse* response) GRPC_OVERRIDE {
@@ -224,7 +225,8 @@ class TestServiceImplDupPkg
class End2endTest : public ::testing::Test {
protected:
- End2endTest() : kMaxMessageSize_(8192), special_service_("special"), thread_pool_(2) {}
+ End2endTest()
+ : kMaxMessageSize_(8192), special_service_("special"), thread_pool_(2) {}
void SetUp() GRPC_OVERRIDE {
int port = grpc_pick_unused_port_or_die();
diff --git a/test/cpp/end2end/mock_test.cc b/test/cpp/end2end/mock_test.cc
index 2809ab8d3c..59a8edf509 100644
--- a/test/cpp/end2end/mock_test.cc
+++ b/test/cpp/end2end/mock_test.cc
@@ -86,7 +86,9 @@ class MockClientReaderWriter<EchoRequest, EchoResponse> GRPC_FINAL
msg->set_message(last_message_);
return true;
}
- bool Write(const EchoRequest& msg) GRPC_OVERRIDE {
+
+ bool Write(const EchoRequest& msg,
+ const WriteOptions& options) GRPC_OVERRIDE {
gpr_log(GPR_INFO, "mock recv msg %s", msg.message().c_str());
last_message_ = msg.message();
return true;
diff --git a/tools/buildgen/generate_projects.py b/tools/buildgen/generate_projects.py
index 95d338652b..5de06354d1 100755
--- a/tools/buildgen/generate_projects.py
+++ b/tools/buildgen/generate_projects.py
@@ -50,7 +50,10 @@ jobs = []
for root, dirs, files in os.walk('templates'):
for f in files:
if os.path.splitext(f)[1] == '.template':
- out = '.' + root[len('templates'):] + '/' + os.path.splitext(f)[0]
+ out_dir = '.' + root[len('templates'):]
+ out = out_dir + '/' + os.path.splitext(f)[0]
+ if not os.path.exists(out_dir):
+ os.makedirs(out_dir)
cmd = ['tools/buildgen/mako_renderer.py']
for plugin in plugins:
cmd.append('-p')
diff --git a/tools/jenkins/docker_run_jenkins.sh b/tools/jenkins/docker_run_jenkins.sh
index 90107169ba..eb6c9144c6 100755
--- a/tools/jenkins/docker_run_jenkins.sh
+++ b/tools/jenkins/docker_run_jenkins.sh
@@ -42,4 +42,4 @@ cd /var/local/git/grpc
nvm use 0.12
rvm use ruby-2.1
tools/run_tests/prepare_travis.sh
-tools/run_tests/run_tests.py -t -c $config -l $language -x report.xml
+$arch tools/run_tests/run_tests.py -t -c $config -l $language -x report.xml
diff --git a/tools/jenkins/run_jenkins.sh b/tools/jenkins/run_jenkins.sh
index af49ea0bd0..dcd0424aec 100755
--- a/tools/jenkins/run_jenkins.sh
+++ b/tools/jenkins/run_jenkins.sh
@@ -37,6 +37,16 @@
# NOTE: No empty lines should appear in this file before igncr is set!
set -ex -o igncr || set -ex
+# Grabbing the machine's architecture
+arch=`uname -m`
+
+case $platform in
+ i386)
+ arch="i386"
+ platform="linux"
+ ;;
+esac
+
if [ "$platform" == "linux" ]
then
echo "building $language on Linux"
@@ -61,6 +71,7 @@ then
docker run \
-e "config=$config" \
-e "language=$language" \
+ -e "arch=$arch" \
-i \
-v "$git_root:/var/local/jenkins/grpc" \
--cidfile=docker.cid \
diff --git a/tools/run_tests/jobset.py b/tools/run_tests/jobset.py
index b9af169739..46137f01f4 100755
--- a/tools/run_tests/jobset.py
+++ b/tools/run_tests/jobset.py
@@ -209,10 +209,11 @@ class Job(object):
elif self._state == _RUNNING and time.time() - self._start > 600:
self._tempfile.seek(0)
stdout = self._tempfile.read()
+ filtered_stdout = filter(lambda x: x in string.printable, stdout.decode(errors='ignore'))
message('TIMEOUT', self._spec.shortname, stdout, do_newline=True)
self.kill()
if self._xml_test is not None:
- ET.SubElement(self._xml_test, 'system-out').text = stdout
+ ET.SubElement(self._xml_test, 'system-out').text = filtered_stdout
ET.SubElement(self._xml_test, 'error', message='Timeout')
return self._state
diff --git a/tools/run_tests/python_tests.json b/tools/run_tests/python_tests.json
index 77473db2ed..3d75d8de36 100755
--- a/tools/run_tests/python_tests.json
+++ b/tools/run_tests/python_tests.json
@@ -48,6 +48,18 @@
]
},
{
+ "module": "grpc._links._lonely_invocation_link_test",
+ "pythonVersions": [
+ "2.7"
+ ]
+ },
+ {
+ "module": "grpc._links._transmission_test",
+ "pythonVersions": [
+ "2.7"
+ ]
+ },
+ {
"module": "grpc.early_adopter.implementations_test",
"pythonVersions": [
"2.7"